"""Clean cutout boundary triangles and taper the split thickness profile.""" from .depth_surface import _math from .nodes import ( boolean_node, compare_node, evaluate_field, group_input, read_float_attribute, sample_field, store_float_attribute, ) from .smoothing import edge_boundary_field from .boundary_smoothing import boundary_influence def remove_boundary_triangles(group, geometry): """Remove all-boundary triangles once on the current topology.""" nodes, links = group.nodes, group.links rim = evaluate_field(group, edge_boundary_field(nodes, links), "FLOAT", "POINT") all_boundary = compare_node(group, "GREATER_THAN", evaluate_field(group, rim, "FLOAT", "FACE"), 0.99999) count = nodes.new("GeometryNodeInputMeshFaceNeighbors").outputs["Vertex Count"] selected = boolean_node(group, "AND", all_boundary, compare_node(group, "EQUAL", count, 3, data_type="INT")) delete = nodes.new("GeometryNodeDeleteGeometry") delete.domain, delete.mode = "FACE", "ALL" links.new(geometry, delete.inputs["Geometry"]) links.new(selected, delete.inputs["Selection"]) return delete.outputs["Geometry"] def original_boundary_field(group, original, rim=None): """Sample the unsplit outline in UV space after projection and separation.""" nodes, links = group.nodes, group.links uv = nodes.new("GeometryNodeInputNamedAttribute") uv.data_type = "FLOAT_VECTOR" uv.inputs["Name"].default_value = "UVMap" original_uv = nodes.new("GeometryNodeSetPosition") links.new(original, original_uv.inputs["Geometry"]) links.new(uv.outputs["Attribute"], original_uv.inputs["Position"]) nearest = nodes.new("GeometryNodeSampleNearest") nearest.domain = "POINT" links.new(original_uv.outputs["Geometry"], nearest.inputs["Geometry"]) links.new(uv.outputs["Attribute"], nearest.inputs["Sample Position"]) if rim is None: rim = evaluate_field(group, edge_boundary_field(nodes, links), "FLOAT", "POINT") return sample_field(group, original, rim, "FLOAT", index=nearest.outputs["Index"]) def taper_split_profile(group, geometry, original_boundary): """Taper balloon thickness through the shared two-ring influence builder.""" nodes = group.nodes links = group.links influence = boundary_influence(group, original_boundary) controls = group_input(nodes, {"Depth Split"}) has_split = compare_node(group, "GREATER_THAN", controls.outputs["Depth Split"], 0) band = nodes.new("GeometryNodeSwitch") band.input_type = "FLOAT" band.inputs["False"].default_value = 0.0 links.new(has_split, band.inputs["Switch"]) links.new(influence, band.inputs["True"]) band = band.outputs[0] retained = _math(group, "SQRT", _math(group, "MAXIMUM", _math(group, "SUBTRACT", 1, band), 0)) profile = _math(group, "MULTIPLY", read_float_attribute(group, "o_balloon"), retained) return store_float_attribute(group, geometry, "o_balloon", profile)