feat(hero): Ionic base and abacus, medallions onto the parapet face

Detail pass on the hero, all verified by measuring the built mesh:

- Column base moulding: a 1.36 m wide, 0.22 m tall cylinder at each shaft foot
  (2.00-2.22 m). The shaft previously started straight off the square pedestal,
  so the base never read in silhouette.
- Abacus: a 1.30 m square, 0.14 m thick slab over each capital. It takes the top
  of the capital zone, so the colonnade still ends at 9.70 m and the 53.1 %
  proportion is unchanged.
- Medallions fixed: they were built with the Z-axis cylinder helper, so the two
  'discs' were pancakes lying flat on the parapet coping (1.1 x 1.1 x 0.02,
  z 14.02-14.04). New disc_on_wall() helper rotates the cylinder 90 deg about X;
  they now stand 1.1 m across on the parapet face (y -1.32..-1.28,
  z 13.05..14.15).

Hero rebuilt (95 parts, 2420 tris, still 14.50 m), re-imported, DemoMap rebuilt,
screenshot set re-rendered. Part report records the new dims and their assumption
notes. Screenshots README notes the detail pass.
This commit is contained in:
zwitschi committed 2026-11-08 17:39:52 +01:00
1 parent fe8a23e5ee
commit 70c6e322e2
29 files changed
+117 -60

No files matched your search

