OverviewFilesCommitsIssuesDocsReleasesPeers

hardops / operators / misc / to_plane.py

217 lines · python ref:
import bpy, bmesh, mathutils, math, copy
from ... utility import addon
from ... utils.objects import set_active
from ... utility import object, math as hops_math
from mathutils import Matrix, Vector


class HOPS_OT_Conv_To_Plane(bpy.types.Operator):
    bl_idname = "hops.to_plane"
    bl_label = "Hops To Plane"
    bl_options = {'REGISTER', 'UNDO'}
    bl_description = """LMB - Creates plane from selection.
LMB+SHIFT - Creates plane for each selected object from selection

"""
    axis: bpy.props.EnumProperty(
        name="Axis",
        description="What axis to dice on",
        items=[
            ('+X', "+X", "Create a plane on the +X axis"),
            ('+Y', "+Y", "Create a plane on the +Y axis"),
            ('+Z', "+Z", "Create a plane on the +Z axis"),
            ('-X', "-X", "Create a plane on the -X axis"),
            ('-Y', "-Y", "Create a plane on the -Y axis"),
            ('-Z', "-Z", "Create a plane on the -Z axis")],
        default='+X')

    modified: bpy.props.BoolProperty(
        name="Modified",
        description="Take the bounding box dimensions from the modified object",
        default=True)
    offset: bpy.props.FloatProperty(
        name="Offset",
        description="Offset plane from selection",
        default=0.03)

    individual: bpy.props.BoolProperty(
    name="Individual", description="Create plane per object", default = False)

    @classmethod
    def poll(cls, context):
        obj = context.active_object
        return obj and obj.type == 'MESH'

    def draw(self, context):
        self.layout.prop(self, "axis")
        self.layout.prop(self, "modified")
        self.layout.prop(self, "individual")
        self.layout.prop(self, "offset")

    def invoke (self, context, event):
        if event.shift:
            self.individual = True
        else:
            self.individual = False

        return self.execute(context)


    def execute(self, context):
        bpy.context.view_layer.update()
        selected = context.selected_objects[:]
        if self.individual:

            if context.mode == 'OBJECT':
                for obj in selected:
                    plane = obj.copy()
                    for col in obj.users_collection:
                        col.objects.link(plane)

                    plane.name = f"{obj.name} Plane {self.axis}"
                    plane.modifiers.clear()
                    plane.select_set(True)

                    obj.select_set(False)
                    context.view_layer.objects.active = plane
                    bb = self.get_coords_obj( obj)

                    plane.data = self.plane_mesh( bb, obj.name)

                    self.center_origin(plane, context )

            elif context.mode =='EDIT_MESH':
                for obj in selected:
                    coords = self.get_coords_edit(context, obj)

                    if len (coords) <2:
                        continue

                    plane = obj.copy()
                    for col in obj.users_collection:
                        col.objects.link(plane)

                    plane.name = f"{obj.name} Plane {self.axis}"
                    plane.modifiers.clear()
                    plane.select_set(True)

                    bb = hops_math.coords_to_bounds(coords)

                    plane.data= self.plane_mesh( bb, obj.name)

                    self.center_origin(plane,context )

        else:

            coords = []
            if context.mode == 'OBJECT':
                for obj in selected:

                    coords.extend(self.get_coords_obj(obj, obj.matrix_world))

                    obj.select_set(False)

            elif context.mode =='EDIT_MESH':
                for obj in selected:

                    coords.extend(self.get_coords_edit(context, obj, obj.matrix_world))
                    obj.select_set(False)

            if len (coords) <2:
                return {'CANCELLED'}

            bb = hops_math.coords_to_bounds(coords)

            plane_mesh = self.plane_mesh( bb, obj.name)
            plane = bpy.data.objects.new(f"{obj.name} Plane {self.axis}", plane_mesh)

            for col in obj.users_collection:
                col.objects.link(plane)

            if context.mode == 'OBJECT':
                plane.select_set(True)
                context.view_layer.objects.active = plane

        return {'FINISHED'}

    @staticmethod
    def center_origin(plane, context) :
        context.view_layer.update()
        center = object.center(plane)
        plane.location = plane.matrix_world @ center
        plane.data.transform(Matrix.Translation(-center))


    def plane_mesh (self, bb, name = ""):
            if self.axis == '+X':
                bb = (bb[7], bb[6], bb[5], bb[4])
            elif self.axis == '+Y':
                bb = (bb[3], bb[2], bb[6], bb[7])
            elif self.axis == '+Z':
                bb = (bb[5], bb[6], bb[2], bb[1])
            elif self.axis == '-X':
                bb = (bb[0], bb[1], bb[2], bb[3])
            elif self.axis == '-Y':
                bb = (bb[0], bb[4], bb[5], bb[1])
            elif self.axis == '-Z':
                bb = (bb[0], bb[3], bb[7], bb[4])

            bm = bmesh.new()
            verts = [bm.verts.new(v) for v in bb]
            bm.faces.new(verts)

            plane_mesh = bpy.data.meshes.new(f"{name} Plane {self.axis}")

            bpy.context.view_layer.update()
            offset = self.offset
            if self.axis in {'-X', '-Y', '-Z'}:
                offset*= -1
            if self.axis in {'+X', '-X'}:
                bmesh.ops.transform(bm, matrix = Matrix.Translation(Vector((offset,0,0))), verts = bm.verts)
            elif self.axis in {'+Y', '-Y'}:
                bmesh.ops.transform(bm, matrix = Matrix.Translation(Vector((0,offset,0))), verts = bm.verts)
            elif self.axis in {'+Z', '-Z'}:
                bmesh.ops.transform(bm, matrix = Matrix.Translation(Vector((0,0,offset))), verts = bm.verts)

            bm.to_mesh(plane_mesh)
            bm.free()

            return plane_mesh

    def get_coords_obj(self, obj, matrix =None):

        if self.modified:

            if matrix == None:
                bb = [Vector(v) for v in obj.bound_box]
            else:
                bb = [matrix @ Vector(v) for v in obj.bound_box]
        else:

            if matrix == None:
                coord = [v.co for v in obj.data.vertices]
            else:
                coord = [matrix @ v.co for v in obj.data.vertices]
            bb = hops_math.coords_to_bounds(coord)

        return bb

    def get_coords_edit (self, context, obj, matrix = None):


        me = obj.data
        if self.modified:
            depsgraph = context.evaluated_depsgraph_get()
            bm = bmesh.new()
            bm.from_object(obj,depsgraph, True)
            bmesh.update_edit_mesh(me)
        else:
            bm = bmesh.from_edit_mesh(me)
        if matrix == None:
          coords = [v.co for v in bm.verts if v.select]
        else:
            coords = [matrix @ v.co for v in bm.verts if v.select]
        bm.free()

        return coords