10
$\begingroup$

Given 3 vertices, or a chord and height, how can I create a mathematically correct arc/circular segment with an even distribution of vertices while controlling for the number of vertices.

diagram of an arc

I frequently need to model arcs in Blender from real-world measurements. Typically I know the cord and height of the arc, giving me 3 points on the full circle.

To create a mathematically correct arc, controlling for the number of vertices, my workflow is as follows:

  1. Plug the coordinates of the three vertices into a digital graphics calculator.
  2. Retrieve the location of the centre of the full circle, and the angle between the vertices at either end of the cord.
  3. Place the cursor at the centre of the full circle in Blender by editing the 3D cursor coordinates.
  4. Select one vertex on the chord and use the spin tool, manually entering in the angle retrieved from the graphics calculator and the number of desired vertices.

While this produces an accurate result it is a rather tedious process. How can I achieve this same result using a faster workflow?

$\endgroup$
14
$\begingroup$

Add Primitive Arc Operator

enter image description here

The theory is well covered here Calculate the radius of a circle given the chord length and height of a segment

The text editor > Templates > Python > Operator Add Mesh template modified to add an arc.

Input the arc length, arc height and number of segments and it creates an arc.

Notes. Haven't dealt with the restriction that arc height can only ever by at most half chord length for a semi circle.

bl_info = {
    "name": "Circular Arc",
    "author": "batFINGER",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "View3D > Add > Mesh > Circular Arc",
    "description": "Adds a mesh circumcircle given base and height",
    "warning": "",
    "doc_url": "",
    "category": "Add Mesh",
}


import bpy
import bmesh
from mathutils import Matrix
from math import asin
from bpy_extras.object_utils import AddObjectHelper

from bpy.props import (
    IntProperty,
    BoolProperty,
    FloatProperty,
)


class MESH_OT_primitive_arc_add(AddObjectHelper, bpy.types.Operator):
    """Add a simple arc mesh"""
    bl_idname = "mesh.primitive_arc_add"
    bl_label = "Add Circular Arc"
    bl_options = {'REGISTER', 'UNDO'}

    length: FloatProperty(
        name="length",
        description="Chord Length",
        min=0.01,
        max=100.0,
        default=2.0,
        unit='LENGTH',
    )
    height: FloatProperty(
        name="Height",
        description="Arc Height",
        min=0.01,
        max=100.0,
        unit='LENGTH',
        default=1.0,
    )
    segments: IntProperty(
        name="Arc Segments",
        description="Number of Segments",
        min=1,
        default=8,
    )

    def draw(self, context):
        '''Generic Draw'''
        layout = self.layout
        # annnotated on this class
        for prop in self.__class__.__annotations__.keys():
            layout.prop(self, prop)
        # annotated on AddObjectHelper
        for prop in AddObjectHelper.__annotations__.keys():
            layout.prop(self, prop)


    def execute(self, context):
        h = self.height
        a = self.length / 2
        r = (a * a + h * h) / (2 * h)
        if abs(a / r) > 1:
            # math domain error on arcsin
            return {'CANCELLED'}
        angle = 2 * asin(a / r)

        mesh = bpy.data.meshes.new("Arc")

        bm = bmesh.new()
        v = bm.verts.new((0, r, 0))
        bmesh.ops.rotate(
            bm, verts=[v], matrix=Matrix.Rotation(angle / 2, 3, 'Z'))
        bmesh.ops.spin(
            bm,
            geom=[v],
            axis=(0, 0, 1),
            steps=self.segments,
            angle=-angle,
        )

        for v in bm.verts:
            v.co.y -= r - h
            v.select = True
        bm.to_mesh(mesh)
        #mesh.update()

        # add the mesh as an object into the scene with this utility module
        from bpy_extras import object_utils
        object_utils.object_data_add(context, mesh, operator=self)

        return {'FINISHED'}


def menu_func(self, context):
    self.layout.operator(MESH_OT_primitive_arc_add.bl_idname, icon='MESH_CUBE')


def register():
    bpy.utils.register_class(MESH_OT_primitive_arc_add)
    bpy.types.VIEW3D_MT_mesh_add.append(menu_func)


def unregister():
    bpy.utils.unregister_class(MESH_OT_primitive_arc_add)
    bpy.types.VIEW3D_MT_mesh_add.remove(menu_func)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.mesh.primitive_arc_add()
