# Mount Pleasant Crest · Home & Sun

> **Main workspace version:** this root application is based on [`huyuxin0429/bto_simulator`](https://github.com/huyuxin0429/bto_simulator) commit `2d838a1efa97c95a563b9351a18b11a97fabe4f2`, with validated editing, persistence, portable studies, adjustable future massing, production packaging and render-lifecycle work merged from the previous workspace app. See [MERGE.md](MERGE.md) for the exact ledger and deliberate non-ports.

## Run this workspace

```sh
npm install
npm run dev
```

Open the printed local URL, normally <http://127.0.0.1:5173/>. The production build is generated with `npm run build`; `npm run preview` serves it.

## Reference-based precinct revision · 18 September 2026

This revision supersedes the earlier disconnected wing and colour-mask road reconstruction. It uses the supplied `reference_images.zip` (especially physical-model views bto_13–16), brochure physical pages 3–5 and 18, and the relevant upper-storey plans. The wider masterplan images are context, not evidence that every pictured building belongs to Crest.

- Rebuilt four connected residential block footprints, including corridor/core joins and the 15/40, 32/45 and 35/46-storey steps. The level-31 apartment and window openings are preserved.
- Corrected the two five-storey car parks, three pavilions, three separate drop-off porches, preschool, residents’ network centre and future-amenity podium. Playground and pavilion outlines incorrectly absorbed into towers are removed from those towers.
- Added planted roofs, pale gable ends, dark window bands, white fins and a red-roofed pavilion informed by the retained Old Drill Shed illustration. Assigning that appearance to the Blk 103 pavilion is an inference; its detailed construction is not provided.
- Replaced the failed road colour-mask with curved road traces and separate drainage. Existing Thomson Road and PIE use mapped roads. Planned road widths, connections beyond the brochure frame, site grading, paths between traced linkways, columns and facility equipment remain estimates.
- Routes stop at safe public approaches to facilities. Roof amenities are illustrated on the car-park roofs; the walk ends below them. The route does not simulate using lifts to those roofs.
- Paths and roads are checked against building footprints. Roads and the off-site MRT approach follow the rendered terrain. Roof planting avoids parapets and equipment zones.

### Verification

The retained upstream precinct manifest checks 34 routes using lateral samples across a 0.56 m body width at 0.55 m and 1.62 m heights. It checks terrain clearance, finite mesh positions and zero-area triangles. The current result has no detected route collisions, buried road/route samples or invalid triangles. `data/precinct_validation.json` records the independent polygon-intersection audit. `npm run check` audits these retained manifests in this workspace. Intended structural contacts (columns supporting roofs, fins attached to façades) are retained.

The retained upstream window manifest checks 52 rays through the flat’s windows, a deliberately inserted covering wall, and the own tower above the flat. `npm run check` audits that result and `npm run check:browser` exercises the current browser scene. These are targeted regression checks, not a claim that every possible camera angle or detailed as-built dimension has been certified.

The reconstruction generators remain in the referenced upstream repository. This workspace retains their reviewed generated browser geometry and validation reports under `data/`; supplied reference photographs are not redistributed as textures.

## Government context, real facade geometry and home selector · 19 September 2026

- The header's **Choose home** dialog exposes every sale-flat block, available storey and numbered stack published in the HDB brochure: 1,348 block/storey/unit combinations across Blocks 100A, 104A and 105A. It displays the matching official floor-plan sheet and marks the selected stack. Block 101A is included in the block list, but HDB does not publish its rental-unit numbers or private layouts; the simulator says so and does not fabricate them.
- The current fully walkable 3D interior remains Block 105A, storey 31, unit 191. Other choices are evidence-backed plan previews. This prevents a 4-room geometry from being misleadingly reused for unlike 2-room Flexi and 3-room homes; their private floor-plan layouts are modeled in the chooser at the fidelity the brochure supports.
- Mount Pleasant Crest no longer uses a painted facade texture. More than 9,000 recessed window bays plus horizontal slab bands and vertical frame/fins are modeled as instanced geometry, following the massing model in supplied photos `bto_13.jpg` through `bto_16.jpg`. Exact bay widths and setbacks remain photo-informed estimates, not construction drawings.
- Current off-site road edges and lane markings use data.gov.sg's LTA Kerb Line and LTA Lane Marking layers. Thirteen nearby mapped building footprints are replaced with URA Master Plan 2025 outlines while retaining the existing height/ground-elevation evidence. Planned roads and Crest buildings continue to use the newer October 2025 HDB brochure because current public-map layers cannot authoritatively describe unbuilt final geometry.
- The data.gov.sg discovery pass also assessed HDB Property Information / Existing Building, SLA National Map Line and Cadastral Land Parcel, and URA MP2025 land-use, rail and monument layers. These are useful corroborating sources, but HDB existing-property datasets correctly do not contain the future Crest blocks, cadastral parcels do not define buildings, and broad master-plan layers do not supply unit layouts or terrain elevation.

Generated provenance lives in `data/unit-catalog.json` and `data/government-road-detail.json`. Reproducible generators are `tools/prepare-unit-catalog.py` and `tools/prepare-government-context.py`; neither contains or writes an API key. Dataset downloads are governed by the Singapore Open Data Licence v1.0.

## Substation correction · 20 September 2026

A pixel audit of brochure page 18 against the model frame (`work/audit_*.py`, `work/fix_substations.py`, run by the Hermes agent on 19 September) found the one-storey box named "100 electrical substation" actually sits in the road gap directly north of the 102 PWCS bin centre, matching the brochure's ESS beside that bin centre, while the ESS strip drawn along the 100 multi-storey car park's road-facing edge was not modelled. `data/project_buildings.json` now names the existing box "102 electrical substation" and adds a one-storey "100 electrical substation" (80 m², 1.5 m off the car park edge, `kind: service`). Utility boxes of this size do not affect tower geometry, sightlines, solar results or roads; `npm run check` expects 13 project buildings.

## Rendering budget pass · 19 September 2026 (later)

Interior and plan views were paying for the whole precinct every frame. The changes below cut the work per frame without changing what is drawn; `npm run check:frametime` measures each view under software rendering as a weak-device proxy.

- **Plan modes render no context.** Isometric and shared-floor views clip the whole context with a plane, so it is now parked on a shadow-only layer: the sun's shadow camera still renders it (the tower above the flat keeps shading the rooms and the floor sun patch still raycasts it), but the main camera skips its ~1.5 million triangles and ~800 draw calls. Isometric went from 1.56 M to 17 k triangles per frame.
- **Spatial cells for frustum culling.** Road surfaces, facade window/slab/frame instances, mapped-building batches and tree crowns are split into grid cells (`chunking.js`), so the window view and outdoor walk only process the cells in front of the camera. The `check:performance` tree-LOD report now sums the cells of each tier.
- **Static props merged.** Unnamed static siblings sharing a material (playground and fitness equipment, pavilions, stair flights and rails, lift doors) merge into one mesh per parent, with their edge outlines merged likewise. Raycast solids, clipped walls and toggled caps stay separate. Precinct draw calls fell from about 1,500 to 850, the shared floor from 900 to 740.
- **Cheaper mid-range crowns and view-specific detail.** Medium-range crowns use a 20-face icosahedron (they cover about 10 px at 380 m); lane paint and trunks are hidden from the flat, where they are sub-pixel or under crowns.
- **Interior views get far copies of the outdoor detail.** From the flat, 90 m up, road, path and drainage surfaces switch to simplified copies (`simplifyDraped`: interior vertices clustered on a 12 m grid, boundary chains Douglas-Peucker simplified to 0.6 m; 327 k → 37 k triangles for the road surface), facade bays keep only their two facade-parallel faces (4 of 12 triangles), and the mapped terrain surfaces are split into cells so the half behind the camera is culled. The ground-level views keep the full meshes. Window view and interior walk went from about 690 k to 320 k triangles per frame.
- **Lights that contribute nothing leave the shaders.** The five lamp point lights and the moon light are hidden (not just zeroed) when off, so every material's fragment shader stops evaluating them; interior fill-rate fell about 20 % under software rendering.
- **Fixed runaway render loops after orbiting.** OrbitControls damping fires `change` inside `controls.update()`, which requested a *second* animation frame from within the frame loop; every drag doubled the number of parallel loops, and with traffic animation keeping them alive the scene was being rendered about 1,100 times per frame after a single drag (22,180 renders per 20 frames measured; 43,060 after two drags). Exactly one frame can be pending now. This was the continuous lag on otherwise capable machines.
- **Fixed the outdoor walk slowing down with distance.** The guidance-route search sampled the terrain (a point-in-polygon test against every asphalt polygon) for every route segment that was closer than the last, so each frame cost grew with the distance already walked: 18 ms at the lobby, 340 ms after 93 m on this desktop, ~2 fps at the road on a MacBook. The terrain is now sampled once per search and the on-road test uses a 16 m grid of candidate polygons with a memo; frame time is flat along the whole route. Fixing it exposed a second bug the slowness had hidden: the route search measured plan-view length while the walker's arc length is 3D, so on slopes the estimate lagged by more than one 60 fps step and the guided walk froze at 113 m. Both lengths now agree and the route completes.
- **Browser-side fixes for Apple GPUs and Metal-backed browsers.** The renderer no longer asks for `preserveDrawingBuffer` (it forced a back-buffer copy every frame; the PNG export renders synchronously before reading the canvas instead), shader compile-status checks are only kept on localhost or with `?debug` so production compiles in parallel, and all view/lamp shader variants are compiled 0.8 s after load with `compileAsync` so switching views or turning to face new materials no longer stalls. `navigator.deviceMemory` being absent (Safari, Firefox) no longer marks a machine as low power. Append `?perf` to the URL for a live readout of fps, draw calls, triangles, canvas size, pixel ratio, program count and GPU name.
- **Adaptive resolution watches the real frame interval**, which stalls under GPU back-pressure, instead of the CPU submit time, so GPU-bound devices now step the pixel ratio down (to 0.55×). Pixel ratio caps are 1.25 (1.0 on touch-only devices) and the day-playback re-renders the shadow map at ~12 Hz rather than every frame.

## Performance pass · 19 September 2026

The original outdoor view rendered about 6.75–6.80 million triangles per frame, mostly from seven 396-triangle spheres per tree cluster. Vegetation now uses three distance tiers: five rounded low-poly crowns nearby, three icosahedral crowns at medium range, and one coarse crown at long range. The visual footprint remains layered while exterior views render about 1.55–1.62 million triangles, roughly 76% fewer.

Additional safeguards include a 2,048 px shadow map instead of 4,096 px, shadow-map updates only when sun or shadow-casting scene state changes, a mobile/low-power pixel-ratio cap, adaptive resolution under sustained slow frames, cached world-label discovery, 30 Hz pedestrian transforms, and reduced pedestrian counts in apartment-focused modes. Current LTA road detail is hidden where it cannot contribute to apartment views. The full precinct and outdoor walk retain all 55 pedestrians; cars have been removed. Run `npm run check:performance` against a running app for the current headless performance guardrails; results are written to `artifacts/performance-main-current.json`.

## Furnished simulation for every published home

The selected-home isometric view uses a camera-relative section limited to
exterior walls. The whole-floor view uses a fixed 0.95 m section through every
wall so the corridor, lobby and enclosed wash area remain readable, with wall
heads capped at the section height. Walking restores every partition and common-
area wall to its full 2.60 m height. Window frames, glazing, heads, lintels, edge
lines and optional
blinds are classified with their supporting wall, so near exterior assemblies
are sectioned together and no element hangs in space. Orbiting restores the newly
far-facing exterior envelope. The **Isometric walls** control can instead show
every wall at full height or hide all walls visually while retaining their
shadows and collision geometry. Door leaves remain omitted
for circulation, but 693 conservative PDF-width doorway gaps have visible
2.10–2.60 m lintels where their supporting far wall is retained. The lintel
filter excludes all detected windows and accepts only narrow 0.52–1.45 m gaps
bounded by source walls. Navigation stays open below each head.
Plaster walls use subtle procedural relief plus restrained corner lines, giving
depth cues without photographic textures. Run `npm run check:walls` and
`npm run check:walls-browser` for all 200 Default/White variants and live
isometric/shared-floor rendering, including exterior classification, camera
rotation, support consistency, all-room compass sweeps and the capped wash-area
section.

Detailed windows now use 607 distinct openings from the 800 raw PDF detections.
Repeated frame strokes are deduplicated. Full-height wall polygons that crossed
61 glazing openings are cut only between sill and head, retaining solid sills,
lintels and walking collision. A small furniture refit in four variants preserves
room access after adding the physical sill barriers. All item dimensions and
room programmes remain intact. Regenerate with
`python tools/prepare-window-openings.py` (Shapely plus Node), then run
`npm run check:windows` and `npm run check:windows-browser`. The checks cover
9,108 glazing rays and furnished access across 200 Default/White variants.

Residential tower shells and selected interiors now share registered PDF floor
geometry. Common unit positions align the twelve sheets to one frame per block
within 16 mm; a rigid rotation/translation places each block on the brochure site
trace without changing room dimensions. Low/high bands retain their differing
footprints. Sun/moon directions and landmark views use that same transform. In
precinct and outdoor modes always use a complete canonical tower exterior, so
no floor is highlighted, recessed or otherwise changed by the selected home.
Only indoor/window modes use the selection-aware shell opening needed to see the
active home. The precinct camera offset also rotates with the registered block,
keeping equivalent site views consistent when the selected block changes.
Site placement is still schematic: the fitted site-outline agreement is 0.67–0.81,
not a survey. Run `npm run check:registration` and
`npm run check:registration-browser` and `npm run check:precinct-selection` for
contour/coordinate, live-view and cross-selection invariance checks.

Floor elevation, roof levels and façade bands now share one vertical contract:
storey 2 starts above the 4.5 m assumed void deck, then storeys repeat at 84/29 m.
Selecting a top floor no longer raises a lower wing's roof or extends its façade.
The roof remains above the highest residential floor, and occupied-floor massing
keeps the full clear-height interval open. Run `npm run check:levels`; against
the development server, `npm run check:levels-browser` also audits actual project
meshes and façade instances for low, transition and top-floor selections.

Road/path meshes are checked directly with `npm run check:road-meshes`: all
722,530 rendered road/path/paint triangles must be finite, non-collapsed and
upward wound. After a road-mesh rebuild, run the normalization, paint alignment
and bus-approach generators in that order to refresh dependent geometry/hashes.

On-site walking guides now use straight, clearance-checked segments. The 33
routes retain their lobby start and existing facility-access endpoint while
reducing raster stair steps from 929 to 514 vertices. Porch/pavilion/pad floors
are included in connectivity, and 0.027 m² of visible threshold infill closes
tracing seams no wider than 5 cm. Guides stay within 0.65 m of their source route
with 0.42 m obstacle clearance. Animated pedestrians use the same on-site paths
and turn around at open endpoints. Regenerate with
`python tools/prepare-pedestrian-guides.py`; verify with `npm run check:paths`
and `npm run check:paths-browser`. Roof amenities still require vertical travel.

Bus approaches now check the whole carriageway union. The previous schematic
Thomson shelter stood on an adjacent slip road despite clearing its assigned road
centreline; it is now on the nearby verge. Two explicit estimated approach paths
stay outside roads/buildings and follow the terrain. The north-side future stop
remains visible, but its guide stops on the precinct side with a **crossing not
modelled** notice. Exact current stop coordinates and future crossing engineering
remain unverified. Run `npm run check:bus` and `npm run check:bus-browser`.

Choose any published block, storey and unit, then **Open furnished home**. All
1,348 sale-home choices use the same PDF-derived model and furnishing pipeline;
#31-191 has no special entry or separate interior renderer. Each bedroom receives
a bed and wardrobe; living/dining, kitchen, laundry, bathrooms and shelter receive
appropriately sized furniture and fittings. Furniture is illustrative, not HDB's
supplied fit-out. Placement preserves walls and connected room access.

The selected home drives the room list, shared floor, window view, approximate
storey elevation, sunlight study and exports. Furniture editing, undo/redo and
saved arrangements work independently per block/storey/unit. Study format v2
includes that identity; imports for another home are rejected before mutation.
Old v1 studies and their local storage are retained but are not applied to the
new geometry. Select the corresponding home and export a new study.

The lightweight floor page uses the same furniture renderer. The main app retains
the precinct, outdoor walking and solar controls. Site registration is an
approximation using the selected block's brochure massing; individual room and
wall dimensions retain the PDF scale. Eligible 3-/4-room homes offer both Default
and **White flat · open living / sleeping zones** under Interior layout. The White
option follows HDB physical page 10 / printed 16: only bedroom partitions are
omitted. Kitchen, bathroom, shelter, structural and party walls are retained;
furniture remains an illustrative arrangement of the open space. Both viewers,
URLs, exports and per-layout saves retain this choice. Default saves keep their
existing storage keys. The secondary dimensional PDF's fitted examples (with new
partitions) and unpublished rental interiors remain outside the current model.

