Knowledge Base

Overview
Inspection & Test Plans

Scan-Derived Geometry (Technical Reference)

Technical Referencereality-capturetechnicalpoint-cloudpostgisfloorplan-elementsprovenance
Stuart Carroll - Updated 23 Aug 2026

Scope

The companion to "Working with Reality Capture Scans": how a floorplan gets built out of a point cloud, and how its origin is recorded.

A facility with fully scan-derived architecture: walls, doors and rooms drawn entirely from extraction output, with no authored IFC behind them.

A facility with fully scan-derived architecture: walls, doors and rooms drawn entirely from extraction output, with no authored IFC behind them.

The boundary, first

A scan-derived facility is real and supported. The facility above has no authored IFC at all. Every wall, door and room came out of a point cloud, and the map draws it, Directions routes through it, and BIM Compare measures against it.

Promoting scan geometry into the model is not a button. There is no Scan to BIM action, no Derive walls from scan, and no Discard scan-derived geometry. Those were removed on 2026-08-23. What the product does is report: BIM Compare tells you where the scan and the model disagree, you accept or reject each finding, and a person decides which of the two is wrong.

The capability was removed for its shape, not because it was unwanted. It wrote inferred walls, doors and rooms into floorplan_elements and rooms in one click, in bulk, with no per-item review and no record of origin. Once it had run, nothing distinguished a surveyed wall from one a point cloud had guessed at, and that distinction is the one property this schema exists to preserve.

The replacement is specified in [#475](https://github.com/Requify-Pty-Ltd/reqtwin/issues/475) and is not built: a delta type for fabric the scan sees and the model lacks, per-finding accept and dismiss, no bulk apply, provenance on anything written, and per-finding reversal. Until then the pipeline below is reached by script when building a fixture, not by a user. The extraction code is unchanged. Only its trigger changed.

Data model

The Reality Capture registry is reality_captures, reality_assets, pointcloud_sources, pointcloud_slices, pointcloud_room_clips, pointcloud_links, pano_poses, drone_tiles, capture_suggestions and reality_capture_stations. These interlock with, but stay separate from, the Twin's own geometry (facilities, levels, rooms, floorplan_elements, discrete_assets, nav_nodes, nav_edges). Findings land in bim_capture_deltas, stamped back to reality_captures by capture_id.

Extraction code lives under app/(app)/lib/server/pointcloud/: wall and asset extraction, banding and slicing, georeferencing, and the format readers. Above it sit derive-walls.ts, scan-to-bim.ts, bim-compare-run.ts and reality-capture.ts.

persist_scan_derived_details()

Migration 20260727212027_persist_scan_derived_details.sql:

sql
persist_scan_derived_details(
p_document_id uuid,
p_facility_id uuid,
p_level_id uuid,
p_segments jsonb, -- [{"kind":"wall"|"door","a":[lon,lat],
-- "b":[lon,lat],"thickness_m":0.1}, ...]
p_source_ref text default 'scan_extract_v1'
) returns table (detail_type text, inserted integer)

Geometry is built in PostGIS rather than TypeScript, because PostGIS already buffers correctly and the app would otherwise grow a second, worse implementation.

Blast radius. A run first deletes what a previous run of *this source* wrote for this level, matched on bim_data->>'scan_source' = p_source_ref. Scoping by source tag rather than by detail_type means a re-run cannot widen into authored or IFC-derived geometry. A re-run reproduces identical source_element_ids with no row growth.

endcap=flat join=mitre is not a detail. Round caps overshoot a segment's endpoints by half the wall thickness and refuse to meet neighbours squarely, which is what produced rounded free-floating wall stubs in a corridor. Verified on dev: a 10 m segment at 0.1 m thickness yields a 5-point polygon of area 1.005 m², an implied thickness of exactly 0.100 m, and two segments sharing an endpoint intersect. Zero-length segments are dropped; thickness is floored at 0.02 m.

Provenance

Origin is recorded, never implied.

FieldWhat it says
bim_data.scan_sourceWhich extraction run wrote it
bim_data.provenance = 'scan'Derived from measurement
is_synthesizedAttested versus derived

Derived is not the same claim as invented, and the two flags say different things. Scan-derived walls carry provenance = 'scan' and so pass the real-fabric filter that routing and the overhead classifier read, despite also carrying is_synthesized = true.

Scan-derived assets are held to a stricter standard: drawn teal and dashed, and excluded from the Assets panel until reviewed, because unverified extraction output must never pass as authored fabric.

Limitations

•

Wall extraction produces candidates. Nothing asserts a candidate is a real wall, which is exactly why no user-facing action writes one into the model.

•

Only walls and door openings are persisted. Windows, stairs and railings are not extracted from scan data.

•

Heavy parsing (LAS/LAZ/E57, voxel downsampling, rigid-transform alignment, COG generation) is designed to run in a container, not an edge worker.

•

No automated test coverage for the persistence function. The evidence is live dev-database verification.

See "How Architectural Detail Reaches the Twin Map" for how the same provenance rules apply to IFC-sourced and code-synthesised elements.