$\endgroup$
5
  • 4
    $\begingroup$ Works amazingly. Thanks! Should definitely be added to the bundled "Extra Objects" add-on. $\endgroup$
    – BlenderBro
    Mar 9 '19 at 22:09
  • 1
    $\begingroup$ Maybe, in the spirit of this add-on, make an add-on to place the 3D cursor at the circumcenter (sorry, circumcentre) of 3 objects, or 3 vertices? .. it would be very useful.. maybe it exists somewhere else that doesn't involve hauling in too much clobber with it, I don't know. $\endgroup$ Jun 25 '20 at 8:37
  • 2
    $\begingroup$ Similar to ?? Fixed above to install as addon and put length in units. Had a look at getting a 0.5 chord to bay a window gap (in edit mode) and appears there is some miscalc. Adding a chord of edges between two verts could also be considered I suppose. Missed with every bullet so far Robin. Noticed a couple of questions re stools that would be right down your alley.. I had so many, small mercy I went for extruding solids over any mention of fluids. $\endgroup$
    – batFINGER
    Jun 25 '20 at 9:35
  • 1
    $\begingroup$ @RobinBetts (Sure I have done something with this on here before, cannot find) Anyhoo Using en.wikipedia.org/wiki/… script to add circumcircle Run with 3 objects are selected. pasteall.org/M4cX $\endgroup$
    – batFINGER
    Jun 25 '20 at 11:29
  • 1
    $\begingroup$ @batFINGER I thought you had, too.. didn't think it would be much work for you. There was I, chugging away, out of interest, seeing what I could do myself. Actually chose the same route as you.. not too bad.. but then received this lesson in bpy, and idiomatic Python :D. $\endgroup$ Jun 25 '20 at 13:31
2
$\begingroup$
  1. Insert a plane with the edge length matching your chord's length;
  2. Remove the plane's two opposite vertices to have a segment;
  3. Subdivide the segment the odd number of times;
  4. Use proportional editing with the circular fall-off, the middle vertex selected, and move everything along Z axis to the desired height.

You may also join the opposite ends of your arc creating an edge if needed.

$\endgroup$
2
  • 2
    $\begingroup$ For the purposes of this question I'm interested in creating mathematically accurate arcs as opposed to eyeballing approximations. $\endgroup$
    – BlenderBro
    Mar 9 '19 at 12:41
  • $\begingroup$ With arcs and circles it's always an approximation as there's this pi number. And if you input given values, approximated as well, exactly you get the result. $\endgroup$ Mar 9 '19 at 13:18
1
$\begingroup$

The shipped 'TinyCAD' add-on has a CCEN: Resurrect Circle Center operator.

Given 3 vertices, it will find their circumcenter, and put the 3D cursor there. You can either use the circle the operator constructs, or use the Spin tool to draw an arc from one of the vertices.

$\endgroup$
0
$\begingroup$

If you can construct perpendicular bisectors, you can do the classic compass and straightedge construction for a circumcenter - take the perpendicular bisector of your chord, add the height to find the third point in your triangle, then take the perpendicular bisector of one of the other chords. Those two intersect at the circumcenter.

Unfortunately this will not let you do arbitrary partitions of the angle.

$\endgroup$
5
  • $\begingroup$ The trouble is, it's not easy to get a fix on the intersection of the bisectors? Unless you're using something like TinyCAD, and if you are, you may as well use it to do the job... $\endgroup$ Jan 11 at 15:51
  • $\begingroup$ @RobinBetts You can use booleans to find exact intersections-- well, exact to the limits of floating point math. Here, it adds to the unwieldiness of the solution, but there are times that it can be used for scriptless, dynamic solutions. $\endgroup$
    – Nathan
    Jan 11 at 16:08
  • $\begingroup$ @Nathan I only asked because I'd really like this to work, it would be nice to do this kind of construction.. I've had a few goes.. I'm struggling, without tinyCAD. Maybe it's me. I'll ttry again after giving it a rest. $\endgroup$ Jan 11 at 16:54
  • 2
    $\begingroup$ @RobinBetts Then check out pasteall.org/blend/2728e96c87674af8805556a6a334124a . I assume proper placement of the three points from the empties, use a rig to create perpendicular bisectors, and then do a live boolean to find the point of intersection. I didn't bother limiting the intersection to the plane containing the circle, but I think that should be obvious enough from what I implemented. $\endgroup$
    – Nathan
    Jan 11 at 17:24
  • $\begingroup$ @Nathan WOW! That's a lot of ingenuity packed into one file :D I _think _ I get it.. will take more time to decypher $\endgroup$ Jan 11 at 17:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.