mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 14:33:28 +00:00
Merge branch 'ifcviewer-wgpu' of https://github.com/IfcOpenShell/IfcOpenShell into ifcviewer-wgpu
This commit is contained in:
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
@@ -88,37 +88,35 @@ std::optional<express::Base> ElementRegistry::findEntity(uint32_t object_id) con
|
||||
}
|
||||
}
|
||||
|
||||
void ElementRegistry::onSidecarElementsReady(uint32_t /*mid*/,
|
||||
std::vector<PackedElementInfo> elements,
|
||||
void ElementRegistry::onSidecarElementsReady(uint32_t /*model_id*/,
|
||||
std::vector<ElementTableRecord> elements,
|
||||
std::string string_table) {
|
||||
auto str = [&](uint32_t offset, uint32_t length) -> QString {
|
||||
auto string_from_table = [&](uint32_t offset, uint32_t length) -> QString {
|
||||
if (length == 0 || offset + length > string_table.size()) return {};
|
||||
return QString::fromStdString(string_table.substr(offset, length));
|
||||
};
|
||||
|
||||
for (const auto& pe : elements) {
|
||||
for (const auto& packed_element : elements) {
|
||||
BasicElementInfo info;
|
||||
info.object_id = pe.object_id;
|
||||
info.model_id = pe.model_id;
|
||||
info.ifc_id = pe.ifc_id;
|
||||
info.parent_id = pe.parent_id;
|
||||
info.guid = str(pe.guid_offset, pe.guid_length);
|
||||
info.name = str(pe.name_offset, pe.name_length);
|
||||
info.type = str(pe.type_offset, pe.type_length);
|
||||
info.object_id = packed_element.object_id;
|
||||
info.model_id = packed_element.model_id;
|
||||
info.ifc_id = packed_element.ifc_id;
|
||||
info.guid = string_from_table(packed_element.guid_offset, packed_element.guid_length);
|
||||
info.name = string_from_table(packed_element.name_offset, packed_element.name_length);
|
||||
info.type = string_from_table(packed_element.type_offset, packed_element.type_length);
|
||||
elements_[info.object_id] = info;
|
||||
}
|
||||
}
|
||||
|
||||
void ElementRegistry::onStreamedElementsReady(uint32_t /*mid*/, std::vector<ElementInfo> elements) {
|
||||
for (const auto& e : elements) {
|
||||
void ElementRegistry::onStreamedElementsReady(uint32_t /*model_id*/, std::vector<ElementInfo> elements) {
|
||||
for (const auto& element : elements) {
|
||||
BasicElementInfo info;
|
||||
info.object_id = e.object_id;
|
||||
info.model_id = e.model_id;
|
||||
info.ifc_id = e.ifc_id;
|
||||
info.parent_id = e.parent_id;
|
||||
info.guid = QString::fromStdString(e.guid);
|
||||
info.name = QString::fromStdString(e.name);
|
||||
info.type = QString::fromStdString(e.type);
|
||||
info.object_id = element.object_id;
|
||||
info.model_id = element.model_id;
|
||||
info.ifc_id = element.ifc_id;
|
||||
info.guid = QString::fromStdString(element.guid);
|
||||
info.name = QString::fromStdString(element.name);
|
||||
info.type = QString::fromStdString(element.type);
|
||||
elements_[info.object_id] = info;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <vector>
|
||||
|
||||
class SceneLoader;
|
||||
struct PackedElementInfo;
|
||||
struct ElementTableRecord;
|
||||
struct ElementInfo;
|
||||
|
||||
namespace bonsaiviewer {
|
||||
@@ -39,7 +39,6 @@ struct BasicElementInfo {
|
||||
uint32_t object_id = 0;
|
||||
uint32_t model_id = 0;
|
||||
int ifc_id = 0;
|
||||
int parent_id = 0;
|
||||
QString guid;
|
||||
QString name;
|
||||
QString type;
|
||||
@@ -58,10 +57,10 @@ public:
|
||||
std::optional<express::Base> findEntity(uint32_t object_id) const;
|
||||
|
||||
private:
|
||||
void onSidecarElementsReady(uint32_t mid,
|
||||
std::vector<PackedElementInfo> elements,
|
||||
void onSidecarElementsReady(uint32_t model_id,
|
||||
std::vector<ElementTableRecord> elements,
|
||||
std::string string_table);
|
||||
void onStreamedElementsReady(uint32_t mid, std::vector<ElementInfo> elements);
|
||||
void onStreamedElementsReady(uint32_t model_id, std::vector<ElementInfo> elements);
|
||||
|
||||
SceneLoader* loader_ = nullptr;
|
||||
std::unordered_map<uint32_t, BasicElementInfo> elements_;
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
@@ -100,10 +100,10 @@ void MainWindow::setupChrome() {
|
||||
QMainWindow::AllowTabbedDocks |
|
||||
QMainWindow::GroupedDragging);
|
||||
|
||||
auto bind_shortcut = [this](const QKeySequence& sequence, auto fn) {
|
||||
auto bind_shortcut = [this](const QKeySequence& sequence, auto handler) {
|
||||
auto* shortcut = new QShortcut(sequence, this);
|
||||
shortcut->setContext(Qt::WindowShortcut);
|
||||
connect(shortcut, &QShortcut::activated, this, fn);
|
||||
connect(shortcut, &QShortcut::activated, this, handler);
|
||||
};
|
||||
bind_shortcut(QKeySequence("Ctrl+Shift+L"), [this]() {
|
||||
modules::viewport::commands::toggleDistance(*viewport_widget_->viewport());
|
||||
@@ -496,6 +496,20 @@ void MainWindow::setupStatus() {
|
||||
status_perf_label_->setVisible(show);
|
||||
if (!show) status_perf_label_->clear();
|
||||
});
|
||||
|
||||
// Nav mouse preset. The WGPU_NAV_PRESET env var is a dev override applied at
|
||||
// ViewportWindow construction; otherwise apply the persisted Settings choice
|
||||
// here, and re-apply live whenever the user changes it in the dialog.
|
||||
if (!std::getenv("WGPU_NAV_PRESET")) {
|
||||
if (auto* vp = viewport_widget_->viewport())
|
||||
vp->applyNavPreset(AppSettings::navPresetName(AppSettings::instance().navPreset()));
|
||||
}
|
||||
connect(&AppSettings::instance(), &AppSettings::navPresetChanged, this,
|
||||
[this](AppSettings::NavPreset preset) {
|
||||
if (auto* vp = viewport_widget_->viewport())
|
||||
vp->applyNavPreset(AppSettings::navPresetName(preset));
|
||||
});
|
||||
|
||||
connect(session_state_, &bonsaiviewer::SessionState::statusMessageChanged,
|
||||
this, [this](const QString& mode, const QString& detail) {
|
||||
status_mode_label_->setText(mode);
|
||||
@@ -530,17 +544,17 @@ void MainWindow::setupLoader() {
|
||||
});
|
||||
|
||||
connect(viewport_widget_->viewport(), &ViewportWindow::frameStatsUpdated, this,
|
||||
[this](const ViewportWindow::FrameStats& s) {
|
||||
[this](const ViewportWindow::FrameStats& stats) {
|
||||
if (!status_perf_label_->isVisible()) return;
|
||||
status_perf_label_->setText(
|
||||
QString("%1 fps | %2 ms | %3/%4 obj | %5/%6 tri | %7 draws")
|
||||
.arg(s.fps, 0, 'f', 1)
|
||||
.arg(s.frame_time_ms, 0, 'f', 1)
|
||||
.arg(s.visible_objects)
|
||||
.arg(s.total_objects)
|
||||
.arg(s.visible_triangles)
|
||||
.arg(s.total_triangles)
|
||||
.arg(s.gl_draw_calls));
|
||||
.arg(stats.fps, 0, 'f', 1)
|
||||
.arg(stats.frame_time_ms, 0, 'f', 1)
|
||||
.arg(stats.visible_objects)
|
||||
.arg(stats.total_objects)
|
||||
.arg(stats.visible_triangles)
|
||||
.arg(stats.total_triangles)
|
||||
.arg(stats.gl_draw_calls));
|
||||
});
|
||||
connect(viewport_widget_->viewport(), &ViewportWindow::objectPicked,
|
||||
this, [this](uint32_t object_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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
@@ -64,25 +64,25 @@ void SessionState::createLoader(ViewportWindow* viewport) {
|
||||
});
|
||||
connect(loader_, &SceneLoader::progressChanged, this, &SessionState::setProgress);
|
||||
connect(loader_, &SceneLoader::loadedFromSidecar, this,
|
||||
[this, format_elapsed](uint32_t mid, qint64 elapsed_ms) {
|
||||
[this, format_elapsed](uint32_t model_id, qint64 elapsed_ms) {
|
||||
setStatusMessage("Loaded",
|
||||
QString("%1 from cache in %2")
|
||||
.arg(loader_->displayName(mid))
|
||||
.arg(loader_->displayName(model_id))
|
||||
.arg(format_elapsed(elapsed_ms)));
|
||||
endProgress();
|
||||
emit modelGeometryReady(mid);
|
||||
emit modelGeometryReady(model_id);
|
||||
});
|
||||
connect(loader_, &SceneLoader::loadedFromStream, this,
|
||||
[this, format_elapsed](uint32_t mid, qint64 elapsed_ms) {
|
||||
[this, format_elapsed](uint32_t model_id, qint64 elapsed_ms) {
|
||||
setStatusMessage("Loaded",
|
||||
QString("%1 streamed in %2")
|
||||
.arg(loader_->displayName(mid))
|
||||
.arg(loader_->displayName(model_id))
|
||||
.arg(format_elapsed(elapsed_ms)));
|
||||
endProgress();
|
||||
emit modelGeometryReady(mid);
|
||||
emit modelGeometryReady(model_id);
|
||||
});
|
||||
connect(loader_, &SceneLoader::loadCancelled, this, [this](uint32_t mid) {
|
||||
setStatusMessage("Cancelled", loader_->displayName(mid));
|
||||
connect(loader_, &SceneLoader::loadCancelled, this, [this](uint32_t model_id) {
|
||||
setStatusMessage("Cancelled", loader_->displayName(model_id));
|
||||
endProgress();
|
||||
});
|
||||
connect(loader_, &SceneLoader::loadError, this,
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
.. This file was generated with the assistance of an AI coding tool.
|
||||
|
||||
Capturing debug output
|
||||
======================
|
||||
|
||||
When BonsaiViewer misbehaves — a crash, a black viewport, a hang — the
|
||||
single most useful thing you can hand off to a developer is a
|
||||
**stack trace at the point of failure**. This page covers how to capture
|
||||
one on Linux, macOS, and Windows.
|
||||
|
||||
Pre-flight: always run from a terminal
|
||||
--------------------------------------
|
||||
|
||||
GUI launches discard everything BonsaiViewer prints. Double-clicking
|
||||
``BonsaiViewer`` (or ``open -a`` on macOS, or a Start Menu shortcut on
|
||||
Windows) hides every ``qWarning``, ``[wgpu] …`` line, and runtime error
|
||||
message. **Always launch from a terminal first** when something is
|
||||
wrong:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Linux
|
||||
/path/to/BonsaiViewer
|
||||
|
||||
# macOS
|
||||
/Applications/BonsaiViewer.app/Contents/MacOS/BonsaiViewer
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
:: Windows
|
||||
"C:\path\to\BonsaiViewer.exe"
|
||||
|
||||
If the problem reproduces just by running from a terminal, copy the
|
||||
console output verbatim — it's usually enough on its own. If the
|
||||
process disappears without printing anything useful, you need a
|
||||
debugger; pick the section below for your OS.
|
||||
|
||||
Linux
|
||||
-----
|
||||
|
||||
GDB is the standard debugger and ships with every distribution.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
gdb --args /path/to/BonsaiViewer <any args you'd normally pass>
|
||||
(gdb) run
|
||||
# ... reproduce the crash ...
|
||||
(gdb) bt 30 # backtrace of the crashing thread, top 30 frames
|
||||
(gdb) thread apply all bt # all threads, full backtraces
|
||||
|
||||
If BonsaiViewer crashed *without* a debugger and you have core dumps
|
||||
enabled (``ulimit -c unlimited``), open the core file directly:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
gdb /path/to/BonsaiViewer /path/to/core.<pid>
|
||||
(gdb) bt 30
|
||||
|
||||
Most distributions hand crashes to ``systemd-coredump``; ``coredumpctl
|
||||
list`` shows recent dumps and ``coredumpctl debug BonsaiViewer`` jumps
|
||||
straight into GDB.
|
||||
|
||||
For runtime memory errors (use-after-free, buffer overflows) that don't
|
||||
crash immediately, run under ``valgrind`` or rebuild with ASAN
|
||||
(``-fsanitize=address``).
|
||||
|
||||
macOS
|
||||
-----
|
||||
|
||||
LLDB is bundled with the Xcode Command Line Tools (``xcode-select
|
||||
--install`` if you don't already have it).
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
lldb -- /Applications/BonsaiViewer.app/Contents/MacOS/BonsaiViewer
|
||||
(lldb) run
|
||||
# ... reproduce the crash ...
|
||||
(lldb) bt 30 # backtrace of the crashing thread
|
||||
(lldb) thread backtrace all # all threads
|
||||
|
||||
macOS also writes automatic crash reports (``.ips`` files since macOS
|
||||
12) at ``~/Library/Logs/DiagnosticReports/``. ``ls -lt
|
||||
~/Library/Logs/DiagnosticReports/ | head`` shows the most recent ones.
|
||||
Open the newest ``BonsaiViewer-*.ips`` in Console.app or any text
|
||||
editor — the JSON contains a full symbolicated stack.
|
||||
|
||||
Obj-C use-after-free aborts (``message sent to deallocated instance
|
||||
…``) often show up as a vague segfault with NSZombieEnabled disabled.
|
||||
Re-run with zombies turned on to get the real recipient of the dead
|
||||
message:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
NSZombieEnabled=YES lldb -- /Applications/BonsaiViewer.app/Contents/MacOS/BonsaiViewer
|
||||
(lldb) run
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
There's no preinstalled debugger on Windows; install **WinDbg Preview**
|
||||
from the Microsoft Store (free) or grab **ProcDump** as a single
|
||||
standalone ``.exe`` from
|
||||
https://learn.microsoft.com/en-us/sysinternals/downloads/procdump.
|
||||
|
||||
**Live debugging with WinDbg Preview**
|
||||
|
||||
1. Install ``WinDbg Preview`` from the Microsoft Store.
|
||||
2. ``File → Launch executable → Browse`` to ``BonsaiViewer.exe``.
|
||||
3. Press F5 to start. Reproduce the crash.
|
||||
4. When WinDbg breaks in, type ``kn30`` in the command bar at the
|
||||
bottom — that's the stack trace with frame numbers.
|
||||
|
||||
**Post-mortem dump with ProcDump (no install)**
|
||||
|
||||
Useful when the crash is hard to reach inside a debugger (e.g. it
|
||||
happens during a Bonsai-launched child process):
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
procdump -ma -e BonsaiViewer.exe -accepteula
|
||||
|
||||
In a second terminal, run ``BonsaiViewer.exe``. When it crashes,
|
||||
ProcDump writes ``BonsaiViewer.exe_YYMMDD_HHMMSS.dmp`` in the current
|
||||
directory. Open that ``.dmp`` in WinDbg Preview
|
||||
(``File → Open dump file``) and type ``kn30``.
|
||||
|
||||
**Getting symbolicated frames**
|
||||
|
||||
The shipping ``BonsaiViewer.exe`` doesn't include ``.pdb`` symbols. The
|
||||
stack will show DLL boundaries (which is often enough to narrow a bug
|
||||
down to ``BonsaiViewer.exe`` vs. ``wgpu_native.dll`` vs.
|
||||
``KERNELBASE.dll``), but function names will be addresses. To get
|
||||
symbol names:
|
||||
|
||||
1. Find the matching ``BonsaiViewer.pdb`` in the build artifacts of the
|
||||
workflow run that produced your ``.exe`` (under "Upload Build
|
||||
Logs").
|
||||
2. Drop it next to ``BonsaiViewer.exe``.
|
||||
3. Re-launch under WinDbg; it'll pick up the symbols automatically.
|
||||
|
||||
For runtime memory errors that don't crash immediately, the
|
||||
**Application Verifier** (in the Windows SDK) is the closest analogue
|
||||
to valgrind / ASAN.
|
||||
|
||||
What to send the developer
|
||||
--------------------------
|
||||
|
||||
A stack trace alone is usually enough. If the crash is reliable but
|
||||
needs specific input to trigger, also include:
|
||||
|
||||
1. The exact command line you launched with (env vars matter — e.g.
|
||||
``WGPU_PRESENT_MODE=immediate``).
|
||||
2. The model file (or its publicly-shareable equivalent) that triggers
|
||||
the crash, plus the smallest model that *doesn't* trigger it.
|
||||
3. The terminal output from before the crash (the ``[wgpu …]`` /
|
||||
``Sidecar metadata read: …`` / ``Streamer done: …`` lines tell us
|
||||
which subsystem was active when things went south).
|
||||
4. Your GPU + driver version and OS version. On Linux: ``glxinfo |
|
||||
grep "OpenGL renderer"`` and ``uname -r``. On macOS: Apple menu →
|
||||
About this Mac. On Windows: ``dxdiag``.
|
||||
@@ -1,277 +1,438 @@
|
||||
Viewport architecture
|
||||
=====================
|
||||
|
||||
This page documents the IFC viewer renderer that lives at
|
||||
``src/ifcviewer/`` and underpins both BonsaiViewer and the standalone
|
||||
``IfcViewerMinimal`` testbed.
|
||||
This page documents the WebGPU IFC viewer renderer in ``src/ifcviewer``. The
|
||||
same renderer is used by BonsaiViewer, IfcViewerMinimal, and the web build. It
|
||||
explains the path from an IFC model or ``.ifcview`` cache to rendered triangles,
|
||||
including the optimisation stages that decide what is loaded, what is resident
|
||||
in GPU memory, and what is drawn.
|
||||
|
||||
Consumers
|
||||
---------
|
||||
Overview
|
||||
--------
|
||||
|
||||
``src/ifcviewer/`` is a static library (``IfcViewer``) — it does not
|
||||
ship as an executable on its own. Two consumers link against it:
|
||||
|
||||
* **BonsaiViewer** (``src/bonsaiviewer/``) — full project shell with
|
||||
side panels, federation tree, settings dialog, connector picker,
|
||||
ribbon. The end-user product.
|
||||
* **IfcViewerMinimal** (``src/ifcviewer-minimal/``) — single-file
|
||||
testbed that opens an IFC, a ``.ifcview`` sidecar, or a federation
|
||||
``.ifcfed`` and shows just the viewport. Used for screenshot
|
||||
regression tests, benchmarks, and isolating renderer-only issues
|
||||
from the Bonsai Viewer shell.
|
||||
|
||||
Stack
|
||||
-----
|
||||
|
||||
* **wgpu-native v29** for rendering, fetched as a pre-built binary in
|
||||
``src/ifcviewer/CMakeLists.txt``. WebGPU API on top of Vulkan
|
||||
(Linux/Windows) or Metal (macOS, via the ``MetalSurface_mac``
|
||||
Cocoa bridge).
|
||||
* **Qt6** for windowing (a raw ``QWindow``, not ``QOpenGLWidget``)
|
||||
and the surface integration with the OS. Qt is also the source of
|
||||
``QSettings`` for persisted user preferences (``AppSettings``),
|
||||
the event loop driving render scheduling, and the timer/elapsed
|
||||
primitives used in instrumentation.
|
||||
* **IfcOpenShell C++ libs** (IfcParse / IfcGeom) for IFC parsing and
|
||||
geometry generation, plus **helpers** for the schema-agnostic
|
||||
helpers (``unit``, ``geolocation``, ``placement``).
|
||||
* **Eigen3** for 4×4 matrices and small linear algebra.
|
||||
* **meshoptimizer** at sidecar-build time only — decimates each
|
||||
unique mesh into an LOD1 slice. Not pulled at runtime.
|
||||
|
||||
Five core ideas
|
||||
---------------
|
||||
|
||||
The renderer is built around five decisions worth knowing about
|
||||
before reading the code:
|
||||
|
||||
**1. Unique-mesh GPU instancing.** IFC scenes are dominated by
|
||||
repeated geometry (identical doors, windows, studs, pipes, …). The
|
||||
``IfcGeom::Iterator`` surfaces representation identity, so each
|
||||
unique mesh is uploaded once into a per-model vertex buffer slice
|
||||
and every placement becomes a tiny ``InstanceCpu`` record
|
||||
(transform + ``object_id`` + optional colour override). On a 1 M-
|
||||
placement BIM scene this collapses tens of millions of duplicate
|
||||
vertices into a few hundred MB of unique meshes.
|
||||
|
||||
**2. Quantized 12-byte vertex format.** Each vertex is exactly 12
|
||||
bytes:
|
||||
The renderer is built around a small number of data transformations:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
offset 0 pos 3 × uint16 normalised → mix(mesh.aabb_min, mesh.aabb_max, t)
|
||||
offset 6 normal 2 × int8 normalised → [-1,1]; octahedral-decoded
|
||||
offset 8 color 4 × uint8 normalised → [0,1]; alpha is real (see "Transparency")
|
||||
IFC / .ifcview input
|
||||
-> SceneLoader
|
||||
-> GeometryStreamer or sidecar metadata reader
|
||||
-> SidecarData-shaped model metadata
|
||||
-> ViewportCore::applyCachedModel()
|
||||
-> per-model chunks, mesh table, instance table
|
||||
-> cullModelCpuCompute()
|
||||
-> visible_draws + prefix_sums
|
||||
-> driveStreamingLoads()
|
||||
-> applyStreamedChunk()
|
||||
-> WebGPU draw() calls
|
||||
-> WGSL expands visible draws into indexed triangles
|
||||
|
||||
The dequantisation basis is per-mesh, uploaded once in a
|
||||
``MeshGpu`` SSBO at binding 2; the vertex shader reads it via the
|
||||
mesh index that comes through the visible-draws table. ``int8``
|
||||
normals carry ~1.4° worst-case angular error, invisible for the
|
||||
overwhelmingly axis-aligned faces (walls, floors, slabs) BIM models
|
||||
produce.
|
||||
The important split is between **metadata** and **geometry bytes**. Metadata
|
||||
describes meshes, instances, bounds, transforms, element ids, and chunk layout.
|
||||
Geometry bytes are the quantised vertices and indices. A sidecar load reads
|
||||
metadata first and leaves geometry chunks non-resident until the camera needs
|
||||
them. A direct IFC load builds the same structures from the geometry iterator,
|
||||
then uploads all chunks at finalisation.
|
||||
|
||||
**3. Streaming chunked storage backed by a probed VRAM pool.** Per-
|
||||
model state isn't loaded all at once. The sidecar's vertex/index
|
||||
data is split into spatially-coherent chunks (Morton-sorted at bake
|
||||
time, packed greedily); ``ViewportWindow`` keeps each chunk's bytes
|
||||
resident in a single GPU buffer pool whose size is determined at
|
||||
startup by ``wgpuDevicePushErrorScope(OutOfMemory)`` probing. As
|
||||
the camera moves, chunks page in and out via the
|
||||
``StreamingThread`` background worker; the render thread never
|
||||
issues a blocking disk read.
|
||||
Main components
|
||||
---------------
|
||||
|
||||
**4. Sidecar cache as the fast path.** Loading an IFC the first
|
||||
time runs the ``IfcGeom::Iterator`` (expensive); the result is
|
||||
serialised to a ``.ifcview`` file (``SIDECAR_VERSION = 13``) next
|
||||
to the source. Subsequent opens go straight from disk to GPU
|
||||
buffers — no iteration, no geometry-engine cost. The sidecar
|
||||
format embeds chunked vertex sections with a byte-range table of
|
||||
contents so the streaming thread can fetch any chunk by ``pread``
|
||||
without scanning the file.
|
||||
``SceneLoader``
|
||||
Orchestrates model loading. It first tries to read ``.ifcview`` metadata
|
||||
when sidecar reads are enabled. If that fails, it starts the geometry
|
||||
streamer. It also keeps the ``ifcopenshell::file`` used by property and UI
|
||||
code.
|
||||
|
||||
**5. Event-driven rendering.** No render timer. Frames are
|
||||
scheduled only via ``QWindow::requestUpdate()`` when something
|
||||
actually changes — camera motion, streaming chunk arrival, hover,
|
||||
selection, settings edits. When the camera is idle, the cull pass
|
||||
short-circuits and the main thread blocks in the Qt event loop;
|
||||
the viewer costs zero CPU/GPU on a static scene.
|
||||
``GeometryStreamer``
|
||||
Runs ``IfcGeom::Iterator`` on a worker thread for raw IFC loads. It emits a
|
||||
``StreamedMesh`` once for each unique representation mesh and a
|
||||
``StreamedInstance`` for each placed occurrence.
|
||||
|
||||
Per-frame pipeline
|
||||
------------------
|
||||
``SidecarBuilder``
|
||||
Optionally mirrors the streamer's output while a raw IFC is loading. It
|
||||
writes a new ``.ifcview`` cache after the stream finishes, so the next open
|
||||
can use the sidecar fast path.
|
||||
|
||||
Each call to ``ViewportWindow::render()`` runs the same six phases:
|
||||
``ViewportWindow``
|
||||
The Qt-facing window wrapper. It owns UI/window integration and forwards
|
||||
renderer work to ``ViewportCore``.
|
||||
|
||||
``ViewportCore``
|
||||
The renderer core. It owns the WebGPU device-facing model state, pipelines,
|
||||
chunk residency, culling, streaming, picking, screenshot capture, and render
|
||||
loop.
|
||||
|
||||
``ModelGpuData``
|
||||
Per-model runtime state: mesh metadata, instances, chunks, GPU buffers,
|
||||
per-chunk visibility scratch, object lookup maps, and CPU-side caches used
|
||||
by tools.
|
||||
|
||||
``BufferPool``
|
||||
Sub-allocates chunk vertex and index bytes from one or more WebGPU buffers.
|
||||
Chunks enter and leave this pool as the streaming system pages geometry in
|
||||
and out.
|
||||
|
||||
``StreamingThread`` and web range loading
|
||||
Desktop sidecar streaming uses a background worker to read and decompress
|
||||
chunk frames. The web build uses asynchronous byte-range reads from a
|
||||
registered file or URL source.
|
||||
|
||||
Load path 1: sidecar hit
|
||||
------------------------
|
||||
|
||||
The fast path starts when ``SceneLoader`` finds a readable ``.ifcview`` cache.
|
||||
The reader validates the sidecar header, skips the compressed geometry section,
|
||||
and reads the metadata blocks.
|
||||
|
||||
For desktop loading, ``readSidecarMetadataOnly()`` returns a
|
||||
``StreamingSidecar`` containing:
|
||||
|
||||
- the sidecar file path
|
||||
- the geometry section offset
|
||||
- mesh records
|
||||
- instance records
|
||||
- georeferencing state
|
||||
- the baked chunk table of contents
|
||||
- element metadata
|
||||
|
||||
For web loading, ``ViewportCore::loadSidecarMetadataWeb()`` reads only the
|
||||
header and critical metadata before creating the model. It records the location
|
||||
of the deferred element metadata block and fetches that later only when UI code
|
||||
needs it.
|
||||
|
||||
``ViewportCore::applyCachedModel()`` then converts sidecar metadata into
|
||||
runtime model state:
|
||||
|
||||
1. It creates the chunk list. If the sidecar contains a baked
|
||||
``SidecarChunk`` table, that table is authoritative. Each chunk is a
|
||||
consecutive mesh range with compressed vertex and index frame locations. If
|
||||
the model came from a direct in-memory load and has no table, the chunk plan
|
||||
is derived from mesh centroids with Morton sorting and greedy packing.
|
||||
2. It creates per-chunk scratch buffers: ``visible_draws``, ``prefix_sums``,
|
||||
and a small uniform with draw counts.
|
||||
3. It uploads the per-mesh quantisation table, ``MeshGpu``.
|
||||
4. It uploads the per-instance GPU table, ``InstanceGpu``.
|
||||
5. It builds CPU lookup tables: object id to instance, instance to chunk,
|
||||
mesh-local offsets inside each chunk, chunk AABBs, and chunk instance lists.
|
||||
6. It leaves the actual chunk vertex and index bytes non-resident. They are
|
||||
fetched later by ``driveStreamingLoads()``.
|
||||
|
||||
At this point the viewer knows the model's structure and bounds, but a sidecar
|
||||
model has not necessarily uploaded any triangles yet.
|
||||
|
||||
Load path 2: direct IFC stream
|
||||
------------------------------
|
||||
|
||||
When no usable sidecar exists, ``SceneLoader`` starts ``GeometryStreamer``.
|
||||
Streamer signals are queued from the worker thread into the UI/render thread:
|
||||
|
||||
``StreamedMesh``
|
||||
A unique mesh in transfer form. Vertices are seven floats per vertex:
|
||||
position, normal, and packed colour.
|
||||
|
||||
``StreamedInstance``
|
||||
One placed occurrence of a mesh, with object id, colour override,
|
||||
placement transform, and world AABB.
|
||||
|
||||
During the stream, ``ViewportCore::uploadStreamedMesh()`` and
|
||||
``ViewportCore::uploadStreamedInstance()`` stage the data in a ``SidecarData``
|
||||
shape. Mesh vertices are converted to the renderer's packed 12-byte format,
|
||||
indices are stored, and instance records are accumulated. The viewport does not
|
||||
incrementally draw these raw streamed mesh records one by one.
|
||||
|
||||
When the streamer finishes, ``SceneLoader::onStreamerFinished()`` calls
|
||||
``ViewportCore::finalizeModel()``. Finalisation wraps the staged data in a
|
||||
``StreamingSidecar`` object and calls the same ``applyCachedModel()`` path used
|
||||
by sidecar loads. It then gathers the already-staged vertex and index bytes per
|
||||
chunk and calls ``applyStreamedChunk()`` for each chunk. Direct IFC loads
|
||||
therefore become resident after finalisation rather than streaming from disk.
|
||||
|
||||
If sidecar writes are enabled, ``SidecarBuilder`` finalises its mirrored copy,
|
||||
builds LOD1 where available, reorders geometry into streaming chunk order, and
|
||||
writes the ``.ifcview`` file for the next open.
|
||||
|
||||
Mesh and vertex representation
|
||||
------------------------------
|
||||
|
||||
IFC geometry is represented as unique meshes plus instances. Repeated IFC
|
||||
representations are uploaded once, and every placement becomes an instance that
|
||||
references a mesh.
|
||||
|
||||
The GPU vertex format is 12 bytes per vertex:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
offset 0 position uint16[3] quantised against the mesh local AABB
|
||||
offset 6 normal int8[2] octahedral encoded
|
||||
offset 8 colour uint8[4] RGBA
|
||||
|
||||
The mesh's ``MeshGpu`` record stores ``aabb_min`` and ``aabb_max``. The vertex
|
||||
shader reconstructs local positions by mapping the three 16-bit coordinates
|
||||
back into that local AABB. The instance transform then moves the vertex to
|
||||
world space.
|
||||
|
||||
Indices are ``uint32``. Each mesh has an LOD0 index range and may have an LOD1
|
||||
index range. LOD1 reuses the same vertices with a smaller index list generated
|
||||
at sidecar-build time.
|
||||
|
||||
Chunk layout and residency
|
||||
--------------------------
|
||||
|
||||
Chunks are the unit of streaming, residency, culling priority, and rendering
|
||||
bind groups. A chunk owns:
|
||||
|
||||
- a list of mesh ids
|
||||
- a list of instance ids
|
||||
- a world AABB
|
||||
- compressed sidecar frame locations, for sidecar-backed models
|
||||
- local vertex and index offsets for meshes inside the chunk
|
||||
- GPU pool slices when resident
|
||||
- cull output buffers
|
||||
|
||||
For sidecar-backed models, chunks start non-resident. They have metadata and
|
||||
small cull buffers, but no vertex/index pool slices. A chunk becomes resident
|
||||
when ``applyStreamedChunk()`` receives its decompressed vertex and index bytes.
|
||||
|
||||
``applyStreamedChunk()``:
|
||||
|
||||
1. Allocates vertex and index slices from ``BufferPool``.
|
||||
2. Uploads chunk bytes with ``wgpuQueueWriteBuffer``.
|
||||
3. Builds the chunk bind group.
|
||||
4. Marks the chunk resident.
|
||||
5. Scans alpha bytes so culling can route transparent meshes to the transparent
|
||||
pass.
|
||||
6. Computes mesh-local volumes and triangle CPU shadows used by measurement
|
||||
tools.
|
||||
|
||||
``unloadChunk()`` releases the chunk's bind group and returns its pool slices
|
||||
to ``BufferPool``. Metadata and cull scratch remain allocated so the chunk can
|
||||
be loaded again later.
|
||||
|
||||
Per-frame render pipeline
|
||||
-------------------------
|
||||
|
||||
Each frame in ``ViewportCore::render()`` follows this order:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
render():
|
||||
1. fpsIntegrate() ← advance fly-mode camera by wall-clock dt
|
||||
2. drainHizReadbacks() ← absorb HiZ readback completions
|
||||
3. uploadSelectionFlagsIfDirty()
|
||||
4. cullModelCpuCompute() ← parallel per-model frustum+contrib+HiZ+LOD
|
||||
cullModelCpuUpload() ← writeBuffer visible-draws + prefix-sums
|
||||
5. driveStreamingLoads() ← enqueue chunk fetches, apply completions
|
||||
6. encode passes:
|
||||
opaque main (depthWriteEnabled=True, no blend)
|
||||
transparent main (depthWriteEnabled=True, SrcAlpha/InvSrcAlpha blend)
|
||||
edge silhouette (samples depth buffer)
|
||||
overlay (HUD, labels, gizmos, measurements, marquee)
|
||||
wgpuSurfacePresent()
|
||||
drain completed HiZ readbacks
|
||||
upload selection flags if dirty
|
||||
acquire surface texture
|
||||
update frame uniforms
|
||||
cull visible instances and upload visible draw tables
|
||||
drive chunk streaming and eviction
|
||||
encode opaque main pass
|
||||
encode transparent main pass
|
||||
encode in-pass overlays
|
||||
encode edge silhouette pass
|
||||
encode HiZ resolve if enabled
|
||||
encode post-main overlays
|
||||
submit command buffer
|
||||
present surface
|
||||
|
||||
The two-pass alpha split (#6) routes each visible instance into
|
||||
either the opaque half or the transparent half of the per-chunk
|
||||
``visible_draws_scratch`` based on a per-mesh ``has_alpha`` flag +
|
||||
the instance's ``color_override_rgba8`` alpha byte. The transparent
|
||||
pass uses the same buffers, just with ``firstVertex`` offset to the
|
||||
opaque half's end. ``Alt+X`` forces every instance through the
|
||||
transparent pass via the ``xray_alpha_cap`` frame uniform, giving a
|
||||
free X-ray mode.
|
||||
Streaming runs after culling. That means a chunk that becomes resident during
|
||||
``driveStreamingLoads()`` is usually first visible on a later frame. The
|
||||
renderer requests a short burst of follow-up frames while streaming work is
|
||||
settling so an event-driven render loop does not stop before newly resident
|
||||
chunks are drawn.
|
||||
|
||||
Cull
|
||||
~~~~
|
||||
Culling
|
||||
-------
|
||||
|
||||
CPU-side, runs parallel per model via ``std::async``. The cascade
|
||||
per instance is:
|
||||
Culling is CPU-side and model-parallel on desktop. The web build currently uses
|
||||
the serial path because the WebAssembly build is not wired for pthread-backed
|
||||
``std::async``.
|
||||
|
||||
1. **Chunk-level frustum cull** — each chunk's AABB is tested first;
|
||||
off-frustum chunks skip every instance inside them in one shot.
|
||||
2. **Per-instance frustum cull** — for chunks that pass.
|
||||
3. **Contribution cull** — instances whose sphere projects below
|
||||
``viewport/min_pixel_radius`` (stationary) or
|
||||
``viewport/motion_min_pixel_radius`` (during motion) get dropped.
|
||||
4. **HiZ occlusion** — projected AABB tested against the previous
|
||||
frame's depth pyramid, when the VP matches. The pyramid is
|
||||
captured at end-of-frame, downsampled, read back to CPU, and
|
||||
queried with conservative all-fine-texels-agree semantics. Off
|
||||
by default; ``WGPU_HIZ=1`` re-enables.
|
||||
5. **LOD selection** — instances with sub-``viewport/lod1_pixel_threshold``
|
||||
projected size and an available LOD1 slice route through the
|
||||
LOD1 index range. Same vertex buffer, different ``firstIndex``.
|
||||
``cullModelCpuCompute()`` is chunk-driven:
|
||||
|
||||
Survivors are appended to the chunk's ``visible_draws_scratch`` and
|
||||
cumulative ``prefix_sums_scratch`` — the vertex shader uses a
|
||||
binary search on prefix_sums to translate ``vertex_index`` into a
|
||||
``(draw_index, vertex_in_draw)`` pair.
|
||||
1. Reset each chunk's per-frame scratch and counters.
|
||||
2. Frustum-test the chunk AABB. If the chunk is outside the frustum, every
|
||||
instance inside it is skipped.
|
||||
3. For frustum-passing chunks, process each instance in the chunk.
|
||||
4. Skip hidden object ids.
|
||||
5. Frustum-test the instance AABB.
|
||||
6. Estimate projected pixel radius and projected AABB area.
|
||||
7. Accumulate projected AABB area into the chunk's streaming priority.
|
||||
8. Apply contribution culling. Instances below the current pixel-radius
|
||||
threshold are dropped.
|
||||
9. Mark the chunk contribution-visible. This is the signal that
|
||||
``driveStreamingLoads()`` uses to decide whether a non-resident chunk is
|
||||
worth fetching.
|
||||
10. Optionally apply HiZ occlusion using the previous frame's depth pyramid.
|
||||
11. Choose LOD0 or LOD1 based on projected size and LOD1 availability.
|
||||
12. Classify the draw as opaque or transparent.
|
||||
13. Append a ``VisibleDrawGpu`` record and extend the chunk prefix sum.
|
||||
|
||||
Streaming
|
||||
~~~~~~~~~
|
||||
The contribution threshold is higher while the camera is moving when
|
||||
``viewport/motion_min_pixel_radius`` exceeds the still threshold. This drops
|
||||
more sub-pixel work during navigation and reduces cull and streaming pressure.
|
||||
|
||||
The buffer pool (``BufferPool``) is a free-list sub-allocator over
|
||||
one or more wgpu buffers, sized at startup by probing for
|
||||
``WGPUErrorType_OutOfMemory`` on a series of growing allocations.
|
||||
``StreamingThread`` is one worker thread that owns disk I/O:
|
||||
``driveStreamingLoads()`` posts requests for the highest-priority
|
||||
non-resident chunks and drains completions. The pool's eviction
|
||||
policy is "evict the lowest-priority resident chunk whose priority
|
||||
is below ``candidate_priority / EVICT_PRIORITY_RATIO``" — strict
|
||||
enough to prevent thrash, lax enough that a single panning frame
|
||||
doesn't refuse the move.
|
||||
HiZ occlusion is optional. When enabled, the renderer uses a depth pyramid from
|
||||
a previous compatible view-projection matrix. Contribution culling runs before
|
||||
HiZ because it is much cheaper and reduces the number of HiZ tests.
|
||||
|
||||
Priorities come from a per-chunk screen-space score computed during
|
||||
cull (chunk AABB projected to a screen-rect area). Spatial sort at
|
||||
bake time means screen-adjacent chunks are byte-adjacent on disk,
|
||||
so the typical fetch is a single ``pread`` range, not scatter-
|
||||
gather.
|
||||
Visible draw tables
|
||||
-------------------
|
||||
|
||||
Overlay / picking / section cuts
|
||||
--------------------------------
|
||||
Culling does not issue draw calls directly. It writes compact per-chunk tables:
|
||||
|
||||
* ``OverlayRenderer`` runs after the main passes. Bundles the HUD
|
||||
text, world-anchored labels (measurement readouts), the
|
||||
marquee-select rect, the corner axis gizmo, the orbit pivot
|
||||
indicator, and section-plane outline rendering. Has its own
|
||||
pipelines with the usual ``SrcAlpha/OneMinusSrcAlpha`` blend.
|
||||
* **Picking** is a second render pass with a dedicated pipeline
|
||||
writing object IDs into an ``R32UInt`` framebuffer. Clicking
|
||||
triggers a ``wgpuQueueOnSubmittedWorkDone`` + ``mapAsync``
|
||||
readback of one pixel. No CPU-side raycasting.
|
||||
* **Section cuts** (the ``K`` tool) push up to six clipping planes
|
||||
into the frame uniform; ``is_section_clipped()`` in WGSL discards
|
||||
fragments on the positive side. The plane gizmo and visible
|
||||
cross-section outline come from OverlayRenderer.
|
||||
``visible_draws``
|
||||
One record per visible instance draw. It stores mesh id, instance index,
|
||||
first index, and base vertex.
|
||||
|
||||
Federation
|
||||
----------
|
||||
``prefix_sums``
|
||||
A monotonic array that maps a flat vertex id inside one chunk draw call to
|
||||
a visible draw record.
|
||||
|
||||
``Federation`` (in ``IfcViewer``, not Bonsai) is the multi-model
|
||||
data model. Each model in a federation has a fed_id, optional
|
||||
group membership, an explicit ``ModelTransformation``, a
|
||||
``CoordinateOperation`` (from the IFC's IfcMapConversion), and a
|
||||
visibility flag. The viewport composes a single
|
||||
``transform_meters`` per instance as:
|
||||
``per_chunk_uniform``
|
||||
Four counters: total visible draws, total visible vertices, opaque visible
|
||||
vertices, and opaque visible draws.
|
||||
|
||||
After culling, ``cullModelCpuUpload()`` uploads those arrays to the chunk's GPU
|
||||
buffers. Chunks with zero visible draws have their uniform zeroed and are
|
||||
skipped by the render pass.
|
||||
|
||||
Triangle rendering
|
||||
------------------
|
||||
|
||||
The main render uses one WebGPU ``draw()`` per resident visible chunk, not one
|
||||
draw per IFC element. The CPU has already compacted the visible instances into
|
||||
the chunk's ``visible_draws`` table.
|
||||
|
||||
The vertex shader receives a flat ``vertex_index`` from the draw call. It:
|
||||
|
||||
1. Binary-searches ``prefix_sums`` to find the visible draw record.
|
||||
2. Computes the local vertex within that draw.
|
||||
3. Reads the mesh-local index from the chunk index buffer.
|
||||
4. Adds the draw's base vertex to get the packed vertex record.
|
||||
5. Decodes quantised position, octahedral normal, and colour.
|
||||
6. Reads the instance transform and object id.
|
||||
7. Transforms the local vertex to world and clip space.
|
||||
|
||||
The fragment shader applies lighting, selection/active-object highlighting,
|
||||
x-ray alpha cap, section clipping, and transparency output. Section clipping is
|
||||
also used by picking so selected section planes match what the user sees.
|
||||
|
||||
Opaque and transparent passes
|
||||
-----------------------------
|
||||
|
||||
Culling partitions visible draws into opaque and transparent halves. Opaque
|
||||
draws are written first in each chunk's arrays; transparent draws are appended
|
||||
after them.
|
||||
|
||||
The main pass then renders:
|
||||
|
||||
1. Opaque pipeline with depth writes and no blending, drawing
|
||||
``opaque_visible_vertices``.
|
||||
2. Transparent pipeline with blending, drawing the remaining vertices starting
|
||||
at ``opaque_visible_vertices``.
|
||||
|
||||
Transparency is chosen from the baked mesh alpha flag, an instance colour
|
||||
override alpha, or forced x-ray mode. The transparent pass uses the same chunk
|
||||
bind group and buffers as the opaque pass.
|
||||
|
||||
Streaming and eviction
|
||||
----------------------
|
||||
|
||||
``driveStreamingLoads()`` decides which non-resident sidecar chunks should be
|
||||
loaded and which resident chunks may be evicted.
|
||||
|
||||
Each frame it:
|
||||
|
||||
1. Drains completed desktop worker reads and applies successful chunks.
|
||||
2. Updates resident chunk visibility history.
|
||||
3. Builds candidates from non-resident chunks that passed contribution
|
||||
visibility in the current cull.
|
||||
4. Sorts candidates by current projected-area priority.
|
||||
5. Loads up to a fixed number of chunks per frame.
|
||||
6. Ensures the buffer pool can fit the chunk before issuing the load.
|
||||
7. Evicts lower-value resident chunks when the pool is full.
|
||||
|
||||
The first eviction pass drops the least-recently-visible resident chunk that
|
||||
was not visible this frame. If every resident chunk is currently visible, a
|
||||
second pass may evict the lowest-priority resident chunk, but only when the
|
||||
candidate has meaningfully higher priority. This hysteresis prevents simple
|
||||
swap loops while still allowing a saturated pool to follow the camera.
|
||||
|
||||
Desktop sidecar chunks are read by ``StreamingThread``. Each request contains
|
||||
the sidecar path, geometry section offset, compressed vertex frame location,
|
||||
and compressed index frame location. The worker reads and decompresses those
|
||||
frames, then ``driveStreamingLoads()`` applies the result on the render thread.
|
||||
|
||||
Web sidecar chunks are fetched asynchronously by byte range. Vertex and index
|
||||
frames are requested separately and joined before decompression and upload.
|
||||
The web path caps concurrent chunk downloads so the highest-priority chunks can
|
||||
arrive and render progressively instead of sharing bandwidth across the entire
|
||||
visible set.
|
||||
|
||||
Sidecar format relationship
|
||||
---------------------------
|
||||
|
||||
The ``.ifcview`` file exists to feed this renderer. Its critical metadata block
|
||||
contains enough data to build ``ModelGpuData`` without reading geometry:
|
||||
|
||||
- mesh table
|
||||
- instance table
|
||||
- georeferencing cache
|
||||
- chunk table of contents
|
||||
|
||||
The geometry section contains one compressed vertex frame and one compressed
|
||||
index frame per chunk. The chunk table lets the streaming system jump directly
|
||||
to the frames for a visible chunk without scanning the file or reading
|
||||
unrelated geometry.
|
||||
|
||||
The sidecar version documented by the current code is version 16.
|
||||
|
||||
Picking, overlays, and tools
|
||||
----------------------------
|
||||
|
||||
Picking is a GPU pass, not CPU raycasting. The pick pipeline uses the same
|
||||
``visible_draws`` and chunk buffers as the main pass and writes object ids to
|
||||
an integer texture. A click reads back the selected pixel. The section tool also
|
||||
uses a normal and world-position pick target so it can create planes from the
|
||||
actual rendered surface.
|
||||
|
||||
Overlay rendering is host-provided. ``ViewportCore`` encodes the shared section
|
||||
gizmo and calls host hooks for in-pass and post-main overlays. The Qt host
|
||||
forwards those hooks to the desktop overlay renderer; the web host can no-op or
|
||||
provide its own implementation.
|
||||
|
||||
Edges are drawn after the main pass using the depth buffer. HiZ resolve, when
|
||||
enabled, also happens after the main pass so a later frame can use the captured
|
||||
depth pyramid for occlusion culling.
|
||||
|
||||
Federation and transforms
|
||||
-------------------------
|
||||
|
||||
Federated models compose several transforms before upload:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
federated_false_origin · model_transformation · coord_op · placement
|
||||
federated_false_origin
|
||||
* model_transformation
|
||||
* coordinate_operation
|
||||
* placement_transformation
|
||||
|
||||
``federated_false_origin`` is shared across all models in the
|
||||
session and cancels the bulk of the big surveyor coordinates BIM
|
||||
files carry, so float32 vertex math stays well within precision.
|
||||
The first-added model's first geometry point auto-seeds the false
|
||||
origin via ``ViewportView::guessFederatedFalseOriginFromFirstModel``
|
||||
when the add-model command arms the guess; see that function for
|
||||
why the arm/consume mechanism exists (it replaced a stack-
|
||||
overflowing slot-emit-in-slot recursion).
|
||||
The sidecar stores double-precision placement transforms so large IFC
|
||||
coordinates can be combined with coordinate operation and false-origin matrices
|
||||
before being narrowed to the float transform used by the GPU. This protects
|
||||
rendering precision for survey-coordinate models.
|
||||
|
||||
Files map
|
||||
---------
|
||||
What to remember
|
||||
----------------
|
||||
|
||||
Render core (the wgpu side):
|
||||
|
||||
* ``ViewportWindow.{h,cpp}`` — the main render window. Owns the
|
||||
wgpu surface, device, queue, all pipelines, the per-model
|
||||
``ModelGpuData``, the cull pass, the frame uniforms. ~7500 lines;
|
||||
the bulk of the renderer.
|
||||
* ``ModelGpuData.h`` — per-model GPU state (chunks, instances,
|
||||
meshes, pool slices, alpha flags, scratch buffers used by cull).
|
||||
* ``InstancedGeometry.h`` — wire structs shared by the streamer
|
||||
and the viewport (``MeshInfo``, ``InstanceCpu``, ``InstanceGpu``,
|
||||
``MeshChunk``, ``InstanceChunk``, vertex layout constants).
|
||||
* ``VertexQuantization.h`` — pack/unpack helpers for the 12-byte
|
||||
vertex format.
|
||||
* ``BufferPool.{h,cpp}`` — VRAM sub-allocator with multi-sub-buffer
|
||||
growth.
|
||||
* ``StreamingThread.{h,cpp}`` — background ``pread`` worker.
|
||||
* ``StreamingLoader.{h,cpp}`` — chunk-priority planner driving the
|
||||
thread; owns the eviction policy and the residency map.
|
||||
* ``OverlayRenderer.{h,cpp}`` — HUD / labels / gizmos / section-cut
|
||||
outline / marquee. Separate pipelines + WGSL shader strings.
|
||||
* ``MetalSurface_mac.{h,mm}`` — Cocoa bridge for the CAMetalLayer
|
||||
surface attach on macOS. Compiled only on Apple via the
|
||||
``OBJCXX`` language CMake enables.
|
||||
* ``SelectionState.h``, ``VisibilityState.h`` — pure CPU state
|
||||
machines for the selection set and the hidden-objects set;
|
||||
header-only; covered by ``ifcviewer/tests/test_selection.cpp``
|
||||
and ``test_visibility.cpp``.
|
||||
* ``AreaMeasurement.{h,cpp}``, ``LengthMeasurement.{h,cpp}`` — the
|
||||
measurement tools (triangle-area accumulation; world-space
|
||||
polyline distance).
|
||||
|
||||
IFC ingestion and on-disk format:
|
||||
|
||||
* ``GeometryStreamer.{h,cpp}`` — wraps ``IfcGeom::Iterator`` on a
|
||||
background thread; emits ``MeshChunk`` (unique geometry) and
|
||||
``InstanceChunk`` (one per placement) to ``SceneLoader``.
|
||||
* ``SceneLoader.{h,cpp}`` — orchestrates load. Owns the per-model
|
||||
``ifcopenshell::file`` for property lookup; detects sidecar
|
||||
presence; serialises queue of pending loads.
|
||||
* ``SidecarCache.{h,cpp}`` — binary read/write of ``.ifcview``
|
||||
files. Magic ``IFVW``, version 13 (see the per-version comment
|
||||
block at the top of the header for the schema evolution).
|
||||
* ``SidecarBuilder.{h,cpp}`` — live-load sidecar writer. Accumulates
|
||||
the data as the streamer emits it, writes the sidecar after
|
||||
``finalizeModel``.
|
||||
* ``LodBuilder.{h,cpp}`` — meshoptimizer wrapper that builds each
|
||||
mesh's LOD1 index slice at sidecar-bake time. Gated behind
|
||||
``WITH_MESH_OPTIMIZER``; otherwise the LOD1 slot is left empty
|
||||
and the renderer always uses LOD0.
|
||||
|
||||
Federation + settings:
|
||||
|
||||
* ``Federation.{h,cpp}`` — multi-model session: groups, model
|
||||
transforms, coordinate operations, the federated false origin.
|
||||
Persisted as ``.ifcfed``. Covered by
|
||||
``ifcviewer/tests/test_federation.cpp``.
|
||||
* ``AppSettings.{h,cpp}`` — ``QSettings``-backed preferences:
|
||||
``viewport/min_pixel_radius``, ``viewport/lod1_pixel_threshold``,
|
||||
``viewport/hiz_enabled``, ``viewport/nav_preset``, etc. See
|
||||
:doc:`env-vars` for the full key list.
|
||||
- Raw IFC streaming produces mesh and instance chunks, but the WebGPU viewport
|
||||
does not draw each emitted mesh immediately. It stages them, finalises a model,
|
||||
then uses the same chunk path as sidecar loads.
|
||||
- Sidecar loads create a renderable model from metadata first. Geometry becomes
|
||||
visible only as chunks become resident.
|
||||
- Culling produces compact visible draw tables per chunk.
|
||||
- The renderer draws one flat vertex stream per visible chunk. The shader
|
||||
expands that stream back into indexed, instanced triangles using
|
||||
``visible_draws`` and ``prefix_sums``.
|
||||
- Streaming priority comes from the current view. Chunks are fetched only when
|
||||
their instances are large enough on screen to be worth drawing.
|
||||
- The buffer pool is finite, so residency is dynamic. The renderer keeps the
|
||||
chunks that matter most for the current view and evicts lower-value chunks
|
||||
under memory pressure.
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
@@ -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;
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
@@ -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;
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
@@ -67,9 +67,9 @@ ViewportView::ViewportView(bonsaiviewer::SessionState* session_state,
|
||||
// first geometry-ready consumes the arm). refresh() stays terminal —
|
||||
// any federation mutation from the guess propagates through
|
||||
// SessionState's federatedFalseOriginChanged relay.
|
||||
connect(session_state_, &SessionState::modelGeometryReady, this, [this](uint32_t mid) {
|
||||
connect(session_state_, &SessionState::modelGeometryReady, this, [this](uint32_t model_id) {
|
||||
if (modules::models::consumeFederatedFalseOriginGuess()) {
|
||||
guessFederatedFalseOriginFromFirstModel(mid);
|
||||
guessFederatedFalseOriginFromFirstModel(model_id);
|
||||
}
|
||||
refresh();
|
||||
});
|
||||
@@ -136,34 +136,34 @@ void ViewportView::refresh() {
|
||||
viewport_->setFederatedFalseOrigin(
|
||||
composeFederatedFalseOrigin(federation->federatedFalseOrigin(), federation->config()));
|
||||
|
||||
for (uint32_t mid : session_state_->modelIds()) {
|
||||
applyCoordinateOperation(mid);
|
||||
applyModelVisibility(mid);
|
||||
for (uint32_t model_id : session_state_->modelIds()) {
|
||||
applyCoordinateOperation(model_id);
|
||||
applyModelVisibility(model_id);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportView::applyCoordinateOperation(uint32_t mid) {
|
||||
void ViewportView::applyCoordinateOperation(uint32_t model_id) {
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity();
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(mid)) {
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(model_id)) {
|
||||
if (georef->has_coordinate_operation) {
|
||||
matrix = georef->coordinate_operation_meters;
|
||||
}
|
||||
}
|
||||
viewport_->setModelCoordinateOperation(mid, matrix);
|
||||
applyModelTransformation(mid);
|
||||
viewport_->setModelCoordinateOperation(model_id, matrix);
|
||||
applyModelTransformation(model_id);
|
||||
}
|
||||
|
||||
void ViewportView::applyModelTransformation(uint32_t mid) {
|
||||
void ViewportView::applyModelTransformation(uint32_t model_id) {
|
||||
Federation* federation = session_state_->federation();
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity();
|
||||
const QString fed_id = session_state_->fedIdForModelId(mid);
|
||||
const QString fed_id = session_state_->fedIdForModelId(model_id);
|
||||
if (!fed_id.isEmpty()) {
|
||||
if (const Federation::Model* model = federation->findById(fed_id)) {
|
||||
ModelUnits units;
|
||||
Eigen::Matrix4d coordinate_operation = Eigen::Matrix4d::Identity();
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(mid)) {
|
||||
if (const ModelGeoref* georef = loader->modelGeoref(model_id)) {
|
||||
units = georef->units;
|
||||
if (georef->has_coordinate_operation) {
|
||||
coordinate_operation = georef->coordinate_operation_meters;
|
||||
@@ -173,18 +173,18 @@ void ViewportView::applyModelTransformation(uint32_t mid) {
|
||||
model->model_transformation, federation->config(), units, coordinate_operation);
|
||||
}
|
||||
}
|
||||
viewport_->setModelTransformation(mid, matrix);
|
||||
viewport_->setModelTransformation(model_id, matrix);
|
||||
}
|
||||
|
||||
void ViewportView::applyModelVisibility(uint32_t mid) {
|
||||
void ViewportView::applyModelVisibility(uint32_t model_id) {
|
||||
Federation* federation = session_state_->federation();
|
||||
const QString fed_id = session_state_->fedIdForModelId(mid);
|
||||
const QString fed_id = session_state_->fedIdForModelId(model_id);
|
||||
if (fed_id.isEmpty()) return;
|
||||
|
||||
if (federation->isModelEffectivelyVisible(fed_id)) {
|
||||
viewport_->showModel(mid);
|
||||
viewport_->showModel(model_id);
|
||||
} else {
|
||||
viewport_->hideModel(mid);
|
||||
viewport_->hideModel(model_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ void ViewportView::applyModelVisibility(uint32_t mid) {
|
||||
// mutation here propagates through SessionState's federation relay
|
||||
// (federatedFalseOriginChanged → notifyFederationChanged) without
|
||||
// re-entering this function.
|
||||
void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t mid) {
|
||||
void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t model_id) {
|
||||
Federation* federation = session_state_->federation();
|
||||
if (!federation->filePath().isEmpty()) return;
|
||||
|
||||
@@ -218,10 +218,10 @@ void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t mid) {
|
||||
if (current.xyz != defaults.xyz || current.rz_deg != defaults.rz_deg) return;
|
||||
|
||||
Eigen::Vector3d first_geometry_point_m;
|
||||
if (!viewport_->firstGeometryPointWorldM(mid, first_geometry_point_m)) return;
|
||||
if (!viewport_->firstGeometryPointWorldM(model_id, first_geometry_point_m)) return;
|
||||
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
const ModelGeoref* georef = loader->modelGeoref(mid);
|
||||
const ModelGeoref* georef = loader->modelGeoref(model_id);
|
||||
if (georef == nullptr) return;
|
||||
|
||||
federation->setFederatedFalseOrigin(::guessFederatedFalseOrigin(
|
||||
@@ -236,42 +236,42 @@ void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t mid) {
|
||||
// (0,0,0) — the federated false origin in render space — capped at
|
||||
// 100 m so a model with crazy-coord geometry can't pull the camera
|
||||
// back into nothing.
|
||||
viewport_->frameOnFederatedOrigin(mid, 100.0f);
|
||||
viewport_->frameOnFederatedOrigin(model_id, 100.0f);
|
||||
}
|
||||
|
||||
void ViewportView::updateVolumeReadout() {
|
||||
if (viewport_->toolMode() != ViewportWindow::ToolMode::Volume) return;
|
||||
|
||||
const auto& sel = viewport_->selection().selectionIds();
|
||||
if (sel.empty()) {
|
||||
const auto& selection_ids = viewport_->selection().selectionIds();
|
||||
if (selection_ids.empty()) {
|
||||
viewport_->setHudText(std::string());
|
||||
viewport_->setOverlayLabels({});
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ids(sel.begin(), sel.end());
|
||||
const auto per_obj = volumesPerObject(*viewport_, ids);
|
||||
std::vector<uint32_t> object_ids(selection_ids.begin(), selection_ids.end());
|
||||
const auto volumes_by_object = volumesPerObject(*viewport_, object_ids);
|
||||
|
||||
double total = 0.0;
|
||||
std::vector<OverlayRenderer::Label> labels;
|
||||
labels.reserve(per_obj.size());
|
||||
for (const auto& [oid, v] : per_obj) {
|
||||
total += v;
|
||||
labels.reserve(volumes_by_object.size());
|
||||
for (const auto& [object_id, volume] : volumes_by_object) {
|
||||
total += volume;
|
||||
Eigen::Vector3f mn, mx;
|
||||
if (!viewport_->computeObjectAabb(oid, mn, mx)) continue;
|
||||
if (!viewport_->computeObjectAabb(object_id, mn, mx)) continue;
|
||||
OverlayRenderer::Label lbl;
|
||||
const Eigen::Vector3f c = (mn + mx) * 0.5f;
|
||||
lbl.world_pos[0] = c.x();
|
||||
lbl.world_pos[1] = c.y();
|
||||
lbl.world_pos[2] = c.z();
|
||||
lbl.text = QString::number(v, 'f', 4) + " m³";
|
||||
const Eigen::Vector3f center = (mn + mx) * 0.5f;
|
||||
lbl.world_pos[0] = center.x();
|
||||
lbl.world_pos[1] = center.y();
|
||||
lbl.world_pos[2] = center.z();
|
||||
lbl.text = QString::number(volume, 'f', 4) + " m³";
|
||||
labels.push_back(std::move(lbl));
|
||||
}
|
||||
|
||||
viewport_->setHudText(QString("Volume: %1 m³ (%2 object%3)")
|
||||
.arg(total, 0, 'f', 4)
|
||||
.arg(per_obj.size())
|
||||
.arg(per_obj.size() == 1 ? "" : "s").toStdString());
|
||||
.arg(volumes_by_object.size())
|
||||
.arg(volumes_by_object.size() == 1 ? "" : "s").toStdString());
|
||||
viewport_->setOverlayLabels(labels);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user