Binary file not shown.
+26 -8
View File
@@ -3,18 +3,18 @@
"frame": "project frame, metres, X east Y north Z up",
"bounds_m": {
"min": [
-13.02,
-13.08,
-12.5,
0.0
],
"max": [
13.02,
0.07,
13.08,
0.13,
14.5
],
"size": [
26.04,
12.57,
26.16,
12.63,
14.5
]
},
@@ -37,6 +37,24 @@
],
"evidence": "6 columns at 4.96 m centres (sibling notes x measured frontage)"
},
{
"object": "Hero_ColumnBase_x6",
"dims_m": [
1.36,
1.36,
0.22
],
"evidence": "assumption: Ionic base moulding; photographs show a wider base at the shaft foot"
},
{
"object": "Hero_Abacus_x6",
"dims_m": [
1.3,
1.3,
0.14
],
"evidence": "assumption: square abacus under the architrave, as Ionic capitals carry"
},
{
"object": "Hero_Architrave",
"dims_m": [
@@ -86,10 +104,10 @@
"object": "Hero_Medallion_x2",
"dims_m": [
1.1,
1.1,
0.02
0.04,
1.1
],
"evidence": "photographs: two stone discs beside the motto"
"evidence": "photographs: two stone discs beside the motto, mounted on the parapet face"
},
{
"object": "Hero_Stylobate",
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -16,7 +16,7 @@ Suggested blender skills: **references** (reference-image-match and visual-match
### Patch tasks - active - 01a1b26b-3c94-707f-9665-2f6b4a79b564 (branch `scale-facade-0.1.2`)
- [x] Correct building scale and street proportions flagged against references. **Mapbox adopted 2026-11-09.** `Geo/tools/mapbox_buildings.py` merges our OSM heights with Mapbox's building data for the same footprints; after adopting its two findings the cross-check reports **66 match, 6 non-building, 0 disagreements**. Adopted: **`LEVEL_HEIGHT` 3.2 → 3.1 m** (`block_height_report.py`, and the Blosm `--level-height` default), plus explicit `HEIGHT_OVERRIDES` (3.0 m) for the three rear `building=yes`/`apartments` structures Mapbox gives as single-storey. Blockout regenerated, frontage re-exported/re-imported, DemoMap rebuilt; packaged `-qaselftest` all PASS. Measured effect: 6922 42.1 → 39.1 m, El Capitan Theatre 20.0 → 20.5 m (both now ~1.9 m above their table height = roof mass).
- [ ] Refine hero façade details where reference photography shows discrepancies. **Started 2026-11-09:** built `Tools/facade_detail_concepts.py`, which generates architectural detail plates from the local reference photographs with an image model (`--list` free, `--all` generates, sidecars + `-vN` no-overwrite, output gitignored). Six **frontal building** studies generated locally under `Art/reference/facade-details/` (whole elevation, entrance in its facade, building with neighbours, pavement view, three-quarter, theatre frontage) — rephrased on owner instruction to show the building/entrance/neighbours rather than close-up detail crops. **First fix from the measurement pass:** window panes were 1.44 m in front of the wall (ahead of the columns) and now sit on the wall plane behind the colonnade. Remaining: use the plates to correct the capital/base profiles and the medallion/cornice detail.
- [ ] Refine hero façade details where reference photography shows discrepancies. **Started 2026-11-09:** built `Tools/facade_detail_concepts.py`, which generates architectural detail plates from the local reference photographs with an image model (`--list` free, `--all` generates, sidecars + `-vN` no-overwrite, output gitignored). Six **frontal building** studies generated locally under `Art/reference/facade-details/` (whole elevation, entrance in its facade, building with neighbours, pavement view, three-quarter, theatre frontage) — rephrased on owner instruction to show the building/entrance/neighbours rather than close-up detail crops. **First fix from the measurement pass:** window panes were 1.44 m in front of the wall (ahead of the columns) and now sit on the wall plane behind the colonnade. **Detail pass 2026-11-09 (all measured on the built mesh):** added an Ionic base moulding (1.36 m × 0.22 m) at each shaft foot and a square abacus (1.30 m × 0.14 m) over each capital, taking the abacus out of the capital zone so the colonnade still ends at 9.70 m; re-oriented the two parapet medallions, which were flat discs lying on the coping (1.1 × 1.1 × 0.02 in the wrong axes) instead of discs on the parapet face; window panes moved onto the wall plane. Hero is now 2 420 tris across 95 parts, still 14.50 m. Cornice/dentil profile refinement remains.
- [x] **Replace the placeholder heights.** **Resolved 2026-11-08 — they were never unknown buildings.** The three "guessed" ways are not building volumes at all: `1360755125` and `1360755126` are `building=roof` at `layer=1` (elevated canopies) and `1417995292` is `amenity=shelter` / `shelter_type=public_transport` (a transit shelter). The report defaulted them to "2 levels × 3.2 m" and produced the bogus 6.4 m. `Geo/tools/block_height_report.py` now classifies roof/shelter explicitly and reports **0 guessed heights**: all 15 block volumes have a real `height` tag, a `building:levels` derivation, or are correctly excluded as non-building volumes. No photograph measurement was needed, and no frontage building was affected.
- [x] Resolve the remaining unknown on the hero: the colonnade currently occupies about 45 % of the height against roughly 50–60 % in the photographs. Fix the vertical proportions. **Fixed 2026-11-09.** Measured baseline: colonnade (columns + capitals) 6.90 m of the 14.50 m envelope = **47.6 %**, because the entablature (3.40 m) and parapet (2.20 m) left only 7.90 m for the order. Thinned both to **3.00 m** and **1.80 m** (`Tools/hero_facade_build.py`), which lifts the colonnade to **7.70 m = 53.1 %** — inside the photographed 50–60 % band, with the 14.50 m envelope, the motto band and the medallions unchanged in position relative to the parapet. Hero rebuilt, re-imported (bounds 2604 × 1257 × 1450 cm) and re-rendered.
- [ ] Tag `v0.1.2` and write `CHANGELOG.md` entry; then remove this section.
+48 -9
View File
@@ -88,6 +88,25 @@ def cylinder(name, x, y, z0, z1, radius, material, vertices=24):
return obj
def disc_on_wall(name, x, y, z, radius, thickness, material=None, vertices=24):
"""A round disc mounted vertically on a wall face.
`cylinder()` builds along Z, so a "medallion" made with it is a pancake lying
flat on top of the parapet rather than a disc on its face — which is what the
first build did (1.1 m across in Y, 0.02 m thick in Z, sitting at the coping).
This rotates the cylinder 90 degrees about X so the axis points along Y and
the disc faces the street.
"""
obj = cylinder(name, x, y, z - thickness / 2.0, z + thickness / 2.0,
radius, material, vertices=vertices)
obj.rotation_euler = (math.radians(90.0), 0.0, 0.0)
bpy.ops.object.select_all(action="DESELECT")
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
return obj
def make_material(name, rgba, roughness=0.75):
mat = bpy.data.materials.new(name)
mat.use_nodes = True
@@ -136,14 +155,32 @@ def build():
col_xs.append(x)
cube(f"Hero_Pedestal_{i + 1}", x, -0.62, PEDESTAL_H + 0.27,
1.24, 1.24, 0.54, stone)
cylinder(f"Hero_Column_{i + 1}", x, -0.55, PEDESTAL_H + 0.55,
PEDESTAL_H + 0.55 + (HEIGHT - ENTABLATURE_H - PARAPET_H - PEDESTAL_H - 0.55 - CAPITAL_H),
# Base moulding: a taller, wider cylinder where the shaft meets the
# pedestal. Without it the shaft starts straight off a square plinth,
# which is not how an Ionic base reads in silhouette.
base_z0 = PEDESTAL_H + 0.55
base_h = 0.22
cylinder(f"Hero_ColumnBase_{i + 1}", x, -0.55, base_z0, base_z0 + base_h,
0.68, stone, vertices=20)
shaft_z0 = base_z0 + base_h
cap_z0 = HEIGHT - ENTABLATURE_H - PARAPET_H - CAPITAL_H
cylinder(f"Hero_Column_{i + 1}", x, -0.55, shaft_z0, cap_z0,
COLUMN_D / 2.0, stone)
cylinder(f"Hero_Capital_{i + 1}", x, -0.55,
HEIGHT - ENTABLATURE_H - PARAPET_H - CAPITAL_H,
HEIGHT - ENTABLATURE_H - PARAPET_H, 0.62, stone, vertices=16)
# Capital block, then the square abacus that carries the architrave.
# The abacus takes the top 0.14 m of the capital zone so the colonnade
# still ends at HEIGHT - ENTABLATURE_H - PARAPET_H (proportion unchanged).
abacus_h = 0.14
cylinder(f"Hero_Capital_{i + 1}", x, -0.55, cap_z0,
HEIGHT - ENTABLATURE_H - PARAPET_H - abacus_h, 0.62, stone, vertices=16)
cube(f"Hero_Abacus_{i + 1}", x, -0.55,
HEIGHT - ENTABLATURE_H - PARAPET_H - abacus_h / 2.0,
1.30, 1.30, abacus_h, stone)
note("Hero_Column_x6", (COLUMN_D, COLUMN_D, HEIGHT - ENTABLATURE_H - PARAPET_H - PEDESTAL_H),
f"6 columns at {COLUMN_BAY} m centres (sibling notes x measured frontage)")
note("Hero_ColumnBase_x6", (1.36, 1.36, 0.22),
"assumption: Ionic base moulding; photographs show a wider base at the shaft foot")
note("Hero_Abacus_x6", (1.30, 1.30, 0.14),
"assumption: square abacus under the architrave, as Ionic capitals carry")
# --- entablature and parapet ------------------------------------------
ent_z0 = HEIGHT - ENTABLATURE_H - PARAPET_H
@@ -167,11 +204,13 @@ def build():
FRONTAGE_W - 3.0, 0.08, 0.85, stone_dark)
note("Hero_MottoBand", (FRONTAGE_W - 3.0, 0.08, 0.85), "photographs: motto panel above the colonnade")
# Medallions either side of the inscription.
# Medallions either side of the inscription: discs on the parapet face, not
# pancakes on the coping (see disc_on_wall).
for i, x in enumerate((-6.2, 6.2), start=1):
cylinder(f"Hero_Medallion_{i}", x, -1.36, HEIGHT - PARAPET_H / 2.0 + 0.42,
HEIGHT - PARAPET_H / 2.0 + 0.44, 0.55, stone_dark, vertices=20)
note("Hero_Medallion_x2", (1.1, 1.1, 0.02), "photographs: two stone discs beside the motto")
disc_on_wall(f"Hero_Medallion_{i}", x, -1.30, HEIGHT - PARAPET_H / 2.0,
0.55, 0.04, stone_dark, vertices=20)
note("Hero_Medallion_x2", (1.1, 0.04, 1.1),
"photographs: two stone discs beside the motto, mounted on the parapet face")
# --- entrances ---------------------------------------------------------
# Three portals between column pairs, each with its own short flight. The