Skip to content
Snippets Groups Projects
add_curve_simple.py 51.2 KiB
Newer Older
  • Learn to ignore specific revisions
  •             min=0.0, soft_min=0.0,
                unit='LENGTH',
                description="Length"
                )
    
        Simple_rounded : FloatProperty(
    
                name="Rounded",
                default=0.0,
                min=0.0, soft_min=0.0,
                unit='LENGTH',
                description="Rounded corners"
                )
    
        # Curve Options
        shapeItems = [
    
            ('2D', "2D", "2D shape Curve"),
            ('3D', "3D", "3D shape Curve")]
    
        shape : EnumProperty(
    
                name="2D / 3D",
                items=shapeItems,
                description="2D or 3D Curve"
                )
    
        outputType : EnumProperty(
                name="Output splines",
                description="Type of splines to output",
                items=[
                ('POLY', "Poly", "Poly Spline type"),
                ('NURBS', "Nurbs", "Nurbs Spline type"),
                ('BEZIER', "Bezier", "Bezier Spline type")],
                default='BEZIER'
                )
        use_cyclic_u : BoolProperty(
                name="Cyclic",
                default=True,
                description="make curve closed"
                )
        endp_u : BoolProperty(
                name="Use endpoint u",
                default=True,
                description="stretch to endpoints"
                )
        order_u : IntProperty(
                name="Order u",
                default=4,
                min=2, soft_min=2,
                max=6, soft_max=6,
                description="Order of nurbs spline"
                )
        handleType : EnumProperty(
                name="Handle type",
                default='VECTOR',
                description="Bezier handles type",
                items=[
                ('VECTOR', "Vector", "Vector type Bezier handles"),
                ('AUTO', "Auto", "Automatic type Bezier handles")]
                )
    
    
        def draw(self, context):
            layout = self.layout
    
            # general options
            col = layout.column()
    
            col.prop(self, "Simple_Type")
    
    
            l = 0
            s = 0
    
            if self.Simple_Type == 'Line':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_endlocation")
    
                v = Vector(self.Simple_endlocation) - Vector(self.Simple_startlocation)
                l = v.length
    
            if self.Simple_Type == 'Distance':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_length")
                col.prop(self, "Simple_center")
    
                l = self.Simple_length
    
            if self.Simple_Type == 'Angle':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_length")
                col.prop(self, "Simple_angle")
    
    
                #row = layout.row()
                #row.prop(self, "Simple_degrees_or_radians", expand=True)
    
    
            if self.Simple_Type == 'Circle':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_sides")
                col.prop(self, "Simple_radius")
    
    
                l = 2 * pi * abs(self.Simple_radius)
                s = pi * self.Simple_radius * self.Simple_radius
    
            if self.Simple_Type == 'Ellipse':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_a", text="Radius a")
                col.prop(self, "Simple_b", text="Radius b")
    
                l = pi * (3 * (self.Simple_a + self.Simple_b) -
                              sqrt((3 * self.Simple_a + self.Simple_b) *
                              (self.Simple_a + 3 * self.Simple_b)))
    
    
                s = pi * abs(self.Simple_b) * abs(self.Simple_a)
    
            if self.Simple_Type == 'Arc':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_sides")
                col.prop(self, "Simple_radius")
    
                col = box.column(align=True)
                col.prop(self, "Simple_startangle")
                col.prop(self, "Simple_endangle")
    
                #row = layout.row()
                #row.prop(self, "Simple_degrees_or_radians", expand=True)
    
                l = abs(pi * self.Simple_radius * (self.Simple_endangle - self.Simple_startangle) / 180)
    
            if self.Simple_Type == 'Sector':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_sides")
                col.prop(self, "Simple_radius")
    
                col = box.column(align=True)
                col.prop(self, "Simple_startangle")
                col.prop(self, "Simple_endangle")
    
                #row = layout.row()
                #row.prop(self, "Simple_degrees_or_radians", expand=True)
    
    
                l = abs(pi * self.Simple_radius *
                       (self.Simple_endangle - self.Simple_startangle) / 180) + self.Simple_radius * 2
    
                s = pi * self.Simple_radius * self.Simple_radius * \
                    abs(self.Simple_endangle - self.Simple_startangle) / 360
    
    
            if self.Simple_Type == 'Segment':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_sides")
                col.prop(self, "Simple_a", text="Radius a")
                col.prop(self, "Simple_b", text="Radius b")
    
                col = box.column(align=True)
                col.prop(self, "Simple_startangle")
                col.prop(self, "Simple_endangle")
    
    
                #row = layout.row()
                #row.prop(self, "Simple_degrees_or_radians", expand=True)
    
                la = abs(pi * self.Simple_a * (self.Simple_endangle - self.Simple_startangle) / 180)
                lb = abs(pi * self.Simple_b * (self.Simple_endangle - self.Simple_startangle) / 180)
                l = abs(self.Simple_a - self.Simple_b) * 2 + la + lb
    
    
                sa = pi * self.Simple_a * self.Simple_a * \
                    abs(self.Simple_endangle - self.Simple_startangle) / 360
    
                sb = pi * self.Simple_b * self.Simple_b * \
                    abs(self.Simple_endangle - self.Simple_startangle) / 360
    
    
                s = abs(sa - sb)
    
            if self.Simple_Type == 'Rectangle':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_width")
                col.prop(self, "Simple_length")
                col.prop(self, "Simple_rounded")
    
                box.prop(self, "Simple_center")
    
                l = 2 * abs(self.Simple_width) + 2 * abs(self.Simple_length)
                s = abs(self.Simple_width) * abs(self.Simple_length)
    
            if self.Simple_Type == 'Rhomb':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_width")
                col.prop(self, "Simple_length")
                col.prop(self, "Simple_center")
    
    
                g = hypot(self.Simple_width / 2, self.Simple_length / 2)
                l = 4 * g
                s = self.Simple_width * self.Simple_length / 2
    
            if self.Simple_Type == 'Polygon':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_sides")
                col.prop(self, "Simple_radius")
    
    
            if self.Simple_Type == 'Polygon_ab':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text="Polygon ab Options:")
                col.prop(self, "Simple_sides")
                col.prop(self, "Simple_a")
                col.prop(self, "Simple_b")
    
    
            if self.Simple_Type == 'Trapezoid':
                box = layout.box()
    
                col = box.column(align=True)
                col.label(text=self.Simple_Type + " Options:")
                col.prop(self, "Simple_a")
                col.prop(self, "Simple_b")
                col.prop(self, "Simple_h")
    
                box.prop(self, "Simple_center")
    
                g = hypot(self.Simple_h, (self.Simple_a - self.Simple_b) / 2)
                l = self.Simple_a + self.Simple_b + g * 2
                s = (abs(self.Simple_a) + abs(self.Simple_b)) / 2 * self.Simple_h
    
            row = layout.row()
    
            row.prop(self, "shape", expand=True)
    
            
            # output options
            col = layout.column()
            col.label(text="Output Curve Type:")
            col.row().prop(self, "outputType", expand=True)
            
            if self.outputType == 'NURBS':
                col.prop(self, "order_u")
            elif self.outputType == 'BEZIER':
                col.row().prop(self, 'handleType', expand=True)
    
            col = layout.column()
            col.row().prop(self, "use_cyclic_u", expand=True)
            
    
            box = layout.box()
    
            box.label(text="Location:")
    
            box.prop(self, "Simple_startlocation")
    
            box = layout.box()
    
            box.label(text="Rotation:")
    
            box.prop(self, "Simple_rotation_euler")
    
            if l != 0 or s != 0:
                box = layout.box()
                box.label(text="Statistics:", icon="INFO")
    
            if l != 0:
                l_str = str(round(l, 4))
    
                box.label(text="Length: " + l_str)
    
            if s != 0:
                s_str = str(round(s, 4))
    
                box.label(text="Area: " + s_str)
    
    
        @classmethod
        def poll(cls, context):
    
            return context.scene is not None
    
    
        def execute(self, context):
            # main function
            self.align_matrix = align_matrix(context, self.Simple_startlocation)
            main(context, self, self.align_matrix)
    
            return {'FINISHED'}
    
    # ------------------------------------------------------------
    # Fillet
    
    
    class BezierPointsFillet(Operator):
    
        bl_idname = "curve.bezier_points_fillet"
    
        bl_label = "Bezier points Fillet"
        bl_description = "Bezier points Fillet"
    
        bl_options = {'REGISTER', 'UNDO', 'PRESET'}
    
        Fillet_radius : FloatProperty(
    
                name="Radius",
                default=0.25,
                unit='LENGTH',
                description="Radius"
                )
        Types = [('Round', "Round", "Round"),
                 ('Chamfer', "Chamfer", "Chamfer")]
    
        Fillet_Type : EnumProperty(
    
                name="Type",
                description="Fillet type",
                items=Types
                )
    
    
        def draw(self, context):
            layout = self.layout
    
            # general options
            col = layout.column()
    
            col.prop(self, "Fillet_radius")
            col.prop(self, "Fillet_Type", expand=True)
    
    
        @classmethod
        def poll(cls, context):
    
            return context.scene is not None
    
    
        def execute(self, context):
            # main function
    
            if bpy.ops.object.mode_set.poll():
                bpy.ops.object.mode_set(mode='EDIT')
            
    
            spline = bpy.context.object.data.splines.active
    
            bpy.ops.curve.spline_type_set(type='BEZIER')
            
    
            bpy.ops.curve.handle_type_set(type='VECTOR')
    
            n = 0
            ii = []
            for p in spline.bezier_points:
                if p.select_control_point:
                    ii.append(n)
                    n += 1
                else:
                    n += 1
    
            if n > 2:
                jn = 0
                for j in ii:
    
                    j += jn
    
                    selected_all = [p for p in spline.bezier_points]
    
                    bpy.ops.curve.select_all(action='DESELECT')
    
                    if j != 0 and j != n - 1:
                        selected_all[j].select_control_point = True
                        selected_all[j + 1].select_control_point = True
                        bpy.ops.curve.subdivide()
                        selected_all = [p for p in spline.bezier_points]
    
                        selected4 = [selected_all[j - 1], selected_all[j],
                                     selected_all[j + 1], selected_all[j + 2]]
    
                        jn += 1
                        n += 1
    
                    elif j == 0:
                        selected_all[j].select_control_point = True
                        selected_all[j + 1].select_control_point = True
                        bpy.ops.curve.subdivide()
                        selected_all = [p for p in spline.bezier_points]
    
                        selected4 = [selected_all[n], selected_all[0],
                                     selected_all[1], selected_all[2]]
    
                        jn += 1
                        n += 1
    
                    elif j == n - 1:
                        selected_all[j].select_control_point = True
                        selected_all[j - 1].select_control_point = True
                        bpy.ops.curve.subdivide()
                        selected_all = [p for p in spline.bezier_points]
    
                        selected4 = [selected_all[0], selected_all[n],
                                     selected_all[n - 1], selected_all[n - 2]]
    
    
                    selected4[2].co = selected4[1].co
                    s1 = Vector(selected4[0].co) - Vector(selected4[1].co)
                    s2 = Vector(selected4[3].co) - Vector(selected4[2].co)
                    s1.normalize()
                    s11 = Vector(selected4[1].co) + s1 * self.Fillet_radius
                    selected4[1].co = s11
                    s2.normalize()
                    s22 = Vector(selected4[2].co) + s2 * self.Fillet_radius
                    selected4[2].co = s22
    
                    if self.Fillet_Type == 'Round':
                        if j != n - 1:
                            selected4[2].handle_right_type = 'VECTOR'
                            selected4[1].handle_left_type = 'VECTOR'
                            selected4[1].handle_right_type = 'ALIGNED'
                            selected4[2].handle_left_type = 'ALIGNED'
                        else:
                            selected4[1].handle_right_type = 'VECTOR'
                            selected4[2].handle_left_type = 'VECTOR'
                            selected4[2].handle_right_type = 'ALIGNED'
                            selected4[1].handle_left_type = 'ALIGNED'
                    if self.Fillet_Type == 'Chamfer':
                        selected4[2].handle_right_type = 'VECTOR'
                        selected4[1].handle_left_type = 'VECTOR'
                        selected4[1].handle_right_type = 'VECTOR'
                        selected4[2].handle_left_type = 'VECTOR'
    
            return {'FINISHED'}
    
    def subdivide_cubic_bezier(p1, p2, p3, p4, t):
        p12 = (p2 - p1) * t + p1
        p23 = (p3 - p2) * t + p2
        p34 = (p4 - p3) * t + p3
        p123 = (p23 - p12) * t + p12
        p234 = (p34 - p23) * t + p23
        p1234 = (p234 - p123) * t + p123
        return [p12, p123, p1234, p234, p34]
    
    
    # ------------------------------------------------------------
    # BezierDivide Operator
    
    
    class BezierDivide(Operator):
    
        bl_idname = "curve.bezier_spline_divide"
    
        bl_label = "Bezier Spline Divide"
        bl_description = "Bezier Divide (enters edit mode) for Fillet Curves"
    
        bl_options = {'REGISTER', 'UNDO'}
    
        # align_matrix for the invoke
    
        align_matrix : Matrix()
    
        Bezier_t : FloatProperty(
    
                name="t (0% - 100%)",
                default=50.0,
                min=0.0, soft_min=0.0,
                max=100.0, soft_max=100.0,
                description="t (0% - 100%)"
                )
    
    
        @classmethod
        def poll(cls, context):
    
            return context.scene is not None
    
    
        def execute(self, context):
            # main function
    
            if bpy.ops.object.mode_set.poll():
                bpy.ops.object.mode_set(mode='EDIT')
    
    
            spline = bpy.context.object.data.splines.active
    
            bpy.ops.curve.spline_type_set(type='BEZIER')
            
    
            ii = []
            for p in spline.bezier_points:
                if p.select_control_point:
                    ii.append(n)
                    n += 1
                else:
                    n += 1
    
            if n > 2:
                jn = 0
                for j in ii:
    
                    selected_all = [p for p in spline.bezier_points]
    
                    bpy.ops.curve.select_all(action='DESELECT')
    
                    if (j in ii) and (j + 1 in ii):
                        selected_all[j + jn].select_control_point = True
                        selected_all[j + 1 + jn].select_control_point = True
                        h = subdivide_cubic_bezier(
                            selected_all[j + jn].co, selected_all[j + jn].handle_right,
                            selected_all[j + 1 + jn].handle_left, selected_all[j + 1 + jn].co, self.Bezier_t / 100
                            )
                        bpy.ops.curve.subdivide(1)
                        selected_all = [p for p in spline.bezier_points]
                        selected_all[j + jn].handle_right_type = 'FREE'
                        selected_all[j + jn].handle_right = h[0]
                        selected_all[j + 1 + jn].co = h[2]
                        selected_all[j + 1 + jn].handle_left_type = 'FREE'
                        selected_all[j + 1 + jn].handle_left = h[1]
                        selected_all[j + 1 + jn].handle_right_type = 'FREE'
                        selected_all[j + 1 + jn].handle_right = h[3]
                        selected_all[j + 2 + jn].handle_left_type = 'FREE'
                        selected_all[j + 2 + jn].handle_left = h[4]
                        jn += 1
                    
                    if j == n - 1 and (0 in ii) and spline.use_cyclic_u:
                        selected_all[j + jn].select_control_point = True
                        selected_all[0].select_control_point = True
                        h = subdivide_cubic_bezier(
                            selected_all[j + jn].co, selected_all[j + jn].handle_right,
                            selected_all[0].handle_left, selected_all[0].co, self.Bezier_t / 100
                            )
                        bpy.ops.curve.subdivide(1)
                        selected_all = [p for p in spline.bezier_points]
                        selected_all[j + jn].handle_right_type = 'FREE'
                        selected_all[j + jn].handle_right = h[0]
                        selected_all[j + 1 + jn].co = h[2]
                        selected_all[j + 1 + jn].handle_left_type = 'FREE'
                        selected_all[j + 1 + jn].handle_left = h[1]
                        selected_all[j + 1 + jn].handle_right_type = 'FREE'
                        selected_all[j + 1 + jn].handle_right = h[3]
                        selected_all[0].handle_left_type = 'FREE'
                        selected_all[0].handle_left = h[4]                
    
    classes = [
        Simple,
        BezierDivide,
        BezierPointsFillet
    ]
    
        from bpy.utils import register_class
        for cls in classes:
            register_class(cls)
    
        bpy.types.VIEW3D_MT_curve_add.append(menu)
    
        bpy.types.VIEW3D_MT_edit_curve_context_menu.prepend(Simple_curve_edit_menu)
    
        from bpy.utils import unregister_class
        for cls in reversed(classes):
            unregister_class(cls)
    
        bpy.types.VIEW3D_MT_curve_add.remove(menu)
    
        bpy.types.VIEW3D_MT_edit_curve_context_menu.remove(Simple_curve_edit_menu)
    
    
    if __name__ == "__main__":
        register()