[Bf-extensions-cvs] [e648555e] master: 3D Print Toolbox: Add Align to XY Plane

Jaggz H noreply at git.blender.org
Tue Feb 15 05:57:05 CET 2022


Commit: e648555eb6b11be56b08f2d10a16c3782271df92
Author: Jaggz H
Date:   Tue Feb 15 15:42:56 2022 +1100
Branches: master
https://developer.blender.org/rBAe648555eb6b11be56b08f2d10a16c3782271df92

3D Print Toolbox: Add Align to XY Plane

Allow an object to be rotated so one face/selection can lie flat -
parallel to the - XY plane. This is useful for 3d printing setup. The
button is added in the 3d Print tools addon, in the transform section.

Reviewed By: campbellbarton

Ref D13094

===================================================================

M	object_print3d_utils/__init__.py
M	object_print3d_utils/operators.py
M	object_print3d_utils/ui.py

===================================================================

diff --git a/object_print3d_utils/__init__.py b/object_print3d_utils/__init__.py
index 3019fd40..3c5d4cf9 100644
--- a/object_print3d_utils/__init__.py
+++ b/object_print3d_utils/__init__.py
@@ -42,6 +42,12 @@ else:
 
 
 class SceneProperties(PropertyGroup):
+    use_alignxy_face_area: BoolProperty(
+        name="Face Areas",
+        description="Normalize normals proportional to face areas",
+        default=False,
+    )
+
     export_format: EnumProperty(
         name="Format",
         description="Format type to export to",
@@ -141,6 +147,7 @@ classes = (
     operators.MESH_OT_print3d_select_report,
     operators.MESH_OT_print3d_scale_to_volume,
     operators.MESH_OT_print3d_scale_to_bounds,
+    operators.MESH_OT_print3d_align_to_xy,
     operators.MESH_OT_print3d_export,
 )
 
diff --git a/object_print3d_utils/operators.py b/object_print3d_utils/operators.py
index 6da53219..3f66f07b 100644
--- a/object_print3d_utils/operators.py
+++ b/object_print3d_utils/operators.py
@@ -724,6 +724,77 @@ class MESH_OT_print3d_scale_to_bounds(Operator):
         return wm.invoke_props_dialog(self)
 
 
+class MESH_OT_print3d_align_to_xy(Operator):
+    bl_idname = "mesh.print3d_align_to_xy"
+    bl_label = "Align (rotate) object to XY plane"
+    bl_description = (
+        "Rotates entire object (not mesh) so the selected faces/vertices lie, on average, parallel to the XY plane "
+        "(it does not adjust Z location)"
+    )
+    bl_options = {'REGISTER', 'UNDO'}
+
+    def execute(self, context):
+        # FIXME: Undo is inconsistent.
+        # FIXME: Would be nicer if rotate could pick some object-local axis.
+
+        from mathutils import Vector
+
+        print_3d = context.scene.print_3d
+        face_areas = print_3d.use_alignxy_face_area
+
+        self.context = context
+        mode_orig = context.mode
+        skip_invalid = []
+
+        for obj in context.selected_objects:
+            orig_loc = obj.location.copy()
+            orig_scale = obj.scale.copy()
+
+            # When in edit mode, do as the edit mode does.
+            if mode_orig == 'EDIT_MESH':
+                bm = bmesh.from_edit_mesh(obj.data)
+                faces = [f for f in bm.faces if f.select]
+            else:
+                faces = [p for p in obj.data.polygons if p.select]
+
+            face_count = len(faces)
+            if face_count < 1:
+                skip_invalid.append(obj.name)
+                continue
+
+            # Rotate object so average normal of selected faces points down.
+            normal = Vector((0.0, 0.0, 0.0))
+            if face_areas:
+                for face in faces:
+                    normal += (face.normal * face.calc_area())
+            else:
+                for face in faces:
+                    normal += face.normal
+            normal = normal.normalized()
+            normal.rotate(obj.matrix_world)  # local -> world.
+            offset = normal.rotation_difference(Vector((0.0, 0.0, -1.0)))
+            offset = offset.to_matrix().to_4x4()
+            obj.matrix_world = offset @ obj.matrix_world
+            obj.scale = orig_scale
+            obj.location = orig_loc
+
+        if len(skip_invalid) > 0:
+            for name in skip_invalid:
+                print(f"Align to XY: Skipping object {name}. No faces selected.")
+            if len(skip_invalid) == 1:
+                self.report({'WARNING'}, f"Skipping object {skip_invalid[0]}. No faces selected.")
+            else:
+                self.report({'WARNING'}, f"Skipping some objects. No faces selected. See terminal.")
+        return {'FINISHED'}
+
+    def invoke(self, context, event):
+        if context.mode in {'EDIT_MESH', 'OBJECT'}:
+            pass
+        else:
+            return {'CANCELLED'}
+        return self.execute(context)
+
+
 # ------
 # Export
 
diff --git a/object_print3d_utils/ui.py b/object_print3d_utils/ui.py
index d08d60c2..b5d69214 100644
--- a/object_print3d_utils/ui.py
+++ b/object_print3d_utils/ui.py
@@ -109,10 +109,15 @@ class VIEW3D_PT_print3d_transform(View3DPrintPanel, Panel):
     def draw(self, context):
         layout = self.layout
 
+        print_3d = context.scene.print_3d
+
         layout.label(text="Scale To")
         row = layout.row(align=True)
         row.operator("mesh.print3d_scale_to_volume", text="Volume")
         row.operator("mesh.print3d_scale_to_bounds", text="Bounds")
+        row = layout.row(align=True)
+        row.operator("mesh.print3d_align_to_xy", text="Align to XY Plane")
+        row.prop(print_3d, "use_alignxy_face_area")
 
 
 class VIEW3D_PT_print3d_export(View3DPrintPanel, Panel):



More information about the Bf-extensions-cvs mailing list