Skip to content
Snippets Groups Projects
arrange_on_curve.py 12.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • # gpl author: Mano-Wii
    
    bl_info = {
        "name": "Arrange on Curve",
        "author": "Mano-Wii",
        "version": (6, 3, 0),
    
        "blender": (2, 77, 0),
    
        "location": "3D View > Toolshelf > Create > Arrange on Curve",
    
        "description": "Arrange objects along a curve",
        "warning": "Select curve",
        "wiki_url": "",
        "category": "3D View"
        }
    
    
    # Note: scene properties are moved into __init__
    # search for patterns advanced_objects and adv_obj
    
    
    import bpy
    import mathutils
    from bpy.types import (
            Operator,
            Panel,
            )
    from bpy.props import (
            EnumProperty,
            FloatProperty,
            IntProperty,
            )
    
    FLT_MIN = 0.004
    
    
    class PanelDupliCurve(Panel):
    
        bl_idname = "VIEW3D_PT_arranjar_numa_curva"
    
        bl_space_type = "VIEW_3D"
        bl_region_type = "TOOLS"
        bl_context = "objectmode"
        bl_category = "Create"
    
        bl_label = "Arrange on Curve"
    
        bl_options = {'DEFAULT_CLOSED'}
    
        @classmethod
        def poll(cls, context):
            return context.object and context.mode == 'OBJECT' and context.object.type == 'CURVE'
    
        def draw(self, context):
            layout = self.layout
    
            adv_obj = context.scene.advanced_objects
    
            layout.prop(adv_obj, "arrange_c_use_selected")
    
            if not adv_obj.arrange_c_use_selected:
                layout.prop(adv_obj, "arrange_c_select_type", expand=True)
                if adv_obj.arrange_c_select_type == 'O':
    
                    layout.column(align=True).prop_search(
    
                                  adv_obj, "arrange_c_obj_arranjar",
                                  bpy.data, "objects"
                                  )
                elif adv_obj.arrange_c_select_type == 'G':
    
                    layout.column(align=True).prop_search(
    
                                  adv_obj, "arrange_c_obj_arranjar",
    
            if context.object.type == 'CURVE':
                layout.operator("object.arranjar_numa_curva", text="Arrange Objects")
    
    
    class DupliCurve(Operator):
        bl_idname = "object.arranjar_numa_curva"
    
        bl_label = "Arrange Objects along a Curve"
        bl_description = "Arange chosen / selected objects along the Active Curve"
    
        bl_options = {'REGISTER', 'UNDO'}
    
    
        use_distance: EnumProperty(
    
                name="Arrangement",
                items=[
                    ("D", "Distance", "Objects are arranged depending on the distance", 0),
                    ("Q", "Quantity", "Objects are arranged depending on the quantity", 1),
                    ("R", "Range", "Objects are arranged uniformly between the corners", 2)
                    ]
                )
    
        distance: FloatProperty(
    
                name="Distance",
                description="Distance between Objects",
                default=1.0,
                min=FLT_MIN,
                soft_min=0.1,
                unit='LENGTH',
                )
    
        object_qt: IntProperty(
    
                name="Quantity",
                description="Object amount",
                default=2,
                min=0,
                )
    
        scale: FloatProperty(
    
                name="Scale",
                description="Object Scale",
                default=1.0,
                min=FLT_MIN,
                unit='LENGTH',
    
        Yaw: FloatProperty(
    
                name="X",
                description="Rotate around the X axis (Yaw)",
                default=0.0,
                unit='ROTATION'
                )
    
        Pitch: FloatProperty(
    
                default=0.0,
                description="Rotate around the Y axis (Pitch)",
                name="Y",
                unit='ROTATION'
                )
    
        Roll: FloatProperty(
    
                default=0.0,
                description="Rotate around the Z axis (Roll)",
                name="Z",
                unit='ROTATION'
                )
    
        max_angle: FloatProperty(
    
                default=1.57079,
                max=3.141592,
                name="Angle",
                unit='ROTATION'
                )
    
        offset: FloatProperty(
    
                default=0.0,
                name="Offset",
                unit='LENGTH'
                )
    
    
        @classmethod
        def poll(cls, context):
            return context.mode == 'OBJECT'
    
        def draw(self, context):
            layout = self.layout
            col = layout.column()
            col.prop(self, "use_distance", text="")
            col = layout.column(align=True)
            if self.use_distance == "D":
                col.prop(self, "distance")
            elif self.use_distance == "Q":
                col.prop(self, "object_qt")
            else:
                col.prop(self, "distance")
                col.prop(self, "max_angle")
                col.prop(self, "offset")
    
            col = layout.column(align=True)
            col.prop(self, "scale")
            col.prop(self, "Yaw")
            col.prop(self, "Pitch")
            col.prop(self, "Roll")
    
        def Glpoints(self, curve):
            Gpoints = []
            for i, spline in enumerate(curve.data.splines):
                segments = len(spline.bezier_points)
                if segments >= 2:
                    r = spline.resolution_u + 1
    
                    points = []
                    for j in range(segments):
                        bp1 = spline.bezier_points[j]
                        inext = (j + 1)
                        if inext == segments:
                            if not spline.use_cyclic_u:
                                break
                            inext = 0
                        bp2 = spline.bezier_points[inext]
                        if bp1.handle_right_type == bp2.handle_left_type == 'VECTOR':
                            _points = (bp1.co, bp2.co) if j == 0 else (bp2.co,)
                        else:
                            knot1 = bp1.co
                            handle1 = bp1.handle_right
                            handle2 = bp2.handle_left
                            knot2 = bp2.co
                            _points = mathutils.geometry.interpolate_bezier(knot1, handle1, handle2, knot2, r)
                        points.extend(_points)
                    Gpoints.append(tuple((curve.matrix_world * p for p in points)))
                elif len(spline.points) >= 2:
                    l = [curve.matrix_world * p.co.xyz for p in spline.points]
                    if spline.use_cyclic_u:
                        l.append(l[0])
                    Gpoints.append(tuple(l))
    
                if self.use_distance == "R":
                    max_angle = self.max_angle
                    tmp_Gpoints = []
                    sp = Gpoints[i]
                    sp2 = [sp[0], sp[1]]
                    lp = sp[1]
                    v1 = lp - sp[0]
                    for p in sp[2:]:
                        v2 = p - lp
                        try:
                            if (3.14158 - v1.angle(v2)) < max_angle:
                                tmp_Gpoints.append(tuple(sp2))
                                sp2 = [lp]
                        except Exception as e:
    
                            print("\n[Add Advanced  Objects]\nOperator: "
                                  "object.arranjar_numa_curva\nError: {}".format(e))
    
                            pass
                        sp2.append(p)
                        v1 = v2
                        lp = p
                    tmp_Gpoints.append(tuple(sp2))
                    Gpoints = Gpoints[:i] + tmp_Gpoints
    
            lengths = []
            if self.use_distance != "D":
                for sp in Gpoints:
                    lp = sp[1]
                    leng = (lp - sp[0]).length
                    for p in sp[2:]:
                        leng += (p - lp).length
                        lp = p
                    lengths.append(leng)
            return Gpoints, lengths
    
        def execute(self, context):
            if context.object.type != 'CURVE':
                return {'CANCELLED'}
    
            curve = context.active_object
            Gpoints, lengs = self.Glpoints(curve)
    
            adv_obj = context.scene.advanced_objects
    
            if adv_obj.arrange_c_use_selected:
    
                G_Objeto = context.selected_objects
                G_Objeto.remove(curve)
    
                if not G_Objeto:
                    return {'CANCELLED'}
    
    
            elif adv_obj.arrange_c_select_type == 'O':
                G_Objeto = bpy.data.objects[adv_obj.arrange_c_obj_arranjar],
            elif adv_obj.arrange_c_select_type == 'G':
    
                G_Objeto = bpy.data.collections[adv_obj.arrange_c_obj_arranjar].objects
    
            yawMatrix = mathutils.Matrix.Rotation(self.Yaw, 4, 'X')
            pitchMatrix = mathutils.Matrix.Rotation(self.Pitch, 4, 'Y')
            rollMatrix = mathutils.Matrix.Rotation(self.Roll, 4, 'Z')
    
    
            max_angle = self.max_angle  # max_angle is called in Glpoints
    
    
            if self.use_distance == "D":
                dist = self.distance
                for sp_points in Gpoints:
                    dx = 0.0  # Length of initial calculation of section
                    last_point = sp_points[0]
                    j = 0
                    for point in sp_points[1:]:
                        vetorx = point - last_point  # Vector spline section
                        quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z')  # Tracking the selected objects
                        quat = quat.to_matrix().to_4x4()
    
                        v_len = vetorx.length
                        if v_len > 0.0:
                            dx += v_len  # Defined length calculation equal total length of the spline section
                            v_norm = vetorx / v_len
                            while dx > dist:
                                object = G_Objeto[j % len(G_Objeto)]
                                j += 1
                                dx -= dist  # Calculating the remaining length of the section
                                obj = object.copy()
    
                                context.collection.objects.link(obj)
    
                                obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
                                # Placing in the correct position
                                obj.matrix_world.translation = point - v_norm * dx
                                obj.scale *= self.scale
                            last_point = point
    
            elif self.use_distance == "Q":
                object_qt = self.object_qt + 1
                for i, sp_points in enumerate(Gpoints):
                    dx = 0.0  # Length of initial calculation of section
                    dist = lengs[i] / object_qt
                    last_point = sp_points[0]
                    j = 0
                    for point in sp_points[1:]:
                        vetorx = point - last_point  # Vector spline section
                        # Tracking the selected objects
                        quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z')
                        quat = quat.to_matrix().to_4x4()
    
                        v_len = vetorx.length
                        if v_len > 0.0:
                            # Defined length calculation equal total length of the spline section
                            dx += v_len
                            v_norm = vetorx / v_len
                            while dx > dist:
                                object = G_Objeto[j % len(G_Objeto)]
                                j += 1
                                dx -= dist  # Calculating the remaining length of the section
                                obj = object.copy()
    
                                context.collection.objects.link(obj)
    
                                obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
                                # Placing in the correct position
                                obj.matrix_world.translation = point - v_norm * dx
                                obj.scale *= self.scale
                            last_point = point
    
            else:
                dist = self.distance
                offset2 = 2 * self.offset
                for i, sp_points in enumerate(Gpoints):
                    leng = lengs[i] - offset2
                    rest = leng % dist
                    offset = offset2 + rest
                    leng -= rest
                    offset /= 2
                    last_point = sp_points[0]
    
                    dx = dist - offset  # Length of initial calculation of section
                    j = 0
                    for point in sp_points[1:]:
                        vetorx = point - last_point  # Vector spline section
                        # Tracking the selected objects
                        quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z')
                        quat = quat.to_matrix().to_4x4()
    
                        v_len = vetorx.length
                        if v_len > 0.0:
                            dx += v_len
                            v_norm = vetorx / v_len
                            while dx >= dist and leng >= 0.0:
                                leng -= dist
                                dx -= dist  # Calculating the remaining length of the section
                                object = G_Objeto[j % len(G_Objeto)]
                                j += 1
                                obj = object.copy()
    
                                context.collection.objects.link(obj)
    
                                obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
                                # Placing in the correct position
                                obj.matrix_world.translation = point - v_norm * dx
                                obj.scale *= self.scale
                            last_point = point
    
            return {"FINISHED"}
    
    
    def register():
        bpy.utils.register_class(PanelDupliCurve)
        bpy.utils.register_class(DupliCurve)
    
    
    def unregister():
        bpy.utils.unregister_class(PanelDupliCurve)
        bpy.utils.unregister_class(DupliCurve)
    
    
    if __name__ == "__main__":
        register()