White variants cover 75 unit/floor plans and 807 home/storey choices. Regenerate
with `python tools/prepare-white-flats.py /path/to/mount_pleasant_crest.pdf` after
updating the default floor/furniture assets. Run `npm run check:white` for actual
removed-wall raycasts, 10,547 retained-wall probes, complete furniture access and
source hashes; `npm run check:white-browser` exercises selection, reload,
Default restoration, isolated saves, unavailable types and mobile layout.

Run `npm run check` for all 125 distinct furnished unit/floor variants,
including complete room programmes, furniture clearance, navigation and edit
rollback. `npm run check:browser` covers all four flat types, #31-191 through
the same selector, editing/persistence, portable studies and mobile containment.

## Published floor simulation · 20 September 2026

The Block 105 driveway now joins Wong Chin Yoke Road through the gap shown in the
HDB p18 planted verge. The misplaced western opening is restored as landscape;
the western service lane remains connected to the new driveway. Road paint and
grass cuts are regenerated together. Every declared planned junction is checked
against the road it names, and mesh raycasts verify the relocated entrance.

Road paint now follows the actual rendered carriageway triangles. The previous
nearest-route-point heights left some paint metres above or below sloped roads.
The alignment generator projects paint onto its supporting road, splitting only
where slope changes require it. Government survey fragments outside the current
rendered roads are omitted from display while their original data is retained.
This corrects rendering alignment; the roads themselves still need a plan audit.
The standard check covers over two million paint/survey samples, collapsed
triangles, and source hashes. After rebuilding roads, run
`python tools/align-road-markings.py` with NumPy and Shapely installed.

