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
This commit is contained in:
parent
d03905c1bc
commit
e648555eb6
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue