mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 06:32:09 +00:00
ifcviewer: overhaul model/object ID tracking
Rename the two overloaded model identifiers and make object_id assignment single-authority, fixing a pick -> properties mismatch. Identifiers: - Per-model UUID fed_id -> model_id; the uint32 runtime handle model_id -> session_model_id (SessionState accessors + mirror hashes renamed to match). "fed_id" was a misnomer -- the federation is the whole collection, not one model. object_id assignment (fixes wrong class on click): - Producers (GeometryStreamer, .ifcview sidecar) now stamp model-LOCAL object_ids; ViewportCore::applyCachedModel is the sole authority that assigns the session-global id (base + local). Removed SceneLoader::next_object_id_, GeometryStreamer::lastObjectId(), and the streamer's start_object_id parameter. - The element table is stamped by the same base on both load paths (applySidecarData and onStreamerFinished), so registry ids match the ids pick returns. Previously the sidecar path double-rebased instances vs the registry (click IfcSite -> showed IfcDoor); the live-stream path had the same latent mismatch. Both closed. Naming / cleanup: - SceneLoader::addFiles -> queueModels; startStreamLoadFor -> loadFromGeometryStreamer; readSidecarMetadataOnly -> readSidecarMetadata. - Federation::addModel takes an explicit display_name (no QFileInfo fallback); callers pass QFileInfo(path).fileName(). - Disambiguate cryptic short locals (d->sidecar, m->model, c->chunk, ...) in SceneLoader, Federation, ViewportWindow, AreaMeasurement, SectionGizmoRenderer, and the SidecarData/SidecarReadPlan spots in ViewportCore. Tests: 125/125 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -166,31 +166,31 @@ void removeGroup(SessionState& session, QWidget& host, const QString& group_id)
|
||||
session.setStatusMessage("Models", "Group removed");
|
||||
}
|
||||
|
||||
void removeModel(SessionState& session, ViewportWindow& viewport, QWidget& host, const QString& fed_id) {
|
||||
const Federation::Model* model = session.federation()->findById(fed_id);
|
||||
const QString label = model ? model->display_name : fed_id;
|
||||
void removeModel(SessionState& session, ViewportWindow& viewport, QWidget& host, const QString& model_id) {
|
||||
const Federation::Model* model = session.federation()->findById(model_id);
|
||||
const QString label = model ? model->display_name : model_id;
|
||||
const auto choice = QMessageBox::question(
|
||||
&host, "Remove Model",
|
||||
QString("Remove model '%1' from the federation?").arg(label),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
if (choice != QMessageBox::Yes) return;
|
||||
|
||||
const uint32_t model_id = session.modelIdForFedId(fed_id);
|
||||
if (model_id == 0) {
|
||||
session.federation()->removeModel(fed_id);
|
||||
const uint32_t session_model_id = session.sessionModelIdForModelId(model_id);
|
||||
if (session_model_id == 0) {
|
||||
session.federation()->removeModel(model_id);
|
||||
session.notifyFederationChanged();
|
||||
session.setStatusMessage("Models", "Model removed");
|
||||
return;
|
||||
}
|
||||
if (session.loader()->isLoadingModel(model_id)) return;
|
||||
if (session.loader()->isLoadingModel(session_model_id)) return;
|
||||
|
||||
viewport.setSelectedObjectId(0);
|
||||
session.setSelectedObjectId(0);
|
||||
session.federation()->removeModel(fed_id);
|
||||
viewport.removeModel(model_id);
|
||||
session.loader()->removeModel(model_id);
|
||||
session.elementRegistry()->removeModel(model_id);
|
||||
session.removeModelMappingByFedId(fed_id);
|
||||
session.federation()->removeModel(model_id);
|
||||
viewport.removeModel(session_model_id);
|
||||
session.loader()->removeModel(session_model_id);
|
||||
session.elementRegistry()->removeModel(session_model_id);
|
||||
session.removeModelMappingByModelId(model_id);
|
||||
session.notifySelectionChanged();
|
||||
session.notifyModelsChanged();
|
||||
session.setStatusMessage("Models", "Model removed");
|
||||
@@ -198,12 +198,12 @@ void removeModel(SessionState& session, ViewportWindow& viewport, QWidget& host,
|
||||
|
||||
namespace detail {
|
||||
|
||||
void loadModels(SessionState& session, const QStringList& paths, const QStringList& fed_ids) {
|
||||
void loadModels(SessionState& session, const QStringList& paths, const QStringList& model_ids) {
|
||||
if (paths.isEmpty()) return;
|
||||
|
||||
const auto model_ids = session.loader()->addFiles(paths);
|
||||
for (int i = 0; i < paths.size() && i < static_cast<int>(model_ids.size()) && i < fed_ids.size(); ++i) {
|
||||
session.setModelMapping(fed_ids[i], model_ids[i]);
|
||||
const auto session_model_ids = session.loader()->queueModels(paths);
|
||||
for (int i = 0; i < paths.size() && i < static_cast<int>(session_model_ids.size()) && i < model_ids.size(); ++i) {
|
||||
session.setModelMapping(model_ids[i], session_model_ids[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,20 +269,21 @@ void addModel(SessionState& session, QWidget& host) {
|
||||
// models yet — the first model that finishes loading will set the
|
||||
// origin via ViewportView. Checked here (before federation->addModel)
|
||||
// because federation->addModel doesn't yet populate SessionState's
|
||||
// model mapping; modelIds() reflects pre-add state at this point.
|
||||
if (session.modelIds().isEmpty()) {
|
||||
// model mapping; sessionModelIds() reflects pre-add state at this point.
|
||||
if (session.sessionModelIds().isEmpty()) {
|
||||
armFederatedFalseOriginGuess();
|
||||
}
|
||||
|
||||
QStringList accepted_paths;
|
||||
QStringList accepted_fed_ids;
|
||||
QStringList accepted_model_ids;
|
||||
for (const auto& path : paths) {
|
||||
const QString fed_id = session.federation()->addModel(path);
|
||||
if (fed_id.isEmpty()) continue;
|
||||
const QString model_id =
|
||||
session.federation()->addModel(path, QFileInfo(path).fileName());
|
||||
if (model_id.isEmpty()) continue;
|
||||
accepted_paths << path;
|
||||
accepted_fed_ids << fed_id;
|
||||
accepted_model_ids << model_id;
|
||||
}
|
||||
detail::loadModels(session, accepted_paths, accepted_fed_ids);
|
||||
detail::loadModels(session, accepted_paths, accepted_model_ids);
|
||||
session.notifyModelsChanged();
|
||||
}
|
||||
|
||||
@@ -317,15 +318,15 @@ void addModelFromCloud(SessionState& session, QWidget& host) {
|
||||
proc->call("pull_models_interactive", QJsonValue(),
|
||||
[sguard, connector_id](const QJsonValue& result) {
|
||||
if (!sguard) return;
|
||||
// Arm before the first addCloudModel — modelIds() reflects the
|
||||
// Arm before the first addCloudModel — sessionModelIds() reflects the
|
||||
// session state at the moment the connector returns, which is
|
||||
// when the user's "add into empty session" intent applies.
|
||||
if (sguard->modelIds().isEmpty()) {
|
||||
if (sguard->sessionModelIds().isEmpty()) {
|
||||
armFederatedFalseOriginGuess();
|
||||
}
|
||||
const QJsonArray arr = result.toArray();
|
||||
QStringList paths;
|
||||
QStringList fed_ids;
|
||||
QStringList model_ids;
|
||||
int added = 0;
|
||||
for (const QJsonValue& value : arr) {
|
||||
if (value.isNull() || !value.isObject()) continue;
|
||||
@@ -337,19 +338,19 @@ void addModelFromCloud(SessionState& session, QWidget& host) {
|
||||
QString src_connector = source.value("connector").toString();
|
||||
if (src_connector.isEmpty()) src_connector = connector_id;
|
||||
|
||||
const QString fed_id = sguard->federation()->addCloudModel(
|
||||
const QString model_id = sguard->federation()->addCloudModel(
|
||||
display_name, src_connector, source);
|
||||
if (fed_id.isEmpty()) continue;
|
||||
if (model_id.isEmpty()) continue;
|
||||
|
||||
const QJsonObject meta = entry.value("metadata").toObject();
|
||||
sguard->setCloudMetadata(fed_id, meta.toVariantMap());
|
||||
sguard->setCloudMetadata(model_id, meta.toVariantMap());
|
||||
|
||||
paths << path;
|
||||
fed_ids << fed_id;
|
||||
model_ids << model_id;
|
||||
++added;
|
||||
}
|
||||
if (!paths.isEmpty()) {
|
||||
detail::loadModels(*sguard, paths, fed_ids);
|
||||
detail::loadModels(*sguard, paths, model_ids);
|
||||
sguard->notifyModelsChanged();
|
||||
}
|
||||
sguard->setStatusMessage("Cloud",
|
||||
@@ -368,28 +369,28 @@ void addModelFromCloud(SessionState& session, QWidget& host) {
|
||||
namespace {
|
||||
|
||||
// Shared "local path on disk" lookup for the right-click cloud commands:
|
||||
// the loader keeps the path keyed by model_id (set when a file or pull_models
|
||||
// the loader keeps the path keyed by session_model_id (set when a file or pull_models
|
||||
// path was queued). Both local-sourced and resolved cloud-sourced models
|
||||
// have one; only un-resolved cloud models (where pull_models hasn't
|
||||
// returned yet) won't.
|
||||
QString localPathForModel(SessionState& session, const QString& fed_id) {
|
||||
const uint32_t model_id = session.modelIdForFedId(fed_id);
|
||||
if (model_id == 0 || !session.loader()) return {};
|
||||
return session.loader()->filePath(model_id);
|
||||
QString localPathForModel(SessionState& session, const QString& model_id) {
|
||||
const uint32_t session_model_id = session.sessionModelIdForModelId(model_id);
|
||||
if (session_model_id == 0 || !session.loader()) return {};
|
||||
return session.loader()->filePath(session_model_id);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void saveModelToCloud(SessionState& session, QWidget& host, const QString& fed_id) {
|
||||
void saveModelToCloud(SessionState& session, QWidget& host, const QString& model_id) {
|
||||
auto* federation = session.federation();
|
||||
const Federation::Model* model = federation->findById(fed_id);
|
||||
const Federation::Model* model = federation->findById(model_id);
|
||||
if (!model) return;
|
||||
if (model->source_connector == "local") {
|
||||
QMessageBox::information(&host, "Save Model To Cloud",
|
||||
"This model has no cloud target. Use \"Save As To Cloud\" first.");
|
||||
return;
|
||||
}
|
||||
const QString local_path = localPathForModel(session, fed_id);
|
||||
const QString local_path = localPathForModel(session, model_id);
|
||||
if (local_path.isEmpty()) {
|
||||
QMessageBox::warning(&host, "Save Model To Cloud",
|
||||
"Cannot find a local copy of this model to push.");
|
||||
@@ -416,15 +417,15 @@ void saveModelToCloud(SessionState& session, QWidget& host, const QString& fed_i
|
||||
|
||||
QPointer<SessionState> sguard(&session);
|
||||
proc->call("push_model", params,
|
||||
[sguard, fed_id, connector_id](const QJsonValue& result) {
|
||||
[sguard, model_id, connector_id](const QJsonValue& result) {
|
||||
if (!sguard) return;
|
||||
const QJsonObject obj = result.toObject();
|
||||
const QJsonObject new_source = obj.value("source").toObject();
|
||||
QString new_connector = new_source.value("connector").toString();
|
||||
if (new_connector.isEmpty()) new_connector = connector_id;
|
||||
sguard->federation()->setModelSource(fed_id, new_connector, new_source);
|
||||
sguard->federation()->setModelSource(model_id, new_connector, new_source);
|
||||
const QJsonObject meta = obj.value("metadata").toObject();
|
||||
sguard->setCloudMetadata(fed_id, meta.toVariantMap());
|
||||
sguard->setCloudMetadata(model_id, meta.toVariantMap());
|
||||
sguard->setStatusMessage("Cloud",
|
||||
QString("Saved to %1").arg(new_connector));
|
||||
},
|
||||
@@ -438,10 +439,10 @@ void saveModelToCloud(SessionState& session, QWidget& host, const QString& fed_i
|
||||
});
|
||||
}
|
||||
|
||||
void saveModelAsToCloud(SessionState& session, QWidget& host, const QString& fed_id) {
|
||||
const Federation::Model* model = session.federation()->findById(fed_id);
|
||||
void saveModelAsToCloud(SessionState& session, QWidget& host, const QString& model_id) {
|
||||
const Federation::Model* model = session.federation()->findById(model_id);
|
||||
if (!model) return;
|
||||
const QString local_path = localPathForModel(session, fed_id);
|
||||
const QString local_path = localPathForModel(session, model_id);
|
||||
if (local_path.isEmpty()) {
|
||||
QMessageBox::warning(&host, "Save Model As To Cloud",
|
||||
"Cannot find a local copy of this model to push.");
|
||||
@@ -480,20 +481,20 @@ void saveModelAsToCloud(SessionState& session, QWidget& host, const QString& fed
|
||||
|
||||
QPointer<SessionState> sguard(&session);
|
||||
proc->call("push_model_interactive", params,
|
||||
[sguard, fed_id, connector_id](const QJsonValue& result) {
|
||||
[sguard, model_id, connector_id](const QJsonValue& result) {
|
||||
if (!sguard) return;
|
||||
const QJsonObject obj = result.toObject();
|
||||
const QJsonObject new_source = obj.value("source").toObject();
|
||||
QString new_connector = new_source.value("connector").toString();
|
||||
if (new_connector.isEmpty()) new_connector = connector_id;
|
||||
sguard->federation()->setModelSource(fed_id, new_connector, new_source);
|
||||
sguard->federation()->setModelSource(model_id, new_connector, new_source);
|
||||
|
||||
const QString new_name = obj.value("display_name").toString();
|
||||
if (!new_name.isEmpty()) {
|
||||
sguard->federation()->setModelDisplayName(fed_id, new_name);
|
||||
sguard->federation()->setModelDisplayName(model_id, new_name);
|
||||
}
|
||||
const QJsonObject meta = obj.value("metadata").toObject();
|
||||
sguard->setCloudMetadata(fed_id, meta.toVariantMap());
|
||||
sguard->setCloudMetadata(model_id, meta.toVariantMap());
|
||||
sguard->setStatusMessage("Cloud",
|
||||
QString("Pushed to %1").arg(new_connector));
|
||||
},
|
||||
|
||||
@@ -58,7 +58,7 @@ void renameGroup(SessionState& session, QWidget& host, const QString& group_id);
|
||||
void moveGroup(SessionState& session, const QString& id, const QString& parent_group_id);
|
||||
void moveModels(SessionState& session, const QStringList& ids, const QString& parent_group_id);
|
||||
void removeGroup(SessionState& session, QWidget& host, const QString& group_id);
|
||||
void removeModel(SessionState& session, ViewportWindow& viewport, QWidget& host, const QString& fed_id);
|
||||
void removeModel(SessionState& session, ViewportWindow& viewport, QWidget& host, const QString& model_id);
|
||||
void addModel(SessionState& session, QWidget& host);
|
||||
// Connector picker → pull_models_interactive → addCloudModel + load.
|
||||
// Reachable from AddModelDialog's CloudModel button; the underlying call
|
||||
@@ -66,10 +66,10 @@ void addModel(SessionState& session, QWidget& host);
|
||||
void addModelFromCloud(SessionState& session, QWidget& host);
|
||||
// push_model: push a cloud-sourced model back to its existing target.
|
||||
// Only valid when model.source_connector != "local". Async.
|
||||
void saveModelToCloud(SessionState& session, QWidget& host, const QString& fed_id);
|
||||
void saveModelToCloud(SessionState& session, QWidget& host, const QString& model_id);
|
||||
// push_model_interactive: pick a connector and push to a fresh cloud
|
||||
// target. Valid for any model (local or already cloud-sourced). Async.
|
||||
void saveModelAsToCloud(SessionState& session, QWidget& host, const QString& fed_id);
|
||||
void saveModelAsToCloud(SessionState& session, QWidget& host, const QString& model_id);
|
||||
void convertIfcToDatabase(SessionState& session, QWidget& host);
|
||||
void exportGeometryDatabase(SessionState& session, QWidget& host);
|
||||
void openSettings(SessionState& session, QWidget& host);
|
||||
@@ -80,7 +80,7 @@ void openSettings(SessionState& session, QWidget& host);
|
||||
namespace detail {
|
||||
|
||||
// Queues already-federated models on the loader and maps their federation-ids to mids.
|
||||
void loadModels(SessionState& session, const QStringList& paths, const QStringList& fed_ids);
|
||||
void loadModels(SessionState& session, const QStringList& paths, const QStringList& model_ids);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
@@ -87,9 +87,9 @@ QStandardItem* FederationItemModel::makeGroupNameItem(const QString& group_id, c
|
||||
return item;
|
||||
}
|
||||
|
||||
QStandardItem* FederationItemModel::makeModelNameItem(const QString& fed_id, const QString& display_name) const {
|
||||
QStandardItem* FederationItemModel::makeModelNameItem(const QString& model_id, const QString& display_name) const {
|
||||
auto* item = new QStandardItem(components::icons::makeSvgIcon(":/icons/cube.svg"), display_name);
|
||||
item->setData(fed_id, IdRole);
|
||||
item->setData(model_id, IdRole);
|
||||
item->setData(int(ItemKind::Model), KindRole);
|
||||
item->setEditable(false);
|
||||
return item;
|
||||
@@ -129,14 +129,14 @@ QStandardItem* FederationItemModel::parentItemForGroup(const QString& parent_gro
|
||||
return found ? found : invisibleRootItem();
|
||||
}
|
||||
|
||||
void FederationItemModel::appendModelTo(QStandardItem* parent_item, const QString& fed_id) {
|
||||
const Federation::Model* model = federation_->findById(fed_id);
|
||||
void FederationItemModel::appendModelTo(QStandardItem* parent_item, const QString& model_id) {
|
||||
const Federation::Model* model = federation_->findById(model_id);
|
||||
if (!model) return;
|
||||
auto* name_item = makeModelNameItem(fed_id, model->display_name);
|
||||
auto* vis_item = makeVisibilityItem(ItemKind::Model, federation_->isModelEffectivelyVisible(fed_id));
|
||||
auto* name_item = makeModelNameItem(model_id, model->display_name);
|
||||
auto* vis_item = makeVisibilityItem(ItemKind::Model, federation_->isModelEffectivelyVisible(model_id));
|
||||
parent_item->appendRow({name_item, vis_item});
|
||||
id_to_name_item_.insert(fed_id, name_item);
|
||||
styleRowVisibility(name_item, federation_->isModelEffectivelyVisible(fed_id));
|
||||
id_to_name_item_.insert(model_id, name_item);
|
||||
styleRowVisibility(name_item, federation_->isModelEffectivelyVisible(model_id));
|
||||
}
|
||||
|
||||
void FederationItemModel::appendGroupSubtreeTo(QStandardItem* parent_item, const QString& group_id) {
|
||||
@@ -228,38 +228,38 @@ void FederationItemModel::onGroupVisibilityChanged(const QString& group_id, bool
|
||||
refreshSubtreeVisibility(item);
|
||||
}
|
||||
|
||||
void FederationItemModel::onModelAdded(const QString& fed_id) {
|
||||
const Federation::Model* model = federation_->findById(fed_id);
|
||||
void FederationItemModel::onModelAdded(const QString& model_id) {
|
||||
const Federation::Model* model = federation_->findById(model_id);
|
||||
if (!model) return;
|
||||
QStandardItem* parent_item = parentItemForGroup(model->group_id);
|
||||
appendModelTo(parent_item, fed_id);
|
||||
appendModelTo(parent_item, model_id);
|
||||
}
|
||||
|
||||
void FederationItemModel::onModelRemoved(const QString& fed_id) {
|
||||
QStandardItem* item = findItem(fed_id);
|
||||
void FederationItemModel::onModelRemoved(const QString& model_id) {
|
||||
QStandardItem* item = findItem(model_id);
|
||||
if (!item) return;
|
||||
id_to_name_item_.remove(fed_id);
|
||||
id_to_name_item_.remove(model_id);
|
||||
QStandardItem* parent_item = item->parent();
|
||||
if (!parent_item) parent_item = invisibleRootItem();
|
||||
parent_item->removeRow(item->row());
|
||||
}
|
||||
|
||||
void FederationItemModel::onModelVisibilityChanged(const QString& fed_id, bool /*visible*/) {
|
||||
QStandardItem* item = findItem(fed_id);
|
||||
void FederationItemModel::onModelVisibilityChanged(const QString& model_id, bool /*visible*/) {
|
||||
QStandardItem* item = findItem(model_id);
|
||||
if (!item) return;
|
||||
refreshSubtreeVisibility(item);
|
||||
}
|
||||
|
||||
void FederationItemModel::onModelChanged(const QString& fed_id) {
|
||||
QStandardItem* item = findItem(fed_id);
|
||||
void FederationItemModel::onModelChanged(const QString& model_id) {
|
||||
QStandardItem* item = findItem(model_id);
|
||||
if (!item) return;
|
||||
const Federation::Model* model = federation_->findById(fed_id);
|
||||
const Federation::Model* model = federation_->findById(model_id);
|
||||
if (!model) return;
|
||||
item->setText(model->display_name);
|
||||
}
|
||||
|
||||
void FederationItemModel::onModelGroupChanged(const QString& fed_id, const QString& new_group_id) {
|
||||
QStandardItem* item = findItem(fed_id);
|
||||
void FederationItemModel::onModelGroupChanged(const QString& model_id, const QString& new_group_id) {
|
||||
QStandardItem* item = findItem(model_id);
|
||||
if (!item) return;
|
||||
QStandardItem* current_parent = item->parent();
|
||||
if (!current_parent) current_parent = invisibleRootItem();
|
||||
|
||||
@@ -58,27 +58,27 @@ private slots:
|
||||
void onGroupRemoved(const QString& group_id);
|
||||
void onGroupChanged(const QString& group_id);
|
||||
void onGroupVisibilityChanged(const QString& group_id, bool visible);
|
||||
void onModelAdded(const QString& fed_id);
|
||||
void onModelRemoved(const QString& fed_id);
|
||||
void onModelVisibilityChanged(const QString& fed_id, bool visible);
|
||||
void onModelGroupChanged(const QString& fed_id, const QString& new_group_id);
|
||||
void onModelChanged(const QString& fed_id);
|
||||
void onModelAdded(const QString& model_id);
|
||||
void onModelRemoved(const QString& model_id);
|
||||
void onModelVisibilityChanged(const QString& model_id, bool visible);
|
||||
void onModelGroupChanged(const QString& model_id, const QString& new_group_id);
|
||||
void onModelChanged(const QString& model_id);
|
||||
|
||||
private:
|
||||
QStandardItem* makeGroupNameItem(const QString& group_id, const QString& display_name) const;
|
||||
QStandardItem* makeModelNameItem(const QString& fed_id, const QString& display_name) const;
|
||||
QStandardItem* makeModelNameItem(const QString& model_id, const QString& display_name) const;
|
||||
QStandardItem* makeVisibilityItem(ItemKind kind, bool visible) const;
|
||||
void styleRowVisibility(QStandardItem* name_item, bool visible) const;
|
||||
|
||||
QStandardItem* findItem(const QString& id) const;
|
||||
QStandardItem* parentItemForGroup(const QString& parent_group_id) const;
|
||||
|
||||
void appendModelTo(QStandardItem* parent_item, const QString& fed_id);
|
||||
void appendModelTo(QStandardItem* parent_item, const QString& model_id);
|
||||
void appendGroupSubtreeTo(QStandardItem* parent_item, const QString& group_id);
|
||||
void refreshSubtreeVisibility(QStandardItem* root);
|
||||
|
||||
Federation* federation_ = nullptr;
|
||||
QHash<QString, QStandardItem*> id_to_name_item_; // both group_ids and fed_ids
|
||||
QHash<QString, QStandardItem*> id_to_name_item_; // both group_ids and model_ids
|
||||
};
|
||||
|
||||
} // namespace bonsaiviewer::modules::models
|
||||
|
||||
@@ -378,7 +378,7 @@ void SettingsDialog::populateModelTable() {
|
||||
model_table_->setItem(row, 0, model_item);
|
||||
|
||||
ModelRowWidgets widgets;
|
||||
widgets.fed_id = model.id;
|
||||
widgets.model_id = model.id;
|
||||
|
||||
widgets.frame = new QComboBox(model_table_);
|
||||
widgets.frame->addItem("Local", static_cast<int>(AFrame::ModelLocal));
|
||||
@@ -448,7 +448,7 @@ void SettingsDialog::updateSelectedModelGeoref() {
|
||||
return;
|
||||
}
|
||||
|
||||
settings_view_->refresh(model_rows_[row].fed_id);
|
||||
settings_view_->refresh(model_rows_[row].model_id);
|
||||
}
|
||||
|
||||
void SettingsDialog::onAccepted() {
|
||||
@@ -473,7 +473,7 @@ void SettingsDialog::onAccepted() {
|
||||
transformation.b = parseVector3(row.to_point->text());
|
||||
transformation.rxyz_deg = parseVector3(row.rotate->text());
|
||||
transformation.pivot = parseVector3(row.pivot->text());
|
||||
federation_->setModelTransformation(row.fed_id, transformation);
|
||||
federation_->setModelTransformation(row.model_id, transformation);
|
||||
}
|
||||
if (session_state_) {
|
||||
session_state_->notifyFederationChanged();
|
||||
|
||||
@@ -55,7 +55,7 @@ protected:
|
||||
|
||||
private:
|
||||
struct ModelRowWidgets {
|
||||
QString fed_id;
|
||||
QString model_id;
|
||||
QComboBox* frame = nullptr;
|
||||
QTableWidgetItem* from_point = nullptr;
|
||||
QTableWidgetItem* to_point = nullptr;
|
||||
|
||||
@@ -212,7 +212,7 @@ SettingsView::SettingsView(SettingsDialog* widget,
|
||||
{
|
||||
}
|
||||
|
||||
void SettingsView::refresh(const QString& fed_id) const {
|
||||
void SettingsView::refresh(const QString& model_id) const {
|
||||
if (!widget_) {
|
||||
return;
|
||||
}
|
||||
@@ -228,18 +228,18 @@ void SettingsView::refresh(const QString& fed_id) const {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t model_id = session_state_->modelIdForFedId(fed_id);
|
||||
if (model_id == 0) {
|
||||
const uint32_t session_model_id = session_state_->sessionModelIdForModelId(model_id);
|
||||
if (session_model_id == 0) {
|
||||
widget_->renderSelectedModelGeoref(unknownState("Not loaded", "No live model"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* ifc_file = loader->ifcFile(model_id)) {
|
||||
if (auto* ifc_file = loader->ifcFile(session_model_id)) {
|
||||
widget_->renderSelectedModelGeoref(stateFromLiveFile(ifc_file));
|
||||
return;
|
||||
}
|
||||
|
||||
const ModelGeoref* georef = loader->modelGeoref(model_id);
|
||||
const ModelGeoref* georef = loader->modelGeoref(session_model_id);
|
||||
if (!georef) {
|
||||
widget_->renderSelectedModelGeoref(unknownState("Not available yet", "No data source"));
|
||||
return;
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
explicit SettingsView(SettingsDialog* widget,
|
||||
bonsaiviewer::SessionState* session_state);
|
||||
|
||||
void refresh(const QString& fed_id) const;
|
||||
void refresh(const QString& model_id) const;
|
||||
|
||||
private:
|
||||
SettingsDialog* widget_ = nullptr;
|
||||
|
||||
@@ -56,9 +56,9 @@ namespace {
|
||||
void clearScene(SessionState& session, ViewportWindow& viewport) {
|
||||
viewport.setSelectedObjectId(0);
|
||||
session.setSelectedObjectId(0);
|
||||
for (uint32_t model_id : session.modelIds()) {
|
||||
viewport.removeModel(model_id);
|
||||
session.loader()->removeModel(model_id);
|
||||
for (uint32_t session_model_id : session.sessionModelIds()) {
|
||||
viewport.removeModel(session_model_id);
|
||||
session.loader()->removeModel(session_model_id);
|
||||
}
|
||||
session.clearModelMappings();
|
||||
session.elementRegistry()->clear();
|
||||
@@ -81,7 +81,7 @@ bool confirmDiscardIfDirty(SessionState& session, QWidget& host) {
|
||||
// Fire-and-forget async resolution of any non-local models in the
|
||||
// federation. Groups by source_connector and issues one pull_models per
|
||||
// group. For each returned entry:
|
||||
// - if the fed_id already has a scene entry pointed at the same path,
|
||||
// - if the model_id already has a scene entry pointed at the same path,
|
||||
// just refresh cloud metadata (no reload, preserves view state);
|
||||
// - if the path differs, tear down the stale scene entry and queue a
|
||||
// fresh load (federation entry is preserved either way);
|
||||
@@ -90,21 +90,21 @@ bool confirmDiscardIfDirty(SessionState& session, QWidget& host) {
|
||||
// has already shown its own UI.
|
||||
void resolveCloudModels(SessionState& session, ViewportWindow& viewport) {
|
||||
auto* federation = session.federation();
|
||||
QHash<QString, QStringList> connector_to_fed_ids;
|
||||
QHash<QString, QStringList> connector_to_model_ids;
|
||||
for (const auto& model : federation->models()) {
|
||||
if (model.source_connector == "local") continue;
|
||||
connector_to_fed_ids[model.source_connector].push_back(model.id);
|
||||
connector_to_model_ids[model.source_connector].push_back(model.id);
|
||||
}
|
||||
if (connector_to_fed_ids.isEmpty()) return;
|
||||
if (connector_to_model_ids.isEmpty()) return;
|
||||
|
||||
auto* registry = session.connectorRegistry();
|
||||
QPointer<SessionState> sguard(&session);
|
||||
QPointer<ViewportWindow> vguard(&viewport);
|
||||
|
||||
for (auto it = connector_to_fed_ids.constBegin();
|
||||
it != connector_to_fed_ids.constEnd(); ++it) {
|
||||
for (auto it = connector_to_model_ids.constBegin();
|
||||
it != connector_to_model_ids.constEnd(); ++it) {
|
||||
const QString connector_id = it.key();
|
||||
const QStringList fed_ids = it.value();
|
||||
const QStringList model_ids = it.value();
|
||||
|
||||
auto* proc = registry->get(connector_id);
|
||||
if (!proc) {
|
||||
@@ -114,8 +114,8 @@ void resolveCloudModels(SessionState& session, ViewportWindow& viewport) {
|
||||
}
|
||||
|
||||
QJsonArray params;
|
||||
for (const QString& fed_id : fed_ids) {
|
||||
const Federation::Model* model = federation->findById(fed_id);
|
||||
for (const QString& model_id : model_ids) {
|
||||
const Federation::Model* model = federation->findById(model_id);
|
||||
if (!model) continue;
|
||||
QJsonObject source = model->source_data;
|
||||
source["connector"] = model->source_connector;
|
||||
@@ -127,25 +127,25 @@ void resolveCloudModels(SessionState& session, ViewportWindow& viewport) {
|
||||
}
|
||||
|
||||
proc->call("pull_models", params,
|
||||
[sguard, vguard, fed_ids](const QJsonValue& result) {
|
||||
[sguard, vguard, model_ids](const QJsonValue& result) {
|
||||
if (!sguard) return;
|
||||
const QJsonArray arr = result.toArray();
|
||||
QStringList paths_to_load;
|
||||
QStringList fed_ids_to_load;
|
||||
QStringList model_ids_to_load;
|
||||
bool any_detached = false;
|
||||
for (int i = 0; i < arr.size() && i < fed_ids.size(); ++i) {
|
||||
for (int i = 0; i < arr.size() && i < model_ids.size(); ++i) {
|
||||
if (arr[i].isNull()) continue;
|
||||
const QJsonObject obj = arr[i].toObject();
|
||||
const QString new_path = obj.value("path").toString();
|
||||
if (new_path.isEmpty()) continue;
|
||||
const QString fed_id = fed_ids[i];
|
||||
const QString model_id = model_ids[i];
|
||||
const QJsonObject meta = obj.value("metadata").toObject();
|
||||
|
||||
const uint32_t existing_mid = sguard->modelIdForFedId(fed_id);
|
||||
const uint32_t existing_mid = sguard->sessionModelIdForModelId(model_id);
|
||||
if (existing_mid != 0 && sguard->loader()) {
|
||||
const QString existing_path = sguard->loader()->filePath(existing_mid);
|
||||
if (QDir::cleanPath(existing_path) == QDir::cleanPath(new_path)) {
|
||||
sguard->setCloudMetadata(fed_id, meta.toVariantMap());
|
||||
sguard->setCloudMetadata(model_id, meta.toVariantMap());
|
||||
continue;
|
||||
}
|
||||
// Path changed (new revision lives in a fresh cache dir).
|
||||
@@ -153,13 +153,13 @@ void resolveCloudModels(SessionState& session, ViewportWindow& viewport) {
|
||||
if (vguard) vguard->removeModel(existing_mid);
|
||||
sguard->loader()->removeModel(existing_mid);
|
||||
sguard->elementRegistry()->removeModel(existing_mid);
|
||||
sguard->removeModelMappingByFedId(fed_id);
|
||||
sguard->removeModelMappingByModelId(model_id);
|
||||
any_detached = true;
|
||||
}
|
||||
|
||||
sguard->setCloudMetadata(fed_id, meta.toVariantMap());
|
||||
sguard->setCloudMetadata(model_id, meta.toVariantMap());
|
||||
paths_to_load << new_path;
|
||||
fed_ids_to_load << fed_id;
|
||||
model_ids_to_load << model_id;
|
||||
}
|
||||
if (any_detached) {
|
||||
if (vguard) vguard->setSelectedObjectId(0);
|
||||
@@ -168,7 +168,7 @@ void resolveCloudModels(SessionState& session, ViewportWindow& viewport) {
|
||||
}
|
||||
if (!paths_to_load.isEmpty()) {
|
||||
modules::models::commands::detail::loadModels(
|
||||
*sguard, paths_to_load, fed_ids_to_load);
|
||||
*sguard, paths_to_load, model_ids_to_load);
|
||||
sguard->notifyModelsChanged();
|
||||
}
|
||||
},
|
||||
@@ -183,8 +183,8 @@ void resolveCloudModels(SessionState& session, ViewportWindow& viewport) {
|
||||
}
|
||||
|
||||
int total = 0;
|
||||
for (auto it = connector_to_fed_ids.constBegin();
|
||||
it != connector_to_fed_ids.constEnd(); ++it) {
|
||||
for (auto it = connector_to_model_ids.constBegin();
|
||||
it != connector_to_model_ids.constEnd(); ++it) {
|
||||
total += it.value().size();
|
||||
}
|
||||
session.setStatusMessage("Cloud", QString("Resolving %1 model(s)...").arg(total));
|
||||
@@ -224,7 +224,7 @@ bool openProjectAt(SessionState& session, QWidget& host, ViewportWindow& viewpor
|
||||
clearScene(session, viewport);
|
||||
|
||||
QStringList paths;
|
||||
QStringList fed_ids;
|
||||
QStringList model_ids;
|
||||
for (const auto& model : session.federation()->models()) {
|
||||
if (model.source_connector != "local") continue;
|
||||
if (!QFileInfo::exists(model.source_path)) {
|
||||
@@ -232,9 +232,9 @@ bool openProjectAt(SessionState& session, QWidget& host, ViewportWindow& viewpor
|
||||
continue;
|
||||
}
|
||||
paths << model.source_path;
|
||||
fed_ids << model.id;
|
||||
model_ids << model.id;
|
||||
}
|
||||
modules::models::commands::detail::loadModels(session, paths, fed_ids);
|
||||
modules::models::commands::detail::loadModels(session, paths, model_ids);
|
||||
|
||||
if (!warnings.isEmpty()) {
|
||||
QMessageBox::warning(&host, "Open Project",
|
||||
|
||||
@@ -67,9 +67,9 @@ ViewportView::ViewportView(bonsaiviewer::SessionState* session_state,
|
||||
// first geometry-ready consumes the arm). refresh() stays terminal —
|
||||
// any federation mutation from the guess propagates through
|
||||
// SessionState's federatedFalseOriginChanged relay.
|
||||
connect(session_state_, &SessionState::modelGeometryReady, this, [this](uint32_t model_id) {
|
||||
connect(session_state_, &SessionState::modelGeometryReady, this, [this](uint32_t session_model_id) {
|
||||
if (modules::models::consumeFederatedFalseOriginGuess()) {
|
||||
guessFederatedFalseOriginFromFirstModel(model_id);
|
||||
guessFederatedFalseOriginFromFirstModel(session_model_id);
|
||||
}
|
||||
refresh();
|
||||
});
|
||||
@@ -136,34 +136,34 @@ void ViewportView::refresh() {
|
||||
viewport_->setFederatedFalseOrigin(
|
||||
composeFederatedFalseOrigin(federation->federatedFalseOrigin(), federation->config()));
|
||||
|
||||
for (uint32_t model_id : session_state_->modelIds()) {
|
||||
applyCoordinateOperation(model_id);
|
||||
applyModelVisibility(model_id);
|
||||
for (uint32_t session_model_id : session_state_->sessionModelIds()) {
|
||||
applyCoordinateOperation(session_model_id);
|
||||
applyModelVisibility(session_model_id);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportView::applyCoordinateOperation(uint32_t model_id) {
|
||||
void ViewportView::applyCoordinateOperation(uint32_t session_model_id) {
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity();
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(model_id)) {
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(session_model_id)) {
|
||||
if (georef->has_coordinate_operation) {
|
||||
matrix = georef->coordinate_operation_meters;
|
||||
}
|
||||
}
|
||||
viewport_->setModelCoordinateOperation(model_id, matrix);
|
||||
applyModelTransformation(model_id);
|
||||
viewport_->setModelCoordinateOperation(session_model_id, matrix);
|
||||
applyModelTransformation(session_model_id);
|
||||
}
|
||||
|
||||
void ViewportView::applyModelTransformation(uint32_t model_id) {
|
||||
void ViewportView::applyModelTransformation(uint32_t session_model_id) {
|
||||
Federation* federation = session_state_->federation();
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity();
|
||||
const QString fed_id = session_state_->fedIdForModelId(model_id);
|
||||
if (!fed_id.isEmpty()) {
|
||||
if (const Federation::Model* model = federation->findById(fed_id)) {
|
||||
const QString model_id = session_state_->modelIdForSessionModelId(session_model_id);
|
||||
if (!model_id.isEmpty()) {
|
||||
if (const Federation::Model* model = federation->findById(model_id)) {
|
||||
ModelUnits units;
|
||||
Eigen::Matrix4d coordinate_operation = Eigen::Matrix4d::Identity();
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(model_id)) {
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(session_model_id)) {
|
||||
units = georef->units;
|
||||
if (georef->has_coordinate_operation) {
|
||||
coordinate_operation = georef->coordinate_operation_meters;
|
||||
@@ -173,18 +173,18 @@ void ViewportView::applyModelTransformation(uint32_t model_id) {
|
||||
model->model_transformation, federation->config(), units, coordinate_operation);
|
||||
}
|
||||
}
|
||||
viewport_->setModelTransformation(model_id, matrix);
|
||||
viewport_->setModelTransformation(session_model_id, matrix);
|
||||
}
|
||||
|
||||
void ViewportView::applyModelVisibility(uint32_t model_id) {
|
||||
void ViewportView::applyModelVisibility(uint32_t session_model_id) {
|
||||
Federation* federation = session_state_->federation();
|
||||
const QString fed_id = session_state_->fedIdForModelId(model_id);
|
||||
if (fed_id.isEmpty()) return;
|
||||
const QString model_id = session_state_->modelIdForSessionModelId(session_model_id);
|
||||
if (model_id.isEmpty()) return;
|
||||
|
||||
if (federation->isModelEffectivelyVisible(fed_id)) {
|
||||
viewport_->showModel(model_id);
|
||||
if (federation->isModelEffectivelyVisible(model_id)) {
|
||||
viewport_->showModel(session_model_id);
|
||||
} else {
|
||||
viewport_->hideModel(model_id);
|
||||
viewport_->hideModel(session_model_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ void ViewportView::applyModelVisibility(uint32_t model_id) {
|
||||
// mutation here propagates through SessionState's federation relay
|
||||
// (federatedFalseOriginChanged → notifyFederationChanged) without
|
||||
// re-entering this function.
|
||||
void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t model_id) {
|
||||
void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t session_model_id) {
|
||||
Federation* federation = session_state_->federation();
|
||||
if (!federation->filePath().isEmpty()) return;
|
||||
|
||||
@@ -218,10 +218,10 @@ void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t model_id) {
|
||||
if (current.xyz != defaults.xyz || current.rz_deg != defaults.rz_deg) return;
|
||||
|
||||
Eigen::Vector3d first_geometry_point_m;
|
||||
if (!viewport_->firstGeometryPointWorldM(model_id, first_geometry_point_m)) return;
|
||||
if (!viewport_->firstGeometryPointWorldM(session_model_id, first_geometry_point_m)) return;
|
||||
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
const ModelGeoref* georef = loader->modelGeoref(model_id);
|
||||
const ModelGeoref* georef = loader->modelGeoref(session_model_id);
|
||||
if (georef == nullptr) return;
|
||||
|
||||
federation->setFederatedFalseOrigin(::guessFederatedFalseOrigin(
|
||||
@@ -236,7 +236,7 @@ void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t model_id) {
|
||||
// (0,0,0) — the federated false origin in render space — capped at
|
||||
// 100 m so a model with crazy-coord geometry can't pull the camera
|
||||
// back into nothing.
|
||||
viewport_->frameOnFederatedOrigin(model_id, 100.0f);
|
||||
viewport_->frameOnFederatedOrigin(session_model_id, 100.0f);
|
||||
}
|
||||
|
||||
void ViewportView::updateVolumeReadout() {
|
||||
|
||||
@@ -50,10 +50,10 @@ public:
|
||||
|
||||
private:
|
||||
void refresh();
|
||||
void applyCoordinateOperation(uint32_t model_id);
|
||||
void applyModelTransformation(uint32_t model_id);
|
||||
void applyModelVisibility(uint32_t model_id);
|
||||
void guessFederatedFalseOriginFromFirstModel(uint32_t model_id);
|
||||
void applyCoordinateOperation(uint32_t session_model_id);
|
||||
void applyModelTransformation(uint32_t session_model_id);
|
||||
void applyModelVisibility(uint32_t session_model_id);
|
||||
void guessFederatedFalseOriginFromFirstModel(uint32_t session_model_id);
|
||||
void updateVolumeReadout();
|
||||
|
||||
bonsaiviewer::SessionState* session_state_ = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user