Skip to content

Commit b7068f4

Browse files
TMHSDigitalclaude
andcommitted
fix(examples): redesign gn-socket-rename as a height gauge on a granite surface plate
The still was a lone engraved copper block on a grey slab and read as a ruler. It now stages the gauge as a height gauge on a bench: a speckled granite surface plate, and a brass scriber carriage with a clamp knob clamped on the column, reaching out to rest its point on a wrung stack of three steel gauge blocks. The column's gauge_h-driven graduations, which witness the Random Value axis, are unchanged. The bench props are built after check() in dress_bench and parented to the gauge object. The copper emission drops from 0.24 to 0.14 and the key and glint are dimmed so the stage stays dark. The GN tree and the checks are unchanged. Evidence (live runs, local): - E:\Blender-Developer-Tools\.scratch\blender-5.2.1-windows-x64\blender.exe (Blender 5.2.1 LTS): check exit 0, verts=16 faces=12 zmax=1.94 gauge_h_hi=8 gauge_h_lo=8. --legacy-ids exits 5 ("FunctionNodeRandomValue has no inputs identifier 'Min_001'"). - E:\Blender-Developer-Tools\.scratch\blender-4.5.11-windows-x64\blender.exe (Blender 4.5.11 LTS): check and --output both exit 0 with the same numbers. - Framing: fill x=0.309 y=0.733, margins l=0.234 r=0.156 b=0.067 t=0.061. - Contact sheet: candidate luma 0.233 / stage 0.227 / warmth -0.037 / sat 0.130, against armature-bend 0.243/0.192/-0.144/0.237, damped-track-aim 0.237/0.218/+0.105/0.178, and bmesh-gear 0.236/0.177/+0.303/0.250. Luma and stage are in range; warmth and saturation fall between armature-bend and damped-track-aim, and the lowest saturation reflects the grey granite and steel blocks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent c03b0e5 commit b7068f4

7 files changed

Lines changed: 133 additions & 8 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ random access.
638638
</tr>
639639
<tr>
640640
<td width="46%" valign="middle">
641-
<a href="examples/gn-socket-rename/"><img src="examples/gn-socket-rename/preview.webp" alt="GN socket rename: a brushed copper gauge column on a steel plinth in a dark studio, graduated every 0.1 m from its stored gauge_h attribute, warm wedge on the back wall" /></a>
641+
<a href="examples/gn-socket-rename/"><img src="examples/gn-socket-rename/preview.webp" alt="GN socket rename: a brushed copper height-gauge column on a granite surface plate, graduated every 0.1 m from its stored gauge_h attribute, with a brass scriber resting on a stack of steel gauge blocks" /></a>
642642
</td>
643643
<td valign="middle">
644644

19.1 KB
Loading
7.32 KB
Loading

‎examples/gallery.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@
694694
"name": "gn-socket-rename",
695695
"dir": "examples/gn-socket-rename",
696696
"teaches": "Compare and Random Value socket identifiers collapsed onto reused names in 5.2; enabled-name lookup wires on 4.5, 5.1, and 5.2",
697-
"alt": "A copper measuring column with graduated tick marks standing on a grey base plate.",
697+
"alt": "A copper height-gauge column engraved with graduations on a granite surface plate, its brass scriber carriage reaching out to touch a stack of steel gauge blocks.",
698698
"witnessesFix": "A_INT and Min_001 present on 4.5.11 and 5.1.2, absent on 5.2.1. --legacy-ids exits 5 only on 5.2. Evaluated 16 verts and gauge_h 1.80 on eight column verts; the still draws the column graduations from gauge_h, so an unwired Random Value renders a blank column",
699699
"hero": "docs/gallery/assets/gn-socket-rename-hero.webp",
700700
"preview": "examples/gn-socket-rename/preview.webp",

‎examples/gn-socket-rename/README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ nothing in it feeds back into `check()`; the column is also turned to
6565
18° for the still so two faces show, which `check()` cannot see because
6666
it reads object-space vertices.
6767

68+
The gauge is staged as a height gauge on a bench: it stands on a granite
69+
surface plate, and a brass scriber carriage clamped on the column reaches
70+
out to rest its point on a wrung stack of three steel gauge blocks. The
71+
bench props are built after `check()` in `dress_bench` and parented to the
72+
gauge object; no assertion reads them.
73+
6874
## Exit codes
6975

7076
Per-script sequential checks. `9` is a valid check code; there is no rule

‎examples/gn-socket-rename/gn_socket_rename.py‎

Lines changed: 125 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ def dress_gauge(copper, steel):
409409
nt.links.new(enabled_sock(base, "outputs", "Result"), emit)
410410
# Brushed, the copper reflects less of the key and went brown at
411411
# thumbnail size; a little more of its own colour keeps it copper.
412-
bsdf.inputs["Emission Strength"].default_value = 0.24
412+
bsdf.inputs["Emission Strength"].default_value = 0.14
413413

