mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-19 11:43:53 +00:00
Improve viewer variable names
Rename short local variables and parameters in the viewer loading, sidecar, and BonsaiViewer command paths to make their responsibilities clearer.\n\nGenerated with the assistance of an AI coding tool.
This commit is contained in:
@@ -60,9 +60,9 @@ ConnectorPickerDialog::ConnectorPickerDialog(const std::vector<ConnectorManifest
|
||||
row->setSpacing(components::style::metrics::padding);
|
||||
|
||||
QList<QToolButton*> buttons;
|
||||
for (const auto& m : manifests) {
|
||||
auto* button = components::buttons::makeButton(m.name, ":/icons/cloud-square.svg", choices);
|
||||
const QString id = m.id;
|
||||
for (const auto& manifest : manifests) {
|
||||
auto* button = components::buttons::makeButton(manifest.name, ":/icons/cloud-square.svg", choices);
|
||||
const QString id = manifest.id;
|
||||
connect(button, &QToolButton::clicked, this, [this, id]() {
|
||||
selected_id_ = id;
|
||||
accept();
|
||||
|
||||
@@ -189,8 +189,8 @@ void ConnectorProcess::dispatchLine(const QByteArray& line) {
|
||||
void ConnectorProcess::failPendingAndClear(int code, const QString& message) {
|
||||
QHash<QString, Pending> snapshot;
|
||||
snapshot.swap(pending_);
|
||||
for (const auto& p : snapshot) {
|
||||
if (p.on_error) p.on_error(code, message);
|
||||
for (const auto& pending_request : snapshot) {
|
||||
if (pending_request.on_error) pending_request.on_error(code, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,14 +48,14 @@ void ConnectorRegistry::refresh() {
|
||||
// exec changed under us. Surviving entries keep their running process.
|
||||
for (auto it = processes_.begin(); it != processes_.end();) {
|
||||
const QString id = it.key();
|
||||
ConnectorProcess* p = it.value();
|
||||
const ConnectorManifest* now = manifestFor(id);
|
||||
const bool stale = !now || !p ||
|
||||
p->manifest().exec_path != now->exec_path;
|
||||
ConnectorProcess* process = it.value();
|
||||
const ConnectorManifest* current_manifest = manifestFor(id);
|
||||
const bool stale = !current_manifest || !process ||
|
||||
process->manifest().exec_path != current_manifest->exec_path;
|
||||
if (stale) {
|
||||
if (p) {
|
||||
p->shutdown();
|
||||
p->deleteLater();
|
||||
if (process) {
|
||||
process->shutdown();
|
||||
process->deleteLater();
|
||||
}
|
||||
it = processes_.erase(it);
|
||||
} else {
|
||||
@@ -65,8 +65,8 @@ void ConnectorRegistry::refresh() {
|
||||
}
|
||||
|
||||
const ConnectorManifest* ConnectorRegistry::manifestFor(const QString& id) const {
|
||||
for (const auto& m : manifests_) {
|
||||
if (m.id == id) return &m;
|
||||
for (const auto& manifest : manifests_) {
|
||||
if (manifest.id == id) return &manifest;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -90,7 +90,7 @@ ConnectorProcess* ConnectorRegistry::get(const QString& id) {
|
||||
processes_.insert(id, proc);
|
||||
connect(proc, &ConnectorProcess::crashed, this, [this, id](const QString& message) {
|
||||
qWarning() << "ifcviewer connectors:" << message;
|
||||
if (auto* p = processes_.take(id)) p->deleteLater();
|
||||
if (auto* process = processes_.take(id)) process->deleteLater();
|
||||
});
|
||||
return proc;
|
||||
}
|
||||
@@ -99,9 +99,9 @@ void ConnectorRegistry::shutdownAll() {
|
||||
const auto procs = processes_;
|
||||
processes_.clear();
|
||||
for (auto it = procs.begin(); it != procs.end(); ++it) {
|
||||
if (auto* p = it.value()) {
|
||||
p->shutdown();
|
||||
p->deleteLater();
|
||||
if (auto* process = it.value()) {
|
||||
process->shutdown();
|
||||
process->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,24 +91,24 @@ QString formatElapsed(qint64 ms) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void toggleVisibility(SessionState& s, ItemKind kind, const QString& id) {
|
||||
Federation* fed = s.federation();
|
||||
void toggleVisibility(SessionState& session, ItemKind kind, const QString& id) {
|
||||
Federation* federation = session.federation();
|
||||
if (kind == ItemKind::Group) {
|
||||
const Federation::Group* group = fed->findGroupById(id);
|
||||
const Federation::Group* group = federation->findGroupById(id);
|
||||
if (!group) return;
|
||||
fed->setGroupVisible(id, !group->visible);
|
||||
s.notifyVisibilityChanged();
|
||||
s.setStatusMessage("Models", group->visible ? "Group hidden" : "Group shown");
|
||||
federation->setGroupVisible(id, !group->visible);
|
||||
session.notifyVisibilityChanged();
|
||||
session.setStatusMessage("Models", group->visible ? "Group hidden" : "Group shown");
|
||||
} else {
|
||||
const Federation::Model* model = fed->findById(id);
|
||||
const Federation::Model* model = federation->findById(id);
|
||||
if (!model) return;
|
||||
fed->setModelVisible(id, !model->visible);
|
||||
s.notifyVisibilityChanged();
|
||||
s.setStatusMessage("Models", model->visible ? "Model hidden" : "Model shown");
|
||||
federation->setModelVisible(id, !model->visible);
|
||||
session.notifyVisibilityChanged();
|
||||
session.setStatusMessage("Models", model->visible ? "Model hidden" : "Model shown");
|
||||
}
|
||||
}
|
||||
|
||||
void addGroup(SessionState& s, QWidget& host, const QString& parent_group_id) {
|
||||
void addGroup(SessionState& session, QWidget& host, const QString& parent_group_id) {
|
||||
bool ok = false;
|
||||
const QString name = QInputDialog::getText(
|
||||
&host, "New Group", "Group name:", QLineEdit::Normal, "Group", &ok);
|
||||
@@ -116,13 +116,13 @@ void addGroup(SessionState& s, QWidget& host, const QString& parent_group_id) {
|
||||
const QString trimmed = name.trimmed();
|
||||
if (trimmed.isEmpty()) return;
|
||||
|
||||
s.federation()->addGroup(trimmed, parent_group_id);
|
||||
s.notifyFederationChanged();
|
||||
s.setStatusMessage("Models", "Group added");
|
||||
session.federation()->addGroup(trimmed, parent_group_id);
|
||||
session.notifyFederationChanged();
|
||||
session.setStatusMessage("Models", "Group added");
|
||||
}
|
||||
|
||||
void renameGroup(SessionState& s, QWidget& host, const QString& group_id) {
|
||||
const Federation::Group* group = s.federation()->findGroupById(group_id);
|
||||
void renameGroup(SessionState& session, QWidget& host, const QString& group_id) {
|
||||
const Federation::Group* group = session.federation()->findGroupById(group_id);
|
||||
if (!group) return;
|
||||
|
||||
bool ok = false;
|
||||
@@ -132,27 +132,27 @@ void renameGroup(SessionState& s, QWidget& host, const QString& group_id) {
|
||||
const QString trimmed = name.trimmed();
|
||||
if (trimmed.isEmpty()) return;
|
||||
|
||||
s.federation()->setGroupName(group_id, trimmed);
|
||||
s.notifyFederationChanged();
|
||||
s.setStatusMessage("Models", "Group renamed");
|
||||
session.federation()->setGroupName(group_id, trimmed);
|
||||
session.notifyFederationChanged();
|
||||
session.setStatusMessage("Models", "Group renamed");
|
||||
}
|
||||
|
||||
void moveGroup(SessionState& s, const QString& id, const QString& parent_group_id) {
|
||||
s.federation()->setGroupParent(id, parent_group_id);
|
||||
s.notifyFederationChanged();
|
||||
s.setStatusMessage("Models", parent_group_id.isEmpty() ? "Group moved to root" : "Group moved");
|
||||
void moveGroup(SessionState& session, const QString& id, const QString& parent_group_id) {
|
||||
session.federation()->setGroupParent(id, parent_group_id);
|
||||
session.notifyFederationChanged();
|
||||
session.setStatusMessage("Models", parent_group_id.isEmpty() ? "Group moved to root" : "Group moved");
|
||||
}
|
||||
|
||||
void moveModels(SessionState& s, const QStringList& ids, const QString& parent_group_id) {
|
||||
void moveModels(SessionState& session, const QStringList& ids, const QString& parent_group_id) {
|
||||
for (const auto& id : ids) {
|
||||
s.federation()->setModelGroup(id, parent_group_id);
|
||||
session.federation()->setModelGroup(id, parent_group_id);
|
||||
}
|
||||
s.notifyFederationChanged();
|
||||
s.setStatusMessage("Models", parent_group_id.isEmpty() ? "Model(s) moved to root" : "Model(s) moved");
|
||||
session.notifyFederationChanged();
|
||||
session.setStatusMessage("Models", parent_group_id.isEmpty() ? "Model(s) moved to root" : "Model(s) moved");
|
||||
}
|
||||
|
||||
void removeGroup(SessionState& s, QWidget& host, const QString& group_id) {
|
||||
const Federation::Group* group = s.federation()->findGroupById(group_id);
|
||||
void removeGroup(SessionState& session, QWidget& host, const QString& group_id) {
|
||||
const Federation::Group* group = session.federation()->findGroupById(group_id);
|
||||
if (!group) return;
|
||||
|
||||
const auto choice = QMessageBox::question(
|
||||
@@ -161,13 +161,13 @@ void removeGroup(SessionState& s, QWidget& host, const QString& group_id) {
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
if (choice != QMessageBox::Yes) return;
|
||||
|
||||
s.federation()->removeGroup(group_id);
|
||||
s.notifyFederationChanged();
|
||||
s.setStatusMessage("Models", "Group removed");
|
||||
session.federation()->removeGroup(group_id);
|
||||
session.notifyFederationChanged();
|
||||
session.setStatusMessage("Models", "Group removed");
|
||||
}
|
||||
|
||||
void removeModel(SessionState& s, ViewportWindow& vp, QWidget& host, const QString& fed_id) {
|
||||
const Federation::Model* model = s.federation()->findById(fed_id);
|
||||
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;
|
||||
const auto choice = QMessageBox::question(
|
||||
&host, "Remove Model",
|
||||
@@ -175,41 +175,41 @@ void removeModel(SessionState& s, ViewportWindow& vp, QWidget& host, const QStri
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
if (choice != QMessageBox::Yes) return;
|
||||
|
||||
const uint32_t mid = s.modelIdForFedId(fed_id);
|
||||
if (mid == 0) {
|
||||
s.federation()->removeModel(fed_id);
|
||||
s.notifyFederationChanged();
|
||||
s.setStatusMessage("Models", "Model removed");
|
||||
const uint32_t model_id = session.modelIdForFedId(fed_id);
|
||||
if (model_id == 0) {
|
||||
session.federation()->removeModel(fed_id);
|
||||
session.notifyFederationChanged();
|
||||
session.setStatusMessage("Models", "Model removed");
|
||||
return;
|
||||
}
|
||||
if (s.loader()->isLoadingModel(mid)) return;
|
||||
if (session.loader()->isLoadingModel(model_id)) return;
|
||||
|
||||
vp.setSelectedObjectId(0);
|
||||
s.setSelectedObjectId(0);
|
||||
s.federation()->removeModel(fed_id);
|
||||
vp.removeModel(mid);
|
||||
s.loader()->removeModel(mid);
|
||||
s.elementRegistry()->removeModel(mid);
|
||||
s.removeModelMappingByFedId(fed_id);
|
||||
s.notifySelectionChanged();
|
||||
s.notifyModelsChanged();
|
||||
s.setStatusMessage("Models", "Model removed");
|
||||
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.notifySelectionChanged();
|
||||
session.notifyModelsChanged();
|
||||
session.setStatusMessage("Models", "Model removed");
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
void loadModels(SessionState& s, const QStringList& paths, const QStringList& fed_ids) {
|
||||
void loadModels(SessionState& session, const QStringList& paths, const QStringList& fed_ids) {
|
||||
if (paths.isEmpty()) return;
|
||||
|
||||
const auto ids = s.loader()->addFiles(paths);
|
||||
for (int i = 0; i < paths.size() && i < static_cast<int>(ids.size()) && i < fed_ids.size(); ++i) {
|
||||
s.setModelMapping(fed_ids[i], ids[i]);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
void addModel(SessionState& s, QWidget& host) {
|
||||
void addModel(SessionState& session, QWidget& host) {
|
||||
AddModelDialog dialog(&host);
|
||||
if (dialog.exec() != QDialog::Accepted) return;
|
||||
|
||||
@@ -253,13 +253,13 @@ void addModel(SessionState& s, QWidget& host) {
|
||||
break;
|
||||
}
|
||||
case SourceMode::CloudModel:
|
||||
addModelFromCloud(s, host);
|
||||
addModelFromCloud(session, host);
|
||||
return;
|
||||
case SourceMode::ConvertToDatabase:
|
||||
convertIfcToDatabase(s, host);
|
||||
convertIfcToDatabase(session, host);
|
||||
return;
|
||||
case SourceMode::ExportGeometryDatabase:
|
||||
exportGeometryDatabase(s, host);
|
||||
exportGeometryDatabase(session, host);
|
||||
return;
|
||||
case SourceMode::None:
|
||||
return;
|
||||
@@ -270,24 +270,24 @@ void addModel(SessionState& s, QWidget& host) {
|
||||
// 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 (s.modelIds().isEmpty()) {
|
||||
if (session.modelIds().isEmpty()) {
|
||||
armFederatedFalseOriginGuess();
|
||||
}
|
||||
|
||||
QStringList accepted_paths;
|
||||
QStringList accepted_fed_ids;
|
||||
for (const auto& path : paths) {
|
||||
const QString fed_id = s.federation()->addModel(path);
|
||||
const QString fed_id = session.federation()->addModel(path);
|
||||
if (fed_id.isEmpty()) continue;
|
||||
accepted_paths << path;
|
||||
accepted_fed_ids << fed_id;
|
||||
}
|
||||
detail::loadModels(s, accepted_paths, accepted_fed_ids);
|
||||
s.notifyModelsChanged();
|
||||
detail::loadModels(session, accepted_paths, accepted_fed_ids);
|
||||
session.notifyModelsChanged();
|
||||
}
|
||||
|
||||
void addModelFromCloud(SessionState& s, QWidget& host) {
|
||||
auto* registry = s.connectorRegistry();
|
||||
void addModelFromCloud(SessionState& session, QWidget& host) {
|
||||
auto* registry = session.connectorRegistry();
|
||||
const auto& manifests = registry->available();
|
||||
if (manifests.empty()) {
|
||||
QMessageBox::information(&host, "Add From Cloud",
|
||||
@@ -310,9 +310,9 @@ void addModelFromCloud(SessionState& s, QWidget& host) {
|
||||
return;
|
||||
}
|
||||
|
||||
s.setStatusMessage("Cloud", QString("Browsing %1...").arg(connector_id));
|
||||
session.setStatusMessage("Cloud", QString("Browsing %1...").arg(connector_id));
|
||||
|
||||
QPointer<SessionState> sguard(&s);
|
||||
QPointer<SessionState> sguard(&session);
|
||||
|
||||
proc->call("pull_models_interactive", QJsonValue(),
|
||||
[sguard, connector_id](const QJsonValue& result) {
|
||||
@@ -327,9 +327,9 @@ void addModelFromCloud(SessionState& s, QWidget& host) {
|
||||
QStringList paths;
|
||||
QStringList fed_ids;
|
||||
int added = 0;
|
||||
for (const QJsonValue& v : arr) {
|
||||
if (v.isNull() || !v.isObject()) continue;
|
||||
const QJsonObject entry = v.toObject();
|
||||
for (const QJsonValue& value : arr) {
|
||||
if (value.isNull() || !value.isObject()) continue;
|
||||
const QJsonObject entry = value.toObject();
|
||||
const QString display_name = entry.value("display_name").toString();
|
||||
const QString path = entry.value("path").toString();
|
||||
if (path.isEmpty()) continue;
|
||||
@@ -368,35 +368,35 @@ void addModelFromCloud(SessionState& s, QWidget& host) {
|
||||
namespace {
|
||||
|
||||
// Shared "local path on disk" lookup for the right-click cloud commands:
|
||||
// the loader keeps the path keyed by mid (set when a file or pull_models
|
||||
// the loader keeps the path keyed by 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& s, const QString& fed_id) {
|
||||
const uint32_t mid = s.modelIdForFedId(fed_id);
|
||||
if (mid == 0 || !s.loader()) return {};
|
||||
return s.loader()->filePath(mid);
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void saveModelToCloud(SessionState& s, QWidget& host, const QString& fed_id) {
|
||||
auto* fed = s.federation();
|
||||
const Federation::Model* model = fed->findById(fed_id);
|
||||
void saveModelToCloud(SessionState& session, QWidget& host, const QString& fed_id) {
|
||||
auto* federation = session.federation();
|
||||
const Federation::Model* model = federation->findById(fed_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(s, fed_id);
|
||||
const QString local_path = localPathForModel(session, fed_id);
|
||||
if (local_path.isEmpty()) {
|
||||
QMessageBox::warning(&host, "Save Model To Cloud",
|
||||
"Cannot find a local copy of this model to push.");
|
||||
return;
|
||||
}
|
||||
const QString connector_id = model->source_connector;
|
||||
auto* registry = s.connectorRegistry();
|
||||
auto* registry = session.connectorRegistry();
|
||||
auto* proc = registry->get(connector_id);
|
||||
if (!proc) {
|
||||
QMessageBox::warning(&host, "Save Model To Cloud",
|
||||
@@ -411,10 +411,10 @@ void saveModelToCloud(SessionState& s, QWidget& host, const QString& fed_id) {
|
||||
params["path"] = local_path;
|
||||
params["source"] = source;
|
||||
|
||||
s.setStatusMessage("Cloud",
|
||||
session.setStatusMessage("Cloud",
|
||||
QString("Saving %1 to %2...").arg(model->display_name, connector_id));
|
||||
|
||||
QPointer<SessionState> sguard(&s);
|
||||
QPointer<SessionState> sguard(&session);
|
||||
proc->call("push_model", params,
|
||||
[sguard, fed_id, connector_id](const QJsonValue& result) {
|
||||
if (!sguard) return;
|
||||
@@ -438,17 +438,17 @@ void saveModelToCloud(SessionState& s, QWidget& host, const QString& fed_id) {
|
||||
});
|
||||
}
|
||||
|
||||
void saveModelAsToCloud(SessionState& s, QWidget& host, const QString& fed_id) {
|
||||
const Federation::Model* model = s.federation()->findById(fed_id);
|
||||
void saveModelAsToCloud(SessionState& session, QWidget& host, const QString& fed_id) {
|
||||
const Federation::Model* model = session.federation()->findById(fed_id);
|
||||
if (!model) return;
|
||||
const QString local_path = localPathForModel(s, fed_id);
|
||||
const QString local_path = localPathForModel(session, fed_id);
|
||||
if (local_path.isEmpty()) {
|
||||
QMessageBox::warning(&host, "Save Model As To Cloud",
|
||||
"Cannot find a local copy of this model to push.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto* registry = s.connectorRegistry();
|
||||
auto* registry = session.connectorRegistry();
|
||||
const auto& manifests = registry->available();
|
||||
if (manifests.empty()) {
|
||||
QMessageBox::information(&host, "Save Model As To Cloud",
|
||||
@@ -475,10 +475,10 @@ void saveModelAsToCloud(SessionState& s, QWidget& host, const QString& fed_id) {
|
||||
QJsonObject params;
|
||||
params["path"] = local_path;
|
||||
|
||||
s.setStatusMessage("Cloud",
|
||||
session.setStatusMessage("Cloud",
|
||||
QString("Pushing %1 to %2...").arg(model->display_name, connector_id));
|
||||
|
||||
QPointer<SessionState> sguard(&s);
|
||||
QPointer<SessionState> sguard(&session);
|
||||
proc->call("push_model_interactive", params,
|
||||
[sguard, fed_id, connector_id](const QJsonValue& result) {
|
||||
if (!sguard) return;
|
||||
@@ -507,7 +507,7 @@ void saveModelAsToCloud(SessionState& s, QWidget& host, const QString& fed_id) {
|
||||
});
|
||||
}
|
||||
|
||||
void convertIfcToDatabase(SessionState& s, QWidget& host) {
|
||||
void convertIfcToDatabase(SessionState& session, QWidget& host) {
|
||||
QFileDialog input_dialog(&host, "Select IFC File to Convert");
|
||||
input_dialog.setFileMode(QFileDialog::ExistingFile);
|
||||
input_dialog.setNameFilter("IFC Files (*.ifc);;All Files (*)");
|
||||
@@ -545,10 +545,10 @@ void convertIfcToDatabase(SessionState& s, QWidget& host) {
|
||||
}
|
||||
}
|
||||
|
||||
s.beginProgress(QString("Converting %1 to %2…")
|
||||
session.beginProgress(QString("Converting %1 to %2…")
|
||||
.arg(QFileInfo(input_path).fileName(),
|
||||
QFileInfo(output_path).fileName()));
|
||||
s.setStatusMessage("Converting",
|
||||
session.setStatusMessage("Converting",
|
||||
QString("%1 → %2").arg(QFileInfo(input_path).fileName(), QFileInfo(output_path).fileName()));
|
||||
|
||||
auto timer = std::make_shared<QElapsedTimer>();
|
||||
@@ -583,20 +583,20 @@ void convertIfcToDatabase(SessionState& s, QWidget& host) {
|
||||
});
|
||||
|
||||
QObject::connect(thread, &QThread::finished, &host,
|
||||
[&s, host_ptr = &host, thread, timer, error_message, input_path, output_path]() {
|
||||
[&session, host_ptr = &host, thread, timer, error_message, input_path, output_path]() {
|
||||
const qint64 elapsed = timer->elapsed();
|
||||
|
||||
s.endProgress();
|
||||
session.endProgress();
|
||||
thread->deleteLater();
|
||||
|
||||
if (!error_message->isEmpty()) {
|
||||
s.setStatusMessage("Error", *error_message);
|
||||
session.setStatusMessage("Error", *error_message);
|
||||
QMessageBox::warning(host_ptr, "Convert IFC to Database",
|
||||
QString("Conversion failed:\n%1").arg(*error_message));
|
||||
return;
|
||||
}
|
||||
|
||||
s.setStatusMessage(
|
||||
session.setStatusMessage(
|
||||
"Converted",
|
||||
QString("%1 → %2 in %3")
|
||||
.arg(QFileInfo(input_path).fileName(),
|
||||
@@ -609,7 +609,7 @@ void convertIfcToDatabase(SessionState& s, QWidget& host) {
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void exportGeometryDatabase(SessionState& s, QWidget& host) {
|
||||
void exportGeometryDatabase(SessionState& session, QWidget& host) {
|
||||
QFileDialog input_dialog(&host, "Select IFC File to Export");
|
||||
input_dialog.setFileMode(QFileDialog::ExistingFile);
|
||||
input_dialog.setNameFilter("IFC Files (*.ifc);;All Files (*)");
|
||||
@@ -637,10 +637,10 @@ void exportGeometryDatabase(SessionState& s, QWidget& host) {
|
||||
output_path += ".rdbview";
|
||||
}
|
||||
|
||||
s.beginProgress(QString("Exporting %1 to %2…")
|
||||
session.beginProgress(QString("Exporting %1 to %2…")
|
||||
.arg(QFileInfo(input_path).fileName(),
|
||||
QFileInfo(output_path).fileName()));
|
||||
s.setStatusMessage("Exporting",
|
||||
session.setStatusMessage("Exporting",
|
||||
QString("%1 → %2").arg(QFileInfo(input_path).fileName(), QFileInfo(output_path).fileName()));
|
||||
|
||||
auto timer = std::make_shared<QElapsedTimer>();
|
||||
@@ -695,13 +695,13 @@ void exportGeometryDatabase(SessionState& s, QWidget& host) {
|
||||
|
||||
// Write to a sibling `.tmp` then rename so a partial file never
|
||||
// appears at the destination (matters for cloud-sync folders).
|
||||
const QString tmp_zip = output_path + ".tmp";
|
||||
QFile::remove(tmp_zip);
|
||||
const QString temporary_zip = output_path + ".tmp";
|
||||
QFile::remove(temporary_zip);
|
||||
{
|
||||
QZipWriter writer(tmp_zip);
|
||||
QZipWriter writer(temporary_zip);
|
||||
if (writer.status() != QZipWriter::NoError) {
|
||||
throw ifcopenshell::exception(
|
||||
("Failed to open " + tmp_zip + " for writing").toStdString());
|
||||
("Failed to open " + temporary_zip + " for writing").toStdString());
|
||||
}
|
||||
writer.setCompressionPolicy(QZipWriter::AutoCompress);
|
||||
|
||||
@@ -731,15 +731,15 @@ void exportGeometryDatabase(SessionState& s, QWidget& host) {
|
||||
writer.close();
|
||||
if (writer.status() != QZipWriter::NoError) {
|
||||
throw ifcopenshell::exception(
|
||||
("Failed to finalize " + tmp_zip).toStdString());
|
||||
("Failed to finalize " + temporary_zip).toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
QFile::remove(output_path);
|
||||
if (!QFile::rename(tmp_zip, output_path)) {
|
||||
QFile::remove(tmp_zip);
|
||||
if (!QFile::rename(temporary_zip, output_path)) {
|
||||
QFile::remove(temporary_zip);
|
||||
throw ifcopenshell::exception(
|
||||
("Failed to move " + tmp_zip + " to " + output_path).toStdString());
|
||||
("Failed to move " + temporary_zip + " to " + output_path).toStdString());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
*error_message = QString::fromUtf8(e.what());
|
||||
@@ -751,20 +751,20 @@ void exportGeometryDatabase(SessionState& s, QWidget& host) {
|
||||
});
|
||||
|
||||
QObject::connect(thread, &QThread::finished, &host,
|
||||
[&s, host_ptr = &host, thread, timer, error_message, input_path, output_path]() {
|
||||
[&session, host_ptr = &host, thread, timer, error_message, input_path, output_path]() {
|
||||
const qint64 elapsed = timer->elapsed();
|
||||
|
||||
s.endProgress();
|
||||
session.endProgress();
|
||||
thread->deleteLater();
|
||||
|
||||
if (!error_message->isEmpty()) {
|
||||
s.setStatusMessage("Error", *error_message);
|
||||
session.setStatusMessage("Error", *error_message);
|
||||
QMessageBox::warning(host_ptr, "Export Geometry Database",
|
||||
QString("Export failed:\n%1").arg(*error_message));
|
||||
return;
|
||||
}
|
||||
|
||||
s.setStatusMessage(
|
||||
session.setStatusMessage(
|
||||
"Exported",
|
||||
QString("%1 → %2 in %3")
|
||||
.arg(QFileInfo(input_path).fileName(),
|
||||
@@ -777,8 +777,8 @@ void exportGeometryDatabase(SessionState& s, QWidget& host) {
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void openSettings(SessionState& s, QWidget& host) {
|
||||
SettingsDialog dialog(&s, &host);
|
||||
void openSettings(SessionState& session, QWidget& host) {
|
||||
SettingsDialog dialog(&session, &host);
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
|
||||
@@ -52,35 +52,35 @@ namespace bonsaiviewer::modules::models::commands {
|
||||
|
||||
// User-facing commands. Each one is responsible for emitting any notify()
|
||||
// signals exactly once, at the end of its execution.
|
||||
void toggleVisibility(SessionState& s, ItemKind kind, const QString& id);
|
||||
void addGroup(SessionState& s, QWidget& host, const QString& parent_group_id);
|
||||
void renameGroup(SessionState& s, QWidget& host, const QString& group_id);
|
||||
void moveGroup(SessionState& s, const QString& id, const QString& parent_group_id);
|
||||
void moveModels(SessionState& s, const QStringList& ids, const QString& parent_group_id);
|
||||
void removeGroup(SessionState& s, QWidget& host, const QString& group_id);
|
||||
void removeModel(SessionState& s, ViewportWindow& vp, QWidget& host, const QString& fed_id);
|
||||
void addModel(SessionState& s, QWidget& host);
|
||||
void toggleVisibility(SessionState& session, ItemKind kind, const QString& id);
|
||||
void addGroup(SessionState& session, QWidget& host, const QString& parent_group_id);
|
||||
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 addModel(SessionState& session, QWidget& host);
|
||||
// Connector picker → pull_models_interactive → addCloudModel + load.
|
||||
// Reachable from AddModelDialog's CloudModel button; the underlying call
|
||||
// is async, so addModelFromCloud returns immediately after kicking it off.
|
||||
void addModelFromCloud(SessionState& s, 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& s, QWidget& host, const QString& fed_id);
|
||||
void saveModelToCloud(SessionState& session, QWidget& host, const QString& fed_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& s, QWidget& host, const QString& fed_id);
|
||||
void convertIfcToDatabase(SessionState& s, QWidget& host);
|
||||
void exportGeometryDatabase(SessionState& s, QWidget& host);
|
||||
void openSettings(SessionState& s, QWidget& host);
|
||||
void saveModelAsToCloud(SessionState& session, QWidget& host, const QString& fed_id);
|
||||
void convertIfcToDatabase(SessionState& session, QWidget& host);
|
||||
void exportGeometryDatabase(SessionState& session, QWidget& host);
|
||||
void openSettings(SessionState& session, QWidget& host);
|
||||
|
||||
// Internal building blocks shared by commands here and by ProjectController.
|
||||
// These NEVER call notify*() — the caller is responsible for emitting once
|
||||
// at the end of its execution.
|
||||
namespace detail {
|
||||
|
||||
// Queues already-federated models on the loader and maps their fed-ids to mids.
|
||||
void loadModels(SessionState& s, const QStringList& paths, const QStringList& fed_ids);
|
||||
// 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);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
@@ -164,8 +164,8 @@ void FederationItemModel::refreshSubtreeVisibility(QStandardItem* root) {
|
||||
const auto kind = static_cast<ItemKind>(item->data(KindRole).toInt());
|
||||
bool visible = true;
|
||||
if (kind == ItemKind::Group) {
|
||||
const Federation::Group* g = federation_->findGroupById(id);
|
||||
visible = g && g->visible;
|
||||
const Federation::Group* group = federation_->findGroupById(id);
|
||||
visible = group && group->visible;
|
||||
} else {
|
||||
visible = federation_->isModelEffectivelyVisible(id);
|
||||
}
|
||||
|
||||
@@ -197,8 +197,9 @@ private:
|
||||
if (!target_index.isValid()) return true;
|
||||
if (kindOf(target_index) != ItemKind::Group) return false;
|
||||
if (group_id == target_group_id) return false;
|
||||
for (QModelIndex cur = target_index; cur.isValid(); cur = cur.parent()) {
|
||||
if (idOf(cur) == group_id) return false;
|
||||
for (QModelIndex ancestor_index = target_index; ancestor_index.isValid();
|
||||
ancestor_index = ancestor_index.parent()) {
|
||||
if (idOf(ancestor_index) == group_id) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -166,10 +166,10 @@ void SettingsDialog::setupUi() {
|
||||
federation_unit_form->setHorizontalSpacing(16);
|
||||
federation_unit_form->setVerticalSpacing(10);
|
||||
unit_combo_ = new QComboBox(federation_unit_body);
|
||||
for (const auto& uc : kUnitChoices) {
|
||||
for (const auto& unit_choice : kUnitChoices) {
|
||||
QStringList data;
|
||||
data << QString::fromUtf8(uc.prefix) << QString::fromUtf8(uc.name);
|
||||
unit_combo_->addItem(uc.label, data);
|
||||
data << QString::fromUtf8(unit_choice.prefix) << QString::fromUtf8(unit_choice.name);
|
||||
unit_combo_->addItem(unit_choice.label, data);
|
||||
}
|
||||
federation_unit_form->addRow("Unit", unit_combo_);
|
||||
federation_unit_section->addBodyWidget(unit_hint);
|
||||
@@ -341,13 +341,13 @@ void SettingsDialog::setupUi() {
|
||||
void SettingsDialog::syncFromFederation() {
|
||||
if (!federation_) return;
|
||||
|
||||
const auto& cfg = federation_->config();
|
||||
const auto& config = federation_->config();
|
||||
int idx = -1;
|
||||
for (int i = 0; i < unit_combo_->count(); ++i) {
|
||||
const QStringList data = unit_combo_->itemData(i).toStringList();
|
||||
if (data.size() == 2 &&
|
||||
data[0].toStdString() == cfg.unit_prefix &&
|
||||
data[1].toStdString() == cfg.unit_name) {
|
||||
data[0].toStdString() == config.unit_prefix &&
|
||||
data[1].toStdString() == config.unit_name) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
@@ -370,7 +370,7 @@ void SettingsDialog::populateModelTable() {
|
||||
|
||||
int row = 0;
|
||||
for (const auto& model : federation_->models()) {
|
||||
const auto& xf = model.model_transformation;
|
||||
const auto& transformation = model.model_transformation;
|
||||
model_table_->insertRow(row);
|
||||
|
||||
auto* model_item = new QTableWidgetItem(model.display_name.isEmpty() ? model.id : model.display_name);
|
||||
@@ -383,13 +383,13 @@ void SettingsDialog::populateModelTable() {
|
||||
widgets.frame = new QComboBox(model_table_);
|
||||
widgets.frame->addItem("Local", static_cast<int>(AFrame::ModelLocal));
|
||||
widgets.frame->addItem("Global", static_cast<int>(AFrame::ModelGlobal));
|
||||
widgets.frame->setCurrentIndex(xf.a_frame == AFrame::ModelGlobal ? 1 : 0);
|
||||
widgets.frame->setCurrentIndex(transformation.a_frame == AFrame::ModelGlobal ? 1 : 0);
|
||||
model_table_->setCellWidget(row, 1, widgets.frame);
|
||||
|
||||
widgets.from_point = new QTableWidgetItem(formatVector3(xf.a));
|
||||
widgets.to_point = new QTableWidgetItem(formatVector3(xf.b));
|
||||
widgets.rotate = new QTableWidgetItem(formatVector3(xf.rxyz_deg));
|
||||
widgets.pivot = new QTableWidgetItem(formatVector3(xf.pivot));
|
||||
widgets.from_point = new QTableWidgetItem(formatVector3(transformation.a));
|
||||
widgets.to_point = new QTableWidgetItem(formatVector3(transformation.b));
|
||||
widgets.rotate = new QTableWidgetItem(formatVector3(transformation.rxyz_deg));
|
||||
widgets.pivot = new QTableWidgetItem(formatVector3(transformation.pivot));
|
||||
model_table_->setItem(row, 2, widgets.from_point);
|
||||
model_table_->setItem(row, 3, widgets.to_point);
|
||||
model_table_->setItem(row, 4, widgets.rotate);
|
||||
@@ -454,12 +454,12 @@ void SettingsDialog::updateSelectedModelGeoref() {
|
||||
void SettingsDialog::onAccepted() {
|
||||
if (federation_) {
|
||||
const QStringList data = unit_combo_->currentData().toStringList();
|
||||
FederationConfig cfg;
|
||||
FederationConfig config;
|
||||
if (data.size() == 2) {
|
||||
cfg.unit_prefix = data[0].toStdString();
|
||||
cfg.unit_name = data[1].toStdString();
|
||||
config.unit_prefix = data[0].toStdString();
|
||||
config.unit_name = data[1].toStdString();
|
||||
}
|
||||
federation_->setConfig(cfg);
|
||||
federation_->setConfig(config);
|
||||
|
||||
FederatedFalseOrigin origin;
|
||||
origin.xyz = Eigen::Vector3d(parseNumber(xyz_x_), parseNumber(xyz_y_), parseNumber(xyz_z_));
|
||||
@@ -467,13 +467,13 @@ void SettingsDialog::onAccepted() {
|
||||
federation_->setFederatedFalseOrigin(origin);
|
||||
|
||||
for (const auto& row : model_rows_) {
|
||||
ModelTransformation xf;
|
||||
xf.a_frame = static_cast<AFrame>(row.frame->currentData().toInt());
|
||||
xf.a = parseVector3(row.from_point->text());
|
||||
xf.b = parseVector3(row.to_point->text());
|
||||
xf.rxyz_deg = parseVector3(row.rotate->text());
|
||||
xf.pivot = parseVector3(row.pivot->text());
|
||||
federation_->setModelTransformation(row.fed_id, xf);
|
||||
ModelTransformation transformation;
|
||||
transformation.a_frame = static_cast<AFrame>(row.frame->currentData().toInt());
|
||||
transformation.a = parseVector3(row.from_point->text());
|
||||
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);
|
||||
}
|
||||
if (session_state_) {
|
||||
session_state_->notifyFederationChanged();
|
||||
|
||||
@@ -39,12 +39,16 @@ QString formatNumber(double value) {
|
||||
|
||||
QString formatAngleDms(double degrees) {
|
||||
const double absolute = std::fabs(degrees);
|
||||
const int d = static_cast<int>(absolute);
|
||||
const double minutes_total = (absolute - static_cast<double>(d)) * 60.0;
|
||||
const int m = static_cast<int>(minutes_total);
|
||||
const double s = (minutes_total - static_cast<double>(m)) * 60.0;
|
||||
const int degree_part = static_cast<int>(absolute);
|
||||
const double minutes_total = (absolute - static_cast<double>(degree_part)) * 60.0;
|
||||
const int minute_part = static_cast<int>(minutes_total);
|
||||
const double second_part = (minutes_total - static_cast<double>(minute_part)) * 60.0;
|
||||
const QString sign = degrees < 0.0 ? "-" : "";
|
||||
return QString("%1%2° %3' %4\"").arg(sign).arg(d).arg(m, 2, 10, QChar('0')).arg(formatNumber(s));
|
||||
return QString("%1%2° %3' %4\"")
|
||||
.arg(sign)
|
||||
.arg(degree_part)
|
||||
.arg(minute_part, 2, 10, QChar('0'))
|
||||
.arg(formatNumber(second_part));
|
||||
}
|
||||
|
||||
SelectedModelGeorefState unknownState(const QString& georef, const QString& type) {
|
||||
@@ -74,8 +78,8 @@ QString formatCachedUnitScale(double meters_per_unit) {
|
||||
std::string enumString(const attribute_value& av) {
|
||||
if (av.isNull()) return {};
|
||||
if (av.type() != ifcopenshell::Argument_ENUMERATION) return {};
|
||||
enumeration_reference er = av;
|
||||
return std::string(er.value() ? er.value() : "");
|
||||
enumeration_reference enumeration = av;
|
||||
return std::string(enumeration.value() ? enumeration.value() : "");
|
||||
}
|
||||
|
||||
QString formatNamedUnit(const express::Base& unit) {
|
||||
@@ -224,18 +228,18 @@ void SettingsView::refresh(const QString& fed_id) const {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t mid = session_state_->modelIdForFedId(fed_id);
|
||||
if (mid == 0) {
|
||||
const uint32_t model_id = session_state_->modelIdForFedId(fed_id);
|
||||
if (model_id == 0) {
|
||||
widget_->renderSelectedModelGeoref(unknownState("Not loaded", "No live model"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* ifc_file = loader->ifcFile(mid)) {
|
||||
if (auto* ifc_file = loader->ifcFile(model_id)) {
|
||||
widget_->renderSelectedModelGeoref(stateFromLiveFile(ifc_file));
|
||||
return;
|
||||
}
|
||||
|
||||
const ModelGeoref* georef = loader->modelGeoref(mid);
|
||||
const ModelGeoref* georef = loader->modelGeoref(model_id);
|
||||
if (!georef) {
|
||||
widget_->renderSelectedModelGeoref(unknownState("Not available yet", "No data source"));
|
||||
return;
|
||||
|
||||
@@ -53,28 +53,28 @@ namespace {
|
||||
// Pure helper — clears the loaded scene without emitting any signals. The
|
||||
// caller (newProject / openProject) emits projectReset / projectOpened once
|
||||
// the whole flow finishes.
|
||||
void clearScene(SessionState& s, ViewportWindow& vp) {
|
||||
vp.setSelectedObjectId(0);
|
||||
s.setSelectedObjectId(0);
|
||||
for (uint32_t mid : s.modelIds()) {
|
||||
vp.removeModel(mid);
|
||||
s.loader()->removeModel(mid);
|
||||
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);
|
||||
}
|
||||
s.clearModelMappings();
|
||||
s.elementRegistry()->clear();
|
||||
session.clearModelMappings();
|
||||
session.elementRegistry()->clear();
|
||||
}
|
||||
|
||||
// Returns false if the user cancelled (i.e. don't proceed with the destructive
|
||||
// op). Handles the Save → Discard → Cancel branch including a follow-on save.
|
||||
bool confirmDiscardIfDirty(SessionState& s, QWidget& host) {
|
||||
if (!s.federation()->isDirty()) return true;
|
||||
bool confirmDiscardIfDirty(SessionState& session, QWidget& host) {
|
||||
if (!session.federation()->isDirty()) return true;
|
||||
const auto result = QMessageBox::question(
|
||||
&host, "Unsaved Project",
|
||||
"The current project has unsaved changes. Save before continuing?",
|
||||
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
|
||||
QMessageBox::Save);
|
||||
if (result == QMessageBox::Cancel) return false;
|
||||
if (result == QMessageBox::Save) return saveProject(s, host);
|
||||
if (result == QMessageBox::Save) return saveProject(session, host);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,18 +88,18 @@ bool confirmDiscardIfDirty(SessionState& s, QWidget& host) {
|
||||
// - if no scene entry exists yet (initial open), queue a load.
|
||||
// Per spec, connector errors are not surfaced to the user; the connector
|
||||
// has already shown its own UI.
|
||||
void resolveCloudModels(SessionState& s, ViewportWindow& vp) {
|
||||
auto* fed = s.federation();
|
||||
void resolveCloudModels(SessionState& session, ViewportWindow& viewport) {
|
||||
auto* federation = session.federation();
|
||||
QHash<QString, QStringList> connector_to_fed_ids;
|
||||
for (const auto& m : fed->models()) {
|
||||
if (m.source_connector == "local") continue;
|
||||
connector_to_fed_ids[m.source_connector].push_back(m.id);
|
||||
for (const auto& model : federation->models()) {
|
||||
if (model.source_connector == "local") continue;
|
||||
connector_to_fed_ids[model.source_connector].push_back(model.id);
|
||||
}
|
||||
if (connector_to_fed_ids.isEmpty()) return;
|
||||
|
||||
auto* registry = s.connectorRegistry();
|
||||
QPointer<SessionState> sguard(&s);
|
||||
QPointer<ViewportWindow> vguard(&vp);
|
||||
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) {
|
||||
@@ -115,13 +115,13 @@ void resolveCloudModels(SessionState& s, ViewportWindow& vp) {
|
||||
|
||||
QJsonArray params;
|
||||
for (const QString& fed_id : fed_ids) {
|
||||
const Federation::Model* m = fed->findById(fed_id);
|
||||
if (!m) continue;
|
||||
QJsonObject source = m->source_data;
|
||||
source["connector"] = m->source_connector;
|
||||
const Federation::Model* model = federation->findById(fed_id);
|
||||
if (!model) continue;
|
||||
QJsonObject source = model->source_data;
|
||||
source["connector"] = model->source_connector;
|
||||
QJsonObject entry;
|
||||
entry["display_name"] = m->display_name;
|
||||
entry["id"] = m->id;
|
||||
entry["display_name"] = model->display_name;
|
||||
entry["id"] = model->id;
|
||||
entry["source"] = source;
|
||||
params.append(entry);
|
||||
}
|
||||
@@ -187,7 +187,7 @@ void resolveCloudModels(SessionState& s, ViewportWindow& vp) {
|
||||
it != connector_to_fed_ids.constEnd(); ++it) {
|
||||
total += it.value().size();
|
||||
}
|
||||
s.setStatusMessage("Cloud", QString("Resolving %1 model(s)...").arg(total));
|
||||
session.setStatusMessage("Cloud", QString("Resolving %1 model(s)...").arg(total));
|
||||
}
|
||||
|
||||
// Byte-equality check for "is this .ifcfed the same as what we have loaded?"
|
||||
@@ -203,29 +203,29 @@ bool isIfcfedUnchanged(const QString& current_path, const QString& candidate_pat
|
||||
return a.readAll() == b.readAll();
|
||||
}
|
||||
|
||||
bool openProjectAt(SessionState& s, QWidget& host, ViewportWindow& vp, const QString& path) {
|
||||
SceneLoader* loader = s.loader();
|
||||
bool openProjectAt(SessionState& session, QWidget& host, ViewportWindow& viewport, const QString& path) {
|
||||
SceneLoader* loader = session.loader();
|
||||
if (loader && loader->isLoading()) {
|
||||
QMessageBox::information(
|
||||
&host, "Open Project",
|
||||
"Wait until the current model load finishes before opening another project.");
|
||||
return false;
|
||||
}
|
||||
if (!confirmDiscardIfDirty(s, host)) return false;
|
||||
if (!confirmDiscardIfDirty(session, host)) return false;
|
||||
|
||||
QStringList warnings;
|
||||
QString err;
|
||||
if (!s.federation()->load(path, &warnings, &err)) {
|
||||
if (!session.federation()->load(path, &warnings, &err)) {
|
||||
QMessageBox::warning(&host, "Open Project",
|
||||
QString("Could not open project:\n%1").arg(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
clearScene(s, vp);
|
||||
clearScene(session, viewport);
|
||||
|
||||
QStringList paths;
|
||||
QStringList fed_ids;
|
||||
for (const auto& model : s.federation()->models()) {
|
||||
for (const auto& model : session.federation()->models()) {
|
||||
if (model.source_connector != "local") continue;
|
||||
if (!QFileInfo::exists(model.source_path)) {
|
||||
warnings << QString("Source not found, kept in project: %1").arg(model.source_path);
|
||||
@@ -234,58 +234,58 @@ bool openProjectAt(SessionState& s, QWidget& host, ViewportWindow& vp, const QSt
|
||||
paths << model.source_path;
|
||||
fed_ids << model.id;
|
||||
}
|
||||
modules::models::commands::detail::loadModels(s, paths, fed_ids);
|
||||
modules::models::commands::detail::loadModels(session, paths, fed_ids);
|
||||
|
||||
if (!warnings.isEmpty()) {
|
||||
QMessageBox::warning(&host, "Open Project",
|
||||
"Project opened with warnings:\n\n" + warnings.join("\n"));
|
||||
}
|
||||
|
||||
s.federation()->markClean();
|
||||
if (s.federation()->hasHomeView()) {
|
||||
const auto& hv = s.federation()->homeView();
|
||||
vp.setCamera(hv.target.x(), hv.target.y(), hv.target.z(),
|
||||
hv.distance, hv.yaw, hv.pitch);
|
||||
session.federation()->markClean();
|
||||
if (session.federation()->hasHomeView()) {
|
||||
const auto& home_view = session.federation()->homeView();
|
||||
viewport.setCamera(home_view.target.x(), home_view.target.y(), home_view.target.z(),
|
||||
home_view.distance, home_view.yaw, home_view.pitch);
|
||||
}
|
||||
s.setStatusMessage("Project", QFileInfo(path).fileName());
|
||||
s.notifyProjectOpened(path);
|
||||
session.setStatusMessage("Project", QFileInfo(path).fileName());
|
||||
session.notifyProjectOpened(path);
|
||||
|
||||
resolveCloudModels(s, vp);
|
||||
resolveCloudModels(session, viewport);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveProjectTo(SessionState& s, QWidget& host, const QString& path) {
|
||||
bool saveProjectTo(SessionState& session, QWidget& host, const QString& path) {
|
||||
QString err;
|
||||
if (!s.federation()->save(path, &err)) {
|
||||
if (!session.federation()->save(path, &err)) {
|
||||
QMessageBox::warning(&host, "Save Project",
|
||||
QString("Could not save project:\n%1").arg(err));
|
||||
return false;
|
||||
}
|
||||
s.setStatusMessage("Project", QFileInfo(path).fileName());
|
||||
s.notifyProjectSaved(path);
|
||||
session.setStatusMessage("Project", QFileInfo(path).fileName());
|
||||
session.notifyProjectSaved(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool newProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
SceneLoader* loader = s.loader();
|
||||
bool newProject(SessionState& session, QWidget& host, ViewportWindow& viewport) {
|
||||
SceneLoader* loader = session.loader();
|
||||
if (loader && loader->isLoading()) {
|
||||
QMessageBox::information(
|
||||
&host, "New Project",
|
||||
"Wait until the current model load finishes before creating a new project.");
|
||||
return false;
|
||||
}
|
||||
if (!confirmDiscardIfDirty(s, host)) return false;
|
||||
if (!confirmDiscardIfDirty(session, host)) return false;
|
||||
|
||||
clearScene(s, vp);
|
||||
s.federation()->clear();
|
||||
s.setStatusMessage("Project", "Untitled");
|
||||
s.notifyProjectReset();
|
||||
clearScene(session, viewport);
|
||||
session.federation()->clear();
|
||||
session.setStatusMessage("Project", "Untitled");
|
||||
session.notifyProjectReset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool openProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
bool openProject(SessionState& session, QWidget& host, ViewportWindow& viewport) {
|
||||
QFileDialog file_dialog(&host, "Open Project");
|
||||
file_dialog.setFileMode(QFileDialog::ExistingFile);
|
||||
file_dialog.setNameFilter("IFC Federation (*.ifcfed);;All Files (*)");
|
||||
@@ -294,25 +294,25 @@ bool openProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
|
||||
const QString path = file_dialog.selectedFiles().value(0);
|
||||
if (path.isEmpty()) return false;
|
||||
return openProjectAt(s, host, vp, path);
|
||||
return openProjectAt(session, host, viewport, path);
|
||||
}
|
||||
|
||||
bool openProjectPath(SessionState& s, QWidget& host, ViewportWindow& vp, const QString& path) {
|
||||
bool openProjectPath(SessionState& session, QWidget& host, ViewportWindow& viewport, const QString& path) {
|
||||
if (path.isEmpty()) return false;
|
||||
return openProjectAt(s, host, vp, path);
|
||||
return openProjectAt(session, host, viewport, path);
|
||||
}
|
||||
|
||||
bool openCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
SceneLoader* loader = s.loader();
|
||||
bool openCloudProject(SessionState& session, QWidget& host, ViewportWindow& viewport) {
|
||||
SceneLoader* loader = session.loader();
|
||||
if (loader && loader->isLoading()) {
|
||||
QMessageBox::information(
|
||||
&host, "Open from Cloud",
|
||||
"Wait until the current model load finishes before opening another project.");
|
||||
return false;
|
||||
}
|
||||
if (!confirmDiscardIfDirty(s, host)) return false;
|
||||
if (!confirmDiscardIfDirty(session, host)) return false;
|
||||
|
||||
auto* registry = s.connectorRegistry();
|
||||
auto* registry = session.connectorRegistry();
|
||||
const auto& manifests = registry->available();
|
||||
if (manifests.empty()) {
|
||||
QMessageBox::information(&host, "Open from Cloud",
|
||||
@@ -336,12 +336,12 @@ bool openCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s.beginProgress(QString("Opening project from %1...").arg(connector_id));
|
||||
s.setStatusMessage("Cloud", QString("Browsing %1...").arg(connector_id));
|
||||
session.beginProgress(QString("Opening project from %1...").arg(connector_id));
|
||||
session.setStatusMessage("Cloud", QString("Browsing %1...").arg(connector_id));
|
||||
|
||||
QPointer<SessionState> sguard(&s);
|
||||
QPointer<SessionState> sguard(&session);
|
||||
QPointer<QWidget> hguard(&host);
|
||||
QPointer<ViewportWindow> vguard(&vp);
|
||||
QPointer<ViewportWindow> vguard(&viewport);
|
||||
|
||||
proc->call("pull_ifcfed_interactive", QJsonValue(),
|
||||
[sguard, hguard, vguard, connector_id](const QJsonValue& result) {
|
||||
@@ -371,16 +371,16 @@ bool openCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
auto* fed = s.federation();
|
||||
bool syncCloudProject(SessionState& session, QWidget& host, ViewportWindow& viewport) {
|
||||
auto* federation = session.federation();
|
||||
|
||||
// Per spec, sync has two independent phases — refreshing the .ifcfed
|
||||
// (requires manifest) and refreshing cloud models (requires any
|
||||
// non-local source). Either is sufficient.
|
||||
const bool has_manifest = fed->hasManifest();
|
||||
const bool has_manifest = federation->hasManifest();
|
||||
bool has_cloud_models = false;
|
||||
for (const auto& m : fed->models()) {
|
||||
if (m.source_connector != "local") { has_cloud_models = true; break; }
|
||||
for (const auto& model : federation->models()) {
|
||||
if (model.source_connector != "local") { has_cloud_models = true; break; }
|
||||
}
|
||||
if (!has_manifest && !has_cloud_models) {
|
||||
QMessageBox::information(&host, "Sync From Cloud",
|
||||
@@ -388,7 +388,7 @@ bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SceneLoader* loader = s.loader();
|
||||
SceneLoader* loader = session.loader();
|
||||
if (loader && loader->isLoading()) {
|
||||
QMessageBox::information(&host, "Sync From Cloud",
|
||||
"Wait until the current model load finishes before syncing.");
|
||||
@@ -399,23 +399,23 @@ bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
// skipped). Just refresh cloud-sourced models against the .ifcfed
|
||||
// already on disk. Federation state is preserved, so no dirty prompt.
|
||||
if (!has_manifest) {
|
||||
s.setStatusMessage("Cloud", "Refreshing cloud models...");
|
||||
resolveCloudModels(s, vp);
|
||||
session.setStatusMessage("Cloud", "Refreshing cloud models...");
|
||||
resolveCloudModels(session, viewport);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Manifest path: the .ifcfed itself may be replaced. Confirm dirty —
|
||||
// even though we'll attempt to preserve the session if the returned
|
||||
// .ifcfed is byte-equal, that's not known until after the round-trip.
|
||||
if (!confirmDiscardIfDirty(s, host)) return false;
|
||||
if (!confirmDiscardIfDirty(session, host)) return false;
|
||||
|
||||
const QString connector_id = fed->manifestConnectorId();
|
||||
const QString connector_id = federation->manifestConnectorId();
|
||||
if (connector_id.isEmpty()) {
|
||||
QMessageBox::warning(&host, "Sync From Cloud",
|
||||
"The project's manifest does not name a connector.");
|
||||
return false;
|
||||
}
|
||||
auto* registry = s.connectorRegistry();
|
||||
auto* registry = session.connectorRegistry();
|
||||
auto* proc = registry->get(connector_id);
|
||||
if (!proc) {
|
||||
QMessageBox::warning(&host, "Sync From Cloud",
|
||||
@@ -424,15 +424,15 @@ bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s.beginProgress(QString("Syncing from %1...").arg(connector_id));
|
||||
s.setStatusMessage("Cloud", QString("Syncing from %1...").arg(connector_id));
|
||||
session.beginProgress(QString("Syncing from %1...").arg(connector_id));
|
||||
session.setStatusMessage("Cloud", QString("Syncing from %1...").arg(connector_id));
|
||||
|
||||
QPointer<SessionState> sguard(&s);
|
||||
QPointer<SessionState> sguard(&session);
|
||||
QPointer<QWidget> hguard(&host);
|
||||
QPointer<ViewportWindow> vguard(&vp);
|
||||
const QString current_path = fed->filePath();
|
||||
QPointer<ViewportWindow> vguard(&viewport);
|
||||
const QString current_path = federation->filePath();
|
||||
|
||||
proc->call("pull_ifcfed", fed->manifest(),
|
||||
proc->call("pull_ifcfed", federation->manifest(),
|
||||
[sguard, hguard, vguard, connector_id, current_path](const QJsonValue& result) {
|
||||
if (!sguard) return;
|
||||
sguard->endProgress();
|
||||
@@ -475,13 +475,13 @@ bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveProject(SessionState& s, QWidget& host) {
|
||||
if (s.federation()->filePath().isEmpty()) return saveProjectAs(s, host);
|
||||
return saveProjectTo(s, host, s.federation()->filePath());
|
||||
bool saveProject(SessionState& session, QWidget& host) {
|
||||
if (session.federation()->filePath().isEmpty()) return saveProjectAs(session, host);
|
||||
return saveProjectTo(session, host, session.federation()->filePath());
|
||||
}
|
||||
|
||||
bool saveProjectAs(SessionState& s, QWidget& host) {
|
||||
QString suggested = s.federation()->filePath();
|
||||
bool saveProjectAs(SessionState& session, QWidget& host) {
|
||||
QString suggested = session.federation()->filePath();
|
||||
if (suggested.isEmpty()) suggested = "project.ifcfed";
|
||||
|
||||
QFileDialog file_dialog(&host, "Save Project As", suggested);
|
||||
@@ -494,7 +494,7 @@ bool saveProjectAs(SessionState& s, QWidget& host) {
|
||||
QString path = file_dialog.selectedFiles().value(0);
|
||||
if (path.isEmpty()) return false;
|
||||
if (!path.endsWith(".ifcfed", Qt::CaseInsensitive)) path += ".ifcfed";
|
||||
return saveProjectTo(s, host, path);
|
||||
return saveProjectTo(session, host, path);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -507,7 +507,7 @@ struct TempProjectFile {
|
||||
QString path;
|
||||
};
|
||||
|
||||
TempProjectFile writeProjectToTemp(SessionState& s, QWidget& host, const QString& op_title) {
|
||||
TempProjectFile writeProjectToTemp(SessionState& session, QWidget& host, const QString& op_title) {
|
||||
TempProjectFile out;
|
||||
out.dir = std::make_shared<QTemporaryDir>();
|
||||
if (!out.dir->isValid()) {
|
||||
@@ -517,12 +517,12 @@ TempProjectFile writeProjectToTemp(SessionState& s, QWidget& host, const QString
|
||||
out.dir.reset();
|
||||
return out;
|
||||
}
|
||||
const QString name = s.federation()->filePath().isEmpty()
|
||||
const QString name = session.federation()->filePath().isEmpty()
|
||||
? "project.ifcfed"
|
||||
: QFileInfo(s.federation()->filePath()).fileName();
|
||||
: QFileInfo(session.federation()->filePath()).fileName();
|
||||
const QString tmp_path = QDir(out.dir->path()).filePath(name);
|
||||
QString err;
|
||||
if (!s.federation()->writeCopyTo(tmp_path, &err)) {
|
||||
if (!session.federation()->writeCopyTo(tmp_path, &err)) {
|
||||
QMessageBox::warning(&host, op_title,
|
||||
QString("Failed to write temporary project:\n%1").arg(err));
|
||||
out.dir.reset();
|
||||
@@ -534,12 +534,12 @@ TempProjectFile writeProjectToTemp(SessionState& s, QWidget& host, const QString
|
||||
|
||||
// Shared continuation for push_ifcfed[_interactive]: on success, repoint
|
||||
// Federation to the returned path and notify; on error, log + status.
|
||||
void onPushIfcfedResult(SessionState& s,
|
||||
void onPushIfcfedResult(SessionState& session,
|
||||
QWidget& host,
|
||||
const QString& op_title,
|
||||
const QString& connector_id,
|
||||
const QJsonValue& result) {
|
||||
s.endProgress();
|
||||
session.endProgress();
|
||||
const QString new_path = result.toObject().value("path").toString();
|
||||
if (new_path.isEmpty()) {
|
||||
QMessageBox::warning(&host, op_title,
|
||||
@@ -547,24 +547,24 @@ void onPushIfcfedResult(SessionState& s,
|
||||
return;
|
||||
}
|
||||
QStringList warnings;
|
||||
s.federation()->repointTo(new_path, &warnings);
|
||||
s.setStatusMessage("Cloud",
|
||||
session.federation()->repointTo(new_path, &warnings);
|
||||
session.setStatusMessage("Cloud",
|
||||
QString("Saved to %1 via %2")
|
||||
.arg(QFileInfo(new_path).fileName(), connector_id));
|
||||
s.notifyProjectSaved(new_path);
|
||||
session.notifyProjectSaved(new_path);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool saveCloudProject(SessionState& s, QWidget& host) {
|
||||
auto* fed = s.federation();
|
||||
if (!fed->hasManifest()) {
|
||||
bool saveCloudProject(SessionState& session, QWidget& host) {
|
||||
auto* federation = session.federation();
|
||||
if (!federation->hasManifest()) {
|
||||
QMessageBox::information(&host, "Save To Cloud",
|
||||
"This project has no cloud target. Use \"Save As To Cloud\" first.");
|
||||
return false;
|
||||
}
|
||||
const QString connector_id = fed->manifestConnectorId();
|
||||
auto* registry = s.connectorRegistry();
|
||||
const QString connector_id = federation->manifestConnectorId();
|
||||
auto* registry = session.connectorRegistry();
|
||||
auto* proc = registry->get(connector_id);
|
||||
if (!proc) {
|
||||
QMessageBox::warning(&host, "Save To Cloud",
|
||||
@@ -573,26 +573,26 @@ bool saveCloudProject(SessionState& s, QWidget& host) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto tmp = writeProjectToTemp(s, host, "Save To Cloud");
|
||||
if (!tmp.dir) return false;
|
||||
auto temporary_project = writeProjectToTemp(session, host, "Save To Cloud");
|
||||
if (!temporary_project.dir) return false;
|
||||
|
||||
QJsonObject params;
|
||||
params["path"] = tmp.path;
|
||||
params["manifest"] = fed->manifest();
|
||||
params["path"] = temporary_project.path;
|
||||
params["manifest"] = federation->manifest();
|
||||
|
||||
s.beginProgress(QString("Saving to %1...").arg(connector_id));
|
||||
s.setStatusMessage("Cloud", QString("Saving to %1...").arg(connector_id));
|
||||
session.beginProgress(QString("Saving to %1...").arg(connector_id));
|
||||
session.setStatusMessage("Cloud", QString("Saving to %1...").arg(connector_id));
|
||||
|
||||
QPointer<SessionState> sguard(&s);
|
||||
QPointer<SessionState> sguard(&session);
|
||||
QPointer<QWidget> hguard(&host);
|
||||
|
||||
proc->call("push_ifcfed", params,
|
||||
[sguard, hguard, connector_id, tmp_keepalive = tmp.dir](const QJsonValue& result) {
|
||||
[sguard, hguard, connector_id, tmp_keepalive = temporary_project.dir](const QJsonValue& result) {
|
||||
(void)tmp_keepalive;
|
||||
if (!sguard || !hguard) return;
|
||||
onPushIfcfedResult(*sguard, *hguard, "Save To Cloud", connector_id, result);
|
||||
},
|
||||
[sguard, connector_id, tmp_keepalive = tmp.dir](int code, const QString& message) {
|
||||
[sguard, connector_id, tmp_keepalive = temporary_project.dir](int code, const QString& message) {
|
||||
(void)tmp_keepalive;
|
||||
qWarning() << "push_ifcfed to" << connector_id
|
||||
<< "failed:" << code << message;
|
||||
@@ -605,8 +605,8 @@ bool saveCloudProject(SessionState& s, QWidget& host) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveAsCloudProject(SessionState& s, QWidget& host) {
|
||||
auto* registry = s.connectorRegistry();
|
||||
bool saveAsCloudProject(SessionState& session, QWidget& host) {
|
||||
auto* registry = session.connectorRegistry();
|
||||
const auto& manifests = registry->available();
|
||||
if (manifests.empty()) {
|
||||
QMessageBox::information(&host, "Save As To Cloud",
|
||||
@@ -629,25 +629,25 @@ bool saveAsCloudProject(SessionState& s, QWidget& host) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto tmp = writeProjectToTemp(s, host, "Save As To Cloud");
|
||||
if (!tmp.dir) return false;
|
||||
auto temporary_project = writeProjectToTemp(session, host, "Save As To Cloud");
|
||||
if (!temporary_project.dir) return false;
|
||||
|
||||
QJsonObject params;
|
||||
params["path"] = tmp.path;
|
||||
params["path"] = temporary_project.path;
|
||||
|
||||
s.beginProgress(QString("Pushing to %1...").arg(connector_id));
|
||||
s.setStatusMessage("Cloud", QString("Pushing to %1...").arg(connector_id));
|
||||
session.beginProgress(QString("Pushing to %1...").arg(connector_id));
|
||||
session.setStatusMessage("Cloud", QString("Pushing to %1...").arg(connector_id));
|
||||
|
||||
QPointer<SessionState> sguard(&s);
|
||||
QPointer<SessionState> sguard(&session);
|
||||
QPointer<QWidget> hguard(&host);
|
||||
|
||||
proc->call("push_ifcfed_interactive", params,
|
||||
[sguard, hguard, connector_id, tmp_keepalive = tmp.dir](const QJsonValue& result) {
|
||||
[sguard, hguard, connector_id, tmp_keepalive = temporary_project.dir](const QJsonValue& result) {
|
||||
(void)tmp_keepalive;
|
||||
if (!sguard || !hguard) return;
|
||||
onPushIfcfedResult(*sguard, *hguard, "Save As To Cloud", connector_id, result);
|
||||
},
|
||||
[sguard, connector_id, tmp_keepalive = tmp.dir](int code, const QString& message) {
|
||||
[sguard, connector_id, tmp_keepalive = temporary_project.dir](int code, const QString& message) {
|
||||
(void)tmp_keepalive;
|
||||
qWarning() << "push_ifcfed_interactive to" << connector_id
|
||||
<< "failed:" << code << message;
|
||||
@@ -660,14 +660,14 @@ bool saveAsCloudProject(SessionState& s, QWidget& host) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveProjectDialog(SessionState& s, QWidget& host) {
|
||||
SaveProjectDialog dialog(s.federation()->hasManifest(), &host);
|
||||
bool saveProjectDialog(SessionState& session, QWidget& host) {
|
||||
SaveProjectDialog dialog(session.federation()->hasManifest(), &host);
|
||||
if (dialog.exec() != QDialog::Accepted) return false;
|
||||
switch (dialog.selectedTarget()) {
|
||||
case SaveTarget::Local: return saveProject(s, host);
|
||||
case SaveTarget::LocalAs: return saveProjectAs(s, host);
|
||||
case SaveTarget::Cloud: return saveCloudProject(s, host);
|
||||
case SaveTarget::CloudAs: return saveAsCloudProject(s, host);
|
||||
case SaveTarget::Local: return saveProject(session, host);
|
||||
case SaveTarget::LocalAs: return saveProjectAs(session, host);
|
||||
case SaveTarget::Cloud: return saveCloudProject(session, host);
|
||||
case SaveTarget::CloudAs: return saveAsCloudProject(session, host);
|
||||
case SaveTarget::None: return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -32,35 +32,35 @@ namespace bonsaiviewer::modules::project::commands {
|
||||
// User-facing commands. Each owns its own dialogs and confirmations; each
|
||||
// emits exactly one notify() at the end (projectReset / projectOpened /
|
||||
// projectSaved) so views refresh once per command.
|
||||
bool newProject(SessionState& s, QWidget& host, ViewportWindow& vp);
|
||||
bool openProject(SessionState& s, QWidget& host, ViewportWindow& vp);
|
||||
bool newProject(SessionState& session, QWidget& host, ViewportWindow& viewport);
|
||||
bool openProject(SessionState& session, QWidget& host, ViewportWindow& viewport);
|
||||
// Open a specific .ifcfed by path, bypassing the file dialog. Used by the
|
||||
// "Open Recent" menu. Same dirty-check / load / cloud-resolve flow as
|
||||
// openProject; returns false if the load failed or was cancelled.
|
||||
bool openProjectPath(SessionState& s, QWidget& host, ViewportWindow& vp, const QString& path);
|
||||
bool openProjectPath(SessionState& session, QWidget& host, ViewportWindow& viewport, const QString& path);
|
||||
// Pick a connector, then call pull_ifcfed_interactive and open the resulting
|
||||
// .ifcfed as a fresh project. Non-local models in the loaded federation are
|
||||
// resolved asynchronously via pull_models.
|
||||
bool openCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp);
|
||||
bool openCloudProject(SessionState& session, QWidget& host, ViewportWindow& viewport);
|
||||
// pull_ifcfed using the current project's .ifcfed.manifest. Re-downloads
|
||||
// the .ifcfed from the same cloud target it came from (typically without
|
||||
// user interaction), then opens it like a fresh project — discarding any
|
||||
// local edits after the usual dirty-check prompt.
|
||||
bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp);
|
||||
bool saveProject(SessionState& s, QWidget& host);
|
||||
bool saveProjectAs(SessionState& s, QWidget& host);
|
||||
bool syncCloudProject(SessionState& session, QWidget& host, ViewportWindow& viewport);
|
||||
bool saveProject(SessionState& session, QWidget& host);
|
||||
bool saveProjectAs(SessionState& session, QWidget& host);
|
||||
// Push the current federation to the cloud target named in its manifest
|
||||
// (push_ifcfed). No user prompt for destination. Caller is responsible for
|
||||
// gating this on Federation::hasManifest.
|
||||
bool saveCloudProject(SessionState& s, QWidget& host);
|
||||
bool saveCloudProject(SessionState& session, QWidget& host);
|
||||
// Pick a connector and push the current federation to a fresh cloud target
|
||||
// (push_ifcfed_interactive). The connector returns a new path + manifest;
|
||||
// Federation repoints to that location.
|
||||
bool saveAsCloudProject(SessionState& s, QWidget& host);
|
||||
bool saveAsCloudProject(SessionState& session, QWidget& host);
|
||||
// Show the four-way Save dialog (Local / Save As Local / To Cloud / Save
|
||||
// As To Cloud) and dispatch to one of the above. This is what the "Save
|
||||
// Project" ribbon button is wired to.
|
||||
bool saveProjectDialog(SessionState& s, QWidget& host);
|
||||
bool saveProjectDialog(SessionState& session, QWidget& host);
|
||||
|
||||
} // namespace bonsaiviewer::modules::project::commands
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
|
||||
namespace bonsaiviewer::modules::viewport::commands {
|
||||
|
||||
void setHome(SessionState& session, ViewportWindow& vp) {
|
||||
auto camera = vp.cameraState();
|
||||
void setHome(SessionState& session, ViewportWindow& viewport) {
|
||||
auto camera = viewport.cameraState();
|
||||
Federation::HomeView home_view;
|
||||
home_view.target = camera.target;
|
||||
home_view.distance = camera.distance;
|
||||
@@ -37,66 +37,66 @@ void setHome(SessionState& session, ViewportWindow& vp) {
|
||||
session.setStatusMessage("Camera", "Home view updated");
|
||||
}
|
||||
|
||||
void goHome(SessionState& session, ViewportWindow& vp) {
|
||||
void goHome(SessionState& session, ViewportWindow& viewport) {
|
||||
Federation* federation = session.federation();
|
||||
if (!federation->hasHomeView()) {
|
||||
session.setStatusMessage("Camera", "No home view set for this project");
|
||||
return;
|
||||
}
|
||||
const auto& home_view = federation->homeView();
|
||||
vp.setCamera(
|
||||
viewport.setCamera(
|
||||
home_view.target.x(), home_view.target.y(), home_view.target.z(),
|
||||
home_view.distance, home_view.yaw, home_view.pitch);
|
||||
session.setStatusMessage("Camera", "Home view restored");
|
||||
}
|
||||
|
||||
void viewSelected(ViewportWindow& vp) {
|
||||
vp.focusOnSelectedObject();
|
||||
void viewSelected(ViewportWindow& viewport) {
|
||||
viewport.focusOnSelectedObject();
|
||||
}
|
||||
|
||||
void fly(SessionState& session, ViewportWindow& vp) {
|
||||
vp.requestActivate();
|
||||
vp.enterFpsMode();
|
||||
void fly(SessionState& session, ViewportWindow& viewport) {
|
||||
viewport.requestActivate();
|
||||
viewport.enterFpsMode();
|
||||
session.setStatusMessage("Mode", "Fly mode active");
|
||||
}
|
||||
|
||||
void toggleSection(SessionState& session, ViewportWindow& vp) {
|
||||
vp.toggleSectionTool();
|
||||
void toggleSection(SessionState& session, ViewportWindow& viewport) {
|
||||
viewport.toggleSectionTool();
|
||||
session.setStatusMessage("Section",
|
||||
vp.sectionToolActive() ? "Section tool active" : "Section tool off");
|
||||
viewport.sectionToolActive() ? "Section tool active" : "Section tool off");
|
||||
}
|
||||
|
||||
void clearSection(SessionState& session, ViewportWindow& vp) {
|
||||
vp.clearSectionPlanes();
|
||||
void clearSection(SessionState& session, ViewportWindow& viewport) {
|
||||
viewport.clearSectionPlanes();
|
||||
session.setStatusMessage("Section", "Section planes cleared");
|
||||
}
|
||||
|
||||
void toggleDistance(ViewportWindow& vp) {
|
||||
vp.toggleLengthTool();
|
||||
void toggleDistance(ViewportWindow& viewport) {
|
||||
viewport.toggleLengthTool();
|
||||
}
|
||||
|
||||
void toggleArea(ViewportWindow& vp) {
|
||||
vp.toggleAreaTool();
|
||||
void toggleArea(ViewportWindow& viewport) {
|
||||
viewport.toggleAreaTool();
|
||||
}
|
||||
|
||||
void toggleVolume(ViewportWindow& vp) {
|
||||
vp.toggleVolumeTool();
|
||||
void toggleVolume(ViewportWindow& viewport) {
|
||||
viewport.toggleVolumeTool();
|
||||
}
|
||||
|
||||
void hideSelected(ViewportWindow& vp) {
|
||||
vp.hideSelectedElements();
|
||||
void hideSelected(ViewportWindow& viewport) {
|
||||
viewport.hideSelectedElements();
|
||||
}
|
||||
|
||||
void isolateSelected(ViewportWindow& vp) {
|
||||
vp.isolateSelectedElements();
|
||||
void isolateSelected(ViewportWindow& viewport) {
|
||||
viewport.isolateSelectedElements();
|
||||
}
|
||||
|
||||
void showAll(ViewportWindow& vp) {
|
||||
vp.showAllElements();
|
||||
void showAll(ViewportWindow& viewport) {
|
||||
viewport.showAllElements();
|
||||
}
|
||||
|
||||
void invertVisibility(ViewportWindow& vp) {
|
||||
vp.invertElementVisibility();
|
||||
void invertVisibility(ViewportWindow& viewport) {
|
||||
viewport.invertElementVisibility();
|
||||
}
|
||||
|
||||
} // namespace bonsaiviewer::modules::viewport::commands
|
||||
|
||||
@@ -26,22 +26,22 @@ namespace bonsaiviewer { class SessionState; }
|
||||
|
||||
namespace bonsaiviewer::modules::viewport::commands {
|
||||
|
||||
void setHome(SessionState& session, ViewportWindow& vp);
|
||||
void goHome(SessionState& session, ViewportWindow& vp);
|
||||
void viewSelected(ViewportWindow& vp);
|
||||
void setHome(SessionState& session, ViewportWindow& viewport);
|
||||
void goHome(SessionState& session, ViewportWindow& viewport);
|
||||
void viewSelected(ViewportWindow& viewport);
|
||||
|
||||
void fly(SessionState& session, ViewportWindow& vp);
|
||||
void toggleSection(SessionState& session, ViewportWindow& vp);
|
||||
void clearSection(SessionState& session, ViewportWindow& vp);
|
||||
void fly(SessionState& session, ViewportWindow& viewport);
|
||||
void toggleSection(SessionState& session, ViewportWindow& viewport);
|
||||
void clearSection(SessionState& session, ViewportWindow& viewport);
|
||||
|
||||
void toggleDistance(ViewportWindow& vp);
|
||||
void toggleArea(ViewportWindow& vp);
|
||||
void toggleVolume(ViewportWindow& vp);
|
||||
void toggleDistance(ViewportWindow& viewport);
|
||||
void toggleArea(ViewportWindow& viewport);
|
||||
void toggleVolume(ViewportWindow& viewport);
|
||||
|
||||
void hideSelected(ViewportWindow& vp);
|
||||
void isolateSelected(ViewportWindow& vp);
|
||||
void showAll(ViewportWindow& vp);
|
||||
void invertVisibility(ViewportWindow& vp);
|
||||
void hideSelected(ViewportWindow& viewport);
|
||||
void isolateSelected(ViewportWindow& viewport);
|
||||
void showAll(ViewportWindow& viewport);
|
||||
void invertVisibility(ViewportWindow& viewport);
|
||||
|
||||
} // namespace bonsaiviewer::modules::viewport::commands
|
||||
|
||||
|
||||
@@ -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 mid) {
|
||||
connect(session_state_, &SessionState::modelGeometryReady, this, [this](uint32_t model_id) {
|
||||
if (modules::models::consumeFederatedFalseOriginGuess()) {
|
||||
guessFederatedFalseOriginFromFirstModel(mid);
|
||||
guessFederatedFalseOriginFromFirstModel(model_id);
|
||||
}
|
||||
refresh();
|
||||
});
|
||||
@@ -136,34 +136,34 @@ void ViewportView::refresh() {
|
||||
viewport_->setFederatedFalseOrigin(
|
||||
composeFederatedFalseOrigin(federation->federatedFalseOrigin(), federation->config()));
|
||||
|
||||
for (uint32_t mid : session_state_->modelIds()) {
|
||||
applyCoordinateOperation(mid);
|
||||
applyModelVisibility(mid);
|
||||
for (uint32_t model_id : session_state_->modelIds()) {
|
||||
applyCoordinateOperation(model_id);
|
||||
applyModelVisibility(model_id);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportView::applyCoordinateOperation(uint32_t mid) {
|
||||
void ViewportView::applyCoordinateOperation(uint32_t model_id) {
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity();
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(mid)) {
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(model_id)) {
|
||||
if (georef->has_coordinate_operation) {
|
||||
matrix = georef->coordinate_operation_meters;
|
||||
}
|
||||
}
|
||||
viewport_->setModelCoordinateOperation(mid, matrix);
|
||||
applyModelTransformation(mid);
|
||||
viewport_->setModelCoordinateOperation(model_id, matrix);
|
||||
applyModelTransformation(model_id);
|
||||
}
|
||||
|
||||
void ViewportView::applyModelTransformation(uint32_t mid) {
|
||||
void ViewportView::applyModelTransformation(uint32_t 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(mid);
|
||||
const QString fed_id = session_state_->fedIdForModelId(model_id);
|
||||
if (!fed_id.isEmpty()) {
|
||||
if (const Federation::Model* model = federation->findById(fed_id)) {
|
||||
ModelUnits units;
|
||||
Eigen::Matrix4d coordinate_operation = Eigen::Matrix4d::Identity();
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(mid)) {
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(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 mid) {
|
||||
model->model_transformation, federation->config(), units, coordinate_operation);
|
||||
}
|
||||
}
|
||||
viewport_->setModelTransformation(mid, matrix);
|
||||
viewport_->setModelTransformation(model_id, matrix);
|
||||
}
|
||||
|
||||
void ViewportView::applyModelVisibility(uint32_t mid) {
|
||||
void ViewportView::applyModelVisibility(uint32_t model_id) {
|
||||
Federation* federation = session_state_->federation();
|
||||
const QString fed_id = session_state_->fedIdForModelId(mid);
|
||||
const QString fed_id = session_state_->fedIdForModelId(model_id);
|
||||
if (fed_id.isEmpty()) return;
|
||||
|
||||
if (federation->isModelEffectivelyVisible(fed_id)) {
|
||||
viewport_->showModel(mid);
|
||||
viewport_->showModel(model_id);
|
||||
} else {
|
||||
viewport_->hideModel(mid);
|
||||
viewport_->hideModel(model_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ void ViewportView::applyModelVisibility(uint32_t mid) {
|
||||
// mutation here propagates through SessionState's federation relay
|
||||
// (federatedFalseOriginChanged → notifyFederationChanged) without
|
||||
// re-entering this function.
|
||||
void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t mid) {
|
||||
void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t model_id) {
|
||||
Federation* federation = session_state_->federation();
|
||||
if (!federation->filePath().isEmpty()) return;
|
||||
|
||||
@@ -218,10 +218,10 @@ void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t mid) {
|
||||
if (current.xyz != defaults.xyz || current.rz_deg != defaults.rz_deg) return;
|
||||
|
||||
Eigen::Vector3d first_geometry_point_m;
|
||||
if (!viewport_->firstGeometryPointWorldM(mid, first_geometry_point_m)) return;
|
||||
if (!viewport_->firstGeometryPointWorldM(model_id, first_geometry_point_m)) return;
|
||||
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
const ModelGeoref* georef = loader->modelGeoref(mid);
|
||||
const ModelGeoref* georef = loader->modelGeoref(model_id);
|
||||
if (georef == nullptr) return;
|
||||
|
||||
federation->setFederatedFalseOrigin(::guessFederatedFalseOrigin(
|
||||
@@ -236,42 +236,42 @@ void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t mid) {
|
||||
// (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(mid, 100.0f);
|
||||
viewport_->frameOnFederatedOrigin(model_id, 100.0f);
|
||||
}
|
||||
|
||||
void ViewportView::updateVolumeReadout() {
|
||||
if (viewport_->toolMode() != ViewportWindow::ToolMode::Volume) return;
|
||||
|
||||
const auto& sel = viewport_->selection().selectionIds();
|
||||
if (sel.empty()) {
|
||||
const auto& selection_ids = viewport_->selection().selectionIds();
|
||||
if (selection_ids.empty()) {
|
||||
viewport_->setHudText(std::string());
|
||||
viewport_->setOverlayLabels({});
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ids(sel.begin(), sel.end());
|
||||
const auto per_obj = volumesPerObject(*viewport_, ids);
|
||||
std::vector<uint32_t> object_ids(selection_ids.begin(), selection_ids.end());
|
||||
const auto volumes_by_object = volumesPerObject(*viewport_, object_ids);
|
||||
|
||||
double total = 0.0;
|
||||
std::vector<OverlayRenderer::Label> labels;
|
||||
labels.reserve(per_obj.size());
|
||||
for (const auto& [oid, v] : per_obj) {
|
||||
total += v;
|
||||
labels.reserve(volumes_by_object.size());
|
||||
for (const auto& [object_id, volume] : volumes_by_object) {
|
||||
total += volume;
|
||||
Eigen::Vector3f mn, mx;
|
||||
if (!viewport_->computeObjectAabb(oid, mn, mx)) continue;
|
||||
if (!viewport_->computeObjectAabb(object_id, mn, mx)) continue;
|
||||
OverlayRenderer::Label lbl;
|
||||
const Eigen::Vector3f c = (mn + mx) * 0.5f;
|
||||
lbl.world_pos[0] = c.x();
|
||||
lbl.world_pos[1] = c.y();
|
||||
lbl.world_pos[2] = c.z();
|
||||
lbl.text = QString::number(v, 'f', 4) + " m³";
|
||||
const Eigen::Vector3f center = (mn + mx) * 0.5f;
|
||||
lbl.world_pos[0] = center.x();
|
||||
lbl.world_pos[1] = center.y();
|
||||
lbl.world_pos[2] = center.z();
|
||||
lbl.text = QString::number(volume, 'f', 4) + " m³";
|
||||
labels.push_back(std::move(lbl));
|
||||
}
|
||||
|
||||
viewport_->setHudText(QString("Volume: %1 m³ (%2 object%3)")
|
||||
.arg(total, 0, 'f', 4)
|
||||
.arg(per_obj.size())
|
||||
.arg(per_obj.size() == 1 ? "" : "s").toStdString());
|
||||
.arg(volumes_by_object.size())
|
||||
.arg(volumes_by_object.size() == 1 ? "" : "s").toStdString());
|
||||
viewport_->setOverlayLabels(labels);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,10 +50,10 @@ public:
|
||||
|
||||
private:
|
||||
void refresh();
|
||||
void applyCoordinateOperation(uint32_t mid);
|
||||
void applyModelTransformation(uint32_t mid);
|
||||
void applyModelVisibility(uint32_t mid);
|
||||
void guessFederatedFalseOriginFromFirstModel(uint32_t mid);
|
||||
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 updateVolumeReadout();
|
||||
|
||||
bonsaiviewer::SessionState* session_state_ = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user