The main app's shared-floor walking surface now follows the visible slab instead
of older overlapping rectangles. The roof-only western projection is no longer
walkable, and its leftover rail extension is trimmed. All thirteen interior/shared
destinations remain connected in a fresh 12 cm navigation-grid check. Run
`npm run check:shared` against the active app server for this regression.

Choose a block, storey and home, then **Explore selected floor in 3D**. The dedicated
`floor.html` viewer loads one of twelve HDB default floor variants, covering all
1,348 published sale-flat combinations. Whole-floor and selected-home cutaways,
room jumps, indoor walking and corridor walking use the same extracted geometry.
Use WASD/arrows or the touch pad; drag to look. Every home connects to its corridor.
All four published flat types are included, with generated furnishings for the
selected home in both viewers.

The generator reads the printed 10 m scale bar on each sheet, filled structural
walls, outlined partitions and enclosed common-area components. It excludes door
swings, AC ledges and the roof-only projection on the upper-storey 105A plan.
Window openings have separate instanced glazing, frames and sill/head geometry.
Block 104A's pages 27/28 use a thinner partition stroke than the other sheets;
both styles are extracted, with explicit bedroom-divider regression probes.
Wall heights and finishes remain estimates. HDB's open White-flat option is
included; fitted-example partitions, rental interiors, lift travel and
inter-storey stairs are not included.
Wash areas connect through their source-drawn thresholds; chute annotation leaders
are excluded from the physical floor boundaries. This viewer does not assert that
the precinct or older shared-floor model is fully corrected.

Regenerate with Python + PyMuPDF + Shapely:

```sh
python tools/prepare-floor-simulation.py /path/to/mount_pleasant_crest.pdf
npm run check
npm run check:floors   # against the dev server on 5173; BTO_TEST_URL overrides
```

The floor check triangulates the actual polygons and checks their area, rings,
room/corridor connectivity and collision boundaries. The browser check loads
all twelve variants, walks two homes per variant, exercises corridor starts and
mobile controls, and captures screenshots. Furnished rendering has bounded
geometry and on-demand frames. See
[the active improvement audit](reference/IMPROVEMENT_AUDIT.md) for requirements
that remain unverified.

## Verification tiers, evidence panel and deployment · 20 September 2026

Checks are grouped by cost so the right tier runs at the right moment:

