diff options
author | Jaggz H <jaggz> | 2022-02-15 07:42:56 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2022-02-15 07:53:39 +0300 |
commit | e648555eb6b11be56b08f2d10a16c3782271df92 (patch) | |
tree | e1b971e59f99f1719d6bd660e3179ed436265ca7 /object_print3d_utils | |
parent | d03905c1bc0ddd447966c8e30a2a2567ecdd409a (diff) |
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
Diffstat (limited to 'object_print3d_utils')
-rw-r--r-- | object_print3d_utils/__init__.py | 7 | ||||
-rw-r--r-- | object_print3d_utils/operators.py | 71 | ||||
-rw-r--r-- | object_print3d_utils/ui.py | 5 |
3 files changed, 83 insertions, 0 deletions
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): |