414414
# Ground steel: lift it off the stage and give it a machined finish.
415415
sb = steel.node_tree.nodes["Principled BSDF"]
@@ -419,6 +419,124 @@ def dress_gauge(copper, steel):
419419
sb.inputs["Roughness"].default_value = 0.42
420420

421421

422+
# Render-only bench dressing: the gauge stands on a granite surface plate
423+
# with a brass scriber carriage clamped on the column, its point resting on a
424+
# stack of steel gauge blocks. Pure staging — built after check(), parented to
425+
# the gauge object, never read by an assertion.
426+
PLATE = (3.0, 1.9, 0.22)
427+
SCRIBE_Z = 0.62 # scriber height above the plinth top
428+
429+
430+
def _principled_noise(name, base, rough, metal, scale, amount, stretch=(1, 1, 1)):
431+
mat = principled(name, (*base, 1.0), metal, rough)
432+
nt = mat.node_tree
433+
b = nt.nodes["Principled BSDF"]
434+
coord = nt.nodes.new("ShaderNodeTexCoord")
435+
mapping = nt.nodes.new("ShaderNodeMapping")
436+
mapping.inputs["Scale"].default_value = stretch
437+
nt.links.new(coord.outputs["Object"], mapping.inputs["Vector"])
438+
tex = nt.nodes.new("ShaderNodeTexNoise")
439+
tex.inputs["Scale"].default_value = scale
440+
tex.inputs["Detail"].default_value = 10.0
441+
nt.links.new(mapping.outputs["Vector"], tex.inputs["Vector"])
442+
ramp = nt.nodes.new("ShaderNodeValToRGB")
443+
ramp.color_ramp.elements[0].position = 0.4
444+
ramp.color_ramp.elements[0].color = tuple(c * (1 - amount) for c in base) + (1.0,)
445+
ramp.color_ramp.elements[1].position = 0.62
446+
ramp.color_ramp.elements[1].color = tuple(min(1.0, c * (1 + amount)) for c in base) + (1.0,)
447+
nt.links.new(tex.outputs["Fac"], ramp.inputs["Fac"])
448+
nt.links.new(ramp.outputs["Color"], b.inputs["Base Color"])
449+
return mat
450+
451+
452+
def dress_bench(obj):
453+
scene = bpy.context.scene
454+
granite = _principled_noise("Granite", (0.03, 0.031, 0.034), 0.22, 0.0, 140.0, 0.85)
455+
brass = _principled_noise("Brass", (0.86, 0.60, 0.25), 0.28, 1.0, 30.0, 0.12)
456+
blocks = _principled_noise("GaugeSteel", (0.62, 0.63, 0.66), 0.16, 1.0, 12.0, 0.08,
457+
stretch=(40.0, 1.0, 1.0))
458+
knob = principled("Knurl", (0.03, 0.03, 0.035, 1.0), 0.6, 0.4)
459+
460+
def part(name, build, mat, parent=obj):
461+
me = bpy.data.meshes.new(name)
462+
bm = bmesh.new()
463+
try:
464+
build(bm)
465+
bm.to_mesh(me)
466+
finally:
467+
bm.free()
468+
me.materials.append(mat)
469+
me.set_sharp_from_angle(angle=math.radians(40))
470+
for p in me.polygons:
471+
p.use_smooth = True
472+
ob = bpy.data.objects.new(name, me)
473+
scene.collection.objects.link(ob)
474+
ob.parent = parent
475+
return ob
476+
477+
def box(size, center, bevel=0.012):
478+
def b(bm):
479+
res = bmesh.ops.create_cube(bm, size=1.0)
480+
for v in res["verts"]:
481+
v.co = type(v.co)((v.co.x * size[0] + center[0], v.co.y * size[1] + center[1],
482+
v.co.z * size[2] + center[2]))
483+
bmesh.ops.bevel(bm, geom=list(bm.edges), offset=bevel, segments=2,
484+
profile=0.5, affect="EDGES", clamp_overlap=True)
485+
return b
486+
487+
def cyl(r, h, center, axis="Z", segs=32):
488+
def b(bm):
489+
res = bmesh.ops.create_cone(bm, cap_ends=True, segments=segs,
490+
radius1=r, radius2=r, depth=h)
491+
for v in res["verts"]:
492+
x, y, z = v.co
493+
if axis == "X":
494+
x, z = z, x
495+
elif axis == "Y":
496+
y, z = z, y
497+
v.co = type(v.co)((x + center[0], y + center[1], z + center[2]))
498+
return b
499+
500+
# the surface plate; the gauge (and everything parented to it) rides up
501+
plate = part("SurfacePlate", box(PLATE, (0.55, 0.15, -PLATE[2] / 2), 0.03),
502+
granite, parent=None)
503+
plate.rotation_euler = obj.rotation_euler
504+
obj.location.z = PLATE[2]
505+
plate.location.z = PLATE[2]
506+
507+
top = PLINTH_SIZE[2]
508+
z = top + SCRIBE_Z
509+
half = COLUMN_XY / 2
510+
parts = [plate]
511+
# carriage collar around the column, a clamp knob, and the scriber arm
512+
parts.append(part("Carriage", box((COLUMN_XY + 0.16, COLUMN_XY + 0.16, 0.26),
513+
(0.0, 0.0, z + 0.06), 0.02), brass))
514+
parts.append(part("ClampKnob", cyl(0.07, 0.12, (0.0, -(half + 0.14), z + 0.06), "Y"),
515+
knob))
516+
arm_len = 0.95
517+
parts.append(part("ScriberArm", box((arm_len, 0.09, 0.07),
518+
(half + 0.08 + arm_len / 2, 0.0, z + 0.04), 0.01),
519+
brass))
520+
521+
def tip(bm):
522+
res = bmesh.ops.create_cone(bm, cap_ends=True, segments=16, radius1=0.035,
523+
radius2=0.0, depth=0.22)
524+
for v in res["verts"]:
525+
x, y, zz = v.co
526+
v.co = type(v.co)((zz + half + 0.08 + arm_len + 0.11, y, -x + z + 0.04))
527+
parts.append(part("ScriberPoint", tip, blocks))
528+
# a wrung stack of three gauge blocks whose top the scriber point touches
529+
stack = z + 0.04 - 0.035 # up to the underside of the scriber point
530+
heights = (0.30, 0.20, stack - 0.50)
531+
bx = half + 0.08 + arm_len + 0.26
532+
zz = 0.0
533+
for i, h in enumerate(heights):
534+
parts.append(part(f"GaugeBlock{i}", box((0.34, 0.26, h - 0.004), (bx, 0.0, zz + h / 2),
535+
0.006), blocks))
536+
zz += h
537+
return parts
538+
539+
422540
def render_still(obj, path, engine):
423541
scene = bpy.context.scene
424542
dress_gauge(bpy.data.materials["ColumnCopper"], bpy.data.materials["PlinthSteel"])
@@ -427,6 +545,7 @@ def render_still(obj, path, engine):
427545
# corner graduations wrap the edge. check() reads object-space vertices,
428546
# so the turn cannot reach an assertion.
429547
obj.rotation_euler = (0.0, 0.0, math.radians(18))
548+
props = dress_bench(obj)
430549

