New FederationSettingsDialog edits the federation-wide unit and the
FederatedFalseOrigin (XYZ + Z-rotation in that unit). On Ok it calls
Federation::setConfig + setFederatedFalseOrigin, which fire the
granular Federation signals MainWindow listens to → viewport
recomposes immediately.
Reachable from File > Federation Settings. Unit picker is a fixed
combobox of common length units (metres / mm / cm / km / ft / in /
yd / mi); each item carries (prefix, name) in itemData so saving
round-trips correctly. Per-model ModelTransformation editor still
to come — that's a per-model dialog reachable from the model entry,
not the federation-wide settings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Federation grows three granular signals so consumers can recompose only
what's affected:
- configChanged() — federation unit changed
- federatedFalseOriginChanged() — stage 3 changed
- modelTransformationChanged(fed_id) — stage 4 changed for one model
Emitted from setConfig / setFederatedFalseOrigin / setModelTransformation
in addition to the existing dirtyChanged.
MainWindow gains applyFederatedFalseOriginToViewport and
applyModelTransformationToViewport helpers. Each composes the matrix
from the current federation state (using composeFederatedFalseOrigin /
composeModelTransformation, which already exist on Federation.h) and
pushes to the viewport's setFederatedFalseOrigin /
setModelTransformation. ModelTransformation reads ModelUnits and the
active CoordinateOperation matrix from SceneLoader::modelGeoref so
ModelLocal-frame `a` lifts correctly through stage 2 when authored.
Wiring:
- federation.federatedFalseOriginChanged -> applyFederatedFalseOriginToViewport
- federation.configChanged -> stage 3 + walk all models for stage 4
- federation.modelTransformationChanged -> stage 4 for that one model
- applyCoordinateOperationToViewport now also re-pushes stage 4 (the
compose result depends on the active stage 2 when a_frame is ModelLocal)
- openFederation() pushes the loaded FederatedFalseOrigin once load
completes; per-model stage 4 falls out of the existing
onLoadedFromStream / onDataSourceReady path.
End-to-end pipeline is now active under the AppSettings toggle: edit
the federation in memory and the viewport recomposes immediately. UI
for editing (form-based dialog) still pending.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
AppSettings.applyCoordinateOperation (default false, persisted via
QSettings) controls whether each loaded model's IfcCoordinateOperation
is applied at upload time. Off keeps models in their local engineering
frame (current behaviour). On lifts each model into map coordinates
via the stage-2 georef matrix cached on SceneLoader.
MainWindow:
- applyCoordinateOperationToViewport(mid) reads the toggle, fetches
the model's ModelGeoref, and pushes either the
coordinate_operation_meters matrix or identity to the viewport.
- Called from onLoadedFromStream (streamer path) and onDataSourceReady
(sidecar-hit path, where the IFC arrives asynchronously).
- Subscribed to AppSettings::applyCoordinateOperationChanged: a
runtime toggle walks every loaded model and re-applies, so users
can flip georef on/off without reloading.
SettingsWindow gains a "Apply Coordinate Operation" checkbox alongside
the existing per-load toggles.
Default-off so the change is opt-in — users with georeferenced models
(UTM coords etc.) can flip the toggle to see them in their map frame
once they're ready. Visual verification on a real georeferenced
model still pending.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Federation (JSON) tracks an ordered list of model sources plus an
optional home-view camera state. Sources are stored relative when
under the federation file's directory, absolute otherwise.
File menu now exposes New / Open / Save / Save As; Add Files moves
to Ctrl+Shift+O. View menu gains Set/Go to Home View. Window title
binds to dirty state via setWindowModified, and the close-window
prompt offers Save/Discard/Cancel.
Per-model transform (4x4 column-major) and visible round-trip
through load/save but are not yet applied at the viewport — the
georeferencing work uses them.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
The viewer can now open a .rdb directory (as produced by
RocksDbSerializer / convert_path_to_rocksdb) anywhere it accepts an
.ifc file. The full GUI gets an "Add Database..." File menu entry
that opens a directory chooser; the streamer lets the file
constructor autodetect the format and opens the store read-only so
multiple viewers can share a database without taking the exclusive
RocksDB lock.
Parallel mapping on RocksDB-backed files still produces
non-deterministic shape counts (the race is outside the instance
cache), so force num_threads=1 for the iterator when the storage is
RocksDB. Serial RocksDB (~2.6s) and parallel SPF (~0.7s) both
produce 107 shapes on AC20-FZK-Haus; @todo in-source points at the
remaining thread-safety work.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.
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>
Turn src/ifcviewer into libIfcViewer.so holding the rendering engine +
geometry pipeline (ViewportWindow, GeometryStreamer, BvhAccel,
InstancedGeometry, SidecarCache, LodBuilder, AppSettings). Move the
existing UI shell (MainWindow, SettingsWindow, main.cpp) into
src/ifcviewer-full as the IfcViewerFull executable. Add a new
src/ifcviewer-minimal target with a MinimalWindow that hosts only the
viewport and reuses the sidecar fast-path for benchmark/debug runs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>