Refactor Cutout Tool to Use World Space Edge Length
- Replaced the Mesh Detail parameter with a new Edge Length property to define cutout sampling in physical units. - Introduced Minimum Relative Edge Length preference to control effective spacing based on the bounding box of the cutout content. - Updated tests to validate the new edge length functionality and ensure consistent behavior across different resolutions and canvas sizes. - Removed deprecated parameters and adjusted related properties to align with the new edge length implementation. - Enhanced geometry sampling logic to utilize effective world spacing for both standard and fine outline modes.
This commit is contained in:
@@ -8,12 +8,12 @@ from ...preferences import (
|
||||
addon_preferences,
|
||||
configured_geometry_model,
|
||||
configured_max_ai_input_size,
|
||||
configured_max_cutout_mesh_resolution,
|
||||
)
|
||||
from ...properties import (
|
||||
cutout_alpha_threshold_property,
|
||||
cutout_fine_outline_property,
|
||||
cutout_gesture_property,
|
||||
cutout_edge_length_property,
|
||||
)
|
||||
from ...runtime import JobOperatorBase
|
||||
from ...server.model_catalog import DEFAULT_GEOMETRY_MODEL_KEY
|
||||
@@ -59,7 +59,7 @@ from ...common.selection import (
|
||||
from .geometry import (
|
||||
CUTOUT_ALPHA_THRESHOLD,
|
||||
build_base_shape,
|
||||
limit_cutout_point_spacing,
|
||||
effective_cutout_edge_length,
|
||||
)
|
||||
from .interaction import open_shape_pie
|
||||
from .object import (
|
||||
@@ -89,7 +89,7 @@ def create_cutout_shape(
|
||||
context,
|
||||
source_object,
|
||||
shape,
|
||||
point_spacing,
|
||||
edge_length,
|
||||
content_values,
|
||||
content_bounds,
|
||||
*,
|
||||
@@ -101,11 +101,22 @@ def create_cutout_shape(
|
||||
normal_mode=NormalMode.NONE,
|
||||
color_image,
|
||||
gesture="LASSO",
|
||||
report=None,
|
||||
):
|
||||
effective_length = effective_cutout_edge_length(
|
||||
context, source_object, edge_length, content_values, content_bounds, alpha_threshold
|
||||
)
|
||||
if report is not None and effective_length > edge_length * (1 + 1e-6):
|
||||
units = context.scene.unit_settings
|
||||
label = bpy.utils.units.to_string(
|
||||
units.system if units.system != "NONE" else "METRIC", "LENGTH",
|
||||
effective_length * units.scale_length, precision=4,
|
||||
)
|
||||
report({"INFO"}, f"Effective Edge Length: {label}")
|
||||
base_shape = build_base_shape(
|
||||
context,
|
||||
source_object,
|
||||
point_spacing,
|
||||
effective_length,
|
||||
content_values,
|
||||
content_bounds,
|
||||
alpha_threshold=alpha_threshold,
|
||||
@@ -115,7 +126,7 @@ def create_cutout_shape(
|
||||
context,
|
||||
source_object,
|
||||
shape,
|
||||
point_spacing,
|
||||
effective_length,
|
||||
content_values,
|
||||
content_bounds,
|
||||
base_shape,
|
||||
@@ -191,11 +202,7 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
selection_bounds_json: bpy.props.StringProperty(options=_HIDDEN)
|
||||
shape: bpy.props.StringProperty(default="SOLID", options=_HIDDEN)
|
||||
gesture: bpy.props.StringProperty(default="LASSO", options=_HIDDEN)
|
||||
point_spacing: bpy.props.FloatProperty(
|
||||
default=32.0,
|
||||
min=0.01,
|
||||
options=_HIDDEN,
|
||||
)
|
||||
edge_length: cutout_edge_length_property(options=_HIDDEN)
|
||||
model: bpy.props.StringProperty(
|
||||
default=DEFAULT_GEOMETRY_MODEL_KEY,
|
||||
options=_HIDDEN,
|
||||
@@ -234,13 +241,6 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
source_rgba = image_rgba(source_object.data)
|
||||
context.view_layer.objects.active = source_object
|
||||
source_object.select_set(True)
|
||||
crop_width = selection_mask.bounds[2] - selection_mask.bounds[0]
|
||||
crop_height = selection_mask.bounds[3] - selection_mask.bounds[1]
|
||||
self.point_spacing = limit_cutout_point_spacing(
|
||||
self.point_spacing,
|
||||
(crop_width, crop_height),
|
||||
configured_max_cutout_mesh_resolution(context),
|
||||
)
|
||||
self.max_ai_input_size = configured_max_ai_input_size(context)
|
||||
if not self._needs_generation():
|
||||
left, top, right, bottom = selection_mask.bounds
|
||||
@@ -251,11 +251,12 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
)
|
||||
with material_color_image(source_object.data, bounds=selection_mask.bounds, rgba=source_rgba) as color_image:
|
||||
create_cutout_shape(
|
||||
context, source_object, self.shape, self.point_spacing,
|
||||
context, source_object, self.shape, self.edge_length,
|
||||
content_values, selection_mask.bounds,
|
||||
alpha_threshold=self.alpha_threshold,
|
||||
fine_outline=self.fine_outline, color_image=color_image,
|
||||
gesture=self.gesture,
|
||||
report=self.report,
|
||||
)
|
||||
return {"FINISHED"}
|
||||
setup_result = invoke_ai_setup_if_needed(self)
|
||||
@@ -354,7 +355,7 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
context,
|
||||
source_object,
|
||||
self.shape,
|
||||
self.point_spacing,
|
||||
self.edge_length,
|
||||
content_values,
|
||||
content_bounds,
|
||||
depth_image=depth_image,
|
||||
@@ -365,6 +366,7 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
fine_outline=self.fine_outline,
|
||||
color_image=color_image,
|
||||
gesture=self.gesture,
|
||||
report=self.report,
|
||||
)
|
||||
except Exception:
|
||||
if normal_image is not None and normal_image.users == 0:
|
||||
@@ -460,7 +462,7 @@ class CutoutTool(bpy.types.WorkSpaceTool):
|
||||
def draw_settings(context, layout, _tool):
|
||||
settings = context.scene.anyimage_settings
|
||||
layout.prop(settings, "cutout_gesture")
|
||||
layout.prop(settings, "cutout_mesh_detail")
|
||||
layout.prop(settings, "cutout_edge_length")
|
||||
layout.prop(settings, "cutout_fine_outline")
|
||||
layout.prop(settings, "cutout_alpha_threshold")
|
||||
status = draw_ai_setup(layout)
|
||||
|
||||
Reference in New Issue
Block a user