mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-29 19:51:00 +00:00
ifcviewer: wire FederatedFalseOrigin / ModelTransformation to viewport
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>
This commit is contained in:
@@ -43,6 +43,28 @@ MainWindow::MainWindow(QWidget* parent)
|
|||||||
setupMenus();
|
setupMenus();
|
||||||
|
|
||||||
federation_ = new Federation(this);
|
federation_ = new Federation(this);
|
||||||
|
|
||||||
|
// Federation -> viewport: granular signals so we recompose only what's
|
||||||
|
// affected. Each handler reads the current federation state, composes
|
||||||
|
// the matrix, and pushes to the viewport.
|
||||||
|
connect(federation_, &Federation::federatedFalseOriginChanged,
|
||||||
|
this, &MainWindow::applyFederatedFalseOriginToViewport);
|
||||||
|
connect(federation_, &Federation::configChanged, this, [this]() {
|
||||||
|
// Federation unit changed — both stage 3 (uses fed unit) and every
|
||||||
|
// model's stage 4 (b/pivot are in fed units) need recomposing.
|
||||||
|
applyFederatedFalseOriginToViewport();
|
||||||
|
for (const auto& kv : fed_id_to_model_id_) {
|
||||||
|
applyModelTransformationToViewport(kv.second);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(federation_, &Federation::modelTransformationChanged,
|
||||||
|
this, [this](const QString& fed_id) {
|
||||||
|
auto it = fed_id_to_model_id_.find(fed_id);
|
||||||
|
if (it != fed_id_to_model_id_.end()) {
|
||||||
|
applyModelTransformationToViewport(it->second);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
connect(federation_, &Federation::dirtyChanged, this, [this](bool dirty) {
|
connect(federation_, &Federation::dirtyChanged, this, [this](bool dirty) {
|
||||||
setWindowModified(dirty);
|
setWindowModified(dirty);
|
||||||
});
|
});
|
||||||
@@ -276,6 +298,11 @@ bool MainWindow::openFederation(const QString& path) {
|
|||||||
federation_->markClean();
|
federation_->markClean();
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
|
|
||||||
|
// Push the federation's loaded FederatedFalseOrigin to the viewport.
|
||||||
|
// Per-model ModelTransformations get pushed as each model finishes
|
||||||
|
// loading, via applyCoordinateOperationToViewport.
|
||||||
|
applyFederatedFalseOriginToViewport();
|
||||||
|
|
||||||
if (federation_->hasHomeView()) {
|
if (federation_->hasHomeView()) {
|
||||||
const auto& hv = federation_->homeView();
|
const auto& hv = federation_->homeView();
|
||||||
viewport_->setCamera(hv.target.x(), hv.target.y(), hv.target.z(),
|
viewport_->setCamera(hv.target.x(), hv.target.y(), hv.target.z(),
|
||||||
@@ -495,6 +522,41 @@ void MainWindow::applyCoordinateOperationToViewport(uint32_t mid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewport_->setModelCoordinateOperation(mid, M);
|
viewport_->setModelCoordinateOperation(mid, M);
|
||||||
|
// ModelTransformation's compose can depend on the active
|
||||||
|
// CoordinateOperation (when ModelTransformation.a_frame == ModelLocal,
|
||||||
|
// a is lifted through stage 2), so re-push it whenever stage 2 changes.
|
||||||
|
applyModelTransformationToViewport(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::applyModelTransformationToViewport(uint32_t mid) {
|
||||||
|
Eigen::Matrix4d M = Eigen::Matrix4d::Identity();
|
||||||
|
auto fed_it = model_id_to_fed_id_.find(mid);
|
||||||
|
if (fed_it != model_id_to_fed_id_.end()) {
|
||||||
|
if (const Federation::Model* m = federation_->findById(fed_it->second)) {
|
||||||
|
// ModelUnits + the active CoordinateOperation come from the
|
||||||
|
// ModelGeoref cache; defaults are safe (1.0, 1.0, identity)
|
||||||
|
// when the IFC file isn't yet available.
|
||||||
|
ModelUnits units;
|
||||||
|
Eigen::Matrix4d coord_op = Eigen::Matrix4d::Identity();
|
||||||
|
if (const ModelGeoref* gr = loader_->modelGeoref(mid)) {
|
||||||
|
units = gr->units;
|
||||||
|
if (AppSettings::instance().applyCoordinateOperation() &&
|
||||||
|
gr->has_coordinate_operation) {
|
||||||
|
coord_op = gr->coordinate_operation_meters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
M = composeModelTransformation(
|
||||||
|
m->model_transformation, federation_->config(),
|
||||||
|
units, coord_op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewport_->setModelTransformation(mid, M);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::applyFederatedFalseOriginToViewport() {
|
||||||
|
const Eigen::Matrix4d M = composeFederatedFalseOrigin(
|
||||||
|
federation_->federatedFalseOrigin(), federation_->config());
|
||||||
|
viewport_->setFederatedFalseOrigin(M);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onLoadedFromSidecar(uint32_t /*mid*/, qint64 elapsed_ms) {
|
void MainWindow::onLoadedFromSidecar(uint32_t /*mid*/, qint64 elapsed_ms) {
|
||||||
|
|||||||
@@ -103,6 +103,16 @@ private:
|
|||||||
// or its IFC file isn't available (sidecar-hit before data-source
|
// or its IFC file isn't available (sidecar-hit before data-source
|
||||||
// load); the call retries on onDataSourceReady.
|
// load); the call retries on onDataSourceReady.
|
||||||
void applyCoordinateOperationToViewport(uint32_t mid);
|
void applyCoordinateOperationToViewport(uint32_t mid);
|
||||||
|
|
||||||
|
// Push a model's ModelTransformation (stage 4) matrix to the viewport,
|
||||||
|
// composed from the federation's authoring intent + the model's units
|
||||||
|
// + the active CoordinateOperation matrix. Identity when the model is
|
||||||
|
// not in the federation.
|
||||||
|
void applyModelTransformationToViewport(uint32_t mid);
|
||||||
|
|
||||||
|
// Push the federation-wide FederatedFalseOrigin (stage 3) matrix to
|
||||||
|
// the viewport. Affects every loaded model.
|
||||||
|
void applyFederatedFalseOriginToViewport();
|
||||||
QString formatElapsed(qint64 ms) const;
|
QString formatElapsed(qint64 ms) const;
|
||||||
|
|
||||||
ViewportWindow* viewport_ = nullptr;
|
ViewportWindow* viewport_ = nullptr;
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ void Federation::setConfig(const FederationConfig& c) {
|
|||||||
return;
|
return;
|
||||||
config_ = c;
|
config_ = c;
|
||||||
setDirty(true);
|
setDirty(true);
|
||||||
|
emit configChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Federation::setFederatedFalseOrigin(const FederatedFalseOrigin& o) {
|
void Federation::setFederatedFalseOrigin(const FederatedFalseOrigin& o) {
|
||||||
@@ -209,6 +210,7 @@ void Federation::setFederatedFalseOrigin(const FederatedFalseOrigin& o) {
|
|||||||
federated_false_origin_.rz_deg == o.rz_deg) return;
|
federated_false_origin_.rz_deg == o.rz_deg) return;
|
||||||
federated_false_origin_ = o;
|
federated_false_origin_ = o;
|
||||||
setDirty(true);
|
setDirty(true);
|
||||||
|
emit federatedFalseOriginChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Federation::setModelTransformation(const QString& fed_id,
|
void Federation::setModelTransformation(const QString& fed_id,
|
||||||
@@ -217,6 +219,7 @@ void Federation::setModelTransformation(const QString& fed_id,
|
|||||||
if (m.id != fed_id) continue;
|
if (m.id != fed_id) continue;
|
||||||
m.model_transformation = xf;
|
m.model_transformation = xf;
|
||||||
setDirty(true);
|
setDirty(true);
|
||||||
|
emit modelTransformationChanged(fed_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,6 +204,13 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void dirtyChanged(bool dirty);
|
void dirtyChanged(bool dirty);
|
||||||
|
|
||||||
|
// Granular signals so consumers (notably the viewport-pushing layer in
|
||||||
|
// the host app) can recompose only what's needed. Emitted in addition
|
||||||
|
// to dirtyChanged from the corresponding setters.
|
||||||
|
void configChanged();
|
||||||
|
void federatedFalseOriginChanged();
|
||||||
|
void modelTransformationChanged(const QString& fed_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setDirty(bool d);
|
void setDirty(bool d);
|
||||||
static QString generateId();
|
static QString generateId();
|
||||||
|
|||||||
Reference in New Issue
Block a user