Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaggz H <jaggz>2022-02-15 07:42:56 +0300
committerCampbell Barton <ideasman42@gmail.com>2022-02-15 07:53:39 +0300
commite648555eb6b11be56b08f2d10a16c3782271df92 (patch)
treee1b971e59f99f1719d6bd660e3179ed436265ca7 /object_print3d_utils
parentd03905c1bc0ddd447966c8e30a2a2567ecdd409a (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__.py7
-rw-r--r--object_print3d_utils/operators.py71
-rw-r--r--object_print3d_utils/ui.py5
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):