import bpy, bmesh
from ...ui_framework.operator_ui import Master
from ... utility import addon
class HOPS_OT_Edge2Curve(bpy.types.Operator):
bl_idname = "hops.edge2curve"
bl_label = "Curve/Plate Extraction"
bl_options = {'REGISTER', 'UNDO'}
bl_description = """Curve/Plate Extraction
LMB - Converts edge to_curve.
LMB+SHIFT - Converts selection to_plate.
LMB+CTRL - New object from selection.
ALT - Destructive to_curve
ALT+SHIFT - Destructive to_plate
"""
called_ui = False
@classmethod
def poll(cls, context):
if context.active_object != None:
return context.active_object and context.active_object.type == 'MESH'
return False
def invoke(self, context, event):
HOPS_OT_Edge2Curve.called_ui = False
self.original_active = context.active_object
self.new_active = None
self.selection = [o for o in context.selected_objects if o.type == 'MESH']
self.generated_objects = []
self.generated_curves = []
self.result_dict = {}
if context.mode == 'EDIT_MESH':
context.active_object.select_set(True)
self.mode = "Curve"
self.destructive = False
if event.shift and not event.ctrl:
self.mode = "Plate"
if event.ctrl and not event.shift:
self.mode = "Piece"
if event.alt:
self.destructive = True
return self.execute(context)
def execute(self, context):
if self.mode == "Curve":
bm_context = 'EDGES'
elif self.mode == "Plate":
bm_context = 'FACES'
elif self.mode == "Piece" and context.mode == 'EDIT_MESH':
select_mode = bpy.context.tool_settings.mesh_select_mode
if select_mode[0]:
bm_context = 'VERTS'
elif select_mode[1]:
bm_context = 'EDGES'
else:
bm_context = 'FACES'
for obj in self.selection:
deselect_geom =[]
if obj.mode == 'EDIT':
bm = bmesh.from_edit_mesh(obj.data).copy()
geo_type = getattr(bm, str.lower(bm_context))
deselect_geom = [elem for elem in geo_type if not elem.select]
if deselect_geom:
bmesh.ops.delete(bm, geom= deselect_geom, context= bm_context)
if not bm.verts:
self.result_dict[obj.name] = "Failed, Invalid selection"
continue
else:
self.result_dict[obj.name] = "Success"
if self.destructive:
bm_edit = bmesh.from_edit_mesh(obj.data)
selected_geom = [f for f in bm_edit.faces if f.select ]
bmesh.ops.delete(bm_edit, geom=selected_geom, context='FACES')
if bm_context != 'FACES':
floaters = [f for f in bm_edit.verts if f.select and not f.link_faces ]
bmesh.ops.delete(bm_edit, geom= floaters, context='VERTS')
if self.mode == 'Plate':
floaters = [f for f in bm.verts if not f.link_faces ]
bmesh.ops.delete(bm, geom= floaters, context= 'VERTS')
if not bm.verts:
self.result_dict[obj.name] = "Failed, Invalid selection"
continue
else:
self.result_dict[obj.name] = "Success"
else:
bm = bmesh.new()
bm.from_mesh(obj.data)
if self.mode == "Plate":
floaters = [f for f in bm.verts if not f.link_faces ]
bmesh.ops.delete(bm, geom= floaters, context= 'VERTS')
if not bm.verts:
self.result_dict[obj.name] = "Failed, Invalid selection"
continue
else:
self.result_dict[obj.name] = "Success"
new_mesh = obj.data.copy()
new_mesh.name = F"{obj.data.name}_{self.mode}"
bm.to_mesh(new_mesh)
bm.free()
gen_obj = obj.copy()
gen_obj.name = F"{obj.name}_{self.mode}"
gen_obj.data = new_mesh
if obj.mode == 'EDIT' and self.mode == 'Curve':
gen_obj.modifiers.clear()
for col in obj.users_collection:
col.objects.link(gen_obj)
for mod in gen_obj.modifiers:
if mod.type in {'WEIGHTED_NORMAL'}:
gen_obj.modifiers.remove(mod)
self.generated_objects.append(gen_obj)
if obj is context.active_object:
self.new_active = gen_obj
if self.generated_objects:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
for gen_obj in self.generated_objects:
gen_obj.select_set(True)
bpy.context.view_layer.objects.active = gen_obj
bpy.ops.hops.reset_status()
if self.mode == "Curve":
bpy.ops.object.convert(target='CURVE')
if context.active_object.type == 'CURVE':
self.generated_curves.append(gen_obj)
else:
#bpy.ops.object.convert(target='MESH')
bm = bmesh.new()
bm.from_mesh(gen_obj.data)
bmesh.ops.delete(bm, geom = bm.faces, context= 'FACES_ONLY')
bm.to_mesh(gen_obj.data)
bm.free()
bpy.ops.object.convert(target='CURVE')
if context.active_object.type == 'CURVE':
self.generated_curves.append(gen_obj)
else:
self.result_dict[gen_obj.name.replace(F"_{self.mode}", "") ] = "Failed. Geo is too complex or Modifiers took it to another castle"
bpy.data.objects.remove(gen_obj)
continue
gen_obj.select_set(False)
if self.mode == "Curve":
if self.generated_curves:
context.view_layer.objects.active = self.new_active if self.new_active else self.generated_curves[0]
for obj in self.generated_curves:
obj.select_set(True)
bpy.ops.hops.adjust_curve('INVOKE_DEFAULT')
elif self.generated_objects:
context.view_layer.objects.active = self.new_active if self.new_active else self.generated_objects[0]
for obj in self.generated_objects:
obj.select_set(True)
if self.mode == "Plate":
bpy.ops.hops.adjust_tthick('INVOKE_DEFAULT')
bpy.ops.hops.draw_wire_mesh_launcher('INVOKE_DEFAULT', target='SELECTED')
# Operator UI
if not HOPS_OT_Edge2Curve.called_ui:
HOPS_OT_Edge2Curve.called_ui = True
ui = Master()
draw_data = [
["To_" + self.mode],
]
result = [[F"{obj} {result}" ] for obj, result in self.result_dict.items() ]
draw_data.extend(result)
draw_data.append( [F"Conversion to {self.mode}"])
ui.receive_draw_data(draw_data=draw_data)
ui.draw(draw_bg=addon.preference().ui.Hops_operator_draw_bg, draw_border=addon.preference().ui.Hops_operator_draw_border)
return {"FINISHED"}