Refactor comparison operations in geometry nodes

- Introduced a new `compare_node` function to streamline comparison operations across various geometry nodes.
- Replaced instances of mathematical comparison nodes with the new `compare_node` function in multiple files, enhancing code readability and maintainability.
- Updated related logic in cutout, depth surface, and image processing groups to utilize the new comparison method.
- Ensured consistent handling of boolean logic through the introduction of a `boolean_node` function for combining boolean inputs.
This commit is contained in:
2026-09-16 21:42:02 +08:00
parent c62ec2cd48
commit 78bbff6351
19 changed files with 282 additions and 200 deletions

View File

@@ -6,7 +6,8 @@ from anyimage.operators.cutout_tool.shape import NORMAL_REDUCTION_ATTRIBUTE_NAME
from ..common.nodes import (
interface_socket, group_input, float_input, bool_input, vector_input,
evaluate_field, store_float_attribute, read_float_attribute, prepare_node_group,
compare_node, evaluate_field, store_float_attribute, read_float_attribute,
prepare_node_group,
)
from ..common.depth_surface import _math
from ..common.normal_map import ROTATION_ATTRIBUTE, FACE_ATTRIBUTE, AXIS_ATTRIBUTE
@@ -67,7 +68,7 @@ def _align_front(group, geometry, controls):
def _boundary_edge_field(group):
"""Report one on edges that carry a single face."""
neighbors = group.nodes.new('GeometryNodeInputMeshEdgeNeighbors')
return _math(group, 'COMPARE', neighbors.outputs['Face Count'], 1)
return compare_node(group, 'EQUAL', neighbors.outputs['Face Count'], 1, data_type='INT')
def _fold_to_plane(group, geometry, tolerance):
@@ -76,7 +77,7 @@ def _fold_to_plane(group, geometry, tolerance):
position = nodes.new('GeometryNodeInputPosition')
components = nodes.new('ShaderNodeSeparateXYZ')
links.new(position.outputs[0], components.inputs[0])
beyond = _math(group, 'GREATER_THAN', components.outputs['Y'], tolerance)
beyond = compare_node(group, 'GREATER_THAN', components.outputs['Y'], tolerance)
target = nodes.new('ShaderNodeCombineXYZ')
links.new(components.outputs['X'], target.inputs['X'])
links.new(components.outputs['Z'], target.inputs['Z'])
@@ -91,8 +92,8 @@ def _fold_to_plane(group, geometry, tolerance):
links.new(normal.outputs[0], normal_axes.inputs[0])
flat = nodes.new('FunctionNodeBooleanMath')
flat.operation = 'AND'
links.new(_math(group, 'LESS_THAN', center, tolerance), flat.inputs[0])
links.new(_math(group, 'GREATER_THAN', _math(group, 'ABSOLUTE', normal_axes.outputs['Y']), 1 - 1e-3), flat.inputs[1])
links.new(compare_node(group, 'LESS_THAN', center, tolerance), flat.inputs[0])
links.new(compare_node(group, 'GREATER_THAN', _math(group, 'ABSOLUTE', normal_axes.outputs['Y']), 1 - 1e-3), flat.inputs[1])
delete = nodes.new('GeometryNodeDeleteGeometry')
delete.domain, delete.mode = 'FACE', 'ALL'
links.new(place.outputs[0], delete.inputs['Geometry'])
@@ -106,10 +107,10 @@ def _clean_crease(group, geometry, tolerance):
heights = _edge_abs_y(group)
edge_plane = nodes.new('FunctionNodeBooleanMath')
edge_plane.operation = 'AND'
links.new(_math(group, 'LESS_THAN', heights[0], tolerance), edge_plane.inputs[0])
links.new(_math(group, 'LESS_THAN', heights[1], tolerance), edge_plane.inputs[1])
links.new(compare_node(group, 'LESS_THAN', heights[0], tolerance), edge_plane.inputs[0])
links.new(compare_node(group, 'LESS_THAN', heights[1], tolerance), edge_plane.inputs[1])
neighbors = nodes.new('GeometryNodeInputMeshEdgeNeighbors')
interior = _math(group, 'COMPARE', neighbors.outputs['Face Count'], 2)
interior = compare_node(group, 'EQUAL', neighbors.outputs['Face Count'], 2, data_type='INT')
fold = nodes.new('FunctionNodeBooleanMath')
fold.operation = 'AND'
links.new(edge_plane.outputs[0], fold.inputs[0])
@@ -147,7 +148,7 @@ def _clean_crease(group, geometry, tolerance):
def _off_plane_edge_field(group, tolerance):
"""Report one on edges that keep an endpoint off the symmetry plane."""
heights = _edge_abs_y(group)
return _math(group, 'GREATER_THAN', _math(group, 'MAXIMUM', heights[0], heights[1]), tolerance)
return compare_node(group, 'GREATER_THAN', _math(group, 'MAXIMUM', heights[0], heights[1]), tolerance)
def _edge_abs_y(group):
@@ -179,14 +180,9 @@ def _boundary_fields(group, tolerance):
links.new(position.outputs[0], components.inputs[0])
seam = nodes.new('FunctionNodeBooleanMath')
seam.operation = 'AND'
links.new(_math(group, 'GREATER_THAN', boundary, 0), seam.inputs[0])
links.new(compare_node(group, 'GREATER_THAN', boundary, 0), seam.inputs[0])
links.new(
_math(
group,
'LESS_THAN',
_math(group, 'ABSOLUTE', components.outputs['Y']),
tolerance,
),
compare_node(group, 'LESS_THAN', _math(group, 'ABSOLUTE', components.outputs['Y']), tolerance),
seam.inputs[1],
)
return boundary, seam.outputs[0]
@@ -306,12 +302,16 @@ def build_image_cutout_symmetry_group():
links.new(is_boundary, boundary.inputs[0])
# The seam sits on the plane and welds through the merge, so only off-plane edges need walls.
links.new(_off_plane_edge_field(group, snap_tolerance), boundary.inputs[1])
fill_smooth = _math(group, 'MULTIPLY', inputs['Fill Smooth'], inputs['Fill Sides'])
front = _relax_fill(group, front, boundary_field, fill_smooth)
fill_smooth = nodes.new('GeometryNodeSwitch')
fill_smooth.input_type = 'INT'
fill_smooth.inputs['False'].default_value = 0
links.new(inputs['Fill Sides'], fill_smooth.inputs['Switch'])
links.new(inputs['Fill Smooth'], fill_smooth.inputs['True'])
front = _relax_fill(group, front, boundary_field, fill_smooth.outputs[0])
walled = _build_wall(group, front, boundary.outputs[0])
wall_shade = nodes.new('GeometryNodeSetShadeSmooth')
wall_shade.domain = 'FACE'
links.new(_math(group, 'COMPARE', read_float_attribute(group, FACE_ATTRIBUTE), 3), wall_shade.inputs['Selection'])
links.new(compare_node(group, 'EQUAL', read_float_attribute(group, FACE_ATTRIBUTE), 3), wall_shade.inputs['Selection'])
links.new(walled, wall_shade.inputs['Geometry'])
body = nodes.new('GeometryNodeSwitch')
body.input_type = 'GEOMETRY'
@@ -323,14 +323,14 @@ def build_image_cutout_symmetry_group():
mark_back.inputs['Name'].default_value = FACE_ATTRIBUTE
mark_back.inputs['Value'].default_value = 2
links.new(_mirror(group, body.outputs[0]), mark_back.inputs['Geometry'])
links.new(_math(group, 'COMPARE', read_float_attribute(group, FACE_ATTRIBUTE), 1), mark_back.inputs['Selection'])
links.new(compare_node(group, 'EQUAL', read_float_attribute(group, FACE_ATTRIBUTE), 1), mark_back.inputs['Selection'])
join = nodes.new('GeometryNodeJoinGeometry')
for geometry in (body.outputs[0], mark_back.outputs['Geometry']):
links.new(geometry, join.inputs[0])
position = nodes.new('GeometryNodeInputPosition')
axis = nodes.new('ShaderNodeSeparateXYZ')
links.new(position.outputs[0], axis.inputs[0])
on_axis = _math(group, 'LESS_THAN', _math(group, 'ABSOLUTE', axis.outputs['Y']), snap_tolerance)
on_axis = compare_node(group, 'LESS_THAN', _math(group, 'ABSOLUTE', axis.outputs['Y']), snap_tolerance)
merge = nodes.new('GeometryNodeMergeByDistance')
merge.inputs['Distance'].default_value = 0.01
links.new(join.outputs[0], merge.inputs['Geometry'])