Merge branch 'ifcviewer-wgpu' of https://github.com/IfcOpenShell/IfcOpenShell into ifcviewer-wgpu

This commit is contained in:
Thomas Krijnen
2026-07-03 13:35:06 +02:00
137 changed files with 4451 additions and 2902 deletions
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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();
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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);
}
}
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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();
}
}
}
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+117 -117
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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();
}
+22 -22
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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);
}
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+9 -8
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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;
}
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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();
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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;
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+132 -132
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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;
+16 -16
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+88 -14
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -188,10 +188,11 @@ void SettingsDialog::setupUi() {
nav_preset_combo_->addItem("Blender (Orbit MMB, Pan Shift+MMB)");
nav_preset_combo_->addItem("Rhino (Orbit RMB, Pan Shift+RMB)");
nav_preset_combo_->addItem("Revit (Orbit Shift+MMB, Pan MMB)");
nav_preset_combo_->addItem("Web (Orbit LMB, Pan MMB, Select RMB)");
nav_preset_combo_->setToolTip(
"Mouse-button mapping for orbit and pan. Selection stays on "
"left mouse button for every preset, so click + box-select "
"always work.");
"Mouse-button mapping for orbit, pan, and selection. Selection is "
"on the left mouse button for Blender/Rhino/Revit and on the right "
"for Web; click + box-select use whichever the preset assigns.");
form->addRow("Preset", nav_preset_combo_);
section->addBodyWidget(body);
@@ -273,11 +274,84 @@ void SettingsDialog::setupUi() {
return tab;
};
auto make_about_tab = [this]() {
auto* tab = new QWidget(this);
auto* layout = new QVBoxLayout(tab);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(components::style::metrics::padding);
layout->setAlignment(Qt::AlignTop);
auto make_rich_label = [](const QString& text, QWidget* parent) {
const QString link_color = bonsaiviewer::ViewerSettings::instance().color("icon_accent_color");
auto* label = new QLabel(QString("<style>a { color: %1; }</style>%2").arg(link_color, text), parent);
label->setTextFormat(Qt::RichText);
label->setTextInteractionFlags(Qt::TextBrowserInteraction);
label->setOpenExternalLinks(true);
label->setWordWrap(true);
return label;
};
auto* about_section = new components::Section(
"About Bonsai Viewer", components::SectionHeaderMode::Visible, tab);
auto* about_body = new QWidget(about_section);
auto* about_layout = new QVBoxLayout(about_body);
about_layout->setContentsMargins(0, 0, 0, 0);
about_layout->setSpacing(8);
about_layout->addWidget(make_rich_label(
"<b>Bonsai Viewer</b><br>"
"A high performance IFC viewing and coordination tool for large OpenBIM projects.<br><br>"
"<a href=\"https://bonsaibim.org\">bonsaibim.org</a> | "
"<a href=\"https://github.com/IfcOpenShell/IfcOpenShell\">Source code</a>",
about_body));
about_section->addBodyWidget(about_body);
layout->addWidget(about_section);
auto* license_section = new components::Section("License", components::SectionHeaderMode::Visible, tab);
auto* license_body = new QWidget(license_section);
auto* license_layout = new QVBoxLayout(license_body);
license_layout->setContentsMargins(0, 0, 0, 0);
license_layout->setSpacing(8);
license_layout->addWidget(make_rich_label(
"Bonsai Viewer is free software: you can redistribute it and/or modify it under the terms of "
"the <b>GNU General Public License</b> as published by the Free Software Foundation, either "
"version 3.0 of the License, or (at your option) any later version.<br><br>"
"Bonsai Viewer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; "
"without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. "
"See the <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\">GNU General Public License</a> "
"for more details.",
license_body));
license_section->addBodyWidget(license_body);
layout->addWidget(license_section);
auto* dependencies_section = new components::Section(
"Third-party licenses", components::SectionHeaderMode::Visible, tab);
auto* dependencies_body = new QWidget(dependencies_section);
auto* dependencies_layout = new QVBoxLayout(dependencies_body);
dependencies_layout->setContentsMargins(0, 0, 0, 0);
dependencies_layout->setSpacing(8);
dependencies_layout->addWidget(make_rich_label(
"<ul>"
"<li>IfcOpenShell and IfcViewer - LGPL-3.0-or-later</li>"
"<li>Qt - LGPL-3.0-only, GPL-3.0-only, or commercial license depending on distribution</li>"
"<li>Open CASCADE Technology - LGPL-2.1-only with OCCT exception</li>"
"<li>Eigen - MPL-2.0</li>"
"<li>wgpu-native - MPL-2.0</li>"
"<li>zstd - BSD-3-Clause</li>"
"<li>meshoptimizer - MIT, when enabled</li>"
"</ul>"
"See the bundled dependency notices and source distributions for the complete license texts.",
dependencies_body));
dependencies_section->addBodyWidget(dependencies_body);
layout->addWidget(dependencies_section);
layout->addStretch(1);
return tab;
};
addTab("Interface", interface_tab);
addTab("Keybindings", make_placeholder_tab("Keybindings", "Shortcut presets and command bindings will live here."));
addTab("Graphics", graphics_tab);
addTab("Connectors", buildConnectorsTab());
addTab("About", make_placeholder_tab("About", "Version, credits, and environment information will live here."));
addTab("About", make_about_tab());
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
if (auto* ok = buttons->button(QDialogButtonBox::Ok)) {
@@ -390,20 +464,20 @@ QWidget* SettingsDialog::buildConnectorsTab() {
body_layout->addWidget(empty);
}
for (const auto& m : manifests) {
for (const auto& manifest : manifests) {
auto* row = new QWidget(body);
auto* row_layout = new QHBoxLayout(row);
row_layout->setContentsMargins(0, 0, 0, 0);
row_layout->setSpacing(8);
auto* name = new QLabel(m.name, row);
auto* name = new QLabel(manifest.name, row);
auto* version = new QLabel(
m.version.isEmpty() ? QString() : QString("v%1").arg(m.version), row);
manifest.version.isEmpty() ? QString() : QString("v%1").arg(manifest.version), row);
version->setProperty("textRole", "secondary");
auto* settings_button = new QPushButton("Settings…", row);
settings_button->setIcon(components::icons::makeSvgIcon(":/icons/settings.svg"));
const QString connector_id = m.id;
const QString connector_id = manifest.id;
connect(settings_button, &QPushButton::clicked, this,
[this, connector_id, settings_button]() {
if (!session_state_) return;
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+34 -34
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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
+19 -19
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+6 -6
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
+42 -42
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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) + "";
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) + "";
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);
}
+10 -10
View File
@@ -1,19 +1,19 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* This file is part of Bonsai. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* Bonsai is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* Bonsai is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
@@ -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;