Add IRCSS gothic cone translation
This commit is contained in:
parent
6d6b93c3bf
commit
fdda6184e8
8 changed files with 320 additions and 19 deletions
|
|
@ -258,12 +258,15 @@ Completed Blender raycast minigame foothold:
|
|||
Completed IRCSS French Houses foothold:
|
||||
|
||||
- `docs/research/translations/ircss-french-houses.md`
|
||||
- Source group: `MakeSpiral`
|
||||
- Verification: output curve converted to points matches source with ordered
|
||||
max vertex delta `0.0`.
|
||||
- Source groups: `MakeSpiral`, `PointyGothicCone`
|
||||
- Verification: spiral output curve converted to points matches source with
|
||||
ordered max vertex delta `0.0`; plain and ornamented gothic cone cases match
|
||||
source evaluated mesh vertices with sorted max vertex delta `0.0`.
|
||||
- Translation value: procedural-city corpus triage, spiral architectural
|
||||
spines, and explicit blockers for repeat-zone feedback, repeat `Top` outputs,
|
||||
foreach zones, gizmo nodes, and converter duplicate-key failures.
|
||||
spines, roof-cap ornament grown from structural ribs, captured source points
|
||||
through instancing, and explicit blockers for repeat-zone feedback, repeat
|
||||
`Top` outputs, foreach zones, gizmo nodes, and converter duplicate-key
|
||||
failures.
|
||||
|
||||
Inspected but deferred official demo files:
|
||||
|
||||
|
|
|
|||
|
|
@ -547,6 +547,49 @@ copies and become individual geometry you can bend, cut, or shade.
|
|||
Verification cue: test more than one seed/control set. Scatter graphs can match
|
||||
counts at one setting while density, scale, or captured-root behavior is wrong.
|
||||
|
||||
## Ornament Grows On Structure
|
||||
|
||||
Reach for this when a form has a big readable architectural body plus smaller
|
||||
detail that should cling to that body: gothic caps, roof ribs, crenellations,
|
||||
trim, bolts, vines, scales, and surface greebles.
|
||||
|
||||
Mental move: build the host silhouette first, capture the host features that
|
||||
detail needs, then sample or convert those features into stations for the
|
||||
ornament. Do not scatter decorative pieces in empty space and hope they look
|
||||
attached.
|
||||
|
||||
```python
|
||||
point_positions = _capture_attribute_item("VECTOR", "POINT", geometry, position(), "source position")
|
||||
cone_seed = cylinder(fill_type="NGON", vertices=6, side_segments=1, fill_segments=1, radius=1.0, depth=1.0)
|
||||
tapered_cone = scale_elements(
|
||||
domain="FACE",
|
||||
geometry=set_position(geometry=cone_seed.mesh, offset=(0.0, 0.0, 0.5)),
|
||||
selection=cone_seed.top,
|
||||
scale=0.10000002384185791,
|
||||
scale_mode="Uniform",
|
||||
)
|
||||
edge_flags = _capture_attribute_item("BOOLEAN", "EDGE", tapered_cone, cap_or_body_edge, "edge trim")
|
||||
cone_instances = instance_on_points(points=point_positions.geometry, instance=cone_with_skirt.mesh, scale=cone_scale)
|
||||
realized_cones = realize_instances(geometry=cone_instances, realize_all=True, depth=0)
|
||||
|
||||
detail_edges = delete_geometry(mode="ALL", domain="EDGE", geometry=realized_cones, selection=edge_flags.edge_trim)
|
||||
detail_points = resample_curve(
|
||||
keep_last_segment=True,
|
||||
curve=mesh_to_curve(mode="EDGES", mesh=detail_edges),
|
||||
mode="Length",
|
||||
length=gothic_detail_density,
|
||||
)
|
||||
details = instance_on_points(points=detail_points, instance=stretched_detail, rotation=detail_rotation, scale=detail_scale)
|
||||
```
|
||||
|
||||
Metaphor in use: the big form is scaffolding, and ornament is ivy on that
|
||||
scaffolding. If the scaffold is not built first, the ivy floats there looking
|
||||
confident and wrong.
|
||||
|
||||
Verification cue: test the plain host and the ornamented host separately. The
|
||||
IRCSS `PointyGothicCone` translation verifies both `SpawnDetails=False` and
|
||||
`SpawnDetails=True` against evaluated source geometry.
|
||||
|
||||
## Constraint Helpers Are Bumpers
|
||||
|
||||
Reach for constraint helpers when a procedural system has a small physical rule
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
# Translation: IRCSS French Houses Foothold
|
||||
# Translation: IRCSS French Houses Footholds
|
||||
|
||||
## Source
|
||||
|
||||
- Repository: https://github.com/IRCSS/Blender-Geometry-Node-French-Houses
|
||||
- Source file:
|
||||
`GeometryNodesFrenchHous.blend`
|
||||
- Source group translated in this pass:
|
||||
- Source groups translated so far:
|
||||
- `MakeSpiral`
|
||||
- `PointyGothicCone`
|
||||
- Geometry Script recreation:
|
||||
`examples/geometry_script/ircss_french_houses.py`
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ parts, stalls, cliffs, roads, rivers, vegetation, banners, windows, doors, and
|
|||
roof systems. This is the right quarry for procedural-city doctrine, but the
|
||||
first accepted bite has to be small enough to verify.
|
||||
|
||||
## First Accepted Graph
|
||||
## Accepted Graph: MakeSpiral
|
||||
|
||||
Source group: `MakeSpiral`
|
||||
|
||||
|
|
@ -63,9 +64,35 @@ Results from Blender 5.1.1:
|
|||
| Case | Source vertices | Translated vertices | Delta | Status |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| MakeSpiral curve points | 200 | 200 | 0.0 | accepted |
|
||||
| PointyGothicCone plain | 36 | 36 | 0.0 | accepted |
|
||||
| PointyGothicCone with details | 138 | 138 | 0.0 | accepted |
|
||||
|
||||
The verifier converts the output curve to evaluated points and compares ordered
|
||||
vertex positions.
|
||||
## Accepted Graph: PointyGothicCone
|
||||
|
||||
Source group: `PointyGothicCone`
|
||||
|
||||
Geometry Script group: `VG Pointy Gothic Cone`
|
||||
|
||||
Mechanism:
|
||||
|
||||
- Capture each input point position before instancing starts.
|
||||
- Build a tapered six-sided cone seed from a cylinder.
|
||||
- Capture an edge-domain trim mask that marks cone cap/body edges.
|
||||
- Instance one scaled cone at each input point.
|
||||
- When `SpawnDetails` is enabled, delete the marked cone edges, convert the
|
||||
remaining ribs to curves, resample them into detail stations, and instance
|
||||
small tapered five-sided spikes along those ribs.
|
||||
- Orient each detail spike toward the horizontal direction from the source
|
||||
point to the current rib point.
|
||||
|
||||
Metaphor: the input points are roof pegs. The main cone is the roof cap hung on
|
||||
each peg; the optional details grow from the cap ribs after the cap is already
|
||||
real enough to have edges. Build the big readable silhouette first, then let
|
||||
ornament use that structure.
|
||||
|
||||
The spiral case converts the output curve to evaluated points and compares
|
||||
ordered vertex positions. Cone cases feed identical synthetic point meshes into
|
||||
the source and translation, then compare sorted evaluated vertices.
|
||||
|
||||
## Blockers Exposed
|
||||
|
||||
|
|
@ -90,6 +117,11 @@ vertex positions.
|
|||
- City-scale graphs are hierarchies of organs. Start with reusable spines,
|
||||
tiles, rails, façade parts, and topology generators before translating a
|
||||
thousand-node composer.
|
||||
- Ornament often needs a structural host. `PointyGothicCone` first creates the
|
||||
readable cap silhouette, then derives detail placement from the cap's own
|
||||
realized ribs.
|
||||
- Captures are not optional bookkeeping when instancing starts. The original
|
||||
point position and cone edge mask both have to survive later domain changes.
|
||||
- Converter drafts are still useful for triage: they quickly separate clean
|
||||
helpers, repeat-zone blockers, foreach-zone blockers, gizmo-specific groups,
|
||||
and false symbol names.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,21 @@ except ModuleNotFoundError:
|
|||
load_repo_geometry_script()
|
||||
|
||||
from geometry_script import * # noqa: F403 - Geometry Script exposes node functions as DSL globals.
|
||||
from geometry_script.api.node_mapper import OutputsList, set_or_create_link
|
||||
from geometry_script.api.state import State
|
||||
from geometry_script.api.types import Type
|
||||
|
||||
|
||||
def _capture_attribute_item(data_type: str, domain: str, geometry, value, name: str):
|
||||
node = State.current_node_tree.nodes.new("GeometryNodeCaptureAttribute")
|
||||
node.capture_items.new(data_type, name)
|
||||
node.active_index = 0
|
||||
node.domain = domain
|
||||
set_or_create_link(geometry, node.inputs[0])
|
||||
set_or_create_link(value, node.inputs[1])
|
||||
return OutputsList({"geometry": Type(node.outputs[0]), name.lower().replace(" ", "_"): Type(node.outputs[1])})
|
||||
|
||||
|
||||
@tree("VG Make Spiral")
|
||||
def vg_make_spiral(
|
||||
frequency: Float = 12.0,
|
||||
|
|
@ -40,11 +55,130 @@ def vg_make_spiral(
|
|||
return {"Geometry": set_position(geometry=samples, position=combine_xyz(x=x, y=y, z=z), offset=(0.0, 0.0, 0.0))}
|
||||
|
||||
|
||||
@tree("VG Pointy Gothic Cone")
|
||||
def vg_pointy_gothic_cone(
|
||||
geometry: Geometry,
|
||||
column_radius: Float = 0.3,
|
||||
peak_height: Float = 1.0,
|
||||
gothic_detail_density: Float = 0.6,
|
||||
detail_scale: Float = 0.1,
|
||||
spawn_details: Bool = True,
|
||||
rotation: Rotation = (0.0, 0.0, 0.0),
|
||||
):
|
||||
point_positions = _capture_attribute_item("VECTOR", "POINT", geometry, position(), "source position")
|
||||
|
||||
cone_seed = cylinder(fill_type="NGON", vertices=6, side_segments=1, fill_segments=1, radius=1.0, depth=1.0)
|
||||
raised_cone = set_position(geometry=cone_seed.mesh, offset=(0.0, 0.0, 0.5))
|
||||
tapered_cone = scale_elements(
|
||||
domain="FACE",
|
||||
geometry=raised_cone,
|
||||
selection=cone_seed.top,
|
||||
scale=0.10000002384185791,
|
||||
scale_mode="Uniform",
|
||||
axis=(1.0, 0.0, 0.0),
|
||||
)
|
||||
|
||||
not_side = boolean_math(operation="NOT", boolean=cone_seed.side)
|
||||
cap_or_body_edge = boolean_math(
|
||||
operation="OR",
|
||||
boolean=(boolean_math(operation="OR", boolean=(not_side, cone_seed.top)), cone_seed.bottom),
|
||||
)
|
||||
cone_edge_flags = _capture_attribute_item("BOOLEAN", "EDGE", tapered_cone, cap_or_body_edge, "edge trim")
|
||||
|
||||
scaled_cone_position = vector_math(
|
||||
operation="MULTIPLY",
|
||||
vector=(position(), combine_xyz(x=1.0, y=1.0, z=peak_height)),
|
||||
)
|
||||
shaped_cone = set_position(geometry=cone_edge_flags.geometry, position=scaled_cone_position, offset=(0.0, 0.0, 0.0))
|
||||
|
||||
skirt_depth = math(
|
||||
operation="MINIMUM",
|
||||
value=(math(operation="MULTIPLY", value=(peak_height, 0.20000000298023224)), 1.0),
|
||||
)
|
||||
cone_with_skirt = extrude_mesh(
|
||||
mode="FACES",
|
||||
mesh=shaped_cone,
|
||||
selection=cone_seed.bottom,
|
||||
offset_scale=skirt_depth,
|
||||
individual=False,
|
||||
)
|
||||
|
||||
cone_scale = combine_xyz(x=column_radius, y=column_radius, z=1.0)
|
||||
cone_instances = instance_on_points(
|
||||
points=point_positions.geometry,
|
||||
instance=cone_with_skirt.mesh,
|
||||
pick_instance=False,
|
||||
rotation=rotation,
|
||||
scale=cone_scale,
|
||||
)
|
||||
realized_cones = realize_instances(
|
||||
realize_to_point_domain=True,
|
||||
geometry=cone_instances,
|
||||
realize_all=True,
|
||||
depth=0,
|
||||
)
|
||||
|
||||
details_disabled = boolean_math(operation="NOT", boolean=spawn_details)
|
||||
cone_for_detail_edges = delete_geometry(mode="ALL", domain="POINT", geometry=realized_cones, selection=details_disabled)
|
||||
cone_ribs = delete_geometry(mode="ALL", domain="EDGE", geometry=cone_for_detail_edges, selection=cone_edge_flags.edge_trim)
|
||||
rib_curve = mesh_to_curve(mode="EDGES", mesh=cone_ribs)
|
||||
detail_points = resample_curve(
|
||||
keep_last_segment=True,
|
||||
curve=rib_curve,
|
||||
mode="Length",
|
||||
count=5,
|
||||
length=gothic_detail_density,
|
||||
)
|
||||
|
||||
from_source_point = vector_math(operation="SUBTRACT", vector=(position(), point_positions.source_position))
|
||||
z_from_source = separate_xyz(vector=from_source_point)
|
||||
below_peak = compare(
|
||||
operation="LESS_THAN",
|
||||
data_type="FLOAT",
|
||||
mode="ELEMENT",
|
||||
a=math(operation="SUBTRACT", value=(math(operation="ABSOLUTE", value=z_from_source.z), peak_height)),
|
||||
b=-0.09999924898147583,
|
||||
)
|
||||
|
||||
detail_seed = cylinder(fill_type="NGON", vertices=5, side_segments=1, fill_segments=1, radius=1.0, depth=1.0)
|
||||
detail_taper = scale_elements(
|
||||
domain="FACE",
|
||||
geometry=detail_seed.mesh,
|
||||
selection=detail_seed.top,
|
||||
scale=0.20000001788139343,
|
||||
scale_mode="Uniform",
|
||||
axis=(1.0, 0.0, 0.0),
|
||||
)
|
||||
raised_detail = set_position(geometry=detail_taper, offset=(0.0, 0.0, 0.30000001192092896))
|
||||
stretched_detail = set_position(
|
||||
geometry=raised_detail,
|
||||
position=vector_math(operation="MULTIPLY", vector=(position(), (1.0, 1.0, 2.0))),
|
||||
offset=(0.0, 0.0, 0.0),
|
||||
)
|
||||
horizontal_rib_direction = vector_math(operation="MULTIPLY", vector=(from_source_point, (1.0, 1.0, 0.0)))
|
||||
detail_rotation = align_rotation_to_vector(axis="Z", pivot_axis="AUTO", factor=1.0, vector=horizontal_rib_direction)
|
||||
detail_instances = instance_on_points(
|
||||
points=detail_points,
|
||||
selection=below_peak,
|
||||
instance=stretched_detail,
|
||||
pick_instance=False,
|
||||
rotation=detail_rotation,
|
||||
scale=detail_scale,
|
||||
)
|
||||
realized_details = realize_instances(
|
||||
realize_to_point_domain=True,
|
||||
geometry=detail_instances,
|
||||
realize_all=True,
|
||||
depth=0,
|
||||
)
|
||||
return {"Geometry": join_geometry(geometry=[realized_cones, realized_details])}
|
||||
|
||||
|
||||
def _finalize_groups():
|
||||
import bpy
|
||||
|
||||
groups = []
|
||||
for name in ("VG Make Spiral",):
|
||||
for name in ("VG Make Spiral", "VG Pointy Gothic Cone"):
|
||||
group = bpy.data.node_groups.get(name)
|
||||
if group:
|
||||
group.use_fake_user = True
|
||||
|
|
|
|||
|
|
@ -39,14 +39,15 @@ Accepted corpus families so far:
|
|||
- Quellenform `CurveToMeshUV` utility sample.
|
||||
- Official Blender demo tranche: instance attributes, index-of-nearest,
|
||||
shortest-path labyrinth, and raycast minigame helpers.
|
||||
- IRCSS French Houses foothold: `MakeSpiral -> VG Make Spiral`, verified with
|
||||
200 ordered curve points matching source geometry at max delta `0.0`.
|
||||
- IRCSS French Houses footholds: `MakeSpiral -> VG Make Spiral` and
|
||||
`PointyGothicCone -> VG Pointy Gothic Cone`, verified against source
|
||||
evaluated geometry with max delta `0.0`.
|
||||
|
||||
Current frontier: IRCSS French Houses city-scale graph work. The accepted
|
||||
foothold is `MakeSpiral`. Mapped blockers are repeat-zone feedback state,
|
||||
repeat `Top` boolean outputs, foreach zones, gizmo nodes, `nodes_to_script`
|
||||
duplicate-key failures around axes-to-rotation, and a rejected empty-output
|
||||
harness for `WindowBeams`.
|
||||
footholds are `MakeSpiral` and `PointyGothicCone`. Mapped blockers are
|
||||
repeat-zone feedback state, repeat `Top` boolean outputs, foreach zones, gizmo
|
||||
nodes, `nodes_to_script` duplicate-key failures around axes-to-rotation, and a
|
||||
rejected empty-output harness for `WindowBeams`.
|
||||
|
||||
Current authoring prior: use the full Python stack. Python structures intent,
|
||||
tables, loops, variants, cleanup, validation, docs, and scene/render
|
||||
|
|
|
|||
|
|
@ -28,3 +28,4 @@
|
|||
{"ts": "2026-05-04T16:56:12+00:00", "type": "implementation", "status": "accepted", "note": "Expanded the verified Geometry Nodes translation corpus with official Blender demo samples: instance attributes grass tuft, index-of-nearest helpers, shortest-path labyrinth, and raycast minigame helpers. All new verifiers pass in Blender 5.1.1 with sorted max vertex delta 0.0 across their harness cases. Deferred official samples are classified as legacy-node, gizmo-node, simulation-zone, or repeat-zone targets rather than padded into doctrine."}
|
||||
{"ts": "2026-05-04T17:15:09+00:00", "type": "research", "status": "accepted", "note": "Started IRCSS French Houses city-scale corpus target. Inspected 108 geometry node groups and verified first foothold MakeSpiral -> VG Make Spiral with 200 ordered curve points matching source at max delta 0.0. Recorded blockers from larger city graphs: repeat-zone feedback state, repeat Top output sockets, foreach zones, gizmo nodes, converter duplicate-key failures, and empty-output harness rejection."}
|
||||
{"ts": "2026-05-04T18:01:03+00:00", "type": "maintenance", "status": "accepted", "note": "Distilled persistent state: handoff now points to corpus docs instead of duplicating completed graph lists; implementation plan reflects post-corpus contract and IRCSS frontier; compaction helper warns on bloated handoff and stale first-corpus planning."}
|
||||
{"ts": "2026-05-04T19:01:27+00:00", "type": "verification", "status": "accepted", "note": "Added IRCSS PointyGothicCone translation as VG Pointy Gothic Cone; plain and ornamented harness cases match source evaluated geometry with sorted max vertex delta 0.0, proving ornament can be grown from captured structural ribs after instancing."}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ data_flow:
|
|||
- If state grows faster than understanding, cut it back.
|
||||
|
||||
current_status:
|
||||
summary: VibeGeometry has an initial persistence scaffold plus verified Geometry Script translations for local math/utility samples, a first official Blender demo tranche, and an initial IRCSS French Houses city-scale foothold. The IRCSS pass verified MakeSpiral and mapped blockers in larger architectural systems: repeat-zone feedback, repeat Top outputs, foreach zones, gizmo nodes, and converter duplicate-key failures. The repo-local Geometry Script fork includes a Blender 5.1 nodes_to_script converter port plus nested-group and menu-socket fixes needed by these translations.
|
||||
summary: VibeGeometry has an initial persistence scaffold plus verified Geometry Script translations for local math/utility samples, a first official Blender demo tranche, and two IRCSS French Houses city-scale footholds. The IRCSS pass verified MakeSpiral as an architectural spine and PointyGothicCone as ornament grown from a structural host, while mapping blockers in larger systems: repeat-zone feedback, repeat Top outputs, foreach zones, gizmo nodes, and converter duplicate-key failures. The repo-local Geometry Script fork includes a Blender 5.1 nodes_to_script converter port plus nested-group and menu-socket fixes needed by these translations.
|
||||
next_action: Continue IRCSS French Houses by choosing the next city-organ target after either fixing repeat-zone/foreach/gizmo toolchain blockers or selecting another non-zone architectural helper with a non-empty behavioral harness.
|
||||
remotes:
|
||||
vibegeometry: https://github.com/GameCult/VibeGeometry
|
||||
|
|
|
|||
|
|
@ -19,6 +19,32 @@ CASE = {
|
|||
"Height": 3.0,
|
||||
"HeightRadiusGain": 0.25,
|
||||
}
|
||||
CONE_CASES = [
|
||||
{
|
||||
"case": "pointy_gothic_cone_plain",
|
||||
"inputs": {
|
||||
"ColumnRadius": 0.45,
|
||||
"PeakHeight": 1.4,
|
||||
"GothicDetailDensity": 0.6,
|
||||
"DetailScale": 0.1,
|
||||
"SpawnDetails": False,
|
||||
},
|
||||
"point_count": 2,
|
||||
"offset": (1.25, 0.0, 0.0),
|
||||
},
|
||||
{
|
||||
"case": "pointy_gothic_cone_with_details",
|
||||
"inputs": {
|
||||
"ColumnRadius": 0.35,
|
||||
"PeakHeight": 1.2,
|
||||
"GothicDetailDensity": 0.55,
|
||||
"DetailScale": 0.08,
|
||||
"SpawnDetails": True,
|
||||
},
|
||||
"point_count": 1,
|
||||
"offset": (0.0, 0.0, 0.0),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _load_translation() -> None:
|
||||
|
|
@ -78,6 +104,31 @@ def _build_spiral_wrapper(name: str, group: bpy.types.GeometryNodeTree):
|
|||
return wrapper
|
||||
|
||||
|
||||
def _add_mesh_line(nodes, *, count: int, offset):
|
||||
line = nodes.new("GeometryNodeMeshLine")
|
||||
line.mode = "OFFSET"
|
||||
line.count_mode = "TOTAL"
|
||||
_socket_by_name(line.inputs, "Count").default_value = count
|
||||
_socket_by_name(line.inputs, "Offset").default_value = offset
|
||||
return line
|
||||
|
||||
|
||||
def _build_pointy_cone_wrapper(name: str, group: bpy.types.GeometryNodeTree, inputs: dict[str, object], point_count: int, offset):
|
||||
wrapper = _new_geometry_group(name)
|
||||
nodes = wrapper.nodes
|
||||
links = wrapper.links
|
||||
line = _add_mesh_line(nodes, count=point_count, offset=offset)
|
||||
|
||||
group_node = nodes.new("GeometryNodeGroup")
|
||||
group_node.node_tree = group
|
||||
_set_inputs(group_node, inputs)
|
||||
links.new(_socket_by_name(line.outputs, "Mesh"), _socket_by_name(group_node.inputs, "Geometry"))
|
||||
|
||||
group_output = nodes.new("NodeGroupOutput")
|
||||
links.new(_socket_by_name(group_node.outputs, "Geometry"), _socket_by_name(group_output.inputs, "Geometry"))
|
||||
return wrapper
|
||||
|
||||
|
||||
def _evaluated_vertices(group: bpy.types.GeometryNodeTree):
|
||||
mesh = bpy.data.meshes.new(group.name + " Input Mesh")
|
||||
obj = bpy.data.objects.new(group.name + " Object", mesh)
|
||||
|
|
@ -111,7 +162,43 @@ def main() -> int:
|
|||
"max_ordered_vertex_delta": max_delta,
|
||||
}
|
||||
result["ok"] = len(source) == len(translated) and max_delta <= 1e-6
|
||||
payload = {"ok": result["ok"], "results": [result]}
|
||||
results = [result]
|
||||
|
||||
for case in CONE_CASES:
|
||||
source = _evaluated_vertices(
|
||||
_build_pointy_cone_wrapper(
|
||||
f"VG Verify Source {case['case']}",
|
||||
bpy.data.node_groups["PointyGothicCone"],
|
||||
case["inputs"],
|
||||
case["point_count"],
|
||||
case["offset"],
|
||||
)
|
||||
)
|
||||
translated = _evaluated_vertices(
|
||||
_build_pointy_cone_wrapper(
|
||||
f"VG Verify Translated {case['case']}",
|
||||
bpy.data.node_groups["VG Pointy Gothic Cone"],
|
||||
case["inputs"],
|
||||
case["point_count"],
|
||||
case["offset"],
|
||||
)
|
||||
)
|
||||
max_delta = (
|
||||
max((math.dist(a, b) for a, b in zip(sorted(source), sorted(translated))), default=0.0)
|
||||
if len(source) == len(translated)
|
||||
else math.inf
|
||||
)
|
||||
results.append(
|
||||
{
|
||||
"case": case["case"],
|
||||
"source_vertex_count": len(source),
|
||||
"translated_vertex_count": len(translated),
|
||||
"max_sorted_vertex_delta": max_delta,
|
||||
"ok": len(source) == len(translated) and max_delta <= 1e-6,
|
||||
}
|
||||
)
|
||||
|
||||
payload = {"ok": all(item["ok"] for item in results), "results": results}
|
||||
print("VG_IRCSS_FRENCH_HOUSES_TRANSLATION_BEHAVIOR " + json.dumps(payload, sort_keys=True))
|
||||
return 0 if payload["ok"] else 1
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue