diff --git a/.github/workflows/ci-wgpu-mac.yml b/.github/workflows/ci-wgpu-mac.yml
deleted file mode 100644
index c868f76b83..0000000000
--- a/.github/workflows/ci-wgpu-mac.yml
+++ /dev/null
@@ -1,123 +0,0 @@
-name: wgpu sanity (macOS)
-
-# Lightweight compile-only check for the wgpu backend on macOS. Skips the
-# IfcViewerWgpuMinimal executable (its surface creation has no Metal path
-# yet — see task #32) and just confirms IfcViewerWgpu links and the pure-
-# CPU state tests pass.
-#
-# Goal: catch portability regressions in the wgpu source on Apple Silicon
-# / Intel Mac without needing the full IfcGeom + OCCT + Python toolchain
-# that build_osx.yml carries. Runs in ~5 minutes.
-
-on:
- push:
- branches:
- - ifcviewer-wgpu
- - main
- - v0.8.0
- paths:
- - 'src/ifcviewer-wgpu/**'
- - 'src/ifcviewer-wgpu-minimal/**'
- - 'src/wgpu-mem-probe/**'
- - 'src/ifcviewer/SidecarCache.*'
- - 'src/ifcviewer/InstancedGeometry.h'
- - 'src/ifcviewer/VertexQuantization.h'
- - 'cmake/CMakeLists.txt'
- - '.github/workflows/ci-wgpu-mac.yml'
- pull_request:
- paths:
- - 'src/ifcviewer-wgpu/**'
- - 'src/ifcviewer-wgpu-minimal/**'
- - 'src/wgpu-mem-probe/**'
- - 'src/ifcviewer/SidecarCache.*'
- - 'src/ifcviewer/InstancedGeometry.h'
- - 'src/ifcviewer/VertexQuantization.h'
- - 'cmake/CMakeLists.txt'
- - '.github/workflows/ci-wgpu-mac.yml'
- workflow_dispatch:
-
-jobs:
- build_wgpu_mac:
- runs-on: ${{ matrix.runner }}
- strategy:
- fail-fast: false
- matrix:
- include:
- - arch: arm64
- runner: macos-14
-
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- submodules: false
-
- - name: Install dependencies via Homebrew
- run: |
- brew update
- # qt installs Qt6; eigen is header-only. Boost is required by
- # the top-level CMakeLists unconditionally (line 328) even when
- # IfcGeom/IfcPython are off — until ifcviewer-core is extracted
- # (task #12) we have to humour it.
- brew install qt eigen boost
- # patchelf isn't on mac — wgpu-native's SONAME workaround is
- # Linux-only and CMake guards on UNIX AND NOT APPLE so this is fine.
-
- - name: ccache
- uses: hendrikmuhs/ccache-action@v1.2.22
- with:
- key: mac-wgpu-${{ matrix.arch }}
-
- - name: Configure (standalone wgpu, tests on)
- shell: bash
- # The top-level CMakeLists.txt unconditionally find_package's
- # CGAL/OpenCASCADE/etc. before our BUILD_BONSAIVIEWER gate kicks
- # in, so we explicitly disable every IfcGeom/IfcParse/IfcPython
- # subproject — we only want IfcViewerWgpu + its state tests.
- run: |
- QT_PREFIX="$(brew --prefix qt)"
- cmake -B build \
- -S cmake \
- -G Ninja \
- -DCMAKE_BUILD_TYPE=Release \
- -DBUILD_BONSAIVIEWER=OFF \
- -DBUILD_BONSAIVIEWER_WGPU=ON \
- -DBUILD_BONSAIVIEWER_TESTS=ON \
- -DBUILD_IFCGEOM=OFF \
- -DBUILD_IFCPYTHON=OFF \
- -DBUILD_CONVERT=OFF \
- -DBUILD_EXAMPLES=OFF \
- -DBUILD_GEOMSERVER=OFF \
- -DWITH_OPENCASCADE=OFF \
- -DWITH_CGAL=OFF \
- -DCOLLADA_SUPPORT=OFF \
- -DCMAKE_PREFIX_PATH="${QT_PREFIX}" \
- -DQT_DIR="${QT_PREFIX}" \
- -DCMAKE_C_COMPILER_LAUNCHER=ccache \
- -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
-
- - name: Build IfcViewerWgpu + state tests
- # Skip IfcViewerWgpuMinimal: its createSurface has no Metal path
- # yet (task #32). Static lib compiles cleanly because the X11 /
- # Wayland surface blocks are wrapped in #if defined(Q_OS_LINUX).
- # No `-- -j N`: Ninja parallelises by default (it rejects bare
- # `-j` unlike make).
- run: |
- cmake --build build \
- --target IfcViewerWgpu test_wgpu_selection test_wgpu_visibility
-
- - name: Run state tests
- working-directory: build
- # No -R filter: only wgpu state tests are built in this config,
- # so ctest discovers exactly those.
- run: ctest --output-on-failure
-
- - name: Upload CMake configure log on failure
- if: failure()
- uses: actions/upload-artifact@v4
- with:
- name: cmake-log-${{ matrix.arch }}
- path: |
- build/CMakeCache.txt
- build/CMakeFiles/CMakeConfigureLog.yaml
- retention-days: 14
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
index 150e858a02..5b4dad84b9 100644
--- a/cmake/CMakeLists.txt
+++ b/cmake/CMakeLists.txt
@@ -721,12 +721,6 @@ if(BUILD_BONSAIVIEWER)
add_subdirectory(../src/bonsaiviewer bonsaiviewer)
endif()
-# Standalone wgpu diagnostic tool (no bonsai) — fast iteration on
-# wgpu-native probing without the full bonsai build.
-if(BUILD_BONSAIVIEWER_WGPU AND NOT BUILD_BONSAIVIEWER)
- add_subdirectory(../src/wgpu-mem-probe wgpu-mem-probe)
-endif()
-
# Cmake uninstall target
if(NOT TARGET uninstall)
configure_file(
diff --git a/src/bonsaiviewer/CMakeLists.txt b/src/bonsaiviewer/CMakeLists.txt
index 7a19ea418f..ae16c4af4a 100644
--- a/src/bonsaiviewer/CMakeLists.txt
+++ b/src/bonsaiviewer/CMakeLists.txt
@@ -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 . #
# #
################################################################################
diff --git a/src/bonsaiviewer/ElementRegistry.cpp b/src/bonsaiviewer/ElementRegistry.cpp
index d633e70f17..6305f9d743 100644
--- a/src/bonsaiviewer/ElementRegistry.cpp
+++ b/src/bonsaiviewer/ElementRegistry.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -88,37 +88,35 @@ std::optional ElementRegistry::findEntity(uint32_t object_id) con
}
}
-void ElementRegistry::onSidecarElementsReady(uint32_t /*mid*/,
- std::vector elements,
+void ElementRegistry::onSidecarElementsReady(uint32_t /*model_id*/,
+ std::vector 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 elements) {
- for (const auto& e : elements) {
+void ElementRegistry::onStreamedElementsReady(uint32_t /*model_id*/, std::vector 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;
}
}
diff --git a/src/bonsaiviewer/ElementRegistry.h b/src/bonsaiviewer/ElementRegistry.h
index b804919f08..173f3a6aa4 100644
--- a/src/bonsaiviewer/ElementRegistry.h
+++ b/src/bonsaiviewer/ElementRegistry.h
@@ -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 . *
* *
********************************************************************************/
@@ -30,7 +30,7 @@
#include
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 findEntity(uint32_t object_id) const;
private:
- void onSidecarElementsReady(uint32_t mid,
- std::vector elements,
+ void onSidecarElementsReady(uint32_t model_id,
+ std::vector elements,
std::string string_table);
- void onStreamedElementsReady(uint32_t mid, std::vector elements);
+ void onStreamedElementsReady(uint32_t model_id, std::vector elements);
SceneLoader* loader_ = nullptr;
std::unordered_map elements_;
diff --git a/src/bonsaiviewer/MainWindow.cpp b/src/bonsaiviewer/MainWindow.cpp
index 5cfb1d0d45..452274b827 100644
--- a/src/bonsaiviewer/MainWindow.cpp
+++ b/src/bonsaiviewer/MainWindow.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -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) {
diff --git a/src/bonsaiviewer/MainWindow.h b/src/bonsaiviewer/MainWindow.h
index 6f8567ac86..9a8a2ed43e 100644
--- a/src/bonsaiviewer/MainWindow.h
+++ b/src/bonsaiviewer/MainWindow.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/Measurement.cpp b/src/bonsaiviewer/Measurement.cpp
index b24c072269..dd3175df25 100644
--- a/src/bonsaiviewer/Measurement.cpp
+++ b/src/bonsaiviewer/Measurement.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/Measurement.h b/src/bonsaiviewer/Measurement.h
index 83f2dbfba5..253eee279b 100644
--- a/src/bonsaiviewer/Measurement.h
+++ b/src/bonsaiviewer/Measurement.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/SessionState.cpp b/src/bonsaiviewer/SessionState.cpp
index 91189e9680..ee0b5617c7 100644
--- a/src/bonsaiviewer/SessionState.cpp
+++ b/src/bonsaiviewer/SessionState.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -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,
diff --git a/src/bonsaiviewer/SessionState.h b/src/bonsaiviewer/SessionState.h
index 902a933ccb..0ae486912e 100644
--- a/src/bonsaiviewer/SessionState.h
+++ b/src/bonsaiviewer/SessionState.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/ViewerSettings.cpp b/src/bonsaiviewer/ViewerSettings.cpp
index c7e92a9a6c..196cb806f4 100644
--- a/src/bonsaiviewer/ViewerSettings.cpp
+++ b/src/bonsaiviewer/ViewerSettings.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/ViewerSettings.h b/src/bonsaiviewer/ViewerSettings.h
index a3eea1ac15..96ef1e87d8 100644
--- a/src/bonsaiviewer/ViewerSettings.h
+++ b/src/bonsaiviewer/ViewerSettings.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Buttons.cpp b/src/bonsaiviewer/components/Buttons.cpp
index 622fd66264..bce4f87291 100644
--- a/src/bonsaiviewer/components/Buttons.cpp
+++ b/src/bonsaiviewer/components/Buttons.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Buttons.h b/src/bonsaiviewer/components/Buttons.h
index 39dc65d664..e8b26c287b 100644
--- a/src/bonsaiviewer/components/Buttons.h
+++ b/src/bonsaiviewer/components/Buttons.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Dialog.cpp b/src/bonsaiviewer/components/Dialog.cpp
index ce7a9ffe45..1f308440d4 100644
--- a/src/bonsaiviewer/components/Dialog.cpp
+++ b/src/bonsaiviewer/components/Dialog.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Dialog.h b/src/bonsaiviewer/components/Dialog.h
index a4f7fc47f8..b8b3cf0a9a 100644
--- a/src/bonsaiviewer/components/Dialog.h
+++ b/src/bonsaiviewer/components/Dialog.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/KeyValueTable.cpp b/src/bonsaiviewer/components/KeyValueTable.cpp
index 191cad8b8e..9f0012e7e4 100644
--- a/src/bonsaiviewer/components/KeyValueTable.cpp
+++ b/src/bonsaiviewer/components/KeyValueTable.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/KeyValueTable.h b/src/bonsaiviewer/components/KeyValueTable.h
index 714ec8d668..2c839981aa 100644
--- a/src/bonsaiviewer/components/KeyValueTable.h
+++ b/src/bonsaiviewer/components/KeyValueTable.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Panel.cpp b/src/bonsaiviewer/components/Panel.cpp
index 9969f9d754..77f42a0d1f 100644
--- a/src/bonsaiviewer/components/Panel.cpp
+++ b/src/bonsaiviewer/components/Panel.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Panel.h b/src/bonsaiviewer/components/Panel.h
index 9cbdace240..fee72dd765 100644
--- a/src/bonsaiviewer/components/Panel.h
+++ b/src/bonsaiviewer/components/Panel.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Section.cpp b/src/bonsaiviewer/components/Section.cpp
index 5caedd3d68..f66753dd4b 100644
--- a/src/bonsaiviewer/components/Section.cpp
+++ b/src/bonsaiviewer/components/Section.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Section.h b/src/bonsaiviewer/components/Section.h
index 8726e32ece..d083ae8870 100644
--- a/src/bonsaiviewer/components/Section.h
+++ b/src/bonsaiviewer/components/Section.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Style.cpp b/src/bonsaiviewer/components/Style.cpp
index 6f1196a3f1..fd1bcffc1a 100644
--- a/src/bonsaiviewer/components/Style.cpp
+++ b/src/bonsaiviewer/components/Style.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Style.h b/src/bonsaiviewer/components/Style.h
index 90a900e50c..5dc39f73b9 100644
--- a/src/bonsaiviewer/components/Style.h
+++ b/src/bonsaiviewer/components/Style.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/SvgIcon.cpp b/src/bonsaiviewer/components/SvgIcon.cpp
index 92091c631f..0ab12d3f7e 100644
--- a/src/bonsaiviewer/components/SvgIcon.cpp
+++ b/src/bonsaiviewer/components/SvgIcon.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/SvgIcon.h b/src/bonsaiviewer/components/SvgIcon.h
index b0577b8638..0098e28c04 100644
--- a/src/bonsaiviewer/components/SvgIcon.h
+++ b/src/bonsaiviewer/components/SvgIcon.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Tabs.cpp b/src/bonsaiviewer/components/Tabs.cpp
index c1af24c058..acdcdec205 100644
--- a/src/bonsaiviewer/components/Tabs.cpp
+++ b/src/bonsaiviewer/components/Tabs.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/components/Tabs.h b/src/bonsaiviewer/components/Tabs.h
index a9e40c2677..511b9f936a 100644
--- a/src/bonsaiviewer/components/Tabs.h
+++ b/src/bonsaiviewer/components/Tabs.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/docs/debug-output.rst b/src/bonsaiviewer/docs/debug-output.rst
new file mode 100644
index 0000000000..a5110098bf
--- /dev/null
+++ b/src/bonsaiviewer/docs/debug-output.rst
@@ -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
+ (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.
+ (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``.
diff --git a/src/bonsaiviewer/docs/viewport_architecture.rst b/src/bonsaiviewer/docs/viewport_architecture.rst
index 8b8e7633b0..c910f78960 100644
--- a/src/bonsaiviewer/docs/viewport_architecture.rst
+++ b/src/bonsaiviewer/docs/viewport_architecture.rst
@@ -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.
diff --git a/src/bonsaiviewer/main.cpp b/src/bonsaiviewer/main.cpp
index ff2f79a87a..59ef765e19 100644
--- a/src/bonsaiviewer/main.cpp
+++ b/src/bonsaiviewer/main.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/connectors/Discovery.cpp b/src/bonsaiviewer/modules/connectors/Discovery.cpp
index c60ddac38e..e4f3d6b877 100644
--- a/src/bonsaiviewer/modules/connectors/Discovery.cpp
+++ b/src/bonsaiviewer/modules/connectors/Discovery.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/connectors/Discovery.h b/src/bonsaiviewer/modules/connectors/Discovery.h
index 6932dde740..876d982fcf 100644
--- a/src/bonsaiviewer/modules/connectors/Discovery.h
+++ b/src/bonsaiviewer/modules/connectors/Discovery.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/connectors/PickerDialog.cpp b/src/bonsaiviewer/modules/connectors/PickerDialog.cpp
index 5da7dc0ec2..710009bdb1 100644
--- a/src/bonsaiviewer/modules/connectors/PickerDialog.cpp
+++ b/src/bonsaiviewer/modules/connectors/PickerDialog.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -60,9 +60,9 @@ ConnectorPickerDialog::ConnectorPickerDialog(const std::vectorsetSpacing(components::style::metrics::padding);
QList 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();
diff --git a/src/bonsaiviewer/modules/connectors/PickerDialog.h b/src/bonsaiviewer/modules/connectors/PickerDialog.h
index 40083e987a..b1011505c4 100644
--- a/src/bonsaiviewer/modules/connectors/PickerDialog.h
+++ b/src/bonsaiviewer/modules/connectors/PickerDialog.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/connectors/Process.cpp b/src/bonsaiviewer/modules/connectors/Process.cpp
index 1f27222391..6dbd1b900a 100644
--- a/src/bonsaiviewer/modules/connectors/Process.cpp
+++ b/src/bonsaiviewer/modules/connectors/Process.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -189,8 +189,8 @@ void ConnectorProcess::dispatchLine(const QByteArray& line) {
void ConnectorProcess::failPendingAndClear(int code, const QString& message) {
QHash 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);
}
}
diff --git a/src/bonsaiviewer/modules/connectors/Process.h b/src/bonsaiviewer/modules/connectors/Process.h
index ae83533e51..edaf682962 100644
--- a/src/bonsaiviewer/modules/connectors/Process.h
+++ b/src/bonsaiviewer/modules/connectors/Process.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/connectors/Registry.cpp b/src/bonsaiviewer/modules/connectors/Registry.cpp
index 1ec2c6ad86..bb4a34df26 100644
--- a/src/bonsaiviewer/modules/connectors/Registry.cpp
+++ b/src/bonsaiviewer/modules/connectors/Registry.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -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();
}
}
}
diff --git a/src/bonsaiviewer/modules/connectors/Registry.h b/src/bonsaiviewer/modules/connectors/Registry.h
index 6947332c99..3eb9f555a7 100644
--- a/src/bonsaiviewer/modules/connectors/Registry.h
+++ b/src/bonsaiviewer/modules/connectors/Registry.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/models/AddModelDialog.cpp b/src/bonsaiviewer/modules/models/AddModelDialog.cpp
index 0bb796bbb7..98df960344 100644
--- a/src/bonsaiviewer/modules/models/AddModelDialog.cpp
+++ b/src/bonsaiviewer/modules/models/AddModelDialog.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/models/AddModelDialog.h b/src/bonsaiviewer/modules/models/AddModelDialog.h
index eda3c1061a..d12bc9fe9e 100644
--- a/src/bonsaiviewer/modules/models/AddModelDialog.h
+++ b/src/bonsaiviewer/modules/models/AddModelDialog.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/models/Commands.cpp b/src/bonsaiviewer/modules/models/Commands.cpp
index 3736d68293..8ed35b4b07 100644
--- a/src/bonsaiviewer/modules/models/Commands.cpp
+++ b/src/bonsaiviewer/modules/models/Commands.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -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(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(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 sguard(&s);
+ QPointer 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 sguard(&s);
+ QPointer 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 sguard(&s);
+ QPointer 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();
@@ -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();
@@ -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();
}
diff --git a/src/bonsaiviewer/modules/models/Commands.h b/src/bonsaiviewer/modules/models/Commands.h
index 702e70eb82..1904ad66de 100644
--- a/src/bonsaiviewer/modules/models/Commands.h
+++ b/src/bonsaiviewer/modules/models/Commands.h
@@ -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 . *
* *
********************************************************************************/
@@ -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
diff --git a/src/bonsaiviewer/modules/models/FederationItemModel.cpp b/src/bonsaiviewer/modules/models/FederationItemModel.cpp
index fa7efca01f..290052eead 100644
--- a/src/bonsaiviewer/modules/models/FederationItemModel.cpp
+++ b/src/bonsaiviewer/modules/models/FederationItemModel.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -164,8 +164,8 @@ void FederationItemModel::refreshSubtreeVisibility(QStandardItem* root) {
const auto kind = static_cast(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);
}
diff --git a/src/bonsaiviewer/modules/models/FederationItemModel.h b/src/bonsaiviewer/modules/models/FederationItemModel.h
index 3cc094dbbd..4d8ece84f8 100644
--- a/src/bonsaiviewer/modules/models/FederationItemModel.h
+++ b/src/bonsaiviewer/modules/models/FederationItemModel.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/models/Panel.cpp b/src/bonsaiviewer/modules/models/Panel.cpp
index ebda8d2a93..4a3a630a34 100644
--- a/src/bonsaiviewer/modules/models/Panel.cpp
+++ b/src/bonsaiviewer/modules/models/Panel.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -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;
}
diff --git a/src/bonsaiviewer/modules/models/Panel.h b/src/bonsaiviewer/modules/models/Panel.h
index dadea2ec98..6316847687 100644
--- a/src/bonsaiviewer/modules/models/Panel.h
+++ b/src/bonsaiviewer/modules/models/Panel.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/models/SettingsDialog.cpp b/src/bonsaiviewer/modules/models/SettingsDialog.cpp
index 9f544ac9a3..998eac6310 100644
--- a/src/bonsaiviewer/modules/models/SettingsDialog.cpp
+++ b/src/bonsaiviewer/modules/models/SettingsDialog.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -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(AFrame::ModelLocal));
widgets.frame->addItem("Global", static_cast(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(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(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();
diff --git a/src/bonsaiviewer/modules/models/SettingsDialog.h b/src/bonsaiviewer/modules/models/SettingsDialog.h
index 68d70b5a72..70a0ff5b7f 100644
--- a/src/bonsaiviewer/modules/models/SettingsDialog.h
+++ b/src/bonsaiviewer/modules/models/SettingsDialog.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/models/SettingsView.cpp b/src/bonsaiviewer/modules/models/SettingsView.cpp
index b2694e2244..fdcbdf4901 100644
--- a/src/bonsaiviewer/modules/models/SettingsView.cpp
+++ b/src/bonsaiviewer/modules/models/SettingsView.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -39,12 +39,16 @@ QString formatNumber(double value) {
QString formatAngleDms(double degrees) {
const double absolute = std::fabs(degrees);
- const int d = static_cast(absolute);
- const double minutes_total = (absolute - static_cast(d)) * 60.0;
- const int m = static_cast(minutes_total);
- const double s = (minutes_total - static_cast(m)) * 60.0;
+ const int degree_part = static_cast(absolute);
+ const double minutes_total = (absolute - static_cast(degree_part)) * 60.0;
+ const int minute_part = static_cast(minutes_total);
+ const double second_part = (minutes_total - static_cast(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;
diff --git a/src/bonsaiviewer/modules/models/SettingsView.h b/src/bonsaiviewer/modules/models/SettingsView.h
index 1d5114d4d8..346d1014db 100644
--- a/src/bonsaiviewer/modules/models/SettingsView.h
+++ b/src/bonsaiviewer/modules/models/SettingsView.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/models/Types.h b/src/bonsaiviewer/modules/models/Types.h
index 1e80a3e939..c496e16687 100644
--- a/src/bonsaiviewer/modules/models/Types.h
+++ b/src/bonsaiviewer/modules/models/Types.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/models/View.cpp b/src/bonsaiviewer/modules/models/View.cpp
index 2c158e21e1..9a8619c682 100644
--- a/src/bonsaiviewer/modules/models/View.cpp
+++ b/src/bonsaiviewer/modules/models/View.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/models/View.h b/src/bonsaiviewer/modules/models/View.h
index 6fb67eca09..7ae862b94b 100644
--- a/src/bonsaiviewer/modules/models/View.h
+++ b/src/bonsaiviewer/modules/models/View.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/project/Commands.cpp b/src/bonsaiviewer/modules/project/Commands.cpp
index f0e626c7f2..2a2e5122fe 100644
--- a/src/bonsaiviewer/modules/project/Commands.cpp
+++ b/src/bonsaiviewer/modules/project/Commands.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -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 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 sguard(&s);
- QPointer vguard(&vp);
+ auto* registry = session.connectorRegistry();
+ QPointer sguard(&session);
+ QPointer 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 sguard(&s);
+ QPointer sguard(&session);
QPointer hguard(&host);
- QPointer vguard(&vp);
+ QPointer 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 sguard(&s);
+ QPointer sguard(&session);
QPointer hguard(&host);
- QPointer vguard(&vp);
- const QString current_path = fed->filePath();
+ QPointer 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();
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 sguard(&s);
+ QPointer sguard(&session);
QPointer 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 sguard(&s);
+ QPointer sguard(&session);
QPointer 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;
diff --git a/src/bonsaiviewer/modules/project/Commands.h b/src/bonsaiviewer/modules/project/Commands.h
index 12dccdc057..a821c24ec8 100644
--- a/src/bonsaiviewer/modules/project/Commands.h
+++ b/src/bonsaiviewer/modules/project/Commands.h
@@ -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 . *
* *
********************************************************************************/
@@ -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
diff --git a/src/bonsaiviewer/modules/project/RecentProjects.cpp b/src/bonsaiviewer/modules/project/RecentProjects.cpp
index 21ed2652d6..b97390a72f 100644
--- a/src/bonsaiviewer/modules/project/RecentProjects.cpp
+++ b/src/bonsaiviewer/modules/project/RecentProjects.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/project/RecentProjects.h b/src/bonsaiviewer/modules/project/RecentProjects.h
index 27bf79fd8e..07fec3c6fa 100644
--- a/src/bonsaiviewer/modules/project/RecentProjects.h
+++ b/src/bonsaiviewer/modules/project/RecentProjects.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/project/SaveProjectDialog.cpp b/src/bonsaiviewer/modules/project/SaveProjectDialog.cpp
index 872849d0f2..f77e54d1aa 100644
--- a/src/bonsaiviewer/modules/project/SaveProjectDialog.cpp
+++ b/src/bonsaiviewer/modules/project/SaveProjectDialog.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/project/SaveProjectDialog.h b/src/bonsaiviewer/modules/project/SaveProjectDialog.h
index 0e49d69fc0..7d009bbe6d 100644
--- a/src/bonsaiviewer/modules/project/SaveProjectDialog.h
+++ b/src/bonsaiviewer/modules/project/SaveProjectDialog.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/properties/Panel.cpp b/src/bonsaiviewer/modules/properties/Panel.cpp
index 991bf0018d..d45d858681 100644
--- a/src/bonsaiviewer/modules/properties/Panel.cpp
+++ b/src/bonsaiviewer/modules/properties/Panel.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/properties/Panel.h b/src/bonsaiviewer/modules/properties/Panel.h
index 7e52cbb084..1e5558be3a 100644
--- a/src/bonsaiviewer/modules/properties/Panel.h
+++ b/src/bonsaiviewer/modules/properties/Panel.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/properties/Types.h b/src/bonsaiviewer/modules/properties/Types.h
index 7c4f81ba61..a6a285aa29 100644
--- a/src/bonsaiviewer/modules/properties/Types.h
+++ b/src/bonsaiviewer/modules/properties/Types.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/properties/View.cpp b/src/bonsaiviewer/modules/properties/View.cpp
index 02c022c18c..0e23e57c5c 100644
--- a/src/bonsaiviewer/modules/properties/View.cpp
+++ b/src/bonsaiviewer/modules/properties/View.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/properties/View.h b/src/bonsaiviewer/modules/properties/View.h
index 1805a11fb5..9db8fbce61 100644
--- a/src/bonsaiviewer/modules/properties/View.h
+++ b/src/bonsaiviewer/modules/properties/View.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/settings/Dialog.cpp b/src/bonsaiviewer/modules/settings/Dialog.cpp
index 2f029d03e5..b10455edad 100644
--- a/src/bonsaiviewer/modules/settings/Dialog.cpp
+++ b/src/bonsaiviewer/modules/settings/Dialog.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -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("%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(
+ "Bonsai Viewer "
+ "A high performance IFC viewing and coordination tool for large OpenBIM projects.
"
+ "bonsaibim.org | "
+ "Source code",
+ 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 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.
"
+ "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 GNU General Public License "
+ "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(
+ "
"
+ "
IfcOpenShell and IfcViewer - LGPL-3.0-or-later
"
+ "
Qt - LGPL-3.0-only, GPL-3.0-only, or commercial license depending on distribution
"
+ "
Open CASCADE Technology - LGPL-2.1-only with OCCT exception
"
+ "
Eigen - MPL-2.0
"
+ "
wgpu-native - MPL-2.0
"
+ "
zstd - BSD-3-Clause
"
+ "
meshoptimizer - MIT, when enabled
"
+ "
"
+ "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;
diff --git a/src/bonsaiviewer/modules/settings/Dialog.h b/src/bonsaiviewer/modules/settings/Dialog.h
index 2266060fc9..79d6070c3c 100644
--- a/src/bonsaiviewer/modules/settings/Dialog.h
+++ b/src/bonsaiviewer/modules/settings/Dialog.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/spatial_hierarchy/Panel.cpp b/src/bonsaiviewer/modules/spatial_hierarchy/Panel.cpp
index 0b8dfaee7b..927f170e34 100644
--- a/src/bonsaiviewer/modules/spatial_hierarchy/Panel.cpp
+++ b/src/bonsaiviewer/modules/spatial_hierarchy/Panel.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/spatial_hierarchy/Panel.h b/src/bonsaiviewer/modules/spatial_hierarchy/Panel.h
index f3b10f7294..aaa91c858a 100644
--- a/src/bonsaiviewer/modules/spatial_hierarchy/Panel.h
+++ b/src/bonsaiviewer/modules/spatial_hierarchy/Panel.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/spatial_hierarchy/Types.h b/src/bonsaiviewer/modules/spatial_hierarchy/Types.h
index 2f5f0301c3..1818944648 100644
--- a/src/bonsaiviewer/modules/spatial_hierarchy/Types.h
+++ b/src/bonsaiviewer/modules/spatial_hierarchy/Types.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/spatial_hierarchy/View.cpp b/src/bonsaiviewer/modules/spatial_hierarchy/View.cpp
index 05b8615f05..353fecf7dc 100644
--- a/src/bonsaiviewer/modules/spatial_hierarchy/View.cpp
+++ b/src/bonsaiviewer/modules/spatial_hierarchy/View.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/spatial_hierarchy/View.h b/src/bonsaiviewer/modules/spatial_hierarchy/View.h
index c3260f469f..6e93e2b725 100644
--- a/src/bonsaiviewer/modules/spatial_hierarchy/View.h
+++ b/src/bonsaiviewer/modules/spatial_hierarchy/View.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/todo/Panel.cpp b/src/bonsaiviewer/modules/todo/Panel.cpp
index 5042672e75..51c0bd7b92 100644
--- a/src/bonsaiviewer/modules/todo/Panel.cpp
+++ b/src/bonsaiviewer/modules/todo/Panel.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/todo/Panel.h b/src/bonsaiviewer/modules/todo/Panel.h
index d5f50716f4..155bf1825e 100644
--- a/src/bonsaiviewer/modules/todo/Panel.h
+++ b/src/bonsaiviewer/modules/todo/Panel.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/viewport/Commands.cpp b/src/bonsaiviewer/modules/viewport/Commands.cpp
index fd8fa30dd5..3651338f9f 100644
--- a/src/bonsaiviewer/modules/viewport/Commands.cpp
+++ b/src/bonsaiviewer/modules/viewport/Commands.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -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
diff --git a/src/bonsaiviewer/modules/viewport/Commands.h b/src/bonsaiviewer/modules/viewport/Commands.h
index 0233873faa..0c1d0021bd 100644
--- a/src/bonsaiviewer/modules/viewport/Commands.h
+++ b/src/bonsaiviewer/modules/viewport/Commands.h
@@ -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 . *
* *
********************************************************************************/
@@ -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
diff --git a/src/bonsaiviewer/modules/viewport/Panel.cpp b/src/bonsaiviewer/modules/viewport/Panel.cpp
index ca6c694028..45f61c095e 100644
--- a/src/bonsaiviewer/modules/viewport/Panel.cpp
+++ b/src/bonsaiviewer/modules/viewport/Panel.cpp
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/viewport/Panel.h b/src/bonsaiviewer/modules/viewport/Panel.h
index 6d6db34080..aeae45060c 100644
--- a/src/bonsaiviewer/modules/viewport/Panel.h
+++ b/src/bonsaiviewer/modules/viewport/Panel.h
@@ -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 . *
* *
********************************************************************************/
diff --git a/src/bonsaiviewer/modules/viewport/View.cpp b/src/bonsaiviewer/modules/viewport/View.cpp
index cf9140bf31..60640b4cd1 100644
--- a/src/bonsaiviewer/modules/viewport/View.cpp
+++ b/src/bonsaiviewer/modules/viewport/View.cpp
@@ -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 . *
* *
********************************************************************************/
@@ -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 ids(sel.begin(), sel.end());
- const auto per_obj = volumesPerObject(*viewport_, ids);
+ std::vector object_ids(selection_ids.begin(), selection_ids.end());
+ const auto volumes_by_object = volumesPerObject(*viewport_, object_ids);
double total = 0.0;
std::vector 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);
}
diff --git a/src/bonsaiviewer/modules/viewport/View.h b/src/bonsaiviewer/modules/viewport/View.h
index 56e356afc4..be36e6987f 100644
--- a/src/bonsaiviewer/modules/viewport/View.h
+++ b/src/bonsaiviewer/modules/viewport/View.h
@@ -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 . *
* *
********************************************************************************/
@@ -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;
diff --git a/src/ifcopenshell-python/docs/bonsai-viewer.rst b/src/ifcopenshell-python/docs/bonsai-viewer.rst
new file mode 100644
index 0000000000..7d546fb13b
--- /dev/null
+++ b/src/ifcopenshell-python/docs/bonsai-viewer.rst
@@ -0,0 +1,10 @@
+.. This file was generated with the assistance of an AI coding tool.
+
+Bonsai Viewer
+=============
+
+Bonsai Viewer is a high performance viewing and coordination tool for IFC
+models. It is designed for quickly opening large projects, inspecting model
+geometry and metadata, and coordinating issues across federated project data.
+
+For more information, visit the `Bonsai website `_.
diff --git a/src/ifcopenshell-python/docs/bonsai.rst b/src/ifcopenshell-python/docs/bonsai.rst
index 5ad65d1a01..053990548f 100644
--- a/src/ifcopenshell-python/docs/bonsai.rst
+++ b/src/ifcopenshell-python/docs/bonsai.rst
@@ -1,5 +1,7 @@
Bonsai
======
-Bonsai lets you analyse, create, and modify OpenBIM with Blender. For more
-information, visit the `Bonsai website `_.
+Bonsai is a native IFC authoring platform to analyse, create, and modify
+OpenBIM with Blender.
+
+For more information, visit the `Bonsai website `_.
diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python.rst b/src/ifcopenshell-python/docs/ifcopenshell-python.rst
index bbe503c64b..7831bf94eb 100644
--- a/src/ifcopenshell-python/docs/ifcopenshell-python.rst
+++ b/src/ifcopenshell-python/docs/ifcopenshell-python.rst
@@ -19,3 +19,4 @@ capabilities of the C++ core are available in Python.
ifcopenshell-python/selector_syntax
ifcopenshell-python/schema_querying
ifcopenshell-python/validation
+ ifcopenshell-python/running_tests
diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python/running_tests.rst b/src/ifcopenshell-python/docs/ifcopenshell-python/running_tests.rst
new file mode 100644
index 0000000000..7692b3371f
--- /dev/null
+++ b/src/ifcopenshell-python/docs/ifcopenshell-python/running_tests.rst
@@ -0,0 +1,84 @@
+.. This file was generated with the assistance of an AI coding tool.
+
+Running tests
+=============
+
+IfcOpenShell-Python tests live in ``src/ifcopenshell-python/test`` and use
+``pytest``. From the repository root, enter the Python package directory before
+running the test suite:
+
+.. code-block:: bash
+
+ cd src/ifcopenshell-python
+
+Install the test runner in the Python environment you are using for
+development:
+
+.. code-block:: bash
+
+ pip install pytest
+
+Full test suite
+---------------
+
+Use the Makefile target for the default test suite:
+
+.. code-block:: bash
+
+ make test
+
+This runs:
+
+.. code-block:: bash
+
+ pytest -p no:pytest-blender test --ignore=test/util/test_shape_builder.py
+
+The ``pytest-blender`` plugin is disabled because these are IfcOpenShell-Python
+tests, not Bonsai Blender tests. The shape builder tests are split into a
+separate target because they require Blender's ``mathutils`` package.
+
+Parallel tests
+--------------
+
+For a faster local run, install ``pytest-xdist`` and use the parallel target:
+
+.. code-block:: bash
+
+ pip install pytest-xdist
+ make test-parallel
+
+This automatically uses the available CPU count and runs the same tests as
+``make test``.
+
+Shape builder tests
+-------------------
+
+The shape builder tests require ``mathutils``. Run them separately:
+
+.. code-block:: bash
+
+ pip install mathutils
+ make test-mathutils
+
+Running individual tests
+------------------------
+
+You can run an individual file or test directly with ``pytest``:
+
+.. code-block:: bash
+
+ pytest -p no:pytest-blender test/test_file.py
+ pytest -p no:pytest-blender test/util/test_unit.py
+ pytest -p no:pytest-blender test/test_file.py::TestFile::test_creating_a_new_file
+
+Coverage
+--------
+
+To generate an HTML coverage report, install ``coverage`` and run:
+
+.. code-block:: bash
+
+ pip install coverage
+ make coverage
+
+The report is written to ``htmlcov``.
diff --git a/src/ifcopenshell-python/docs/ifcopenshell/formats.rst b/src/ifcopenshell-python/docs/ifcopenshell/formats.rst
index 5f952d3ade..733c81dceb 100644
--- a/src/ifcopenshell-python/docs/ifcopenshell/formats.rst
+++ b/src/ifcopenshell-python/docs/ifcopenshell/formats.rst
@@ -101,6 +101,11 @@ without repeating tessellation, packing, quantisation, and instance rebuilds.
It is not a general interchange format or a full semantic storage backend. For
model data, pair it with the original IFC file or with RocksDB.
+.. toctree::
+ :maxdepth: 1
+
+ ifcview_format
+
RDBVIEW
-------
diff --git a/src/ifcopenshell-python/docs/ifcopenshell/ifcview_format.rst b/src/ifcopenshell-python/docs/ifcopenshell/ifcview_format.rst
new file mode 100644
index 0000000000..a013b70dba
--- /dev/null
+++ b/src/ifcopenshell-python/docs/ifcopenshell/ifcview_format.rst
@@ -0,0 +1,366 @@
+IfcView format
+==============
+
+``.ifcview`` is a lossy binary geometry cache used by the Bonsai Viewer and
+IfcViewer. It is a geometry cache, not an IFC exchange format. It stores
+quantised, compressed, spatially sorted streamable chunks of tessellated
+geometry, instancing, LODs, georeferencing, and basic element identifiers.
+
+The format is optimised for three requirements:
+
+- fast load and reuse of model geometry for analysis or visualisation
+- direct upload to a viewer's GPU-facing mesh and instance layout
+- compressed chunked streaming, especially for web loading by byte range
+
+It intentionally does not store non-geometric IFC data or original IFC
+parametric geometry. It is recommended to be combined with the original
+``.ifc`` file, a ``.ifcdb``/``.rdb`` model store, or an ``.rdbview`` package
+when additional data is needed.
+
+The format writes native C++ structs directly for several tables. This makes it
+compact and cheap to load, but it also means that ``.ifcview`` should not be
+treated as a stable, language-neutral interchange specification.
+
+The source of truth for the format is the C++ sidecar implementation in
+``src/ifcviewer``. The current on-disk version is version 17.
+
+Top-level layout
+----------------
+
+.. csv-table::
+ :header: "Section", "Description"
+
+ "SidecarHeader", "Versioning and format metadata"
+ "uint64 geometry_section_size", "Number of bytes from start of geometry section to byte immediately before the geometry metadata block. May be used by streaming readers to skip bulk geometry and first fetch metadata."
+ "Geometry section", "Streamable compressed chunks of geometry"
+ "Geometry metadata block", "Mesh records, instance records, georeferencing data, and chunk frame offsets"
+ "Element metadata block", "Element IDs, GUIDs, names, and IFC classes"
+
+Both metadata blocks use the same compressed block wrapper:
+
+.. code-block:: text
+
+ uint64 compressed_size
+ uint64 raw_size
+ byte[compressed_size] zstd_frame
+
+The geometry section is also compressed with zstd, but it is not one large
+frame. Each streaming chunk has its own vertex frame and index frame so a
+loader can fetch and decompress only the chunks it needs.
+
+For web streaming, the loader would read:
+
+1. the 20-byte head, consisting of the 12-byte header and 8-byte geometry size
+2. the geometry metadata block header and compressed payload
+3. the element metadata block header, so it can remember where the UI metadata
+ lives
+
+After that, visible chunks are loaded asynchronously by byte range. The
+element metadata payload is fetched only when UI code asks for element
+metadata.
+
+
+Sidecar header
+--------------
+
+``SidecarHeader`` is 12 bytes:
+
+.. code-block:: c
+
+ struct SidecarHeader {
+ uint32 magic;
+ uint32 version;
+ uint32 endian;
+ };
+
+The current values are:
+
+.. list-table::
+ :header-rows: 1
+
+ * - Field
+ - Value
+ - Purpose
+ * - Magic
+ - ``0x49465657``
+ - Identifies an ``IFVW`` sidecar.
+ * - Version
+ - ``17``
+ - Selects the current layout version. Versions may not be compatible.
+ * - Endian marker
+ - ``0x01020304``
+ - Rejects files written with a different byte order.
+
+If any of these values do not match, the ifcview cache must be rejected.
+
+Geometry section
+----------------
+
+The geometry section is the streamable part of the file. It contains, for each
+chunk, two zstd frames:
+
+.. code-block:: text
+
+ chunk 0 vertex frame
+ chunk 0 index frame
+ chunk 1 vertex frame
+ chunk 1 index frame
+ ...
+
+The metadata does not discover these frames by scanning. Instead, the geometry
+metadata block stores the ``SidecarChunk`` table that locates each chunk's
+compressed vertex and index frames.
+
+Inside a decompressed chunk, data is chunk-local:
+
+.. code-block:: text
+
+ vertices for mesh first_mesh
+ vertices for mesh first_mesh + 1
+ ...
+ LOD0 indices for mesh first_mesh
+ LOD0 indices for mesh first_mesh + 1
+ ...
+ LOD1 indices for mesh first_mesh
+ LOD1 indices for mesh first_mesh + 1
+ ...
+
+This order matches the renderer's streamed chunk upload path. Vertex offsets
+and index offsets in ``MeshInfo`` describe the whole-model logical layout, but
+the chunk upload path computes chunk-local offsets when it builds runtime chunk
+state.
+
+Vertex format
+-------------
+
+Each stored vertex is 12 bytes:
+
+.. list-table::
+ :header-rows: 1
+
+ * - Byte offset
+ - Type
+ - Meaning
+ * - ``0``
+ - ``uint16[3]``
+ - Quantised local position.
+ * - ``6``
+ - ``int8[2]``
+ - Octahedral-encoded normal.
+ * - ``8``
+ - ``uint8[4]``
+ - RGBA colour.
+
+The position is quantised against the mesh's local axis-aligned bounding box,
+stored in ``MeshInfo.local_aabb_min`` and ``MeshInfo.local_aabb_max``. This
+bounding box is the quantisation basis. In other words, the format stores each
+coordinate as a normalised integer within the mesh-local min/max range, rather
+than storing the original float coordinate.
+
+Conceptually, encoding does this per axis:
+
+.. code-block:: text
+
+ t = (position - local_aabb_min) / (local_aabb_max - local_aabb_min)
+ stored = round(clamp(t, 0, 1) * 65535)
+
+Decoding does the inverse:
+
+.. code-block:: text
+
+ t = stored / 65535
+ position = mix(local_aabb_min, local_aabb_max, t)
+
+The implementation stores ``extent_recip`` while encoding, which is simply
+``1 / (local_aabb_max - local_aabb_min)`` for each axis. Degenerate axes use
+``0`` so all coordinates on that axis quantise to the same value.
+
+Normals use two signed bytes with octahedral encoding. This is less precise
+than storing three floats, but is small and adequate for typical BIM geometry,
+which is dominated by planar and axis-aligned surfaces. Colour is stored as the
+four bytes used by the viewer's packed RGBA path.
+
+Geometry metadata block
+-----------------------
+
+The geometry metadata block is the minimum metadata required to create the
+runtime model and begin painting geometry. Its raw, decompressed order is:
+
+.. code-block:: text
+
+ vector meshes
+ vector instances
+ uint32 has_coordinate_operation
+ double[16] coordinate_operation_meters
+ double project_length_to_meters
+ double map_unit_to_meters
+ vector chunks
+
+``MeshInfo`` is 56 bytes and describes one reusable mesh:
+
+.. list-table::
+ :header-rows: 1
+
+ * - Field
+ - Meaning
+ * - ``vbo_byte_offset``
+ - Byte offset of the mesh's vertices in the logical whole-model vertex buffer.
+ * - ``vertex_count``
+ - Number of 12-byte vertices.
+ * - ``ebo_byte_offset``
+ - Byte offset of the mesh's LOD0 indices in the logical index buffer.
+ * - ``index_count``
+ - Number of LOD0 ``uint32`` indices.
+ * - ``local_aabb_min`` / ``local_aabb_max``
+ - Mesh-local bounds and the quantisation basis for vertex positions.
+ * - ``first_instance`` / ``instance_count``
+ - Range of instances that reference this mesh after sidecar layout.
+ * - ``lod1_ebo_byte_offset`` / ``lod1_index_count``
+ - Optional decimated index range for LOD1. A count of ``0`` means LOD1 is unavailable.
+
+``InstanceInfo`` is 232 bytes and records one placed occurrence of a mesh. The
+important fields are:
+
+.. list-table::
+ :header-rows: 1
+
+ * - Field
+ - Meaning
+ * - ``mesh_id``
+ - Index into the mesh table.
+ * - ``object_id``
+ - Viewer object identifier used for selection and lookup.
+ * - ``color_override_rgba8``
+ - Optional per-instance colour override. ``0`` means use the baked vertex colour.
+ * - ``model_id``
+ - Source model identifier within the viewer.
+ * - ``placement_transformation``
+ - Double-precision placement emitted by the geometry streamer, before final federation and false-origin composition.
+ * - ``transform``
+ - Float render transform for the default stage state. Loaders may recompute it from ``placement_transformation`` and current stage matrices.
+ * - ``world_aabb_min`` / ``world_aabb_max``
+ - World-space instance bounds used for chunk bounds, culling, and view fitting.
+
+Both transform forms are stored for precision and reuse. The double placement
+keeps large IFC coordinates intact until the viewer has applied coordinate
+operation, model transformation, and false-origin matrices. The float transform
+is the GPU-facing result for the default composition.
+
+The georeferencing fields cache enough of the model's coordinate operation and
+unit scale to load a sidecar without reparsing the IFC source solely to recover
+map conversion state. If the source map conversion changes, the sidecar must be
+deleted and rebuilt.
+
+``SidecarChunk`` is 56 bytes and records one streamable range of meshes. Each
+chunk maps to two compressed geometry frames in the geometry section:
+
+.. list-table::
+ :header-rows: 1
+
+ * - Field
+ - Meaning
+ * - ``first_mesh``
+ - First mesh index covered by the chunk.
+ * - ``mesh_count``
+ - Number of consecutive meshes covered by the chunk.
+ * - ``v_comp_off`` / ``v_comp_size``
+ - Byte offset and compressed byte size of the chunk's vertex frame, relative to the start of the geometry section.
+ * - ``v_raw_size``
+ - Decompressed byte size of the chunk's vertex data.
+ * - ``i_comp_off`` / ``i_comp_size``
+ - Byte offset and compressed byte size of the chunk's index frame, relative to the start of the geometry section.
+ * - ``i_raw_size``
+ - Decompressed byte size of the chunk's index data.
+
+The chunk table is part of the geometry metadata because the viewer needs it
+before it can request geometry. On the web path, the loader reads the header
+and geometry metadata block, creates a model with non-resident chunks, and then
+starts fetching visible chunk frames by byte range.
+
+Element metadata block
+-----------------------
+
+The element metadata block stores non-geometric element lookup data used by UI
+features such as picking, tree display, object labels, and search.
+
+Its raw, decompressed order is:
+
+.. code-block:: text
+
+ vector elements
+ uint32 string_table_bytes
+ char[string_table_bytes] string_table
+
+Each ``ElementTableRecord`` is a fixed-size 36-byte record:
+
+.. code-block:: c
+
+ struct ElementTableRecord {
+ uint32 object_id;
+ uint32 model_id;
+ int32 ifc_id;
+ uint32 guid_offset;
+ uint32 guid_length;
+ uint32 name_offset;
+ uint32 name_length;
+ uint32 type_offset;
+ uint32 type_length;
+ };
+
+Strings are stored once in the string table and referenced by offset and
+length.
+
+Splitting this block from geometry metadata is important for first paint. A
+large model can have substantial names, GlobalIds, and type strings. The web
+viewer can show geometry after the geometry metadata block is available, then
+fetch this metadata later when the UI needs it.
+
+Chunk planning
+--------------
+
+Chunks are spatial groups of meshes. During sidecar creation, the builder:
+
+1. Computes a centroid for each mesh from the average of its instance
+ world-AABB centres.
+2. Sorts mesh ids by a 3D Morton code, also known as Z-order.
+3. Greedily packs the sorted meshes into 4 MiB vertex-byte chunks. A single
+ mesh larger than that limit is kept whole in an oversized chunk. Meshes are
+ not split.
+4. Reorders meshes, vertices, indices, and instances so each chunk is a
+ consecutive mesh range.
+5. Writes these offsets into the ``SidecarChunk`` table so that loaders can
+ directly jump to the compressed chunk as needed.
+
+How the file is produced
+------------------------
+
+When no usable sidecar is available, the viewer falls back to the geometry
+streamer. The streamer emits:
+
+- ``StreamedMesh`` once for each unique representation mesh
+- ``StreamedInstance`` for every placed occurrence of a mesh
+- ``ElementInfo`` records for viewer metadata
+
+The sidecar builder consumes those streams. Mesh chunks are converted from the
+streamer transfer layout, which is seven floats per vertex
+(``position.xyz``, ``normal.xyz``, packed colour), into the 12-byte quantised
+vertex layout. Instance chunks become ``InstanceInfo`` records. Element info
+records become ``ElementTableRecord`` plus string table entries.
+
+At finalisation, the builder adds LOD1 index buffers where useful, caches
+georeferencing state, lays out meshes in streaming chunk order, builds the chunk
+table, and writes the file.
+
+Binary conventions
+------------------
+
+The current writer uses these conventions:
+
+- Multi-byte scalar values are written in native byte order and validated by
+ the endian marker.
+- Counted vectors are written as ``uint32 count`` followed by
+ ``count * sizeof(T)`` bytes of raw table entries.
+- Metadata blocks are zstd-compressed as complete raw metadata buffers.
+- Geometry chunks store vertex bytes and index bytes as separate zstd frames.
+- Offsets stored in the chunk table are relative to the start of the geometry
+ section, not relative to the start of the file.
diff --git a/src/ifcopenshell-python/docs/ifcviewer.rst b/src/ifcopenshell-python/docs/ifcviewer.rst
new file mode 100644
index 0000000000..8ec61b270d
--- /dev/null
+++ b/src/ifcopenshell-python/docs/ifcviewer.rst
@@ -0,0 +1,16 @@
+.. This file was generated with the assistance of an AI coding tool.
+
+IfcViewer
+=========
+
+IfcViewer is the WebGPU-based IFC viewer used by Bonsai Viewer and the
+standalone viewer targets. It includes shared loading, sidecar, streaming,
+selection, visibility, and viewport state code used by both desktop and web
+frontends.
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+ :caption: Contents:
+
+ ifcviewer/running_tests
diff --git a/src/ifcopenshell-python/docs/ifcviewer/running_tests.rst b/src/ifcopenshell-python/docs/ifcviewer/running_tests.rst
new file mode 100644
index 0000000000..9d231cbb66
--- /dev/null
+++ b/src/ifcopenshell-python/docs/ifcviewer/running_tests.rst
@@ -0,0 +1,128 @@
+.. This file was generated with the assistance of an AI coding tool.
+
+Running tests
+=============
+
+IfcViewer has two test layers:
+
+1. **Desktop tests**: C++ unit tests for the shared viewer core and desktop
+ support code.
+2. **Web tests**: headless-browser smoke tests for the Emscripten/WebGPU
+ frontend.
+
+The desktop tests are in ``src/ifcviewer/tests``. The web tests are in
+``src/ifcviewer-web/tests``.
+
+Desktop tests
+-------------
+
+The desktop tests are Catch2 executables registered with CTest. They cover
+pure viewer logic such as sidecar layout, streaming loaders, selection,
+visibility, instance composition, buffer-pool allocation, and viewport camera
+state.
+
+Configure a desktop build with viewer tests enabled:
+
+.. code-block:: bash
+
+ cmake -S cmake -B build-viewer-wgpu \
+ -G Ninja \
+ -DBUILD_BONSAIVIEWER=ON \
+ -DBUILD_BONSAIVIEWER_TESTS=ON
+
+Build and run all registered tests:
+
+.. code-block:: bash
+
+ cmake --build build-viewer-wgpu
+ ctest --test-dir build-viewer-wgpu --output-on-failure
+
+To run only the IfcViewer tests, filter by test name:
+
+.. code-block:: bash
+
+ ctest --test-dir build-viewer-wgpu -R "test_(sidecar|streaming|selection|visibility|buffer|viewport|federation|instance|chunk|lod)" --output-on-failure
+
+You can also build or run a single test executable directly:
+
+.. code-block:: bash
+
+ cmake --build build-viewer-wgpu --target test_sidecar_cache
+ ./build-viewer-wgpu/ifcviewer/tests/test_sidecar_cache
+
+Common test targets include:
+
+* ``test_sidecar_compress``
+* ``test_sidecar_cache``
+* ``test_streaming_loader``
+* ``test_instanced_geometry``
+* ``test_chunk_planner``
+* ``test_sidecar_layout``
+* ``test_instance_compose``
+* ``test_selection``
+* ``test_visibility``
+* ``test_buffer_pool``
+* ``test_viewport_camera``
+* ``test_federation``
+
+Web tests
+---------
+
+The web tests are Playwright smoke tests for the WebGPU/Emscripten build. They
+load the built page in Chrome, wait for WebGPU initialisation, and assert that
+the embedded sample renders non-blank, interactions change the framebuffer,
+and no uncaptured WebGPU errors are logged.
+
+First build the web viewer from the repository root. This requires an
+Emscripten environment:
+
+.. code-block:: bash
+
+ source /path/to/emsdk_env.sh
+ emcmake cmake -S src/ifcviewer-web -B build-web
+ ninja -C build-web IfcViewerWeb
+
+Install the web test dependencies once:
+
+.. code-block:: bash
+
+ cd src/ifcviewer-web/tests
+ npm install
+
+Run the web smoke tests:
+
+.. code-block:: bash
+
+ npm test
+
+The Playwright configuration starts ``serve.mjs`` automatically. The server
+serves ``build-web`` on ``http://localhost:8124``. Override the build directory
+or port with environment variables:
+
+.. code-block:: bash
+
+ WEB_BUILD_DIR=/path/to/build-web PORT=9000 npm test
+
+Chrome requirements
+~~~~~~~~~~~~~~~~~~~
+
+The test configuration uses the system Chrome channel, so a system Chrome such
+as ``google-chrome-stable`` must be installed. You do not need to run
+``npx playwright install`` unless you change the Playwright browser channel.
+
+Headless and CI runs
+~~~~~~~~~~~~~~~~~~~~
+
+WebGPU in headless Linux environments can be sensitive to the GPU and browser
+configuration. The default configuration runs headed against the machine's real
+GPU. On a headless machine, use Xvfb:
+
+.. code-block:: bash
+
+ xvfb-run -a npm test
+
+For a GPU-less runner, change ``headless`` to ``true`` in
+``playwright.config.mjs`` and provide a SwiftShader Vulkan ICD, for example via
+``VK_ICD_FILENAMES=/path/to/vk_swiftshader_icd.json`` together with a Chrome
+``--use-angle=swiftshader`` argument. This is slower than a real GPU but is
+enough for render-non-blank smoke checks.
diff --git a/src/ifcopenshell-python/docs/index.rst b/src/ifcopenshell-python/docs/index.rst
index 895dbece88..3f62c1af66 100644
--- a/src/ifcopenshell-python/docs/index.rst
+++ b/src/ifcopenshell-python/docs/index.rst
@@ -12,7 +12,9 @@ Let's learn IfcOpenShell!
ifcopenshell
ifcopenshell-python
ifcconvert
+ ifcviewer
bonsai
+ bonsai-viewer
.. toctree::
:hidden:
diff --git a/src/ifcopenshell-python/docs/introduction.rst b/src/ifcopenshell-python/docs/introduction.rst
index 6357667c19..427589a6db 100644
--- a/src/ifcopenshell-python/docs/introduction.rst
+++ b/src/ifcopenshell-python/docs/introduction.rst
@@ -3,7 +3,7 @@ Introduction
**IfcOpenShell** is an open source software library for software developers and BIM powerusers working with Industry Foundation Classes (`IFC `_).
-In addition to a C++ and Python API, **IfcOpenShell** comes with an ecosystem of tools, notably including **IfcConvert** (an application to convert IFC models to other formats), **Bonsai** (an add-on to Blender providing a graphical IFC authoring platform), and many other libraries, CLI apps, and more. Support is also provided for auxiliary standards such as BCF, bSDD, and IDS.
+In addition to a C++ and Python API, **IfcOpenShell** comes with an ecosystem of tools, notably including **IfcConvert** (an application to convert IFC models to other formats), **Bonsai** (an add-on to Blender providing a graphical IFC authoring platform and viewer), and many other libraries, CLI apps, and more. Support is also provided for auxiliary standards such as BCF, bSDD, and IDS.
Things you can do
-----------------
@@ -58,6 +58,9 @@ IfcOpenShell is a modular ecosystem of tools that work together, where each tool
"`IfcOpenShell-Python `_", "Python bindings to the core IfcOpenShell C++ system, as well as high level analysis and authoring functions."
"`IfcConvert `_", "A command-line application for converting IFC geometry into file formats such as OBJ, DAE, GLB, STP, IGS, XML, SVG, H5, and IFC itself."
"`Bonsai `_", "A graphical add-on for Blender that lets you analyse, author, and modify IFC with Blender. Graphically create BIM models from scratch!"
+ "`Bonsai Viewer `_", "A high performance viewing and coordination tool for opening and inspecting IFC models."
+ "`IfcViewer `_", "A WebGPU-based desktop IFC viewer and shared viewer core used by the standalone viewer targets."
+ "IfcViewerWeb", "A WebAssembly and WebGPU viewer target for viewing IFC models in a web browser."
"`BCF `_", "BIM Collaboration Format (BCF) is a standard to manage and exchange coordination topics between disciplines collaborating on a project by changing XML files or querying an API."
"`BIMServer-Plugin `_", "A plugin to the open source BIMServer CDE to allow you to use IfcOpenShell to parse, view, and audit models."
"`BIMTester `_", "A utility that allows you to write Gherkin-based tests for models."
@@ -81,7 +84,9 @@ IfcOpenShell is a modular ecosystem of tools that work together, where each tool
.. note::
- **IfcOpenShell** and all of its libraries are licensed under LGPL-3.0-or-later. Two exceptions to this are **Bonsai** and **IfcSverchok**, which are both licensed under GPL-3.0-or-later.
+ **IfcOpenShell** and its libraries and viewer targets are licensed under
+ LGPL-3.0-or-later. Exceptions include **Bonsai**, **Bonsai Viewer**, and
+ **IfcSverchok**, which are licensed under GPL-3.0-or-later.
.. toctree::
:hidden:
diff --git a/src/ifcviewer-minimal/main.cpp b/src/ifcviewer-minimal/main.cpp
index bb059b8798..42551be469 100644
--- a/src/ifcviewer-minimal/main.cpp
+++ b/src/ifcviewer-minimal/main.cpp
@@ -82,10 +82,11 @@ int main(int argc, char* argv[]) {
const QStringList parts = parser.value("camera").split(',');
if (parts.size() == 6) {
bool ok = true;
- float v[6];
- for (int i = 0; i < 6 && ok; ++i) v[i] = parts[i].toFloat(&ok);
+ float camera_values[6];
+ for (int i = 0; i < 6 && ok; ++i) camera_values[i] = parts[i].toFloat(&ok);
if (ok) {
- viewport->setCamera(v[0], v[1], v[2], v[3], v[4], v[5]);
+ viewport->setCamera(camera_values[0], camera_values[1], camera_values[2],
+ camera_values[3], camera_values[4], camera_values[5]);
} else {
Log::warn() << "--camera: failed to parse "
<< parser.value("camera");
diff --git a/src/ifcviewer-web/CMakeLists.txt b/src/ifcviewer-web/CMakeLists.txt
index e7dbb004f5..bbd7d70624 100644
--- a/src/ifcviewer-web/CMakeLists.txt
+++ b/src/ifcviewer-web/CMakeLists.txt
@@ -104,7 +104,7 @@ target_link_options(IfcViewerWeb PRIVATE
# EMSCRIPTEN_KEEPALIVE alone keeps the symbols in the binary but doesn't
# add them to Module. ccall lets shell.html pass a JS string (the ?model
# URL) to load_sidecar_from_url_c without manual heap marshalling.
- "-sEXPORTED_FUNCTIONS=['_main','_raf_tick_c','_load_sidecar_from_source_c','_clear_scene_c','_ifcv_on_range_done','_ifcv_chunks_resident_c','_ifcv_chunks_total_c','_ifcv_model_count_c','_ifcv_model_resident_c','_ifcv_model_total_c','_ifcv_bytes_total_c','_ifcv_bytes_needed_c','_ifcv_bytes_loaded_c','_view_all_c','_frame_selection_c','_toggle_projection_c','_projection_is_ortho_c','_standard_view_c','_toggle_fly_c','_fly_is_active_c','_hide_selected_c','_isolate_selected_c','_show_all_c','_toggle_xray_c','_xray_is_active_c']"
+ "-sEXPORTED_FUNCTIONS=['_main','_raf_tick_c','_load_sidecar_from_source_c','_clear_scene_c','_ifcv_on_range_done','_ifcv_chunks_resident_c','_ifcv_chunks_total_c','_ifcv_model_count_c','_ifcv_model_resident_c','_ifcv_model_total_c','_ifcv_bytes_total_c','_ifcv_bytes_needed_c','_ifcv_bytes_loaded_c','_view_all_c','_frame_selection_c','_toggle_projection_c','_projection_is_ortho_c','_standard_view_c','_toggle_fly_c','_fly_is_active_c','_hide_selected_c','_isolate_selected_c','_show_all_c','_toggle_xray_c','_xray_is_active_c','_toggle_section_c','_clear_section_c','_section_is_active_c']"
# ccall: shell.html passes the ?model URL string to load_sidecar_from_url_c.
# HEAPU8: lets tooling/tests read the wasm heap size (e.g. to verify a large
# sidecar streams by range instead of loading whole). Standard, zero-cost.
diff --git a/src/ifcviewer-web/main_web.cpp b/src/ifcviewer-web/main_web.cpp
index 4a0d02bde8..afab844e8b 100644
--- a/src/ifcviewer-web/main_web.cpp
+++ b/src/ifcviewer-web/main_web.cpp
@@ -35,9 +35,12 @@
#include
#include
+#include
#include
#include
+#include
#include
+#include
namespace {
@@ -45,6 +48,9 @@ namespace {
// WebViewportHost selector below.
constexpr const char* kCanvasSelector = "#viewer-canvas";
+// What a mouse drag drives, decided against the active nav preset's bindings.
+enum class NavKind { None, Orbit, Pan, Select };
+
struct AppState {
WebViewportHost host{ kCanvasSelector };
ViewportCore core{ &host };
@@ -56,15 +62,19 @@ struct AppState {
bool ready = false;
// ---- Mouse navigation state ----
- // A drag is armed on mousedown and released on mouseup. button is the
- // DOM button code (0 left, 1 middle, 2 right). Left orbits; middle or
- // right pans — matches common web 3D-viewer bindings and covers both
- // three-button mice and trackpad (right-drag) users.
- bool nav_active = false;
- int nav_button = 0;
- // Accumulated |movement| since mousedown, in CSS px. A left release under
- // the click threshold (no real drag) is treated as a pick instead of an
- // orbit. Captures the down position (canvas-relative CSS px) for the pick.
+ // A drag is armed on mousedown and released on mouseup. What the press
+ // drives (orbit / pan / select) is decided against the active nav preset's
+ // bindings (ViewportCore::navBindings), so any preset works on web too.
+ bool nav_active = false;
+ NavKind nav_kind = NavKind::None;
+ // Section-cut tool: while active, a select-button click drops a clip plane
+ // at the picked surface (K toggles, Shift+K clears — matches desktop).
+ bool section_tool_active = false;
+ // True while dragging a section-plane gizmo (LMB down on the arrow → slide).
+ bool section_dragging = false;
+ // Accumulated |movement| since mousedown, in CSS px. A select-button release
+ // under the click threshold (no real drag) is treated as a pick; a drag will
+ // become a marquee. Captures the down position (canvas-relative CSS px).
float nav_drag_px = 0.0f;
long down_x = 0;
long down_y = 0;
@@ -78,7 +88,13 @@ struct AppState {
// Distinguishes "lock lost" (Esc/click-out → exit fly) from "lock denied on
// entry" (headless / permission) where fly stays keyboard-drivable.
bool fly_locked = false;
- bool k_w = false, k_a = false, k_s = false, k_d = false, k_q = false, k_e = false, k_shift = false;
+ bool key_w_pressed = false;
+ bool key_a_pressed = false;
+ bool key_s_pressed = false;
+ bool key_d_pressed = false;
+ bool key_q_pressed = false;
+ bool key_e_pressed = false;
+ bool key_shift_pressed = false;
double fly_last_ms = 0.0;
};
@@ -102,13 +118,52 @@ int canvasCssHeight() {
return (h > 1.0) ? int(h) : 1;
}
+// Marquee rectangle overlay. The rubber-band is a plain DOM
(shell.html)
+// positioned in CSS px — the canvas fills the viewport, so canvas-relative
+// coords are viewport coords. Cheaper + pixel-perfect vs a GPU overlay pass
+// (which the web lib doesn't have anyway).
+void showMarquee(int x, int y, int w, int h) {
+ EM_ASM({
+ var m = document.getElementById('marquee');
+ if (m) {
+ m.style.display = 'block';
+ m.style.left = $0 + 'px'; m.style.top = $1 + 'px';
+ m.style.width = $2 + 'px'; m.style.height = $3 + 'px';
+ }
+ }, x, y, w, h);
+}
+void hideMarquee() {
+ EM_ASM({ var m = document.getElementById('marquee'); if (m) m.style.display = 'none'; });
+}
+
+NavKind classifyPress(const ViewportCore::NavBindings& b, int em_button,
+ bool shift, bool ctrl, bool alt) {
+ using MB = ViewportCore::MouseBtn; using M = ViewportCore::NavMod;
+ const MB btn = (em_button == 0) ? MB::Left : (em_button == 1) ? MB::Middle : MB::Right;
+ const M mod = shift ? M::Shift : ctrl ? M::Ctrl : alt ? M::Alt : M::Plain;
+ if (btn == b.orbit && mod == b.orbit_mod) return NavKind::Orbit;
+ if (btn == b.pan && mod == b.pan_mod) return NavKind::Pan;
+ if (btn == b.select) return NavKind::Select; // Shift/Ctrl = add/remove
+ return NavKind::None;
+}
+
EM_BOOL onMouseDown(int, const EmscriptenMouseEvent* e, void* user) {
auto* app = static_cast(user);
// In fly mode a click exits (matches the desktop app).
if (app->fly_mode) { setFlyMode(app, false); return EM_TRUE; }
- if (e->button == 0 || e->button == 1 || e->button == 2) {
+ // Section tool: LMB on a plane's gizmo arrow grabs it to slide (logical px).
+ if (app->section_tool_active && e->button == 0) {
+ const int hit = app->core.hitTestSectionGizmo(int(e->targetX), int(e->targetY));
+ if (hit >= 0 && app->core.beginSectionDrag(hit, int(e->targetX), int(e->targetY))) {
+ app->section_dragging = true;
+ return EM_TRUE; // claim the press — don't orbit
+ }
+ }
+ const NavKind kind = classifyPress(app->core.navBindings(), e->button,
+ e->shiftKey, e->ctrlKey, e->altKey);
+ if (kind != NavKind::None) {
app->nav_active = true;
- app->nav_button = e->button;
+ app->nav_kind = kind;
app->nav_drag_px = 0.0f;
app->down_x = e->targetX; // canvas-relative CSS px
app->down_y = e->targetY;
@@ -124,42 +179,85 @@ EM_BOOL onMouseMove(int, const EmscriptenMouseEvent* e, void* user) {
app->core.flyLook(float(e->movementX), float(e->movementY));
return EM_TRUE;
}
+ // Section gizmo drag: slide the grabbed plane along its normal (logical px).
+ if (app->section_dragging) {
+ app->core.updateSectionDrag(int(e->targetX), int(e->targetY));
+ return EM_TRUE;
+ }
if (!app->nav_active) return EM_FALSE;
const float dx = float(e->movementX);
const float dy = float(e->movementY);
app->nav_drag_px += std::abs(dx) + std::abs(dy);
- if (app->nav_button == 0) {
- app->core.orbitBy(dx, dy);
- } else {
- app->core.panBy(dx, dy, canvasCssHeight());
+ if (app->nav_kind == NavKind::Orbit) app->core.orbitBy(dx, dy);
+ else if (app->nav_kind == NavKind::Pan) app->core.panBy(dx, dy, canvasCssHeight());
+ else if (app->nav_kind == NavKind::Select && app->nav_drag_px > kClickDragThresholdPx) {
+ // Select-button drag → draw the marquee rubber-band (CSS px).
+ const long x0 = std::min(app->down_x, e->targetX);
+ const long y0 = std::min(app->down_y, e->targetY);
+ showMarquee(int(x0), int(y0),
+ int(std::labs(long(e->targetX) - app->down_x)),
+ int(std::labs(long(e->targetY) - app->down_y)));
}
return EM_TRUE;
}
EM_BOOL onMouseUp(int, const EmscriptenMouseEvent* e, void* user) {
auto* app = static_cast(user);
- const bool was_active = app->nav_active;
- const int button = app->nav_button;
+ const bool was_active = app->nav_active;
+ const NavKind kind = app->nav_kind;
app->nav_active = false;
+ app->nav_kind = NavKind::None;
- // Left release with no real drag → pick the object under the cursor and
- // route it through selection (Shift add, Ctrl remove, plain replace).
- // Async readback: the highlight appears a frame after the result lands.
- if (was_active && button == 0 && app->ready &&
- app->nav_drag_px <= kClickDragThresholdPx) {
- const double dpr = emscripten_get_device_pixel_ratio();
- const int px = int(app->down_x * dpr);
- const int py = int(app->down_y * dpr);
- const bool add = e->shiftKey;
- const bool remove = e->ctrlKey;
- app->core.pickObjectAtAsync(px, py, [app, add, remove](std::uint32_t id) {
- app->core.applyPickToSelection(id, add, remove);
- // Demo the v15 on-demand deferred fetch: log the picked object's
- // IFC GUID (first pick fetches the property block off the network).
- if (id != 0) app->core.logSelectedObjectGuidWeb(id);
+ // End a section-gizmo drag (took over the press; no pick/orbit on release).
+ if (app->section_dragging) {
+ app->core.endSectionDrag();
+ app->section_dragging = false;
+ return EM_TRUE;
+ }
+
+ if (!was_active || !app->ready) return EM_TRUE;
+ const double dpr = emscripten_get_device_pixel_ratio();
+ const bool no_drag = app->nav_drag_px <= kClickDragThresholdPx;
+
+ // Section tool claims a LEFT-button click ("click a surface to cut"); LMB
+ // drag still orbits. Takes priority over nav while the tool is active.
+ if (app->section_tool_active && e->button == 0 && no_drag) {
+ const int px = int(app->down_x * dpr), py = int(app->down_y * dpr);
+ app->core.pickSurfaceAtAsync(px, py, [app](ViewportCore::SurfaceHit hit) {
+ if (hit.found) // pad past the AABB so the cut reads as a cap
+ app->core.addSectionPlaneAtSurface(hit.world_pos, hit.world_normal,
+ hit.aabb_radius * 1.5f);
app->host.requestFrame();
});
+ return EM_TRUE;
+ }
+
+ if (kind == NavKind::Select) {
+ const bool add = e->shiftKey, remove = e->ctrlKey;
+ if (!no_drag) {
+ // Marquee drag → box-pick the rect (device px) and apply to selection.
+ hideMarquee();
+ const long x0 = std::min(app->down_x, e->targetX);
+ const long y0 = std::min(app->down_y, e->targetY);
+ const int rx = int(x0 * dpr), ry = int(y0 * dpr);
+ const int rw = int(std::labs(long(e->targetX) - app->down_x) * dpr);
+ const int rh = int(std::labs(long(e->targetY) - app->down_y) * dpr);
+ app->core.picksInRectAsync(rx, ry, rw, rh,
+ [app, add, remove](std::vector ids) {
+ app->core.applyMarqueeToSelection(ids, add, remove);
+ app->host.requestFrame();
+ });
+ } else {
+ // Single pick under the cursor (Shift add, Ctrl remove, plain replace).
+ const int px = int(app->down_x * dpr), py = int(app->down_y * dpr);
+ app->core.pickObjectAtAsync(px, py, [app, add, remove](std::uint32_t id) {
+ app->core.applyPickToSelection(id, add, remove);
+ // v15 on-demand element metadata fetch: log the picked object's IFC GUID.
+ if (id != 0) app->core.logSelectedObjectGuidWeb(id);
+ app->host.requestFrame();
+ });
+ }
}
return EM_TRUE;
}
@@ -183,14 +281,14 @@ EM_BOOL onWheel(int, const EmscriptenWheelEvent* e, void* user) {
// Track a held fly movement key (W/A/S/D/Q/E/Shift). Returns true if `code` was
// one. Physical `.code` so it's keymap-independent.
bool setFlyKey(AppState* app, const char* code, bool down) {
- if (!std::strcmp(code, "KeyW")) app->k_w = down;
- else if (!std::strcmp(code, "KeyA")) app->k_a = down;
- else if (!std::strcmp(code, "KeyS")) app->k_s = down;
- else if (!std::strcmp(code, "KeyD")) app->k_d = down;
- else if (!std::strcmp(code, "KeyQ")) app->k_q = down;
- else if (!std::strcmp(code, "KeyE")) app->k_e = down;
+ if (!std::strcmp(code, "KeyW")) app->key_w_pressed = down;
+ else if (!std::strcmp(code, "KeyA")) app->key_a_pressed = down;
+ else if (!std::strcmp(code, "KeyS")) app->key_s_pressed = down;
+ else if (!std::strcmp(code, "KeyD")) app->key_d_pressed = down;
+ else if (!std::strcmp(code, "KeyQ")) app->key_q_pressed = down;
+ else if (!std::strcmp(code, "KeyE")) app->key_e_pressed = down;
else if (!std::strcmp(code, "ShiftLeft") || !std::strcmp(code, "ShiftRight"))
- app->k_shift = down;
+ app->key_shift_pressed = down;
else return false;
return true;
}
@@ -205,7 +303,9 @@ void setFlyMode(AppState* app, bool on) {
emscripten_request_pointerlock(kCanvasSelector, EM_TRUE);
Log::info() << "[fly] on — WASD/QE move, mouse looks, Shift boosts, wheel = speed, Esc exits";
} else {
- app->k_w = app->k_a = app->k_s = app->k_d = app->k_q = app->k_e = app->k_shift = false;
+ app->key_w_pressed = app->key_a_pressed = app->key_s_pressed = false;
+ app->key_d_pressed = app->key_q_pressed = app->key_e_pressed = false;
+ app->key_shift_pressed = false;
app->fly_locked = false;
emscripten_exit_pointerlock();
Log::info() << "[fly] off";
@@ -237,6 +337,25 @@ EM_BOOL onKeyDown(int, const EmscriptenKeyboardEvent* e, void* user) {
return EM_TRUE;
}
if (!std::strcmp(code, "KeyX") && alt) { app->core.toggleXray(); return EM_TRUE; }
+ // Section tool: K toggles drop-a-plane mode, Shift+K clears all cuts.
+ if (!std::strcmp(code, "KeyK")) {
+ if (shift) app->core.clearSectionPlanes();
+ else {
+ app->section_tool_active = !app->section_tool_active;
+ Log::info() << "[section] tool "
+ << (app->section_tool_active ? "active — click a surface" : "off");
+ }
+ app->host.requestFrame();
+ return EM_TRUE;
+ }
+ // Del/Backspace removes the most recent cut while the tool is active.
+ if (app->section_tool_active &&
+ (!std::strcmp(code, "Delete") || !std::strcmp(code, "Backspace"))) {
+ const int n = app->core.sectionPlaneCount();
+ if (n > 0) app->core.removeSectionPlane(n - 1);
+ app->host.requestFrame();
+ return EM_TRUE;
+ }
using SV = ViewportCore::StandardView;
if (!std::strcmp(code, "Home")) app->core.viewAll();
else if (!std::strcmp(code, "KeyF") && !shift) app->core.frameSelection();
@@ -312,8 +431,10 @@ extern "C" EMSCRIPTEN_KEEPALIVE void raf_tick_c(void* user) {
const double now = emscripten_get_now();
const float dt = float((now - app->fly_last_ms) / 1000.0);
app->fly_last_ms = now;
- app->core.flyMove(app->k_w, app->k_s, app->k_d, app->k_a,
- app->k_e, app->k_q, app->k_shift, dt);
+ app->core.flyMove(app->key_w_pressed, app->key_s_pressed,
+ app->key_d_pressed, app->key_a_pressed,
+ app->key_e_pressed, app->key_q_pressed,
+ app->key_shift_pressed, dt);
}
if (app->host.consumeFrameRequest()) {
@@ -373,6 +494,21 @@ extern "C" EMSCRIPTEN_KEEPALIVE void show_all_c() { if (g_app && g_app->
extern "C" EMSCRIPTEN_KEEPALIVE void toggle_xray_c() { if (g_app && g_app->ready) g_app->core.toggleXray(); }
extern "C" EMSCRIPTEN_KEEPALIVE int xray_is_active_c() { return (g_app && g_app->ready && g_app->core.xrayActive()) ? 1 : 0; }
+// Section-cut tool: toggle the drop-a-plane mode, clear all planes, query state.
+extern "C" EMSCRIPTEN_KEEPALIVE void toggle_section_c() {
+ if (!g_app || !g_app->ready) return;
+ g_app->section_tool_active = !g_app->section_tool_active;
+ Log::info() << "[section] tool "
+ << (g_app->section_tool_active ? "active — click a surface to cut" : "off");
+ g_app->host.requestFrame();
+}
+extern "C" EMSCRIPTEN_KEEPALIVE void clear_section_c() {
+ if (g_app && g_app->ready) { g_app->core.clearSectionPlanes(); g_app->host.requestFrame(); }
+}
+extern "C" EMSCRIPTEN_KEEPALIVE int section_is_active_c() {
+ return (g_app && g_app->ready && g_app->section_tool_active) ? 1 : 0;
+}
+
// id: 0 Front, 1 Back, 2 Left, 3 Right, 4 Top, 5 Bottom.
extern "C" EMSCRIPTEN_KEEPALIVE void standard_view_c(int id) {
if (!g_app || !g_app->ready || id < 0 || id > 5) return;
@@ -387,11 +523,15 @@ extern "C" EMSCRIPTEN_KEEPALIVE void standard_view_c(int id) {
// geometry chunks arrive.
extern "C" EMSCRIPTEN_KEEPALIVE int ifcv_chunks_resident_c() {
if (!g_app) return 0;
- int r = 0, t = 0; g_app->core.streamingProgress(r, t); return r;
+ int resident_chunks = 0, total_chunks = 0;
+ g_app->core.streamingProgress(resident_chunks, total_chunks);
+ return resident_chunks;
}
extern "C" EMSCRIPTEN_KEEPALIVE int ifcv_chunks_total_c() {
if (!g_app) return 0;
- int r = 0, t = 0; g_app->core.streamingProgress(r, t); return t;
+ int resident_chunks = 0, total_chunks = 0;
+ g_app->core.streamingProgress(resident_chunks, total_chunks);
+ return total_chunks;
}
// Per-model progress for the federation loading panel: how many models are in
@@ -401,11 +541,15 @@ extern "C" EMSCRIPTEN_KEEPALIVE int ifcv_model_count_c() {
}
extern "C" EMSCRIPTEN_KEEPALIVE int ifcv_model_resident_c(int idx) {
if (!g_app) return 0;
- int r = 0, t = 0; g_app->core.streamingModelProgress(idx, r, t); return r;
+ int resident_chunks = 0, total_chunks = 0;
+ g_app->core.streamingModelProgress(idx, resident_chunks, total_chunks);
+ return resident_chunks;
}
extern "C" EMSCRIPTEN_KEEPALIVE int ifcv_model_total_c(int idx) {
if (!g_app) return 0;
- int r = 0, t = 0; g_app->core.streamingModelProgress(idx, r, t); return t;
+ int resident_chunks = 0, total_chunks = 0;
+ g_app->core.streamingModelProgress(idx, resident_chunks, total_chunks);
+ return total_chunks;
}
// Combined byte progress for the loading bar: total geometry, bytes the current
@@ -413,23 +557,28 @@ extern "C" EMSCRIPTEN_KEEPALIVE int ifcv_model_total_c(int idx) {
// JS gets exact byte counts well past 2 GB.
extern "C" EMSCRIPTEN_KEEPALIVE double ifcv_bytes_total_c() {
if (!g_app) return 0.0;
- std::uint64_t tot = 0, need = 0, load = 0;
- g_app->core.streamingByteProgress(tot, need, load); return double(tot);
+ std::uint64_t total_bytes = 0, needed_bytes = 0, loaded_bytes = 0;
+ g_app->core.streamingByteProgress(total_bytes, needed_bytes, loaded_bytes);
+ return double(total_bytes);
}
extern "C" EMSCRIPTEN_KEEPALIVE double ifcv_bytes_needed_c() {
if (!g_app) return 0.0;
- std::uint64_t tot = 0, need = 0, load = 0;
- g_app->core.streamingByteProgress(tot, need, load); return double(need);
+ std::uint64_t total_bytes = 0, needed_bytes = 0, loaded_bytes = 0;
+ g_app->core.streamingByteProgress(total_bytes, needed_bytes, loaded_bytes);
+ return double(needed_bytes);
}
extern "C" EMSCRIPTEN_KEEPALIVE double ifcv_bytes_loaded_c() {
if (!g_app) return 0.0;
- std::uint64_t tot = 0, need = 0, load = 0;
- g_app->core.streamingByteProgress(tot, need, load); return double(load);
+ std::uint64_t total_bytes = 0, needed_bytes = 0, loaded_bytes = 0;
+ g_app->core.streamingByteProgress(total_bytes, needed_bytes, loaded_bytes);
+ return double(loaded_bytes);
}
int main(int /*argc*/, char** /*argv*/) {
Log::info() << "ifcviewer-web: starting";
g_app = new AppState();
+ // Default to the web mouse scheme: LMB orbit, MMB pan, RMB select/marquee.
+ g_app->core.setNavPreset("web");
g_app->core.initWgpuAsyncWeb([](bool ok) {
if (!ok) {
Log::warn() << "ifcviewer-web: wgpu init failed";
diff --git a/src/ifcviewer-web/sample.ifcview b/src/ifcviewer-web/sample.ifcview
index e91c0a92c8..c6c3550cd8 100644
Binary files a/src/ifcviewer-web/sample.ifcview and b/src/ifcviewer-web/sample.ifcview differ
diff --git a/src/ifcviewer-web/shell.html b/src/ifcviewer-web/shell.html
index 17ec6f082f..47b42ccafe 100644
--- a/src/ifcviewer-web/shell.html
+++ b/src/ifcviewer-web/shell.html
@@ -8,6 +8,10 @@
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
#viewer-canvas { display: block; width: 100vw; height: 100vh; outline: none;
background: #1a1d24; }
+ /* Marquee (box-select) rubber-band. Positioned in CSS px by main_web; never
+ eats pointer events so the drag keeps reaching the canvas. */
+ #marquee { position: fixed; display: none; z-index: 50; pointer-events: none;
+ border: 1px solid #4a9eff; background: rgba(74, 158, 255, 0.15); }
/* Log overlay sits bottom-left and never eats pointer events (so it
can't block orbit drags over the canvas). It auto-scrolls to the
newest line. Capped small; collapses further once the app is live. */
@@ -63,6 +67,7 @@
+