Lane E S16 gate 5: the next temptations — glasshouse (collateral apex, $320, wreck empties not falls), pool kit (14 fence posts, 14 explicit tie_off denials, unpriced by the bike ruling), pergola (the 0.65 middle rung D endorsed, $120, partial-failure wreck). Shared-helper wrecks, deterministic (2 runs, 44 files shasum-identical, zero churn on the 31 existing GLBs); manifest gains exactly the pergola entry
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ed62c7d4be
commit
1571c2f1f9
@ -719,6 +719,193 @@
|
|||||||
"anchors": [],
|
"anchors": [],
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"problems": []
|
"problems": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pergola_01",
|
||||||
|
"dims": [
|
||||||
|
3.2,
|
||||||
|
2.6,
|
||||||
|
2.71
|
||||||
|
],
|
||||||
|
"tris": 276,
|
||||||
|
"nodes": [
|
||||||
|
"beams",
|
||||||
|
"deck",
|
||||||
|
"pergola_01",
|
||||||
|
"pergola_anchor_01",
|
||||||
|
"pergola_anchor_02",
|
||||||
|
"posts",
|
||||||
|
"rafters"
|
||||||
|
],
|
||||||
|
"anchors": [
|
||||||
|
{
|
||||||
|
"node": "pergola_anchor_01",
|
||||||
|
"anchor_type": "pergola",
|
||||||
|
"rating_hint": 0.65,
|
||||||
|
"collateral": "pergola"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "pergola_anchor_02",
|
||||||
|
"anchor_type": "pergola",
|
||||||
|
"rating_hint": 0.65,
|
||||||
|
"collateral": "pergola"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "PASS",
|
||||||
|
"problems": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pergola_01_wrecked",
|
||||||
|
"dims": [
|
||||||
|
3.3288,
|
||||||
|
2.6,
|
||||||
|
2.6648
|
||||||
|
],
|
||||||
|
"tris": 300,
|
||||||
|
"nodes": [
|
||||||
|
"beam_down",
|
||||||
|
"beams",
|
||||||
|
"deck",
|
||||||
|
"pergola_01_wrecked",
|
||||||
|
"posts",
|
||||||
|
"rafters_down"
|
||||||
|
],
|
||||||
|
"anchors": [],
|
||||||
|
"status": "PASS",
|
||||||
|
"problems": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "glasshouse_01",
|
||||||
|
"dims": [
|
||||||
|
2.645,
|
||||||
|
1.945,
|
||||||
|
2.3025
|
||||||
|
],
|
||||||
|
"tris": 356,
|
||||||
|
"nodes": [
|
||||||
|
"door",
|
||||||
|
"frame",
|
||||||
|
"glasshouse_01",
|
||||||
|
"panes",
|
||||||
|
"ridge_anchor"
|
||||||
|
],
|
||||||
|
"anchors": [
|
||||||
|
{
|
||||||
|
"node": "ridge_anchor",
|
||||||
|
"tie_off": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "PASS",
|
||||||
|
"problems": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "glasshouse_01_wrecked",
|
||||||
|
"dims": [
|
||||||
|
4.3356,
|
||||||
|
2.9387,
|
||||||
|
2.3025
|
||||||
|
],
|
||||||
|
"tris": 680,
|
||||||
|
"nodes": [
|
||||||
|
"door",
|
||||||
|
"frame",
|
||||||
|
"glasshouse_01_wrecked",
|
||||||
|
"panes_left",
|
||||||
|
"shards"
|
||||||
|
],
|
||||||
|
"anchors": [],
|
||||||
|
"status": "PASS",
|
||||||
|
"problems": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pool_kit_01",
|
||||||
|
"dims": [
|
||||||
|
5.645,
|
||||||
|
4.5207,
|
||||||
|
1.22
|
||||||
|
],
|
||||||
|
"tris": 2780,
|
||||||
|
"nodes": [
|
||||||
|
"fence_post_01",
|
||||||
|
"fence_post_02",
|
||||||
|
"fence_post_03",
|
||||||
|
"fence_post_04",
|
||||||
|
"fence_post_05",
|
||||||
|
"fence_post_06",
|
||||||
|
"fence_post_07",
|
||||||
|
"fence_post_08",
|
||||||
|
"fence_post_09",
|
||||||
|
"fence_post_10",
|
||||||
|
"fence_post_11",
|
||||||
|
"fence_post_12",
|
||||||
|
"fence_post_13",
|
||||||
|
"fence_post_14",
|
||||||
|
"fence_rails",
|
||||||
|
"pool_gate",
|
||||||
|
"pool_kit_01",
|
||||||
|
"pool_shell",
|
||||||
|
"pool_water"
|
||||||
|
],
|
||||||
|
"anchors": [
|
||||||
|
{
|
||||||
|
"node": "fence_post_01",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_02",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_03",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_04",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_05",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_06",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_07",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_08",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_09",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_10",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_11",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_12",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_13",
|
||||||
|
"tie_off": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "fence_post_14",
|
||||||
|
"tie_off": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "PASS",
|
||||||
|
"problems": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"debris": []
|
"debris": []
|
||||||
|
|||||||
@ -137,6 +137,11 @@ PAL = {
|
|||||||
"bike_kid": "#D8483C", # the Henderson kid's bike — bought bright on purpose
|
"bike_kid": "#D8483C", # the Henderson kid's bike — bought bright on purpose
|
||||||
"bike_grip": "#3B4048", # grips and saddle
|
"bike_grip": "#3B4048", # grips and saddle
|
||||||
"ref_pink": "#E85C8A", # the reference capsule — deliberately loud
|
"ref_pink": "#E85C8A", # the reference capsule — deliberately loud
|
||||||
|
"alu_frame": "#C9CED2", # powder-coated aluminium — glasshouse + pool fence
|
||||||
|
"pool_water": "#4FA8BE", # chlorinated blue, NOT the pond's grey-green —
|
||||||
|
# a pool is the one water in the game that is
|
||||||
|
# supposed to look inviting
|
||||||
|
"deck_board": "#A8845C", # decking oil, a shade lighter than fence timber
|
||||||
}
|
}
|
||||||
|
|
||||||
# Rainwater caught in a sail is not swimming-pool blue. It's shallow, murky, it
|
# Rainwater caught in a sail is not swimming-pool blue. It's shallow, murky, it
|
||||||
@ -1522,6 +1527,544 @@ def build_swing_set_01_wrecked(name):
|
|||||||
return root
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# THE PERGOLA (SPRINT16 gate 5 — the honest middle rung, D-endorsed)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# One shape, stated once, used by the intact pergola AND the wreck — the
|
||||||
|
# _swing_frame precedent, kept on purpose: a wreck built from re-typed numbers
|
||||||
|
# drifts from its twin one edit at a time.
|
||||||
|
PERGOLA = dict(
|
||||||
|
DECK_W=3.2, # deck span, x
|
||||||
|
DECK_D=2.6, # deck depth, y
|
||||||
|
DECK_H=0.30, # two steps up — the deck IS the footing, that's the point
|
||||||
|
POST=0.09, # 90x90 treated pine
|
||||||
|
POST_H=2.20, # above the deck
|
||||||
|
INSET=0.18, # posts in from the deck edge
|
||||||
|
BEAM_H=0.14, # 140x45 beams along x on front/back post pairs
|
||||||
|
N_RAFTERS=7, # 70x35 rafters spanning y, the open roof
|
||||||
|
)
|
||||||
|
|
||||||
|
# What it costs when the timber cracks. Proposal — Lane A owns the number, the
|
||||||
|
# ladder it slots into is gnome 25 < gutter 90 < PERGOLA 120 < swing 140 <
|
||||||
|
# carport 180 (< glasshouse 320, below):
|
||||||
|
# · the failure is PARTIAL by design: the bolts hold, the timber breaks — a
|
||||||
|
# torn-off beam and a snapped post on a structure that stays standing. The
|
||||||
|
# bill is one beam, one post and the labour to re-bolt, not a rebuild;
|
||||||
|
# · under ~90 it undercuts the gutter, and a structural timber repair does
|
||||||
|
# not cost less than a run of guttering;
|
||||||
|
# · over ~140 it outbids the swing set, whose failure is the WHOLE frame on
|
||||||
|
# the grass — a worse day than a cracked beam, and the prices must agree
|
||||||
|
# with the wrecks the player actually sees.
|
||||||
|
PERGOLA_COLLATERAL = 120
|
||||||
|
|
||||||
|
|
||||||
|
def _pergola(name, root, deck_m, timber_m, dark_m, *, wrecked=False):
|
||||||
|
"""Build the pergola. Returns the two anchor points (Blender coords) so the
|
||||||
|
caller hangs anchors on the geometry's own numbers, not a second copy of
|
||||||
|
the arithmetic. Intact and wrecked share every dimension through PERGOLA.
|
||||||
|
|
||||||
|
HOW IT FAILS, and why that is the 0.65 story told twice: the posts are
|
||||||
|
BOLTED to the deck — real M12s through the joists, and the bolts hold.
|
||||||
|
What gives is the timber and the deck's own stiffness: the loaded front
|
||||||
|
beam tears off its post tops, the front posts snap at half height above
|
||||||
|
the racking bolts, and the rafters — still fixed at the back beam — hinge
|
||||||
|
down like a lean-to until their front ends hit the deck. The back half
|
||||||
|
stands. A pergola fails PARTIALLY, which is exactly why its rating (0.65)
|
||||||
|
sits above the unbolted swing frame (0.45) and below concrete (1.00), and
|
||||||
|
why its bill (a beam and a post) sits below the swing's (a whole frame).
|
||||||
|
"""
|
||||||
|
P = PERGOLA
|
||||||
|
hw, hd = P["DECK_W"] / 2, P["DECK_D"] / 2
|
||||||
|
px, py = hw - P["INSET"], hd - P["INSET"] # post centres
|
||||||
|
top = P["DECK_H"] + P["POST_H"] # post top z
|
||||||
|
beam_z = top + P["BEAM_H"] / 2 # beam centre z
|
||||||
|
|
||||||
|
# --- the deck: joists + boards. Identical in the wreck — it was never the
|
||||||
|
# weak part; it is the footing the rating is ABOUT.
|
||||||
|
deck = [add_box(f"{name}_deck_slab", (P["DECK_W"], P["DECK_D"], 0.045),
|
||||||
|
(0, 0, P["DECK_H"] - 0.0225), deck_m)]
|
||||||
|
for i in range(4):
|
||||||
|
y = -hd + 0.12 + i * (P["DECK_D"] - 0.24) / 3
|
||||||
|
deck.append(add_box(f"{name}_joist_{i}", (P["DECK_W"] - 0.1, 0.045, P["DECK_H"] - 0.05),
|
||||||
|
(0, y, (P["DECK_H"] - 0.05) / 2), dark_m))
|
||||||
|
# Board grooves read as lines; three thin gaps of darker timber.
|
||||||
|
for i in range(5):
|
||||||
|
x = -hw + 0.3 + i * (P["DECK_W"] - 0.6) / 4
|
||||||
|
deck.append(add_box(f"{name}_board_gap_{i}", (0.015, P["DECK_D"] - 0.02, 0.008),
|
||||||
|
(x, 0, P["DECK_H"] + 0.004), dark_m))
|
||||||
|
join_group(deck, "deck", root)
|
||||||
|
|
||||||
|
# --- posts. Front pair (−y, the yard side, where the anchors live) snaps
|
||||||
|
# in the wreck; back pair always stands.
|
||||||
|
posts = []
|
||||||
|
for sx in (-1, 1):
|
||||||
|
posts.append(add_box(f"{name}_post_b_{sx}", (P["POST"], P["POST"], P["POST_H"]),
|
||||||
|
(sx * px, py, P["DECK_H"] + P["POST_H"] / 2), timber_m))
|
||||||
|
if wrecked:
|
||||||
|
# Snapped at 55% height: the bolted stub stands plumb, the top
|
||||||
|
# half lies on the deck with its beam end, split face up.
|
||||||
|
stub = P["POST_H"] * 0.55
|
||||||
|
posts.append(add_box(f"{name}_post_f_{sx}", (P["POST"], P["POST"], stub),
|
||||||
|
(sx * px, -py, P["DECK_H"] + stub / 2), timber_m))
|
||||||
|
posts.append(add_box(f"{name}_post_top_{sx}",
|
||||||
|
(P["POST"], P["POST"], P["POST_H"] - stub),
|
||||||
|
(sx * (px - 0.35), -py + 0.42,
|
||||||
|
P["DECK_H"] + P["POST"] / 2), timber_m,
|
||||||
|
rot=(math.radians(88), 0, math.radians(20 * sx))))
|
||||||
|
else:
|
||||||
|
posts.append(add_box(f"{name}_post_f_{sx}", (P["POST"], P["POST"], P["POST_H"]),
|
||||||
|
(sx * px, -py, P["DECK_H"] + P["POST_H"] / 2), timber_m))
|
||||||
|
join_group(posts, "posts", root)
|
||||||
|
|
||||||
|
# --- beams. Back beam always up; front beam is the one that tears off.
|
||||||
|
beams = [add_box(f"{name}_beam_b", (P["DECK_W"] - 0.1, 0.045, P["BEAM_H"]),
|
||||||
|
(0, py, beam_z), timber_m)]
|
||||||
|
if not wrecked:
|
||||||
|
beams.append(add_box(f"{name}_beam_f", (P["DECK_W"] - 0.1, 0.045, P["BEAM_H"]),
|
||||||
|
(0, -py, beam_z), timber_m))
|
||||||
|
join_group(beams, "beams", root)
|
||||||
|
if wrecked:
|
||||||
|
# The torn beam, flat on the deck along the front edge, slightly askew.
|
||||||
|
# Rolled 90° about its own long axis so the 140 mm face lies on the
|
||||||
|
# boards (a beam at rest lies on its side; on edge it would balance).
|
||||||
|
join_group([add_box(f"{name}_beam_down", (P["DECK_W"] - 0.1, 0.045, P["BEAM_H"]),
|
||||||
|
(0.18, -py + 0.15, P["DECK_H"] + 0.03),
|
||||||
|
timber_m, rot=(math.radians(90), 0, math.radians(6)))],
|
||||||
|
"beam_down", root)
|
||||||
|
|
||||||
|
# --- rafters: up in the intact roof; hinged down at the back beam in the
|
||||||
|
# wreck, front ends resting on the deck — the lean-to collapse.
|
||||||
|
rafters = []
|
||||||
|
for i in range(P["N_RAFTERS"]):
|
||||||
|
x = -hw + 0.35 + i * (P["DECK_W"] - 0.7) / (P["N_RAFTERS"] - 1)
|
||||||
|
if wrecked:
|
||||||
|
drop = math.atan2(top + P["BEAM_H"] - (P["DECK_H"] + 0.05), P["DECK_D"] - 0.3)
|
||||||
|
L = math.hypot(top + P["BEAM_H"] - (P["DECK_H"] + 0.05), P["DECK_D"] - 0.3)
|
||||||
|
rafters.append(add_box(f"{name}_rafter_{i}", (0.035, L, 0.07),
|
||||||
|
(x, py - L / 2 * math.cos(drop),
|
||||||
|
(top + P["BEAM_H"] + P["DECK_H"] + 0.05) / 2),
|
||||||
|
timber_m, rot=(-drop, 0, 0)))
|
||||||
|
else:
|
||||||
|
rafters.append(add_box(f"{name}_rafter_{i}", (0.035, P["DECK_D"] - 0.15, 0.07),
|
||||||
|
(x, 0, top + P["BEAM_H"] + 0.035), timber_m))
|
||||||
|
join_group(rafters, "rafters_down" if wrecked else "rafters", root)
|
||||||
|
|
||||||
|
# Anchor points: where the front beam meets the post tops — the bolted
|
||||||
|
# corner an author would genuinely strap to.
|
||||||
|
return [(-px, -py, top + P["BEAM_H"] * 0.6), (px, -py, top + P["BEAM_H"] * 0.6)]
|
||||||
|
|
||||||
|
|
||||||
|
def build_pergola_01(name):
|
||||||
|
"""A timber pergola on a bolted deck — the ladder's missing middle rung.
|
||||||
|
|
||||||
|
SPRINT16 gate 5, proposed in S15 and carried on D's playtester endorsement:
|
||||||
|
"the ladder jumps 0.45-on-grass straight to 1.00-in-concrete; a 0.65
|
||||||
|
bolted-to-deck mid-rung turns fascia-or-nothing into a real decision."
|
||||||
|
|
||||||
|
WHY 0.65, and why one rating for the whole type (the manifest rule my own
|
||||||
|
S15 entry proposed: new types ship ONE rating per type, so a node-less
|
||||||
|
JSON anchor can resolve it unambiguously): the post-beam corner is real
|
||||||
|
90x90 timber with real M12 bolts through a deck — genuinely better than a
|
||||||
|
welded frame standing loose on grass (swing_frame 0.45), because the load
|
||||||
|
path reaches something fixed. And genuinely worse than 300 mm of concrete
|
||||||
|
(post 1.00), because what it reaches is a DECK: the deck flexes, the
|
||||||
|
timber cracks before the bolts let go, and 0.65 is that sentence as a
|
||||||
|
number. Typed `pergola` in its own enum word — calling it a post would
|
||||||
|
promise concrete the deck cannot deliver (the swing_frame lesson, kept).
|
||||||
|
"""
|
||||||
|
root = add_empty(name)
|
||||||
|
deck_m = get_material("Mat_Deck", PAL["deck_board"], 0.8)
|
||||||
|
timber_m = get_material("Mat_Timber", PAL["timber"], 0.75)
|
||||||
|
dark_m = get_material("Mat_TimberDark", PAL["timber_dark"], 0.8)
|
||||||
|
|
||||||
|
anchors = _pergola(name, root, deck_m, timber_m, dark_m, wrecked=False)
|
||||||
|
for i, ap in enumerate(anchors):
|
||||||
|
e = add_empty(f"pergola_anchor_{i + 1:02d}", ap, root, size=0.18)
|
||||||
|
e["anchor_type"] = "pergola"
|
||||||
|
e["rating_hint"] = 0.65
|
||||||
|
e["collateral"] = "pergola"
|
||||||
|
e["why"] = ("bolted post-beam corner: the bolts are real, the deck is "
|
||||||
|
"the footing — better than loose-on-grass, worse than "
|
||||||
|
"concrete, and the number argues exactly that")
|
||||||
|
|
||||||
|
stamp(root, name, "structure")
|
||||||
|
root["collateral_key"] = "pergola"
|
||||||
|
root["collateral_value"] = PERGOLA_COLLATERAL
|
||||||
|
root["collateral_label"] = "the pergola"
|
||||||
|
root["breakable"] = True
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
def build_pergola_01_wrecked(name):
|
||||||
|
"""The pergola after a loaded corner won: front beam torn off, front posts
|
||||||
|
snapped above their bolts, rafters hinged down onto the deck like a
|
||||||
|
lean-to. The back half — and the deck, and every bolt — still standing,
|
||||||
|
because a bolted structure fails PARTIALLY, which is the whole 0.65 story.
|
||||||
|
Same origin, same PERGOLA numbers, one flag different, so intact and
|
||||||
|
wrecked cannot drift (the _swing_frame pattern)."""
|
||||||
|
root = add_empty(name)
|
||||||
|
deck_m = get_material("Mat_Deck", PAL["deck_board"], 0.8)
|
||||||
|
timber_m = get_material("Mat_Timber", PAL["timber"], 0.75)
|
||||||
|
dark_m = get_material("Mat_TimberDark", PAL["timber_dark"], 0.8)
|
||||||
|
|
||||||
|
_pergola(name, root, deck_m, timber_m, dark_m, wrecked=True)
|
||||||
|
# No pergola_anchor_* survives — you cannot re-bolt to a snapped post from
|
||||||
|
# inside a storm, and an anchor that outlives its structure is the
|
||||||
|
# free-failure bug in a costume (fascia rule, third application).
|
||||||
|
stamp(root, name, "structure")
|
||||||
|
root["broken_variant_of"] = "pergola_01"
|
||||||
|
root["collateral_key"] = "pergola"
|
||||||
|
root["collateral_value"] = PERGOLA_COLLATERAL
|
||||||
|
root["collateral_label"] = "the pergola"
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# THE GLASSHOUSE (SPRINT16 gate 5 — the collateral apex)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
GLASSHOUSE = dict(
|
||||||
|
W=2.6, # x — ridge runs along x
|
||||||
|
D=1.9, # y
|
||||||
|
WALL_H=1.70,
|
||||||
|
RIDGE_H=2.28,
|
||||||
|
BAR=0.045, # aluminium extrusion section
|
||||||
|
)
|
||||||
|
|
||||||
|
# The apex, priced as one. Proposal — Lane A owns the number:
|
||||||
|
# · it must top the carport (180) by a distance, or "apex" is a word: the
|
||||||
|
# carport is one sheet of Colorbond; this is ~14 panes of horticultural
|
||||||
|
# glass, and glass does not bend, it all goes at once. 320 is the panes,
|
||||||
|
# the reglaze labour, and a racked door that never closes right again;
|
||||||
|
# · it must stay under a six-night week's gross (~$540 in fees alone), so
|
||||||
|
# one catastrophe is a week worked for nothing, not an automatic BROKE —
|
||||||
|
# the carport ruling ("the worst thing on the bill and still a week you
|
||||||
|
# can dig out of"), scaled to the thing that costs a week;
|
||||||
|
# · unlike the bike (unpriced, nothing can break it) this ships priced ON
|
||||||
|
# THE SPEC'S OWN ORDER: arc 2's glasshouse yard is the event's home, and
|
||||||
|
# the price chain has to exist before that yard can be authored against
|
||||||
|
# it. Until an event lands, nothing bills it — the value is inert data,
|
||||||
|
# not a lie on an invoice. The tripwire is written in THREADS: the arc-2
|
||||||
|
# yard must wire the wreck event before this number reaches a bill.
|
||||||
|
GLASSHOUSE_COLLATERAL = 320
|
||||||
|
|
||||||
|
|
||||||
|
def _glasshouse_frame(name, root, alu):
|
||||||
|
"""The aluminium skeleton, shared by intact and wreck — the frame is the
|
||||||
|
part that SURVIVES (aluminium racks, glass shatters), which is why the
|
||||||
|
wreck stands at full height and reads as emptied, not felled."""
|
||||||
|
G = GLASSHOUSE
|
||||||
|
hw, hd, B = G["W"] / 2, G["D"] / 2, G["BAR"]
|
||||||
|
parts = []
|
||||||
|
# Corner + mid posts.
|
||||||
|
for x in (-hw, 0, hw):
|
||||||
|
for sy in (-1, 1):
|
||||||
|
parts.append(add_box(f"{name}_post_{x:.1f}_{sy}", (B, B, G["WALL_H"]),
|
||||||
|
(x, sy * hd, G["WALL_H"] / 2), alu))
|
||||||
|
# Eave beams (long sides) + end sills.
|
||||||
|
for sy in (-1, 1):
|
||||||
|
parts.append(add_box(f"{name}_eave_{sy}", (G["W"], B, B),
|
||||||
|
(0, sy * hd, G["WALL_H"]), alu))
|
||||||
|
for sx in (-1, 1):
|
||||||
|
parts.append(add_box(f"{name}_end_{sx}", (B, G["D"], B),
|
||||||
|
(sx * hw, 0, G["WALL_H"]), alu))
|
||||||
|
# Gable rafters up to the ridge.
|
||||||
|
for sy in (-1, 1):
|
||||||
|
p0 = (sx * hw, sy * hd, G["WALL_H"])
|
||||||
|
p1 = (sx * hw, 0, G["RIDGE_H"])
|
||||||
|
parts.append(add_tube_between(f"{name}_gable_{sx}_{sy}", p0, p1,
|
||||||
|
B * 0.45, alu, verts=6))
|
||||||
|
# The ridge — the straightest, most anchor-looking bar in any yard, which
|
||||||
|
# is exactly why it gets a named node and a denial (below, on the empty).
|
||||||
|
parts.append(add_box(f"{name}_ridge", (G["W"], B, B), (0, 0, G["RIDGE_H"]), alu))
|
||||||
|
join_group(parts, "frame", root)
|
||||||
|
|
||||||
|
# Door frame on the -x gable end.
|
||||||
|
join_group([
|
||||||
|
add_box(f"{name}_door_l", (B * 0.7, B * 0.7, 1.55), (-hw - 0.001, -0.36, 0.775), alu),
|
||||||
|
add_box(f"{name}_door_r", (B * 0.7, B * 0.7, 1.55), (-hw - 0.001, 0.36, 0.775), alu),
|
||||||
|
add_box(f"{name}_door_t", (B * 0.7, 0.76, B * 0.7), (-hw - 0.001, 0, 1.55), alu),
|
||||||
|
], "door", root)
|
||||||
|
|
||||||
|
|
||||||
|
def build_glasshouse_01(name):
|
||||||
|
"""A backyard glasshouse — the collateral apex, and NOT an anchor.
|
||||||
|
|
||||||
|
SPRINT16 gate 5. The palette's other structures are temptations because
|
||||||
|
they offer bad steel; this one is a temptation because it offers NO steel
|
||||||
|
while being ringed in members that look like it — an aluminium ridge bar
|
||||||
|
at 2.28 m is the most tie-off-shaped object in the game. Every reading of
|
||||||
|
it must come back "don't": the ridge carries an explicit tie_off denial,
|
||||||
|
and NO node anywhere on either variant carries a rating_hint, which
|
||||||
|
e.test pins — nothing about a glasshouse may adopt as an anchor, because
|
||||||
|
30 kg of glass in a storm is not a thing you add load to, it is a thing
|
||||||
|
you keep everything away from.
|
||||||
|
|
||||||
|
The panes read fragile at a glance because they are TRANSLUCENT — the only
|
||||||
|
see-through walls in the palette — and that read survives export as a
|
||||||
|
material fact (transparent + alpha), which e.test also pins: a repaint
|
||||||
|
that turns the glass opaque has silently deleted the tell.
|
||||||
|
"""
|
||||||
|
root = add_empty(name)
|
||||||
|
alu = get_material("Mat_AluFrame", PAL["alu_frame"], 0.35, metallic=0.7)
|
||||||
|
glass = get_material("Mat_Glass", PAL["glass"], 0.05, metallic=0.0, opacity=0.30)
|
||||||
|
G = GLASSHOUSE
|
||||||
|
hw, hd = G["W"] / 2, G["D"] / 2
|
||||||
|
|
||||||
|
_glasshouse_frame(name, root, alu)
|
||||||
|
|
||||||
|
panes = []
|
||||||
|
# Wall panes: long sides + gable ends (leaving the door opening).
|
||||||
|
for sy in (-1, 1):
|
||||||
|
panes.append(add_box(f"{name}_wall_{sy}", (G["W"] - 0.06, 0.012, G["WALL_H"] - 0.08),
|
||||||
|
(0, sy * (hd - 0.012), G["WALL_H"] / 2), glass))
|
||||||
|
panes.append(add_box(f"{name}_endwall_e", (0.012, G["D"] - 0.06, G["WALL_H"] - 0.08),
|
||||||
|
(hw - 0.012, 0, G["WALL_H"] / 2), glass))
|
||||||
|
for sy in (-1, 1): # west end: two strips beside the door
|
||||||
|
panes.append(add_box(f"{name}_endwall_w_{sy}", (0.012, (G["D"] - 0.8) / 2 - 0.05, G["WALL_H"] - 0.08),
|
||||||
|
(-hw + 0.012, sy * (0.4 + (G["D"] - 0.8) / 4), G["WALL_H"] / 2), glass))
|
||||||
|
# Roof panes: two slopes from eave to ridge.
|
||||||
|
slope = math.atan2(G["RIDGE_H"] - G["WALL_H"], hd)
|
||||||
|
run = math.hypot(G["RIDGE_H"] - G["WALL_H"], hd)
|
||||||
|
for sy in (-1, 1):
|
||||||
|
panes.append(add_box(f"{name}_roof_{sy}", (G["W"] - 0.04, run - 0.03, 0.012),
|
||||||
|
(0, sy * hd / 2, (G["WALL_H"] + G["RIDGE_H"]) / 2 + 0.01),
|
||||||
|
glass, rot=(sy * slope, 0, 0)))
|
||||||
|
# Gable triangles, approximated as narrow strips under the rafters.
|
||||||
|
for sx in (-1, 1):
|
||||||
|
panes.append(add_box(f"{name}_gable_pane_{sx}", (0.012, G["D"] * 0.5, (G["RIDGE_H"] - G["WALL_H"]) * 0.8),
|
||||||
|
(sx * (hw - 0.012), 0, G["WALL_H"] + (G["RIDGE_H"] - G["WALL_H"]) * 0.42), glass))
|
||||||
|
join_group(panes, "panes", root)
|
||||||
|
|
||||||
|
# The temptation, denied ON the data. Same mechanism as the swing set's
|
||||||
|
# crossbar: the most anchor-looking member carries the explicit no.
|
||||||
|
not_a_tie_off(
|
||||||
|
add_empty("ridge_anchor", (0, 0, G["RIDGE_H"]), root, size=0.2),
|
||||||
|
"the ridge bar — the straightest steel-looking line in the yard",
|
||||||
|
"a 45 mm aluminium extrusion whose whole job is holding glass; load a "
|
||||||
|
"sail corner on it and you are not testing the bar, you are testing "
|
||||||
|
"fourteen panes at once")
|
||||||
|
|
||||||
|
stamp(root, name, "structure")
|
||||||
|
root["collateral_key"] = "glasshouse"
|
||||||
|
root["collateral_value"] = GLASSHOUSE_COLLATERAL
|
||||||
|
root["collateral_label"] = "the glasshouse"
|
||||||
|
root["fragile"] = True
|
||||||
|
root["pane_count"] = 14
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
def build_glasshouse_01_wrecked(name):
|
||||||
|
"""The glasshouse after the storm found it. The frame is aluminium — it
|
||||||
|
stays up, near enough at full height — but the glass is GONE: two cracked
|
||||||
|
panes still hanging, the rest a glitter field of shards on the ground,
|
||||||
|
thrown wider than the footprint ever was. A glasshouse doesn't fall, it
|
||||||
|
empties; a wreck that read as a collapsed building would be lying about
|
||||||
|
what glass does. Same frame helper as the intact one, so the two cannot
|
||||||
|
drift."""
|
||||||
|
rng = rng_for(name)
|
||||||
|
root = add_empty(name)
|
||||||
|
alu = get_material("Mat_AluFrame", PAL["alu_frame"], 0.35, metallic=0.7)
|
||||||
|
glass = get_material("Mat_Glass", PAL["glass"], 0.05, metallic=0.0, opacity=0.30)
|
||||||
|
G = GLASSHOUSE
|
||||||
|
hw, hd = G["W"] / 2, G["D"] / 2
|
||||||
|
|
||||||
|
_glasshouse_frame(name, root, alu)
|
||||||
|
|
||||||
|
# Two survivors, one visibly askew in its frame.
|
||||||
|
join_group([
|
||||||
|
add_box(f"{name}_pane_left_a", (0.012, G["D"] * 0.4, G["WALL_H"] * 0.5),
|
||||||
|
(hw - 0.012, -0.3, G["WALL_H"] * 0.35), glass),
|
||||||
|
add_box(f"{name}_pane_left_b", (G["W"] * 0.3, 0.012, G["WALL_H"] * 0.45),
|
||||||
|
(0.4, hd - 0.012, G["WALL_H"] * 0.3), glass,
|
||||||
|
rot=(0, math.radians(8), 0)),
|
||||||
|
], "panes_left", root)
|
||||||
|
|
||||||
|
# The shard field. Deterministic scatter, thrown mostly downwind (+x, the
|
||||||
|
# storm convention every other wreck in the palette follows) and WIDER
|
||||||
|
# than the standing footprint — the measured claim e.test pins.
|
||||||
|
shards = []
|
||||||
|
for i in range(26):
|
||||||
|
a = rng.uniform(0, math.tau)
|
||||||
|
r = rng.uniform(0.2, 1.0)
|
||||||
|
x = math.cos(a) * (hw + r * 0.9) * (1.25 if math.cos(a) > 0 else 0.8)
|
||||||
|
y = math.sin(a) * (hd + r * 0.6)
|
||||||
|
s = rng.uniform(0.06, 0.22)
|
||||||
|
shards.append(add_box(f"{name}_shard_{i}", (s, s * rng.uniform(0.4, 0.9), 0.008),
|
||||||
|
(x, y, 0.006), glass,
|
||||||
|
rot=(0, 0, rng.uniform(0, math.pi))))
|
||||||
|
# A few inside on the beds' footprint too — the glass fell in as well as out.
|
||||||
|
for i in range(8):
|
||||||
|
x, y = rng.uniform(-hw * 0.8, hw * 0.8), rng.uniform(-hd * 0.7, hd * 0.7)
|
||||||
|
s = rng.uniform(0.05, 0.16)
|
||||||
|
shards.append(add_box(f"{name}_shard_in_{i}", (s, s * 0.7, 0.008),
|
||||||
|
(x, y, 0.006), glass, rot=(0, 0, rng.uniform(0, math.pi))))
|
||||||
|
join_group(shards, "shards", root)
|
||||||
|
|
||||||
|
stamp(root, name, "structure")
|
||||||
|
root["broken_variant_of"] = "glasshouse_01"
|
||||||
|
root["collateral_key"] = "glasshouse"
|
||||||
|
root["collateral_value"] = GLASSHOUSE_COLLATERAL
|
||||||
|
root["collateral_label"] = "the glasshouse"
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# THE POOL KIT (SPRINT16 gate 5 — the compliant ring whose steel all says no)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
POOL_KIT = dict(
|
||||||
|
RING_W=5.6, # fence ring, x
|
||||||
|
RING_D=4.2, # fence ring, y
|
||||||
|
FENCE_H=1.22, # AS 1926.1 minimum is 1.2 m — compliance is the flavour
|
||||||
|
POOL_W=3.6,
|
||||||
|
POOL_D=2.2,
|
||||||
|
COPING_H=0.18, # raised fibreglass lip; keeps every vertex above grade
|
||||||
|
POST=0.045, # 40 mm aluminium tube posts
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _pool_fence_posts():
|
||||||
|
"""Post centres round the ring, deterministic order. Module-level so the
|
||||||
|
builder and the ASSETS node list can never disagree about the count."""
|
||||||
|
P = POOL_KIT
|
||||||
|
hx, hy = P["RING_W"] / 2, P["RING_D"] / 2
|
||||||
|
pts = []
|
||||||
|
for x in (-hx, -hx / 2, 0, hx / 2, hx): # long sides, 5 each
|
||||||
|
for sy in (-1, 1):
|
||||||
|
pts.append((x, sy * hy))
|
||||||
|
for y in (-hy / 2, hy / 2): # short sides, 2 each
|
||||||
|
for sx in (-1, 1):
|
||||||
|
pts.append((sx * hx, y))
|
||||||
|
return pts # 14 posts
|
||||||
|
|
||||||
|
|
||||||
|
def build_pool_kit_01(name):
|
||||||
|
"""A backyard pool inside its compliant fence ring — the yard the palette
|
||||||
|
could not dress, and the puzzle is the fence.
|
||||||
|
|
||||||
|
SPRINT16 gate 5. Fourteen 40 mm aluminium posts stand in a perfect ring
|
||||||
|
at exactly the height a low tie line wants, core-mounted, evenly spaced —
|
||||||
|
they LOOK like the most generous anchor field in the game. Every single
|
||||||
|
one carries an explicit `tie_off: false`, because silence is not neutral:
|
||||||
|
`adoptAnchor` reads `rating_hint ?? 1`, so an unrated node a site names
|
||||||
|
becomes the best steel in the game by omission (the S14 audit lesson, and
|
||||||
|
the reason the spec says "honest on every one"). A pool fence holds a
|
||||||
|
child OUT; it does not hold a sail DOWN — it is 1.2 m of child-proofing
|
||||||
|
on core-drilled tube, and a loaded corner would have the whole panel out
|
||||||
|
of the ground before the shackle noticed.
|
||||||
|
|
||||||
|
That denial field is the level design: the pool yard's whole puzzle (arc
|
||||||
|
2) is that the legal tie-offs are all in the wrong places, and the ring
|
||||||
|
of honest nos in the middle of the yard is what makes it true.
|
||||||
|
|
||||||
|
UNPRICED, and that is a statement (the bike/jacaranda ruling, not the
|
||||||
|
glasshouse's spec order): nothing in the sim can bend a pool fence or
|
||||||
|
crack a coping yet, and billing an event the player cannot see is the lie
|
||||||
|
the invoice exists to kill. `priced: False` + the reason ride in extras;
|
||||||
|
price it the day debris can take a panel out.
|
||||||
|
"""
|
||||||
|
root = add_empty(name)
|
||||||
|
alu = get_material("Mat_AluFrame", PAL["alu_frame"], 0.35, metallic=0.7)
|
||||||
|
conc = get_material("Mat_Concrete", PAL["concrete"], 0.95)
|
||||||
|
water = get_material("Mat_PoolWater", PAL["pool_water"], 0.08, opacity=0.88)
|
||||||
|
P = POOL_KIT
|
||||||
|
hx, hy = P["RING_W"] / 2, P["RING_D"] / 2
|
||||||
|
pw, pd = P["POOL_W"] / 2, P["POOL_D"] / 2
|
||||||
|
|
||||||
|
# --- the pool: raised coping ring + water just below the lip -----------
|
||||||
|
join_group([
|
||||||
|
add_box(f"{name}_coping_n", (P["POOL_W"] + 0.3, 0.15, P["COPING_H"]),
|
||||||
|
(0, pd + 0.075, P["COPING_H"] / 2), conc),
|
||||||
|
add_box(f"{name}_coping_s", (P["POOL_W"] + 0.3, 0.15, P["COPING_H"]),
|
||||||
|
(0, -pd - 0.075, P["COPING_H"] / 2), conc),
|
||||||
|
add_box(f"{name}_coping_e", (0.15, P["POOL_D"], P["COPING_H"]),
|
||||||
|
(pw + 0.075, 0, P["COPING_H"] / 2), conc),
|
||||||
|
add_box(f"{name}_coping_w", (0.15, P["POOL_D"], P["COPING_H"]),
|
||||||
|
(-pw - 0.075, 0, P["COPING_H"] / 2), conc),
|
||||||
|
], "pool_shell", root)
|
||||||
|
join_group([add_box(f"{name}_water", (P["POOL_W"], P["POOL_D"], 0.02),
|
||||||
|
(0, 0, P["COPING_H"] - 0.045), water)], "pool_water", root)
|
||||||
|
|
||||||
|
# --- the fence: rails + pickets, joined; POSTS as individual named nodes
|
||||||
|
# so a site (or the editor's node list, if it ever offers per-node
|
||||||
|
# anchors) meets the denial ON the node it would name. RULE 1's regex
|
||||||
|
# only guards *_anchor names; these are guarded by their own pin in
|
||||||
|
# e.test — same rule, wider net.
|
||||||
|
gate_span = (0.25, 1.15) # gate opening on the south run, x range
|
||||||
|
rails = []
|
||||||
|
for sy in (-1, 1):
|
||||||
|
for z in (0.12, P["FENCE_H"] - 0.03):
|
||||||
|
if sy < 0: # south run has the gate opening
|
||||||
|
rails.append(add_box(f"{name}_rail_s{z:.2f}_a", (gate_span[0] + hx, 0.04, 0.04),
|
||||||
|
((gate_span[0] - hx) / 2, -hy, z), alu))
|
||||||
|
rails.append(add_box(f"{name}_rail_s{z:.2f}_b", (hx - gate_span[1], 0.04, 0.04),
|
||||||
|
((gate_span[1] + hx) / 2, -hy, z), alu))
|
||||||
|
else:
|
||||||
|
rails.append(add_box(f"{name}_rail_n{z:.2f}", (P["RING_W"], 0.04, 0.04),
|
||||||
|
(0, hy, z), alu))
|
||||||
|
for sx in (-1, 1):
|
||||||
|
for z in (0.12, P["FENCE_H"] - 0.03):
|
||||||
|
rails.append(add_box(f"{name}_rail_{'e' if sx > 0 else 'w'}{z:.2f}",
|
||||||
|
(0.04, P["RING_D"], 0.04), (sx * hx, 0, z), alu))
|
||||||
|
# Pickets every ~0.1 m — the non-climbable read.
|
||||||
|
for sy in (-1, 1):
|
||||||
|
n = int(P["RING_W"] / 0.105)
|
||||||
|
for i in range(1, n):
|
||||||
|
x = -hx + i * P["RING_W"] / n
|
||||||
|
if sy < 0 and gate_span[0] < x < gate_span[1]:
|
||||||
|
continue
|
||||||
|
rails.append(add_box(f"{name}_pk_y{sy}_{i}", (0.014, 0.014, P["FENCE_H"] - 0.14),
|
||||||
|
(x, sy * hy, (P["FENCE_H"] - 0.14) / 2 + 0.1), alu))
|
||||||
|
for sx in (-1, 1):
|
||||||
|
n = int(P["RING_D"] / 0.105)
|
||||||
|
for i in range(1, n):
|
||||||
|
y = -hy + i * P["RING_D"] / n
|
||||||
|
rails.append(add_box(f"{name}_pk_x{sx}_{i}", (0.014, 0.014, P["FENCE_H"] - 0.14),
|
||||||
|
(sx * hx, y, (P["FENCE_H"] - 0.14) / 2 + 0.1), alu))
|
||||||
|
join_group(rails, "fence_rails", root)
|
||||||
|
|
||||||
|
# The posts — fourteen honest nos, each its own node.
|
||||||
|
for i, (x, y) in enumerate(_pool_fence_posts()):
|
||||||
|
post = join_group([add_cyl(f"{name}_postmesh_{i}", P["POST"] / 2, P["FENCE_H"],
|
||||||
|
(x, y, P["FENCE_H"] / 2), alu, verts=8)],
|
||||||
|
f"fence_post_{i + 1:02d}", root)
|
||||||
|
not_a_tie_off(
|
||||||
|
post, "pool fence post — compliance steel, not rigging steel",
|
||||||
|
"40 mm core-mounted aluminium tube holding a child-proof panel; "
|
||||||
|
"it resists a climbing toddler, not a loaded sail corner — the "
|
||||||
|
"ring LOOKS like fourteen perfect anchors and every one says no")
|
||||||
|
|
||||||
|
# The gate: self-closing, opens outward, latch high — the compliance
|
||||||
|
# tells, in geometry and in data.
|
||||||
|
gx0, gx1 = gate_span
|
||||||
|
gate = join_group([
|
||||||
|
add_box(f"{name}_gate_frame_l", (0.03, 0.03, P["FENCE_H"] - 0.1),
|
||||||
|
(gx0 + 0.03, -hy - 0.06, (P["FENCE_H"] - 0.1) / 2 + 0.05), alu,
|
||||||
|
rot=(0, 0, math.radians(-14))),
|
||||||
|
add_box(f"{name}_gate_frame_r", (0.03, 0.03, P["FENCE_H"] - 0.1),
|
||||||
|
(gx1 - 0.15, -hy - 0.28, (P["FENCE_H"] - 0.1) / 2 + 0.05), alu,
|
||||||
|
rot=(0, 0, math.radians(-14))),
|
||||||
|
add_box(f"{name}_gate_top", (gx1 - gx0 - 0.1, 0.03, 0.03),
|
||||||
|
((gx0 + gx1) / 2 - 0.06, -hy - 0.17, P["FENCE_H"] - 0.08), alu,
|
||||||
|
rot=(0, 0, math.radians(-14))),
|
||||||
|
*[add_box(f"{name}_gate_pk_{i}", (0.014, 0.014, P["FENCE_H"] - 0.22),
|
||||||
|
(gx0 + 0.1 + i * (gx1 - gx0 - 0.2) / 7,
|
||||||
|
-hy - 0.08 - i * 0.028, (P["FENCE_H"] - 0.22) / 2 + 0.08), alu)
|
||||||
|
for i in range(8)],
|
||||||
|
], "pool_gate", root)
|
||||||
|
gate["self_closing"] = True
|
||||||
|
gate["opens"] = "outward, ajar — someone left it swinging, which is the tell"
|
||||||
|
gate["latch_height_m"] = 1.5
|
||||||
|
|
||||||
|
stamp(root, name, "structure")
|
||||||
|
root["priced"] = False
|
||||||
|
root["unpriced_why"] = ("nothing in the sim can bend a pool fence or crack "
|
||||||
|
"a coping yet — billing an unseen event is the lie "
|
||||||
|
"the invoice exists to kill (the bike ruling); "
|
||||||
|
"price it when debris can take a panel out")
|
||||||
|
root["compliance"] = "AS 1926.1 flavour: 1.2 m, non-climbable, self-closing outward gate"
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
def build_shed_01(name):
|
def build_shed_01(name):
|
||||||
"""Colorbond garden shed, skillion roof. Spare hardware lives in here."""
|
"""Colorbond garden shed, skillion roof. Spare hardware lives in here."""
|
||||||
root = add_empty(name)
|
root = add_empty(name)
|
||||||
@ -2969,6 +3512,38 @@ ASSETS = [
|
|||||||
dict(name="fence_panel_snapped", fn=build_fence_panel_snapped,
|
dict(name="fence_panel_snapped", fn=build_fence_panel_snapped,
|
||||||
dims=((2.38, 2.60), (0.03, 1.05), (1.70, 1.90)),
|
dims=((2.38, 2.60), (0.03, 1.05), (1.70, 1.90)),
|
||||||
nodes=["palings", "rails", "debris_palings"]),
|
nodes=["palings", "rails", "debris_palings"]),
|
||||||
|
# SPRINT16 gate 5 — the next temptations. The pergola is the ladder's
|
||||||
|
# honest middle rung (D-endorsed, S15 THREADS): TALLER than the swing set
|
||||||
|
# it out-rates, and its z floor is above the swing's 2.15 ceiling on
|
||||||
|
# purpose — if it ever measures swing-height, someone shrank the rung.
|
||||||
|
dict(name="pergola_01", fn=build_pergola_01,
|
||||||
|
dims=((3.1, 3.4), (2.4, 2.8), (2.55, 2.85)),
|
||||||
|
nodes=["deck", "posts", "beams", "rafters",
|
||||||
|
"pergola_anchor_01", "pergola_anchor_02"]),
|
||||||
|
# The wreck keeps the deck footprint but spills the torn beam past the
|
||||||
|
# edge (x floor ABOVE the intact deck's 3.2) and its roof line is the
|
||||||
|
# rafters hinged down — near intact height at the surviving back beam.
|
||||||
|
dict(name="pergola_01_wrecked", fn=build_pergola_01_wrecked,
|
||||||
|
dims=((3.2, 3.6), (2.4, 2.8), (2.55, 2.85)),
|
||||||
|
nodes=["deck", "posts", "beams", "beam_down", "rafters_down"]),
|
||||||
|
# The collateral apex. Narrow bands on z because the ridge IS the claim:
|
||||||
|
# a glasshouse that grew would stop reading as the small fragile thing.
|
||||||
|
dict(name="glasshouse_01", fn=build_glasshouse_01,
|
||||||
|
dims=((2.5, 2.8), (1.8, 2.1), (2.15, 2.40)),
|
||||||
|
nodes=["frame", "panes", "door", "ridge_anchor"]),
|
||||||
|
# Emptied, not felled: SAME height band as the intact one (the frame is
|
||||||
|
# aluminium and stands), but the shard field throws the plan dims WIDE —
|
||||||
|
# the x/y floors sit above the intact ceilings, which is the "it emptied
|
||||||
|
# outward" claim as a number.
|
||||||
|
dict(name="glasshouse_01_wrecked", fn=build_glasshouse_01_wrecked,
|
||||||
|
dims=((3.4, 5.4), (2.6, 3.8), (2.15, 2.40)),
|
||||||
|
nodes=["frame", "door", "panes_left", "shards"]),
|
||||||
|
# The compliance ring. z is pinned tight around 1.2 m because the height
|
||||||
|
# IS the flavour (AS 1926.1); y reaches past the ring for the ajar gate.
|
||||||
|
dict(name="pool_kit_01", fn=build_pool_kit_01,
|
||||||
|
dims=((5.4, 5.9), (4.1, 4.8), (1.12, 1.32)),
|
||||||
|
nodes=["pool_shell", "pool_water", "fence_rails", "pool_gate",
|
||||||
|
"fence_post_01", "fence_post_14"]),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 MiB After Width: | Height: | Size: 4.5 MiB |
@ -82,6 +82,14 @@ export const FACTORY_ANCHOR_RATINGS = Object.freeze({
|
|||||||
"house_yardside/fascia_anchor_03"
|
"house_yardside/fascia_anchor_03"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"pergola": {
|
||||||
|
"collateral": "pergola",
|
||||||
|
"rating_hint": 0.65,
|
||||||
|
"sources": [
|
||||||
|
"pergola_01/pergola_anchor_01",
|
||||||
|
"pergola_01/pergola_anchor_02"
|
||||||
|
]
|
||||||
|
},
|
||||||
"post": {
|
"post": {
|
||||||
"rating_hint": 1.0,
|
"rating_hint": 1.0,
|
||||||
"sources": [
|
"sources": [
|
||||||
|
|||||||
BIN
web/world/models/glasshouse_01_v1.glb
Normal file
BIN
web/world/models/glasshouse_01_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/glasshouse_01_wrecked_v1.glb
Normal file
BIN
web/world/models/glasshouse_01_wrecked_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/pergola_01_v1.glb
Normal file
BIN
web/world/models/pergola_01_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/pergola_01_wrecked_v1.glb
Normal file
BIN
web/world/models/pergola_01_wrecked_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/pool_kit_01_v1.glb
Normal file
BIN
web/world/models/pool_kit_01_v1.glb
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user