feat(selection): separate selection geometry from image content processing

- Introduced a new design for selection rasterization that decouples the selection geometry from the image content, ensuring that the SelectionMask is independent of the source image's alpha and color.
- Updated the `rasterize_selection_path` function to only accept selection paths and image dimensions, removing parameters related to source alpha and alpha thresholds.
- Implemented a new approach for handling selection paths using non-zero winding fill, ensuring overlapping areas are preserved.
- Enhanced performance by utilizing a scanline algorithm for rasterization, significantly reducing processing time for high-resolution images.
- Updated internal documentation and added comprehensive tests to validate the new behavior and performance improvements.
- Ensured backward compatibility by maintaining existing interfaces where necessary while introducing breaking changes in the selection processing logic.
This commit is contained in:
2026-09-01 22:36:20 +08:00
parent 2ff7b04f56
commit 7f6e47432e
54 changed files with 2831 additions and 634 deletions

View File

@@ -42,15 +42,6 @@ def build_mesh_plane_group():
thickness.min_value = 0.0
thickness.max_value = 10000.0
thickness.subtype = "DISTANCE"
data_panel = interface.new_panel(name="Data", default_closed=True)
material = new_socket(
interface,
"Material",
"INPUT",
"NodeSocketMaterial",
parent=data_panel,
)
material.hide_in_modifier = True
new_socket(interface, "Geometry", "OUTPUT", "NodeSocketGeometry")
nodes = group.nodes
@@ -58,9 +49,8 @@ def build_mesh_plane_group():
geometry_input = new_group_input(nodes, (-1800.0, 500.0), {"Geometry"})
subdivide_input = new_group_input(nodes, (-1800.0, 0.0), {"Subdivide"})
thickness_input = new_group_input(nodes, (100.0, -250.0), {"Thickness"})
material_input = new_group_input(nodes, (900.0, 0.0), {"Material"})
output = nodes.new("NodeGroupOutput")
output.location = (1350.0, 500.0)
output.location = (1750.0, 500.0)
bounds = nodes.new("GeometryNodeBoundBox")
bounds.location = (-1600.0, 500.0)
@@ -196,7 +186,7 @@ def build_mesh_plane_group():
links.new(depth_ratio.outputs[0], rounded_depth.inputs[0])
depth_segments = nodes.new("ShaderNodeMath")
depth_segments.operation = "MAXIMUM"
depth_segments.inputs[1].default_value = 1.0
depth_segments.inputs[1].default_value = 3.0
depth_segments.location = (700.0, -100.0)
links.new(rounded_depth.outputs[0], depth_segments.inputs[0])
depth_vertices = nodes.new("ShaderNodeMath")
@@ -239,11 +229,27 @@ def build_mesh_plane_group():
links.new(place_grid.outputs["Geometry"], geometry_switch.inputs["False"])
links.new(place_cube.outputs["Geometry"], geometry_switch.inputs["True"])
set_material = nodes.new("GeometryNodeSetMaterial")
set_material.location = (1050.0, 500.0)
links.new(geometry_switch.outputs["Output"], set_material.inputs["Geometry"])
links.new(material_input.outputs["Material"], set_material.inputs["Material"])
links.new(set_material.outputs["Geometry"], output.inputs["Geometry"])
capture_source = nodes.new("GeometryNodeCaptureAttribute")
capture_source.domain = "POINT"
capture_source.capture_items.clear()
capture_source.capture_items.new("BOOLEAN", "Source")
capture_source.inputs["Source"].default_value = True
capture_source.location = (750.0, 750.0)
links.new(geometry_input.outputs["Geometry"], capture_source.inputs["Geometry"])
join_material = nodes.new("GeometryNodeJoinGeometry")
join_material.location = (1000.0, 500.0)
links.new(geometry_switch.outputs["Output"], join_material.inputs["Geometry"])
links.new(capture_source.outputs["Geometry"], join_material.inputs["Geometry"])
set_material_index = nodes.new("GeometryNodeSetMaterialIndex")
set_material_index.inputs["Material Index"].default_value = 0
set_material_index.location = (1250.0, 500.0)
links.new(join_material.outputs["Geometry"], set_material_index.inputs["Geometry"])
delete_source = nodes.new("GeometryNodeDeleteGeometry")
delete_source.domain = "POINT"
delete_source.location = (1500.0, 500.0)
links.new(set_material_index.outputs["Geometry"], delete_source.inputs["Geometry"])
links.new(capture_source.outputs["Source"], delete_source.inputs["Selection"])
links.new(delete_source.outputs["Geometry"], output.inputs["Geometry"])
group.color_tag = "GEOMETRY"
group.description = (