How can I create a mathematically correct arc/circular segment? 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.



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:


Plug the coordinates of the three vertices into a digital graphics calculator.
Retrieve the location of the centre of the full circle, and the angle between the vertices at either end of the cord.
Place the cursor at the centre of the full circle in Blender by editing the 3D cursor coordinates.
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?
Add Primitive Arc Operator

The theory is well covered here Calculate the radius of a circle given the chord length and height of a segment
The text editor &gt; Templates &gt; Python &gt; 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 = {
    &quot;name&quot;: &quot;Circular Arc&quot;,
    &quot;author&quot;: &quot;batFINGER&quot;,
    &quot;version&quot;: (1, 0),
    &quot;blender&quot;: (2, 80, 0),
    &quot;location&quot;: &quot;View3D &gt; Add &gt; Mesh &gt; Circular Arc&quot;,
    &quot;description&quot;: &quot;Adds a mesh circumcircle given base and height&quot;,
    &quot;warning&quot;: &quot;&quot;,
    &quot;doc_url&quot;: &quot;&quot;,
    &quot;category&quot;: &quot;Add Mesh&quot;,
}


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):
    &quot;&quot;&quot;Add a simple arc mesh&quot;&quot;&quot;
    bl_idname = &quot;mesh.primitive_arc_add&quot;
    bl_label = &quot;Add Circular Arc&quot;
    bl_options = {'REGISTER', 'UNDO'}

    length: FloatProperty(
        name=&quot;length&quot;,
        description=&quot;Chord Length&quot;,
        min=0.01,
        max=100.0,
        default=2.0,
        unit='LENGTH',
    )
    height: FloatProperty(
        name=&quot;Height&quot;,
        description=&quot;Arc Height&quot;,
        min=0.01,
        max=100.0,
        unit='LENGTH',
        default=1.0,
    )
    segments: IntProperty(
        name=&quot;Arc Segments&quot;,
        description=&quot;Number of Segments&quot;,
        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) &gt; 1:
            # math domain error on arcsin
            return {'CANCELLED'}
        angle = 2 * asin(a / r)

        mesh = bpy.data.meshes.new(&quot;Arc&quot;)

        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__ == &quot;__main__&quot;:
    register()

    # test call
    bpy.ops.mesh.primitive_arc_add()