| Tier | Command | What it covers | When |
| --- | --- | --- | --- |
| Fast | `npm run check` | Manifests, unit catalogue, transport topology, floor simulation, road alignment, furnished homes, home selection, evidence export (≈2 min, headless) | Every push (GitHub Actions `check`), before every deploy |
| Geometry | `npm run check:geometry` | White flats, window openings, wall presentation, guides, bus approaches, road meshes, nature, building levels, registration (headless, tens of minutes) | Nightly `full-check`, before a release |
| Browsers | `npm run check:browsers` | All Playwright suites against a dev server on 5173 and previews on 4180/5180 (`BTO_TEST_URL` overrides; `check:floors` and `check:levels-browser` default to the dev server because they import source modules) | Nightly `full-check`, on demand |
| Everything | `npm run check:all` | The three tiers in order | Before merging a large change |

`npm run deploy` (from the workspace root or here) refuses a dirty tree or a HEAD that differs
from `origin/master` (`tools/predeploy-check.mjs`; `BTO_DEPLOY_UNTRACKED=1` overrides), runs the
fast tier, then builds `dist/`. The Cloudflare Tunnel origin serves `dist/` directly, and
`npm run deploy:cloudflare` publishes the same build to Cloudflare Workers static assets
(`wrangler.jsonc`, worker `bto-simulator`, https://bto-simulator.huyuxin0428.workers.dev) from a
machine that has run `npx wrangler login`; the `deploy-cloudflare` workflow does it after every
green push once the repository secrets `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` exist.
`_headers` gives that origin the same cache policy as the local server (immutable hashed assets,
revalidated everything else) and `_redirects` keeps `/` on `index.html`, because HTML handling is
off so `floor.html` is served exactly as the app links to it.

Baked mesh binaries are `application/octet-stream`, which CDN edges leave uncompressed, and the
two road binaries alone were 22 MB of a 29 MB first load. `tools/sync-simulator-public.mjs` now
writes a gzip sidecar beside every `data/*.bin`; `binary-fetch.js` requests the sidecar and
inflates it with `DecompressionStream`, falling back to the raw file on older browsers. The same
helper rejects non-OK responses, so a missing binary reports its URL instead of becoming silent
garbage geometry, and every fatal load shows a panel with **Retry** and **Open another home**.

**References ↗** now ends with *What is verified · what is still inferred*: the requirement rows
of [`reference/IMPROVEMENT_AUDIT.md`](reference/IMPROVEMENT_AUDIT.md), exported to
`data/evidence.json` by `npm run evidence` and kept current by `tools/evidence-check.mjs` in the
fast tier. Edit the audit, regenerate, commit both.

## Open the simulator

Run `npm run dev`, then open the printed local URL (normally **http://127.0.0.1:5173/**). `npm run build` creates the production site in `dist/`, and `npm run preview` serves it. Rendering happens entirely in your browser using WebGL 2 / Three.js; Unreal does not need to run. All rendering libraries and scene data are bundled locally.

The earlier **MountPleasantSimulator** Unreal Engine project is retained separately. This browser version is a reconstruction using the same plan and geographical inputs; it is not an Unreal stream or an Unreal HTML export.

## Explore

- **Isometric:** drag to orbit, scroll to zoom, right-drag to pan. **Isometric walls** switches between the camera-aware exterior cutaway, every full-height wall, and visually hidden walls. The roof remains open in all three choices; hidden or clipped geometry still blocks sunlight and walking.
- **Shared floor:** opens the level-31 common areas as an orbitable cutaway. Select a feature from the panel or a model label, then choose **Walk to this area**. The room selector also includes the corridor, lift lobby, chute area and both stair landings.
- **Walk inside:** drag to look, hold WASD or arrow keys to move. The on-screen arrows also work with a mouse or touch. Walls, windows and furniture block walking. Movement slides along obstacles and automatically recovers from an invalid position. **Clear walking position** and **Jump to a room** provide an immediate reset. The room selector returns keyboard focus to the scene.
- **Window view:** starts close to the living-room window, facing NNW. Drag to inspect the wider outlook; scroll to change the field of view. The room selector provides other interior viewpoints. Labels identify the mapped Polo Club clubhouse and field when their anchor points are in view and clear of modelled walls/buildings. Select a landmark to look toward it and see its reference. Sightline tests are approximate and do not resolve individual leaves or all terrain details.
- **Door-free circulation:** all apartment door leaves have been removed, retaining the plan’s openings and their full-height lintels. Cabinet fronts remain part of the furniture.
- **Furniture:** click it in the isometric view, choose it under **Arrange furniture**, or aim within reach and press E while walking. Rotate, move in 20 cm steps, or reset that item. The revised arrangement keeps the main circulation route clear. Furniture moves that overlap the model, other furniture, or room access are rejected. Furniture arrangements are illustrative, not a measured renovation layout.
- **Persistent furniture and recovery:** choose any item under **Arrange furniture** or select it in the model. Accepted changes save in this browser; Undo/Redo keeps up to 60 edits during the session, and Reset is undoable. Every accepted or imported arrangement is checked for furniture/wall overlap and access to all eight room destinations.
- **Portable study:** **Download study** saves the arrangement, view/room, sunlight, blinds, lights, haze, traffic and future scenario to JSON. **Open study** validates the complete file and previews it before replacement. **Undo import** restores the prior study until reload. Exact camera pose, walking progress and edit history are not included.
- **Sunlight:** choose a date, any of the twelve month buttons, or a time across the full day. **Watch the day** animates the sun. Blinds block direct light; interior lights illuminate rooms after dark. Haze changes the visual atmosphere.
- **Save image:** downloads the current 3D view as a PNG. It does not include the control panel.
- **Future obstruction scenario:** adds amber hypothetical towers on reserved land. Height (20–200 m) and coverage (15–80%) update both the scene and annual obstruction analysis. These are not approved designs.

## What the model is based on

| Element | Evidence and accuracy |
|---|---|
| Unit | Block 105A, level 31, unit 191. HDB brochure PDF page 32 / printed 41, applicable to 17th–40th storeys. |
| Plan | Four-room geometry re-traced using the supplied brochure’s 10 m scale bar. At 440 dpi the bar spans 424 pixels (42.4 pixels/metre). The frontage between model wall reference lines is approximately 12.10 m; bedroom widths approximately 3.11 / 2.81 / 2.81 m; maximum depth 8.87 m. Allow roughly 0.1–0.2 m for tracing and wall-reference ambiguity. These are drawing-derived dimensions, not surveyed construction dimensions. Furniture remains illustrative. Published flat type is 89 m² including the air-con ledge; this is not a surveyed measurement of the browser mesh. |
| Dimensional cross-check | The supplied [MPC dimensional drawing set](https://d14s2iums0fe7u.cloudfront.net/hdb/floorplan/mpc.pdf) gives the 4-room default frontage as 12.060 m (3.015 / 2.805 / 2.805 / 3.435 m), front-room depths of 4.470–4.490 m, 0.900 m bedroom doors and a 1.200 m entrance. Its frontage is within 40 mm (0.3%) of the HDB-scaled 12.10 m model, so the model is retained without a misleading global rescale. The PDF is BIMECO-branded; HDB authorship or approval was not independently established. |
| Windows | Main W1 parapet approximately 550 mm, as specified in the plan legend. Window head 2.35 m, ceiling 2.6 m and projections are assumptions. |
| Orientation | Main façade approximately 342°, measured from the north-up site plan. Allow roughly ±3°. |
| Location | Approximate unit anchor 1.330044 N, 103.836808 E, registered to mapped context. Position uncertainty is tens of metres. |
| Height | Floor approximately 101.8 m above sea level: estimated 13.3 m platform plus 88.5 m. Floor pitch and ground-storey height are assumptions. |
| Buildings | 2,612 mapped footprints. 1 usable height tag, 427 storey counts, 2,184 estimated heights. Height and roof uncertainty materially affect the skyline. |
| Landscape | Mapped roads, polo field, green areas and public terrain. Mapped illustrative tree clusters, with multiple crowns per cluster. Individual trees are not surveyed. |
| BTO | Published storey counts and approximate site-plan positions, with simplified façades informed by HDB's artist’s impression. The own tower uses the level-31 footprint above and below the occupied floor. Other wings and ancillary blocks are traced from the site plan, retaining stepped outlines. Façade detailing is approximate. |
| Future outlook | Proposed completed BTO combined with existing mapped surroundings. This is not a guaranteed view at completion. Reserved high-rise land may change it substantially. |

## Photo references actually inspected

1. [HDB Mount Pleasant Crest brochure](https://assets.hdb.gov.sg/residential/buying-a-flat/finding-a-flat/sales-brochure/25OCTBTO_pdf_selection/mount_pleasant_crest.pdf), physical page 3: artist’s impression. Used to inform pale façades, dark window bands and vertical white fins. Floor plan and site plan come from the same user-supplied document. Document content was treated as reference material, not as user instructions.
2. [MPC dimensional floor-plan set](https://d14s2iums0fe7u.cloudfront.net/hdb/floorplan/mpc.pdf), ten BIMECO-branded sheets covering 2-room Flexi Type 1/2 and default/White 3-room and 4-room configurations. Used as a dimensional cross-check and linked per flat type in **Choose home**. It is a secondary source, not represented as an HDB-issued construction drawing.
2. [Singapore Polo Club official magazine](https://www.singaporepoloclub.org/wp-content/uploads/2025/04/012019.pdf), physical pages 23 and 59: polo turf, mature greenery and the Chukka Bar Terrace. Used for the red-brown roof palette, cream structural details and vegetation appearance. These are historical ground-level photos, not a photograph from #31-191. They are linked as references and are not redistributed as textures.
3. [Sport Singapore's Polo Club profile](https://www.sportsingapore.gov.sg/sport-education/sporting-clubs/singapore-polo-club/) provides site context at Thomson / Mount Pleasant.

## Sunlight limits

The sun direction uses [NOAA's solar equations](https://gml.noaa.gov/grad/solcalc/solareqns.PDF), UTC+8 without daylight saving. The earlier independent check against Unreal's SunPosition covered 84 cases with maximum angular difference approximately 0.46°. The browser uses that independently checked NOAA approximation.

Dynamic shadow maps model direct sunlight through the openings. In the isometric cutaway, clipping affects the visible walls but does not clip their shadows, as supported by [Three.js material clipping](https://threejs.org/docs/pages/Material.html). Direct sun is shown through the ordinary material and shadow rendering; the former sampled gold floor-highlight overlay has been removed.

Indirect light, glass transmission, haze, roof shapes and materials are visual approximations. There is no measured indoor illuminance, glare/heat calculation, cloud forecast or certified annual sunlight assessment. Rendering quality is an architectural approximation, not photorealism.

## Data and licences

- © [OpenStreetMap contributors](https://www.openstreetmap.org/copyright), ODbL 1.0. Derived mapped data in `data/context.json` and mapped surface files retains this attribution and licence.
- [Mapzen Terrain Tiles](https://registry.opendata.aws/terrain-tiles/), tile N01E103, with underlying public terrain sources. [Terrain attribution](https://github.com/tilezen/joerd/blob/master/docs/attribution.md).
- Three.js r180, MIT licence bundled in `vendor/LICENSE`.
- Furnishings and material patterns are procedural originals. Reference photos are not packaged as photographic assets.

## Practical notes

Tested in the Codex in-app browser on this computer. An up-to-date Chrome or Edge with WebGL 2 and graphics acceleration is recommended. Performance depends on device and resolution. Use Stop.ps1 to stop the local server. The app makes no network requests outside its own local server while rendering; external references open only when clicked.


## Layout and navigation correction

The service-yard / living divider and continuous bathroom rear wall are restored. Bedroom proportions, outer wall runs, window widths and circulation openings are traced from physical PDF page 32. The yard has its own framed side opening, and the isometric cut surfaces are capped so walls do not appear hollow.

The main bed is turned against the side wall, wardrobes are moved away from bedroom entrances, the dining table runs lengthwise with chairs on its sides, and a smaller coffee table leaves a passage between the TV and seating. All dimensions use metres without applying a global scale to furniture or eye height.

Navigation was tested as a connected floor grid at 8 cm spacing with furniture present: every clear grid cell is connected to the living room, and all eight room starting points are reachable. Boundary-wall checks, doorway-clearance checks, repeated movement against an obstacle and recovery from inside a coffee table pass. See `data/navigation_validation.json`. This supersedes the earlier point-only collision checks.


## Shared floor and air-con ledge

The shared areas are traced from the Block 105A 17th–40th-storey plan on physical PDF page 32 (printed page 41), using the same 42.4 pixels/metre scale as the flat:

- Six lift shafts in two banks of three, facing the central lobby.
- Staircase A beside the western lift bank and staircase B between the 195 and 197 wings.
- Two centralised general-refuse chutes, one recycling chute and the adjacent wash area.
- Neighbour entrances #31-187, #31-189, #31-193, #31-195, #31-197 and #31-199; the #31-191 entrance remains door-free.
- An external air-con ledge outside the main bathroom, approximately 2.45 × 1.20 m (2.94 m²), with an illustrative condenser and parapet.

The common corridor connects to the apartment's walking area. Neighbour envelopes are enclosed solid volumes that block corridor sightlines; private interiors are not reconstructed. Lift travel and movement between storeys are not simulated. Stair flights, door appearance, ceiling heights, chute hatches and air-con equipment are illustrative. Plan tracing is approximate and should not be used as a construction or access survey.

The connected walking model was checked on a 10 cm grid with furniture present: all eight interior and five shared destinations are clear and reachable from the living room. See `data/shared_validation.json`. The shared-floor feature picker and lift-lobby viewpoint were also checked in the browser.


## Corridor and environment revision — 16 September 2026

### Structure

The common-area slabs are joined into one polygon mesh. The corridor ceiling underside is at 2.60 m and meets the wall tops, eliminating the former vertical gap and overlapping ceiling surfaces. Exposed corridor edges have parapets, beams and columns; neighbouring homes are enclosed volumes. The three chute hatches are mounted on one continuous full-height shaft wall. Both stairs now show parallel flights, risers, an intermediate landing and handrails. Their dimensions and finishes are estimates; walking is limited to the current-floor landings.

Block 105A remains visible while walking in the corridor. Its combined footprint is used for the storeys above and below level 31. The site context now includes twelve traced wings and ancillary volumes, covering 100A, 101A, 104A, the lower wing of 105A, car parks and supporting blocks. Published storey counts are retained. The shapes are closer to the brochure, but upper-level façade setbacks, detailed windows and vertical structure are not surveyed. The live shadows and direct-sun overlay include the polygon-shaped project volumes; landmark labels also respect these obstructions.

### Roads, landscape and movement

The scene contains 3,447 mapped or plan-traced ways: 2,480 carriageway ways and 967 pedestrian/path ways, including 40 mapped PIE ways in the wider context. Roads are sampled against the actual rendered terrain triangles so the road surface does not sink into the terrain. Major roads have edge/lane markings. Planned Mount Pleasant Avenue, Wong Chin Yoke Road, Old Police Academy Road, the drop-off access and pedestrian links follow the HDB site plan. Roads through construction areas and future alignments remain approximate; temporary traffic diversions are not simulated. The PIE is south/east of the development, so it is not guaranteed to be visible from the NNW living-room window.

There are no simulated cars. They were removed because looping vehicles on isolated route fragments falsely implied junction connectivity and lane behavior. Up to 55 illustrative pedestrians remain on path/linkway centre-lines; apartment-focused views use a smaller active subset. Use **Pedestrians** to toggle them.

### Road crossing and PIE audit · 20 September 2026

Two retained OSM ways (`540529077` and `763051364`) were still tagged as
private parking aisles for the former Old Police Academy. They crossed the
proposed Mount Pleasant Avenue without shared junction nodes and are absent
from HDB's completed-estate plan. The obsolete asphalt is removed only outside
the retained avenue, restoring 165.14 m² as terrain-following grass; no
replacement connection is invented. Current OSM way metadata was checked live
before removal, and the source IDs and decision remain in the generated data.

The graph gate now examines every pair of visible road centrelines within 900 m:
same-level crossings must share an actual junction, while grade-separated
crossings must retain at least 4.5 m clearance. It currently finds zero illegal
same-level crossings and ten valid grade-separated crossings around the PIE.
This validates model consistency, not civil-engineering clearance or future-road
approval. The PIE bridge tags remain sourced from OSM and the 5.5 m deck rise is
still a documented modelling estimate.
Static doorway lintels are merged by selected/neighbour floor group; this keeps
the restored wall heads visually identical while all twelve furnished floor
variants remain below the existing 1,000-draw-call browser budget.

### Precinct transport-layout audit · 19 September 2026

The road and transit layout is now checked as a graph rather than only as a rendered surface. Both future Mount Pleasant Avenue bus stops sit 2.2 m beyond the carriageway edge; the existing Thomson Road stop sits 2.0 m beyond its carriageway edge. Their pedestrian guidance routes now reach the corrected shelters. Every planned road endpoint is classified as a junction, building/service entrance, turning head, or future-phase continuation. The Blk 105A drop-off, PWCS/bin-centre spur and interim end of Wong Chin Yoke Road have explicit turning heads rather than roads ending in grass.

The intended at-grade intersections are restricted to the Avenue/Academy loop, Academy/Wong Chin Yoke junction, Wong Chin Yoke/Blk 105 access, and the two eastern service accesses. Grade-separated roads are not treated as junctions. LTA lane markings without reliable layer metadata are retained only when they match one at-grade route more closely than every elevated route; ambiguous markings around the PIE/Thomson overpass are omitted. See `data/precinct-transport-corrections.json` and run `npm run check`.

Concave roof footprints are triangulated within their outlines instead of using a single pyramid. Foliage has smoother crowns, surface variation and corrected colour multiplication. Plaster, tiles, concrete, paving, asphalt, roof tiles and landscape surfaces have local procedural textures. The Polo Club eaves height is revised from the generic 12 m fallback to a photo-informed 5.8 m estimate; this is an estimate from the terrace-photo context, not a measured height.

### Sun, moon and illustration

The sky has a daylight/twilight gradient and actual small sun and moon discs. **Look toward sun/moon** turns the camera toward the computed body. Walls, frames and the building can still hide it; bodies below the horizon are hidden. The sun direction uses the existing checked NOAA equations. The Moon uses a simplified orbital calculation from [Paul Schlyter's computation notes](https://stjarnhimlen.se/comp/ppcomp.html), without the full lunar perturbation or topocentric corrections. Lunar position and phase appearance are approximate, not ephemeris-grade. Moonlight is a visual approximation. Very wide browser windows now limit horizontal perspective to 100° at reset, avoiding extreme edge stretching.

**Realistic illustration** opens `illustrative-outlook.png`: an AI-generated interpretation of the model's June afternoon outlook. This is a fixed artistic image, not a surveyed photograph, and does not respond to time/date controls. Its extra vegetation, façade detail and traffic must not be used as evidence of the exact future view. The interactive scene remains a 3D reconstruction with approximate materials and indirect lighting.

Additional official context checked: [HDB Mount Pleasant environmental plan](https://www.hdb.gov.sg/-/media/about-us/our-role/plan-and-design-towns/planning-with-the-environment-in-mind/Mount-Pleasant-EMMP.pdf) and [LTA PIE / Thomson Road diversion map](https://onemotoring.lta.gov.sg/content/dam/onemotoring/Driving/Road_Closures/Temporary/PIE/2026.03.13-Notice_Traffic%20Diversion%20along%20PIE%20slip%20Road%20Exit%2017D%20towards%20Thomson%20Rd.pdf). HDB plan geometry and OSM routes remain the primary model inputs.

### Validation

All thirteen interior/shared destinations are clear and reachable on the furnished walking grid. A ray check confirms that neighbour geometry blocks sightlines through the lift-lobby back wall. Current runtime checks confirm zero car objects, preserve the mapped PIE ways and verify finite lunar directions for 72 date/time samples. These lunar checks establish numerical consistency, not observational accuracy. The existing 84-case solar comparison still passes (maximum angular difference about 0.46°). See `data/environment_validation.json`, `data/shared_validation.json` and `data/browser_solar_validation.json`. Browser checks covered the attached chute hatches, stair B flights, evening sun, feature controls and error-free scene loading.


## Precinct, MRT walk and sun-exposure revision — 18 September 2026

### New views

- **Precinct:** an orbitable ground-level model of the whole Mount Pleasant Crest site built from the brochure site plan (physical page 18): the residential blocks with open void decks on columns, both 5-storey multi-storey car parks with roof gardens (planters, trees, pergola, parapet) and glazed first-storey frontages, the three precinct pavilions as open hipped-roof shelters, the sheltered linkway network (roof slabs on 191 columns, area about 4,100 m²), and every numbered legend facility: 2 children playgrounds, 3 adult and 2 elderly fitness stations, the hard court, 3 drop-off porches, 2 spaces reserved for community use, both preschools, the residents’ network centre, the future-amenities space, the eating house, shops and supermarket, plus bicycle shelters, two future bus bays on Mount Pleasant Avenue and the existing Thomson Road bus stops. The side panel lists each facility with its walking distance from the Blk 105A lift lobby, and **Locate** / **Walk there** buttons.
- **Regional nature:** use **Regional nature** in the precinct panel to frame the development with MacRitchie Reservoir, MacRitchie Reservoir Park, Central Catchment Nature Reserve, Windsor Nature Park, Toa Payoh Town Park and Bishan-Ang Mo Kio Park. Horizontal boundaries come from data.gov.sg; the water elevation and forest-canopy instances are explicitly approximate. **Locate** frames each named area without implying a walkable route through unmapped forest trails.
- **Walk to MRT / outdoor walk:** a first-person ground-level walk from the Blk 105A ground-floor lift lobby. WASD or the arrow keys move freely over the modeled precinct and surroundings, with terrain following, bounds checks and collision sliding around known buildings. The highlighted route is optional guidance; **Follow route** smoothly rejoins and follows it at 1.65 m/s. The HUD reports actual distance walked, distance back to/along the route, shelter context and a north-up minimap. **Walk there** from the precinct panel chooses another guide route without restricting manual movement.
- **Labels and path motion:** shared-floor labels remain available in apartment plan and eye-level views; facility labels remain available in precinct and outdoor-walk views; sightline-qualified landmark labels remain available in both indoor and outdoor eye-level views. World labels and HTML pins scale with camera distance/zoom, while screen-space collision offsets keep every in-frustum facility/shared label visible. Route ribbons and moving agents use dense centripetal curve samples; any guide curve that would overshoot a known building falls back to its validated source polyline.
- **Direct sun over the year** (side panel): a monthly chart and table of direct sun on the main NNW façade and on the west-facing service-yard window at the 21st of each month, computed with the checked NOAA solar equations at 5-minute steps against an obstruction horizon built from every modelled building taller than the window. It reports clear hours, hours blocked by modelled buildings (with the blocker named), the lit period, and how far low sun can reach into the living room. Ticking **Future obstruction scenario** recomputes it with the hypothetical amber towers.

### Modelling repairs

- Staircase B previously extended beyond the tower footprint and had no roof, so sky and neighbouring façades were visible from its landing. Both stair cores now have a continuous floor slab, a roof meeting the wall tops, a lintel over the corridor opening, and their footprints are added to the tower massing above and below level 31.
- Ceilings, the corridor soffit and stair roofs rendered olive-brown because the physically based hemisphere light gives downward faces only the ground colour divided by π. The bounce light now uses a neutral light ground colour and a higher intensity, so plaster ceilings read as plaster.
- The terrain was flat only inside a rectangle around the site and stepped 5–9 m at its edges. The platform is now feathered into the public terrain over 100 m, a small level plaza is assumed at the MRT entrance, and the 20 buildings, 28 tree clusters, grass meshes and 110 draped roads in that zone were re-seated on the new ground.
- The 100 multi-storey car park is corrected from 7 to the published 5 storeys.
- The lowest 4.5 m of every residential block is now an open void deck with a paved floor, columns and (for Blk 105A) the solid lift banks, stair and chute cores and a glazed lobby with its door on the linkway side, instead of a solid extrusion into which a ground-level walker would have clipped.

### How the ground-level data was made

| Element | Method and accuracy |
|---|---|
| Site plan registration | The same 2.72 px/m, north-up registration used for the block outlines (page 18 rendered at 1,819 px). Facility markers were detected as filled circles at 300 dpi and their numbers read manually; positions are accurate to a few metres of the drawing, which itself is diagrammatic. |
| Linkways | The brown linkway colour was segmented, cleaned morphologically and turned into roof polygons; columns are placed every 4.2 m along the medial axis of the narrow bands. Widths and heights (2.75 m) are typical values, not dimensions from the plan. |
| Walking routes | Shortest paths on the site-plan raster: cost 1 on linkways, 4 on other ground, impassable across flat footprints and water. Distances are along these routes from the traced lift-lobby exit. Routes through the central garden use the open footpath areas and are reported as unsheltered. |
| MRT | Station box from OpenStreetMap way 298087581 (under construction, layer −1), about 235 m south-west of the unit. No entrance is mapped: the entrance pavilion is placed beside the proposed Old Police Academy Road at the north-east end of the box, and the off-site pavement follows that road’s proposed alignment. The modelled walk is 158 m (94 m sheltered on site, 64 m public pavement, about 2.2 minutes at 1.2 m/s); the real distance depends on the final entrance position and could be substantially longer. |
| Ground levels | Site platform assumed at 13.3 m; off-site ground is public 30 m terrain data feathered to the platform. The walk rises about 7 m towards the assumed entrance because the surrounding terrain data is higher; actual earthworks are unknown. |
| Facility assets | Playground equipment, fitness stations, court markings, shelters, pavilion roofs, roof-garden planting and the MRT pavilion are generated illustrations of what the legend names; none are HDB designs. |

### Validation

Headless Chrome runs on this computer (Playwright driving the installed browser) loaded the scene with zero JavaScript errors and exercised every mode. The MRT walk was driven from 0 to 158.8 m with the HUD switching from “sheltered linkway” to “public pavement” at the site boundary and reporting arrival; an amenity walk (127 m to the nearest playground, 112 m sheltered) also completed. The precinct overview, isometric, shared-floor, interior, window and exposure views were screenshot-inspected after the repairs; the stair cores are closed, ceilings are light, and the isometric cutaway, furniture interaction and landmark labels still behave as before. The annual exposure table was checked with and without the future scenario (for example, June: 11.2 h of direct sun on the façade, 0.9 h blocked by Blk 100A in the early morning; March, April, August and September lose about 1.5 h in the future scenario). See `data/validation.json`.

The Unreal project in `MountPleasantSimulator` was not updated in this revision.


## Site-plan re-tracing and corrections — 18 September 2026 (second revision)

### What changed

- **Block footprints are now traced from the brochure site plan at 300 dpi (7.4 px/m)** instead of hand-placed 8–12-point outlines. The plan’s thick black wall outlines are used as barriers (a near-black threshold; the same edges a Laplacian/Sobel filter brings out), every wall-bounded cell is labelled, and each block is the union of its unit-coloured cells, the white corridor/lift cells between them and the walls themselves. Footprints therefore show the actual unit steps, corridors and cores; storey counts per wing come from the brochure table (105A 15/40, 100A 35/46, 104A 32/45, 101A 26) and the wing split follows the earlier traced wing boundaries. The two 5-storey car parks are the plan’s outlined decks (rotated rectangles); ESS, UC and the bin centre are the plan’s grey service boxes.
- **Roads inside the plan frame are the plan’s own road surfaces** (grey-classified areas outside the site boundary, drainage reserve excluded), drawn as kerbed asphalt with centre markings; vehicles follow skeletonised centre-lines. Mapped OpenStreetMap roads are only drawn outside the plan frame and the old hand-traced “planned” roads are gone. The plan’s roads and the mapped roads still disagree by roughly 8–14 m where they meet the frame edge, because the plan shows the realigned future roads.
- **Linkways are a spline network**: the brown linkway colour is skeletonised, pruned, gap-bridged, smoothed (Chaikin) and swept as 3.2 m strips whose union forms one connected roof; columns sit every 4.2 m on both edges. Pavilions and drop-off porches are the plan’s wide brown areas (hipped roofs and 4.6 m canopies). Walking routes and facility distances are shortest paths on this network, with straight “open footpath” links where a facility is off the network.
- **Registration**: the plan layer is shifted by (−0.33 m, +2.86 m) so the plan’s unit 191 coincides with the flat (unit 193 then agrees within 1 m); the plan’s 191/193 façade line measures −17.3° from east, confirming the 342° bearing within about 1°. The plan-traced 105A tower and the level-31 footprint overlap by 67 %; the remaining difference is drawing detail (balconies, ledges) and the tower part built from the level-31 plan takes precedence where they overlap.
- **Level 31 corridor**: the corridor previously ran 5 m west past stair core A onto what the 17th–40th storey plan shows as the *roof of the 15-storey wing* (thin outline, “roof at 18th storey only” area). It now ends at the stair core’s west wall with a full-height end wall, closing the opening that showed the tower massing through it.
- **Roofs of mapped low-rise buildings** are regenerated as hipped roofs on near-rectangular footprints and parapets elsewhere, seated on the re-based heights so they connect to their walls.
- **Legibility**: thin dark edge lines on walls, slabs and columns (clipped with the cutaway in plan views), and higher-contrast plaster/concrete/paving textures, so corners and depth read on flat-lit surfaces.
- Ground surfaces now sit at consistent levels (roads +0.02 m, linkway paving +0.05 m, void-deck floor +0.06 m above the platform); the old planned roads that floated 0.38 m above the ground are removed.

### Accuracy notes

Footprints are as accurate as the brochure drawing at its scale (roughly ±0.5 m at 7.4 px/m plus the drawing’s own diagrammatic simplifications); label boxes and marker circles can nick outlines locally. Storey counts are published; block heights use 4.5 m ground storey + 2.8 m per storey + 1.5 m roof, which is an assumption. Linkway width (3.2 m) and height (2.75 m) are typical values. The site plan does not show building setbacks above the first storey, so every block is extruded straight.


### Correction — main bedroom west wall

The main bedroom's west wall is modelled as a solid gable end; the earlier side window there was a misreading of a thin wall symbol on the plan and has been removed. The only side-facing opening is the service-yard window (west, 252°), which the sun-exposure table now reports as "Yard h".

### Published refresh and window regression check — 18 September 2026

The latest precinct, MRT walk, traced building shapes and corrected level-31 geometry are retained. An unescaped apostrophe in the exposure notes was repaired because it prevented the app from starting. Public delivery now uses a complete versioned snapshot so cached JavaScript cannot mix an older solid tower with the updated window openings.

The retained `data/window_validation.json` records 52 outward sightlines across the four front windows and service-yard window. All are clear of opaque geometry near the openings. It also records the deliberate covering-wall negative control and confirms the real tower above remains an obstruction. `npm run check` audits this retained evidence, while `npm run check:browser` checks the current runtime.


## Road-network reconstruction — 18 September 2026

This revision supersedes the earlier rectangular site-plan road mask. The scene represents the **proposed completed estate**, combined with surrounding existing mapped roads, rather than current construction diversions.

### Sources and alignment

- Fresh OpenStreetMap API extract (18 September 2026), bbox 103.827,1.321,103.847,1.339, merged by source way ID over the offline `work/osm.xml` and `work/osm_north.xml` extracts. Each rendered route retains its source ID and provenance. The wider offline coverage supplies the distant network.
- HDB Mount Pleasant Crest brochure: physical PDF page 7 supplies the full road loop and its connections; physical page 18 supplies precinct entrances and internal accesses. The proposed OSM ways 1189540301/302/303 provide georeferenced alignments for Mount Pleasant Avenue, Old Police Academy Road and Wong Chin Yoke Road. They originated in the earlier estate proposal; their overall layout was checked against the 2025 HDB brochure.
- SLA OneMap Default tiles were inspected as an independent visual check of the existing Thomson Road, Mount Pleasant Road, PIE and slip roads. These reference tiles remain in the work folder and are not redistributed as app assets.

### Changes

- Continuous road surfaces at junctions, with the complete Old Police Academy loop, Mount Pleasant Avenue–Thomson connection and Onraet connection toward PIE. The existing Mount Pleasant Road meets the new avenue; its superseded eastern limb and obsolete police-academy internal drives are removed from the completed-estate scenario.
- Restored the eastern car-park service access bending around Blk 100 toward Mount Pleasant Avenue, following the brochure. It does not cross the drainage reserve directly to Thomson Road.
- Roads use polygon unions and constrained triangulation instead of disconnected strips or rectangular clipping. Footpaths no longer punch holes through asphalt. Grass/pitch land-use overlays are cut at road/pavement edges. The southern terrain-tile seam is feathered so roads can continue across it.
- Underground NSC ways are excluded from the surface model. Tagged flyovers crossing lower roads have separate elevated surfaces with gradual approaches. Lane counts, one-way directions and widths are taken from OSM where available. Traffic remains illustrative.
- In **Precinct**, select **Surrounding roads** for a wider top-down view. Reset view returns to the close precinct view.

### Limits and validation

This is not a surveyed road engineering model. Brochure registration, untagged widths, bend radii, lane markings, bridge clearance (5.5 m assumed above terrain), approach profiles and finished ground levels are estimates. The broader future road alignments may change before completion. The Wong Chin Yoke terminal uses the mapped alignment; its final turning-island detail is not reproduced. Heights and markings are intended for scene context, not driving or clearance analysis.

The generator checks seven named junction connections (less than 1 mm numerical gap), complete centreline coverage on the three estate roads, valid road/path polygons and no overlap with modelled precinct building footprints. The runtime gates additionally check all inner-area road crossings, PIE clearance, retired-aisle probes, finite/non-degenerate road geometry and 34 pedestrian routes. The separate window test protects all 52 apartment window sightlines. Reports are in `data/road_network_validation.json`, `data/precinct_mesh_validation.json` and `data/window_validation.json`.

The base road/terrain generator workspace is not part of this repository snapshot. The bounded, reproducible `tools/retire-academy-aisles.py` post-process removes the two recorded obsolete aisles, writes the replacement landscape, and preserves every overlapping retained road. Run it after a fresh upstream road rebuild, then regenerate aligned markings and bus approaches before `npm run check:all` plus `npm run build`.

Sources: [HDB brochure](https://assets.hdb.gov.sg/residential/buying-a-flat/finding-a-flat/sales-brochure/25OCTBTO_pdf_selection/mount_pleasant_crest.pdf), [OpenStreetMap](https://www.openstreetmap.org/#map=16/1.3300/103.8368), [SLA OneMap](https://www.onemap.gov.sg/). OSM-derived data is attributed to OpenStreetMap contributors under ODbL.
