update addons (for 5.2)
This commit is contained in:
@@ -1,153 +1,44 @@
|
||||
import bpy
|
||||
|
||||
from .list import (
|
||||
list_canvas_cutters,
|
||||
list_cutter_users,
|
||||
from ..constants import (
|
||||
CONVERTABLE_TYPES,
|
||||
)
|
||||
from .mesh import (
|
||||
is_instanced_mesh,
|
||||
)
|
||||
from .object import (
|
||||
convert_to_mesh,
|
||||
has_evaluated_mesh,
|
||||
)
|
||||
|
||||
|
||||
#### ------------------------------ FUNCTIONS ------------------------------ ####
|
||||
|
||||
def basic_poll(cls, context, check_linked=False):
|
||||
"""Basic poll for boolean operators."""
|
||||
def basic_poll(cls, context, check_active=True):
|
||||
"""Basic poll for Boolean operators."""
|
||||
|
||||
if context.mode != 'OBJECT':
|
||||
return False
|
||||
if context.active_object is None:
|
||||
cls.poll_message_set("Boolean operators can only be performed in Object Mode")
|
||||
return False
|
||||
|
||||
obj = context.active_object
|
||||
if obj.type != 'MESH':
|
||||
cls.poll_message_set("Boolean operators can only be used for mesh objects")
|
||||
return False
|
||||
|
||||
if check_linked and is_linked(context, obj) == True:
|
||||
cls.poll_message_set("Boolean operators can not be executed on linked objects")
|
||||
return False
|
||||
if check_active:
|
||||
if context.active_object is None:
|
||||
cls.poll_message_set("No active object")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def is_linked(context, obj):
|
||||
"""Checks whether the object is linked from an external .blend file (including library-overrides)."""
|
||||
|
||||
if obj not in context.editable_objects:
|
||||
if obj.library:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
if obj.override_library:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def is_canvas(obj):
|
||||
"""Checks whether the object is a boolean canvas (i.e. has boolean cutters)."""
|
||||
|
||||
if obj.booleans.canvas == False:
|
||||
return False
|
||||
else:
|
||||
# Even if object is marked as canvas, check if it actually has any cutters
|
||||
cutters, __ = list_canvas_cutters([obj])
|
||||
if len(cutters) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def is_instanced_data(obj):
|
||||
"""Checks if `obj.data` has more than one users, i.e. is instanced."""
|
||||
"""Function only considers object types as users, and excludes pointers."""
|
||||
|
||||
data = bpy.data.meshes.get(obj.data.name)
|
||||
users = 0
|
||||
|
||||
for key, values in bpy.data.user_map(subset=[data]).items():
|
||||
for value in values:
|
||||
if value.id_type == 'OBJECT':
|
||||
users += 1
|
||||
|
||||
if users > 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def active_modifier_poll(obj):
|
||||
"""Checks whether the active modifier for active object is a boolean."""
|
||||
|
||||
# Check if active modifier exists.
|
||||
if len(obj.modifiers) == 0:
|
||||
return False
|
||||
if obj.modifiers.active is None:
|
||||
return False
|
||||
|
||||
# Check if active modifier is a boolean with a valid object.
|
||||
modifier = obj.modifiers.active
|
||||
if modifier.type != "BOOLEAN":
|
||||
return False
|
||||
if modifier.object is None:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def has_evaluated_mesh(context, obj):
|
||||
"""Checks if an object (non-mesh type) has an evaluated mesh created by Geometry Nodes modifiers."""
|
||||
|
||||
depsgraph = context.view_layer.depsgraph
|
||||
obj_eval = depsgraph.id_eval_get(obj)
|
||||
geometry = obj_eval.evaluated_geometry()
|
||||
|
||||
if geometry.mesh:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def list_candidate_objects(self, context, canvas):
|
||||
"""Filter out objects from the selection that can't be used as a cutter."""
|
||||
|
||||
cutters = []
|
||||
for obj in context.selected_objects:
|
||||
if obj == context.active_object:
|
||||
continue
|
||||
if is_linked(context, obj):
|
||||
self.report({'WARNING'}, f"{obj.name} is linked and can not be used as a cutter")
|
||||
continue
|
||||
|
||||
if obj.type == 'MESH':
|
||||
# Exclude if object is already a cutter for canvas.
|
||||
if canvas in list_cutter_users([obj]):
|
||||
continue
|
||||
# Exclude if canvas is cutting the object (avoid dependancy loop).
|
||||
if obj in list_cutter_users([canvas]):
|
||||
self.report({'WARNING'}, f"{obj.name} can not cut its own cutter (dependancy loop)")
|
||||
continue
|
||||
|
||||
cutters.append(obj)
|
||||
|
||||
elif obj.type in ('CURVE', 'FONT'):
|
||||
if has_evaluated_mesh(context, obj):
|
||||
convert_to_mesh(context, obj)
|
||||
cutters.append(obj)
|
||||
|
||||
return cutters
|
||||
|
||||
|
||||
def destructive_op_confirmation(self, context, event, canvases: list, title="Boolean Operation"):
|
||||
def destructive_op_confirmation(cls, context, event, canvases: list, title="Boolean Operation"):
|
||||
"""
|
||||
Creates & returns the confirmation pop-up window for destructive boolean operators.\n
|
||||
Confirmation window is triggered by canvas objects that have instanced object data or shape keys.\n
|
||||
If none of the canvas objects have them the operator is executed without any confirmation.
|
||||
Creates & returns the confirmation pop-up window for destructive Boolean operators.\n
|
||||
Confirmation window is triggered by canvases that have instanced data or shape keys.\n
|
||||
If none of the canvases have them the operator is executed without any confirmation.
|
||||
"""
|
||||
|
||||
has_instanced_data = any(obj for obj in canvases if is_instanced_data(obj))
|
||||
if len(canvases) == 0:
|
||||
return cls.execute(context)
|
||||
|
||||
has_instanced_data = any(obj for obj in canvases if is_instanced_mesh(obj.data))
|
||||
has_shape_keys = any(obj for obj in canvases if obj.data.shape_keys)
|
||||
|
||||
if has_instanced_data or has_shape_keys:
|
||||
@@ -166,15 +57,74 @@ def destructive_op_confirmation(self, context, event, canvases: list, title="Boo
|
||||
# Combined message.
|
||||
if has_instanced_data and has_shape_keys:
|
||||
message = ("Object(s) you're trying to cut have shape keys and instanced object data.\n"
|
||||
"In order to apply modifiers shape keys need to be applied, and object data made single user.\n"
|
||||
"In order to apply modifiers shape keys need to be applied & object data made single user.\n"
|
||||
"Do you proceed?")
|
||||
|
||||
popup = context.window_manager.invoke_confirm(self, event, title=title,
|
||||
popup = context.window_manager.invoke_confirm(cls, event, title=title,
|
||||
confirm_text="Yes", icon='WARNING',
|
||||
message=message)
|
||||
|
||||
cls._unflippable = True
|
||||
return popup
|
||||
|
||||
# Execute without confirmation window.
|
||||
else:
|
||||
return self.execute(context)
|
||||
return cls.execute(context)
|
||||
|
||||
|
||||
def convert_to_mesh_confirmation(cls, context, event, cutters: list, title="Boolean Operation"):
|
||||
"""
|
||||
Creates & returns the confirmation pop-up window when the object is
|
||||
about to be converted to mesh to be used as a cutter.
|
||||
|
||||
NOTE (1): Only triggers during brush Boolean operators,
|
||||
because object gets destroyed in the destructive one anyway.
|
||||
|
||||
NOTE (2): This is only required because of the limitation of legacy Boolean modifier.
|
||||
Geometry nodes implementation works with any object type. When the add-on is
|
||||
updated to work with custom modifiers this will not be necesary anymore.
|
||||
"""
|
||||
|
||||
if len(cutters) == 0:
|
||||
return cls.execute(context)
|
||||
|
||||
is_convertable = any(
|
||||
obj.type in CONVERTABLE_TYPES and has_evaluated_mesh(context, obj)
|
||||
for obj in cutters
|
||||
)
|
||||
|
||||
if is_convertable:
|
||||
message = ("Some of the selected objects are not of the Mesh type, but output mesh.\n"
|
||||
"In order to use them as cutters, they need to be converted to mesh.\n"
|
||||
"This is a destructive operator. Do you proceed?")
|
||||
|
||||
popup = context.window_manager.invoke_confirm(cls, event, title=title,
|
||||
confirm_text="Yes", icon='WARNING',
|
||||
message=message)
|
||||
|
||||
cls._unflippable = True
|
||||
return popup
|
||||
|
||||
# Execute without confirmation window.
|
||||
else:
|
||||
return cls.execute(context)
|
||||
|
||||
|
||||
|
||||
#### ------------------------------ /operator_helpers/ ------------------------------ ####
|
||||
|
||||
def _guess_toggle_state(modifiers):
|
||||
"""Guess whether cutters should be hidden or revealed."""
|
||||
|
||||
enabled = 0
|
||||
disabled = 0
|
||||
for mod in modifiers:
|
||||
if mod.show_viewport:
|
||||
enabled += 1
|
||||
else:
|
||||
disabled += 1
|
||||
|
||||
if enabled > disabled:
|
||||
return "On"
|
||||
else:
|
||||
return "Off"
|
||||
|
||||
Reference in New Issue
Block a user