Expand verified geometry node corpus
This commit is contained in:
parent
dbf72e1f09
commit
9bc09bb7c2
17 changed files with 1578 additions and 28 deletions
|
|
@ -74,6 +74,7 @@ current VibeGeometry toolchain branch, and applies the patches stored in:
|
|||
```text
|
||||
patches/geometry-script/0001-fix-nested-tree-group-references-on-blender-4.patch
|
||||
patches/geometry-script/0002-Port-node-tree-conversion-operator.patch
|
||||
patches/geometry-script/0003-Tolerate-Blender-menu-socket-defaults.patch
|
||||
```
|
||||
|
||||
`external/` is intentionally ignored by the VibeGeometry repository. The clone
|
||||
|
|
@ -93,6 +94,8 @@ This is the branch VibeGeometry should use locally. It contains:
|
|||
- the nested `@tree` group fix from
|
||||
`vibegeometry/blender-5-nested-tree-groups`
|
||||
- a Blender 5.1 port of the historical `nodes_to_script` converter prototype
|
||||
- a Blender 5.1 menu-socket default tolerance fix so group construction does
|
||||
not fail when `NodeSocketMenu` enum items are not populated yet
|
||||
|
||||
The converter adds:
|
||||
|
||||
|
|
@ -123,6 +126,10 @@ Known limits:
|
|||
- Generated names and ordering reflect extraction mechanics, not spatial intent.
|
||||
- Behavioral equivalence still needs the same evaluated-geometry or scalar
|
||||
harness verification used by the hand translations.
|
||||
- Blender 5 capture items are still a live gap in Geometry Script's generic
|
||||
node builder. The accepted `VG Field Value` translation creates typed capture
|
||||
items explicitly in script instead of relying on the generated
|
||||
`capture_attribute(...)` wrapper.
|
||||
|
||||
## Upstream PR Branch
|
||||
|
||||
|
|
|
|||
|
|
@ -134,13 +134,32 @@ Completed field value helper entry:
|
|||
`Mesh Line.Count` with the result; next-digit branch helper matches source
|
||||
across four cases spanning both sides of `Max Precision`; delete-mask scalar
|
||||
helper matches source across 195 digit/segment-position cases with zero
|
||||
mismatches.
|
||||
mismatches; full `Field Value` default-route geometry matches source with
|
||||
256 vertices and sorted max vertex delta `0.0`.
|
||||
- Toolchain note: full `Field Value` required explicit Blender 5 capture-item
|
||||
creation and a Geometry Script fork fix for menu socket defaults.
|
||||
|
||||
Boundary note: the full `Field Value` graph is a 176-node composition involving
|
||||
menu switches, capture attributes, sample-index reads, instances, transforms,
|
||||
and delete geometry. Before translating it by hand, test or port upstream's
|
||||
historical `nodes_to_script` converter so VibeGeometry gets a generated first
|
||||
draft to inspect.
|
||||
Completed parametric/polar equation entry:
|
||||
|
||||
- `docs/research/translations/shriinivas-paramnpolareq.md`
|
||||
- Source groups: `NodeGroup`, `NodeGroup.001`, `NodeGroup.002`,
|
||||
`NodeGroup.003`, `Archimedes Spiral`, `Epicycloid`,
|
||||
`Geometry Nodes Group.002`
|
||||
- Verification: scalar equation helpers match by placing outputs into a single
|
||||
vertex position; Archimedes, epicycloid, and mirrored root spiral evaluated
|
||||
vertices match source geometry with max sorted deltas at or near `0.0`.
|
||||
- Translation value: accumulated parameter ramps, parametric equations, polar
|
||||
conversion, mirrored curve streams, and scalar-harness design.
|
||||
|
||||
Started CurveToMeshUV utility entry:
|
||||
|
||||
- `docs/research/translations/quellenform-curve-to-mesh-uv.md`
|
||||
- Source group translated so far: `Auto Smooth`
|
||||
- Verification: source and translated `Auto Smooth` preserve cube geometry and
|
||||
match smooth-face flags in Blender 5.1.1.
|
||||
- Boundary: the main `Curve to Mesh UV` group is a 126-node utility graph and
|
||||
should be approached through staged subcontracts: curve resampling, spline
|
||||
parameter UVs, cap masks, named attributes, then final mesh output.
|
||||
|
||||
## Extraction Workflow
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,42 @@ Verification cue: test more than one parameter value. Field graphs often look
|
|||
right at one value and reveal swapped axes or stale constants when controls
|
||||
move.
|
||||
|
||||
## Parameters Are Conveyors
|
||||
|
||||
Reach for `Accumulate Field` when a curve or repeated structure needs a steady
|
||||
parameter ramp: `t`, `theta`, distance, age, row number, phase, growth amount.
|
||||
|
||||
Mental move: make a boring sequence of stations first, compute the step between
|
||||
stations, then let the graph stamp each station with the accumulated parameter.
|
||||
The equation or deformation reads that parameter downstream.
|
||||
|
||||
```python
|
||||
step = math(
|
||||
operation=Math.Operation.DIVIDE,
|
||||
value=(max_value, math(operation=Math.Operation.SUBTRACT, value=(resolution, 1.0))),
|
||||
)
|
||||
theta = accumulate_field(
|
||||
data_type=AccumulateField.DataType.FLOAT,
|
||||
domain=AccumulateField.Domain.POINT,
|
||||
value=step,
|
||||
).trailing
|
||||
|
||||
mesh = mesh_line(
|
||||
mode=MeshLine.Mode.OFFSET,
|
||||
count_mode=MeshLine.CountMode.TOTAL,
|
||||
count=resolution,
|
||||
offset=(0.0, 0.0, 1.0),
|
||||
)
|
||||
geometry = mesh.set_position(position=combine_xyz(x=x_from_theta, y=y_from_theta, z=0.0))
|
||||
```
|
||||
|
||||
Metaphor in use: the mesh line is a conveyor belt. `Accumulate Field` stamps
|
||||
each station with a larger number, and the curve equation tells that station
|
||||
where to stand.
|
||||
|
||||
Verification cue: compare evaluated vertices at a non-default resolution and
|
||||
range. Off-by-one errors hide at endpoints.
|
||||
|
||||
## Set Position Is The Hinge
|
||||
|
||||
Reach for `set_position` when your graph has built an invisible spatial rule
|
||||
|
|
@ -211,6 +247,44 @@ attribute lets some stations use a wider bit without changing the rail.
|
|||
Verification cue: inspect mesh vertex counts and bounding dimensions, not just
|
||||
the existence of a `Curve to Mesh` node.
|
||||
|
||||
## Surface Passes Are Polish Gates
|
||||
|
||||
Reach for surface-state utilities when geometry already exists but needs
|
||||
controlled smoothing, normals, material assignment, UVs, or attributes before
|
||||
it is usable downstream.
|
||||
|
||||
Mental move: keep shape generation separate from finishing state. In `VG Auto
|
||||
Smooth`, the mesh enters unchanged, edge angle and existing smooth flags decide
|
||||
edge smoothing, then the face domain is stamped smooth.
|
||||
|
||||
```python
|
||||
shallow_enough = compare(
|
||||
operation=Compare.Operation.LESS_EQUAL,
|
||||
data_type=Compare.DataType.FLOAT,
|
||||
mode=Compare.Mode.ELEMENT,
|
||||
a=edge_angle().unsigned_angle,
|
||||
b=angle,
|
||||
)
|
||||
edge_pass = set_shade_smooth(
|
||||
domain=SetShadeSmooth.Domain.EDGE,
|
||||
mesh=geometry,
|
||||
selection=is_edge_smooth(),
|
||||
shade_smooth=shallow_enough & is_face_smooth(),
|
||||
)
|
||||
geometry = set_shade_smooth(
|
||||
domain=SetShadeSmooth.Domain.FACE,
|
||||
mesh=edge_pass,
|
||||
selection=True,
|
||||
shade_smooth=True,
|
||||
)
|
||||
```
|
||||
|
||||
Metaphor in use: this is a polishing gate, not a forge. The form passes
|
||||
through; the gate decides which surface state is allowed to survive.
|
||||
|
||||
Verification cue: compare surface flags as well as vertices. A finishing graph
|
||||
can preserve shape perfectly while changing the thing it was built to control.
|
||||
|
||||
## Field Masks Are Chisels
|
||||
|
||||
Reach for field masks when a regular seed mesh should become a shaped part by
|
||||
|
|
@ -354,6 +428,38 @@ anything cumulative.
|
|||
Verification cue: test the composed graph, not only the segment. A baton can be
|
||||
correct locally but passed to the wrong next socket.
|
||||
|
||||
## Capture Before The Crowd
|
||||
|
||||
Reach for `Capture Attribute` when a value or index must survive a domain
|
||||
change, instancing layer, or later deletion pass.
|
||||
|
||||
Mental move: before geometry enters a crowd of instances, pin a name and domain
|
||||
to the value that later stages still need. In `VG Field Value`, the source
|
||||
value, glyph face index, digit-slot index, and vector-component index each have
|
||||
to ride through different instancing layers.
|
||||
|
||||
```python
|
||||
glyph_faces = _capture_attribute_item("INT", "FACE", segment_glyph, index(), "int field")
|
||||
digit_indexed = _capture_attribute_item("INT", "INSTANCE", digit_instances, index(), "int field")
|
||||
|
||||
hidden_segments = vg_delete_segments(
|
||||
digit=final_digit_code,
|
||||
segment_position=glyph_faces.int_field,
|
||||
)
|
||||
visible = delete_geometry(
|
||||
mode=DeleteGeometry.Mode.ALL,
|
||||
domain=DeleteGeometry.Domain.FACE,
|
||||
geometry=realized_digits,
|
||||
selection=hidden_segments,
|
||||
)
|
||||
```
|
||||
|
||||
Metaphor in use: capture is a luggage tag. Once geometry moves through airports
|
||||
of instances and domains, untagged values are gone in the baggage system.
|
||||
|
||||
Verification cue: test the full composed graph after realization or deletion,
|
||||
not just the captured helper. The point of capture is later survival.
|
||||
|
||||
## Branch Helpers Are Switchboards
|
||||
|
||||
Reach for branch helpers when the same output contract can be fed by multiple
|
||||
|
|
|
|||
64
docs/research/translations/quellenform-curve-to-mesh-uv.md
Normal file
64
docs/research/translations/quellenform-curve-to-mesh-uv.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Translation: quellenform Curve To Mesh UV Foothold
|
||||
|
||||
## Source
|
||||
|
||||
- Repository: https://github.com/quellenform/blender-CurveToMeshUV
|
||||
- Source file:
|
||||
`Curve to Mesh UV v1.7.02.230213-FREE - Blender 3.4+.blend`
|
||||
- Source group translated in this pass:
|
||||
- `Auto Smooth`
|
||||
- Geometry Script recreation:
|
||||
`examples/geometry_script/quellenform_curve_to_mesh_uv.py`
|
||||
|
||||
## Why This Graph
|
||||
|
||||
The main `Curve to Mesh UV` group is a 126-node utility network with curve
|
||||
resampling, spline parameters, UV fields, capture attributes, endpoint masks,
|
||||
domain-size accounting, and named attribute outputs. That is useful quarry, but
|
||||
not a single-pass snack.
|
||||
|
||||
`Auto Smooth` is the small utility attached to that system. It teaches how a
|
||||
graph can be a surface-finishing gate instead of a shape generator.
|
||||
|
||||
## Auto Smooth Map
|
||||
|
||||
Source group: `Auto Smooth`
|
||||
|
||||
Geometry Script group: `VG Auto Smooth`
|
||||
|
||||
Mechanism:
|
||||
|
||||
- Read the unsigned mesh edge angle.
|
||||
- Compare it against the exposed `Angle`.
|
||||
- AND that threshold result with the existing face smooth state.
|
||||
- On the edge domain, set shade smooth for already-smooth edges using that
|
||||
thresholded condition.
|
||||
- On the face domain, set shade smooth true for the full mesh.
|
||||
|
||||
Metaphor: the group is a polishing gate. It does not build the object. It
|
||||
decides where the surface may keep a smooth handshake across edges and then
|
||||
hands the mesh back to the larger utility.
|
||||
|
||||
## Verification
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
& 'C:\Program Files (x86)\Steam\steamapps\common\Blender\blender.exe' --background 'E:\Projects\VibeGeometry\experiments\source-repos\blender-CurveToMeshUV\Curve to Mesh UV v1.7.02.230213-FREE - Blender 3.4+.blend' --python '.\tools\verify_curve_to_mesh_uv_translations.py'
|
||||
```
|
||||
|
||||
Results from Blender 5.1.1:
|
||||
|
||||
| Case | Source | Translated | Delta | Status |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| Auto Smooth cube vertices | 8 | 8 | 0.0 | accepted |
|
||||
| Auto Smooth smooth faces | 6 | 6 | exact flags | accepted |
|
||||
|
||||
## Lessons
|
||||
|
||||
- Not every reusable node group creates form. Some preserve or condition
|
||||
surface state for a larger graph.
|
||||
- Edge-domain and face-domain passes can be stacked deliberately: first decide
|
||||
edge continuity, then stamp final face smoothness.
|
||||
- Utility mega-graphs should be entered through their small helpers first.
|
||||
They expose the local contracts the main group relies on.
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
- `Delete Segments`
|
||||
- `Next Digit`
|
||||
- `Digit At`
|
||||
- `Field Value`
|
||||
- Geometry Script recreation:
|
||||
`examples/geometry_script/shriinivas_fieldvalue.py`
|
||||
|
||||
|
|
@ -24,18 +25,6 @@ Together they show the first display-system layers: tiny glyph geometry,
|
|||
field-sculpted segment geometry, segment composition, segment visibility masks,
|
||||
and value-only digit extraction.
|
||||
|
||||
## Current Boundary
|
||||
|
||||
The remaining full `Field Value` group has 176 nodes and combines menu switches,
|
||||
capture attributes across multiple domains, sampled fields, instancing,
|
||||
instance transforms, delete-geometry selection, material assignment, and the
|
||||
helpers translated here.
|
||||
|
||||
That graph should not be translated manually as one heroic sweep. The next
|
||||
right move is to port or test the upstream `nodes_to_script` prototype against
|
||||
this graph so the first draft can be generated, inspected, and then cleaned
|
||||
with doctrine instead of hand-copied node by node.
|
||||
|
||||
## Create Decimal Map
|
||||
|
||||
Source group: `Create Decimal`
|
||||
|
|
@ -183,6 +172,43 @@ Metaphor: `Next Digit` is the read head for the display tape. Before the decimal
|
|||
boundary it reads the fractional reel; after that boundary it reads the whole
|
||||
number reel.
|
||||
|
||||
## Field Value Map
|
||||
|
||||
Source group: `Field Value`
|
||||
|
||||
Geometry Script group: `VG Field Value`
|
||||
|
||||
Mechanism:
|
||||
|
||||
- Use menu switches to route three control choices:
|
||||
input type (`Float` or `Vector`), source domain (`Point`, `Edge`, or `Face`),
|
||||
and text alignment (`Center`, `Right`, or `Left`).
|
||||
- Capture the requested float or vector field on point, edge, and face domains,
|
||||
then choose the captured geometry and captured value stream for the active
|
||||
domain.
|
||||
- Convert edge or face domains to points so the display can instance one value
|
||||
readout per source element.
|
||||
- Build one seven-segment glyph, capture each glyph face index, then instance
|
||||
that glyph across digit slots, vector component slots, and source elements.
|
||||
- Sample source position and normal by domain index so each readout can be
|
||||
placed and oriented back onto the element that produced it.
|
||||
- Compute the active scalar value for the current component, split it into
|
||||
whole/fractional parts, decide visible slots, negative sign, decimal point,
|
||||
overflow, and leading-zero behavior.
|
||||
- Call `VG Next Digit` for digit extraction and `VG Delete Segments` to delete
|
||||
the dark faces from each glyph.
|
||||
- Realize instances and assign the requested material.
|
||||
|
||||
Metaphor: the full graph is a label printer mounted on the mesh. It reads a
|
||||
value from each element, assembles a small digit rack, swings the rack onto the
|
||||
element normal, then punches out the unlit segments.
|
||||
|
||||
Implementation note: Blender 5 capture items and menu sockets exposed two
|
||||
Geometry Script drift points. `VG Field Value` uses a local
|
||||
`_capture_attribute_item(...)` helper to create typed capture items explicitly,
|
||||
and the VibeGeometry Geometry Script fork tolerates menu socket defaults whose
|
||||
enum items do not exist at group-interface construction time.
|
||||
|
||||
## Verification
|
||||
|
||||
Run:
|
||||
|
|
@ -207,6 +233,7 @@ Results from Blender 5.1.1:
|
|||
| Digit At `1234.0`, position `1` | 3 | 3 | 0 | accepted |
|
||||
| Digit At `98765.0`, position `2` | 7 | 7 | 0 | accepted |
|
||||
| Digit At `120305.0`, position `3` | 0 | 0 | 0 | accepted |
|
||||
| Field Value default-route vertices | 256 | 256 | 0.0 | accepted |
|
||||
| Next Digit branch cases | 4 cases | 4 cases | 0 mismatches | accepted |
|
||||
| Delete Segments grid | 195 cases | 195 cases | 0 mismatches | accepted |
|
||||
|
||||
|
|
@ -236,3 +263,8 @@ Results from Blender 5.1.1:
|
|||
verifies positions before and after `Max Precision`, because the interesting
|
||||
behavior is not digit extraction itself but choosing which number part feeds
|
||||
extraction.
|
||||
- Capture attributes are a loading dock for fields. Once a graph crosses into
|
||||
instancing, capture the values and indices that later stages must still know.
|
||||
- Full display graphs are stacked assemblies: source element points carry
|
||||
value racks; value racks carry component stacks; component stacks carry digit
|
||||
slots; digit slots carry segment faces. Preserve each layer's index baton.
|
||||
|
|
|
|||
113
docs/research/translations/shriinivas-paramnpolareq.md
Normal file
113
docs/research/translations/shriinivas-paramnpolareq.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# Translation: Shriinivas Parametric And Polar Equations
|
||||
|
||||
## Source
|
||||
|
||||
- Repository: https://github.com/Shriinivas/geometrynodes
|
||||
- Source file: `paramnpolareq.blend`
|
||||
- Source groups:
|
||||
- `NodeGroup`
|
||||
- `NodeGroup.001`
|
||||
- `NodeGroup.002`
|
||||
- `NodeGroup.003`
|
||||
- `Archimedes Spiral`
|
||||
- `Epicycloid`
|
||||
- `Geometry Nodes Group.002`
|
||||
- Geometry Script recreation:
|
||||
`examples/geometry_script/shriinivas_paramnpolareq.py`
|
||||
|
||||
## Why This Graph
|
||||
|
||||
This sample teaches parametric curve construction without hiding behind final
|
||||
rendering polish. The source graphs make a line of points, accumulate a
|
||||
parameter over that line, turn the parameter into `x/y` fields, and then move
|
||||
the points into curve shapes.
|
||||
|
||||
## Equation Helpers
|
||||
|
||||
`VG Param Epicycloid X` and `VG Param Epicycloid Y` recreate the source
|
||||
epicycloid scalar helpers:
|
||||
|
||||
```text
|
||||
x = (a + b) * cos(t) - b * cos((a / b + 1) * t)
|
||||
y = (a + b) * sin(t) - b * sin((a / b + 1) * t)
|
||||
```
|
||||
|
||||
`VG Polar Spiral X` and `VG Polar Spiral Y` recreate the polar helper pair:
|
||||
|
||||
```text
|
||||
r = a + b * theta
|
||||
x = cos(theta) * r
|
||||
y = sin(theta) * r
|
||||
```
|
||||
|
||||
Metaphor: these helpers are plotter pens without paper. They answer "where
|
||||
should the point be for this parameter?" but they do not decide how many points
|
||||
exist or how the curve is sampled.
|
||||
|
||||
## Parameter Conveyor
|
||||
|
||||
`VG Archimedes Spiral`, `VG Epicycloid`, and `VG Mirrored Root Spiral` share a
|
||||
sampling spine:
|
||||
|
||||
- Create a `Mesh Line` with `Resolution` points.
|
||||
- Compute `step = max_value / (Resolution - 1)`.
|
||||
- Feed `step` into `Accumulate Field`.
|
||||
- Use the trailing accumulated value as `theta` or `t`.
|
||||
- Compute `x/y`, combine them into a position, and move the mesh line with
|
||||
`Set Position`.
|
||||
|
||||
Metaphor: the mesh line is a conveyor belt. `Accumulate Field` stamps each
|
||||
station with a progressively larger parameter, and the equation helper tells
|
||||
that station where to stand.
|
||||
|
||||
## Mirrored Root Spiral
|
||||
|
||||
Source group `Geometry Nodes Group.002` is a two-arm spiral:
|
||||
|
||||
```text
|
||||
theta = accumulated(2 * pi * Round Count)
|
||||
r = a * sqrt(theta)
|
||||
x = r * cos(theta)
|
||||
y = r * sin(theta)
|
||||
```
|
||||
|
||||
The source builds a forward point stream and a mirrored stream at `(-x, -y)`,
|
||||
then joins them.
|
||||
|
||||
Metaphor: the first arm is a chalk line unwinding from the center; the second
|
||||
arm is the same chalk line reflected through the origin.
|
||||
|
||||
## Verification
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
& 'C:\Program Files (x86)\Steam\steamapps\common\Blender\blender.exe' --background '.\experiments\source-blends\shriinivas-paramnpolareq.blend' --python '.\tools\verify_paramnpolareq_translations.py'
|
||||
```
|
||||
|
||||
Results from Blender 5.1.1:
|
||||
|
||||
| Case | Source | Translated | Delta | Status |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| Epicycloid X scalar vertex | 1 vertex | 1 vertex | 0.0 | accepted |
|
||||
| Epicycloid Y scalar vertex | 1 vertex | 1 vertex | 0.0 | accepted |
|
||||
| Polar X scalar vertex | 1 vertex | 1 vertex | 0.0 | accepted |
|
||||
| Polar Y scalar vertex | 1 vertex | 1 vertex | 0.0 | accepted |
|
||||
| Archimedes Spiral vertices | 64 | 64 | 4.47e-8 | accepted |
|
||||
| Epicycloid vertices | 80 | 80 | 0.0 | accepted |
|
||||
| Mirrored Root Spiral vertices | 192 | 192 | 0.0 | accepted |
|
||||
|
||||
The scalar helpers are verified by putting the scalar into a single vertex's X
|
||||
position. The curve groups are verified by sorted evaluated vertex comparison.
|
||||
|
||||
## Lessons
|
||||
|
||||
- `Accumulate Field` is the natural way to build a parameter ramp inside a
|
||||
node graph. Python should describe the ramp; the graph should evaluate it.
|
||||
- Equation helpers are worth naming when several curve generators need the same
|
||||
scalar law.
|
||||
- Verifiers should normalize socket names when comparing source groups to
|
||||
generated groups. `maxT` and `Max T` are the same control for behavior even
|
||||
when the UI spelling drifts.
|
||||
- A scalar harness should preserve numeric value, not just count geometry.
|
||||
Driving a vertex coordinate is often cleaner than driving `Mesh Line.Count`.
|
||||
Loading…
Reference in a new issue