431550
floor_me = bpy.data.meshes.new("Floor")
432551
bm = bmesh.new()
@@ -452,7 +571,7 @@ def render_still(obj, path, engine):
452571
scene.world = world
453572

454573
aim = bpy.data.objects.new("Aim", None)
455-
aim.location = (0.0, 0.0, ZMAX_OK * 0.45)
574+
aim.location = (0.3, 0.0, ZMAX_OK * 0.47)
456575
aim.hide_render = True
457576
scene.collection.objects.link(aim)
458577

@@ -469,13 +588,13 @@ def light(name, loc, energy, size, col):
469588
lc.track_axis = "TRACK_NEGATIVE_Z"
470589
lc.up_axis = "UP_Y"
471590

472-
light("Key", (-3.4, -4.6, 5.4), 680.0, 4.2, (1.0, 0.96, 0.9))
591+
light("Key", (-3.4, -4.6, 5.4), 520.0, 3.4, (1.0, 0.96, 0.9))
473592
light("Fill", (4.8, -3.0, 2.2), 140.0, 8.0, (0.75, 0.85, 1.0))
474593
# At 300 W the rim's glossy reflection off the floor lifted the stage
475594
# right of the column to mid grey-blue (patch mean 104 against 62 with
476595
# the rim off); the column edge still separates at this level.
477596
light("Rim", (0.2, 5.8, 3.4), 120.0, 3.2, (0.6, 0.78, 1.0))
478-
light("Glint", (1.8, -4.8, 5.6), 900.0, 0.85, (1.0, 0.90, 0.70))
597+
light("Glint", (1.8, -4.8, 5.6), 560.0, 0.85, (1.0, 0.90, 0.70))
479598
wedge = bpy.data.lights.new("Wedge", "AREA")
480599
wedge.energy = 480.0
481600
wedge.size = 6.0
@@ -486,9 +605,9 @@ def light(name, loc, energy, size, col):
486605
scene.collection.objects.link(wob)
487606

488607
cam_data = bpy.data.cameras.new("Cam")
489-
cam_data.lens = 50.0
608+
cam_data.lens = 55.0
490609
cam = bpy.data.objects.new("Cam", cam_data)
491-
cam.location = (4.15, -5.85, 2.55)
610+
cam.location = (4.5, -6.6, 2.8)
492611
scene.collection.objects.link(cam)
493612
scene.camera = cam
494613
track = cam.constraints.new("TRACK_TO")
16.1 KB
Loading

0 commit comments

Comments
 (0)