3
$\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$
4
$\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.

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 Arc"
    bl_options = {'REGISTER', 'UNDO'}

    length: FloatProperty(
        name="length",
        description="Chord Length",
        min=0.01,
        max=100.0,
        default=2.0,
    )
    height: FloatProperty(
        name="Height",
        description="Arc Height",
        min=0.01,
        max=100.0,
        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$
  • $\begingroup$ Works amazingly. Thanks! Should definitely be added to the bundled "Extra Objects" add-on. $\endgroup$ – BlenderBro Mar 9 at 22:09
0
$\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
    $\begingroup$ For the purposes of this question I'm interested in creating mathematically accurate arcs as opposed to eyeballing approximations. $\endgroup$ – BlenderBro Mar 9 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$ – Lukasz-40sth Mar 9 at 13:18

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.