Commit Graph

8 Commits

Author SHA1 Message Date
Dion Moult de5eb9641f ifcviewer-full: hide and remove model actions
Right-click a model root in the Elements tree to get Hide/Show and
Remove.  Hide flips the federation's per-model visible flag (already
round-tripped to .ifcfed), pushes ViewportWindow::hideModel/showModel,
and italicises + greys the tree root as a visual cue.  Remove drops
the model from the viewport, the SceneLoader (streamer + caches), the
MainWindow UI maps and tree, and the Federation — disabled while the
model is the active load.

Visibility is reapplied on each model's load completion (sidecar or
stream), so a federation saved with hidden models opens with them
hidden.  clearScene() now also drops SceneLoader state so streamers
no longer leak across federation transitions.

API additions:
- Federation::setModelVisible + modelVisibilityChanged signal
- SceneLoader::removeModel + isLoadingModel

Tests cover the setter (dirty + signal + idempotence + unknown id);
extends the existing round-trip test to actually exercise the
visibility load/save it always claimed to.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 12:22:14 +10:00
Dion Moult f7add7f412 ifcviewer: auto-guess FederatedFalseOrigin on first model added
When the user adds a model into a fresh, untitled federation that still
has the default (0,0,0, no rotation) FederatedFalseOrigin, derive an
origin from the first instance's placement_transformation (lifted
through CoordinateOperation when enabled) and the helmert grid-north
baked into ModelGeoref::coordinate_operation_meters.  Multi-file batches
naturally settle: whichever load finishes first anchors the federation,
the rest see a non-default origin and skip.  Saved .ifcfeds keep their
authoritative origin.

Adds Placement.{h,cpp} (port of util/placement.py — a2p,
get_axis2placement, get_local_placement) so Geolocation no longer needs
its own anonymous getAxis2Placement, and xaxis2angleDeg in Geolocation
mirroring util/geolocation.xaxis2angle.

SceneLoader captures the first instance's placement_transformation from
either the sidecar's InstanceCpu[0] or the streamer's first
InstanceChunk, so the guess works on both load paths without re-reading
the IFC.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 10:04:39 +10:00
Dion Moult bea4e38e65 ifcviewer: cache per-model georef in SceneLoader
Adds ModelGeoref { ModelUnits units; Eigen::Matrix4d stage2_meters; bool
has_stage2; } and computeModelGeoref(file*) in Federation.{h,cpp}.  The
helper reads the project length unit, IfcProjectedCRS.MapUnit, helmert
parameters and WCS, and reduces them to a metres-in/metres-out stage 2
matrix using the existing Geolocation + Unit primitives.  When the model
has no IfcMapConversion it returns an identity stage_2 with has_stage2
== false, so the upload pipeline can branch cheaply.

SceneLoader::Model gains a cached ModelGeoref; SceneLoader::modelGeoref
(uint32_t mid) computes lazily on first call (returns nullptr when the
IFC file isn't available yet — happens on the sidecar-hit path before
the data-source thread populates the streamer) and serves from cache
afterwards.

Not yet consumed by the upload pipeline; that's the next commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 17:31:08 +10:00
Dion Moult 8282f691e8 ifcviewer: rename SceneLoader::Entry to Model
The struct holds per-model bookkeeping; Model describes its contents
rather than its container relationship.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 19:31:48 +10:00
Dion Moult 8f7c8dc1d2 ifcviewer: load rdb/ifc as property data source on sidecar hit
Sidecar hits skipped opening the underlying .rdb/.ifc, so ifcFile() was
null and the property panel only showed cached name/type/guid. Now, after
a sidecar hit, a background thread opens <stem>.rdb (preferred) or
<stem>.ifc and hands the file to GeometryStreamer via setIfcFile(), with
a dataSourceReady signal so the UI refreshes the current selection.

Gated behind a new AppSettings::loadDataSource toggle (default on) so
users can opt into geometry-only viewing; when off, the sidecar-hit
thread is skipped and the stream-path ifc_file_ is released after
the sidecar write completes.

Also adds *.ifcview to the Add Files dialog filter so a cache can be
opened directly without its source file present.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 13:45:04 +10:00
Dion Moult 4f929e90a7 ifcviewer: key sidecar on path stem, drop staleness check
Previously readSidecar/writeSidecar were keyed on (path, file_size) with
staleness rejected at read time.  Switch to pure path-stem keying: foo.ifc
and foo.ifcdb/ both resolve to foo.ifcview, so the same cache serves either
source format.  Staleness is user-managed (delete the sidecar to force a
rebuild), which also lets sidecars be copied or moved independently of the
source.

v8 header drops the source_file_size field.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 21:32:25 +10:00
Dion Moult 4e4553201b Fix viewer load termination
Handle streamer success, failure, and cancellation as distinct terminal states so failed or cancelled loads do not finalize as successful models. Clean up partial model/UI state in the full and minimal viewer apps when a load is cancelled or fails.

Generated with the assistance of an AI coding tool.
2026-04-23 21:32:25 +10:00
Dion Moult 29f5132510 ifcviewer: extract SceneLoader, remove duplicated load orchestration
MainWindow and MinimalWindow each carried ~150 lines of mirrored
load-queue, sidecar-thread, streamer-wiring, and ID-rebase code. Lift
all of it into a SceneLoader QObject in the library; both apps now
consume it via signals. Sidecar writes stay on the full-app side since
they need the consumer's element metadata strings.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 21:32:25 +10:00