Swap interface into IfcViewerFull
Replace the old IfcViewerFull application tree with the interface-based viewer while preserving the IfcViewerFull target and build workflow. Generated with the assistance of an AI coding tool.
@@ -1,3 +1,4 @@
|
||||
# This file was generated with the assistance of an AI coding tool.
|
||||
################################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
@@ -19,19 +20,89 @@
|
||||
|
||||
message("Running CMakeLists.txt in /src/ifcviewer-full")
|
||||
|
||||
file(GLOB IFCVIEWER_FULL_CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||
file(GLOB IFCVIEWER_FULL_H_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
|
||||
set(IFCVIEWER_FULL_FILES ${IFCVIEWER_FULL_CPP_FILES} ${IFCVIEWER_FULL_H_FILES})
|
||||
set(QT_VERSION 6 CACHE STRING "Qt version")
|
||||
find_package(Qt${QT_VERSION} COMPONENTS Core Gui Widgets Svg REQUIRED PATHS ${QT_DIR})
|
||||
|
||||
set(IFCVIEWER_FULL_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ElementRegistry.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ElementRegistry.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/InterfaceSettings.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/InterfaceSettings.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SessionState.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SessionState.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/MainWindow.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/MainWindow.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Measurement.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Measurement.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/SvgIcon.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/SvgIcon.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Style.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Style.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Dialog.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Dialog.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Tabs.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Tabs.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/KeyValueTable.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/KeyValueTable.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Buttons.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Buttons.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Section.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Section.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Panel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components/Panel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/AddModelDialog.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/AddModelDialog.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/SettingsDialog.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/SettingsDialog.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/SettingsView.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/SettingsView.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/Types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/Controller.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/Controller.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/Panel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/Panel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/View.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/models/View.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/todo/Panel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/todo/Panel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/properties/Types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/properties/Panel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/properties/Panel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/properties/View.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/properties/View.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/project/Controller.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/project/Controller.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/settings/Dialog.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/settings/Dialog.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/spatial_hierarchy/Types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/spatial_hierarchy/Panel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/spatial_hierarchy/Panel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/spatial_hierarchy/View.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/spatial_hierarchy/View.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/viewport/Panel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/viewport/Panel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/viewport/Controller.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modules/viewport/Controller.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/interface_resources.qrc
|
||||
)
|
||||
|
||||
add_executable(IfcViewerFull ${IFCVIEWER_FULL_FILES})
|
||||
|
||||
set_target_properties(IfcViewerFull PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
WIN32_EXECUTABLE ON
|
||||
MACOSX_BUNDLE ON
|
||||
)
|
||||
|
||||
target_link_libraries(IfcViewerFull PRIVATE IfcViewer)
|
||||
target_link_libraries(IfcViewerFull PRIVATE
|
||||
IfcViewer
|
||||
Qt${QT_VERSION}::Core
|
||||
Qt${QT_VERSION}::Gui
|
||||
Qt${QT_VERSION}::Svg
|
||||
Qt${QT_VERSION}::Widgets
|
||||
)
|
||||
|
||||
install(TARGETS IfcViewerFull EXPORT ${IFCOPENSHELL_EXPORT_TARGETS})
|
||||
ifcopenshell_deploy_qt_runtime(IfcViewerFull)
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* 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 *
|
||||
* 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, *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include "FederationSettingsDialog.h"
|
||||
#include "Federation.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QShowEvent>
|
||||
#include <QStringList>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace {
|
||||
|
||||
// Common length units users will pick. itemData() carries
|
||||
// (prefix, name) — empty prefix for non-prefixed or conversion-based units.
|
||||
struct UnitChoice {
|
||||
const char* label;
|
||||
const char* prefix;
|
||||
const char* name;
|
||||
};
|
||||
const UnitChoice kUnitChoices[] = {
|
||||
{ "Metres (m)", "", "METRE" },
|
||||
{ "Millimetres (mm)", "MILLI", "METRE" },
|
||||
{ "Centimetres (cm)", "CENTI", "METRE" },
|
||||
{ "Kilometres (km)", "KILO", "METRE" },
|
||||
{ "Feet (ft)", "", "foot" },
|
||||
{ "Inches (in)", "", "inch" },
|
||||
{ "Yards (yd)", "", "yard" },
|
||||
{ "Miles (mi)", "", "mile" },
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
FederationSettingsDialog::FederationSettingsDialog(Federation* federation,
|
||||
QWidget* parent)
|
||||
: QDialog(parent), federation_(federation)
|
||||
{
|
||||
setWindowTitle("Federation Settings");
|
||||
setupUi();
|
||||
}
|
||||
|
||||
void FederationSettingsDialog::setupUi() {
|
||||
auto* root = new QVBoxLayout(this);
|
||||
|
||||
// Unit
|
||||
{
|
||||
auto* group = new QGroupBox("Federation Unit", this);
|
||||
auto* form = new QFormLayout(group);
|
||||
unit_combo_ = new QComboBox(group);
|
||||
for (const auto& uc : kUnitChoices) {
|
||||
QStringList data;
|
||||
data << QString::fromUtf8(uc.prefix) << QString::fromUtf8(uc.name);
|
||||
unit_combo_->addItem(uc.label, data);
|
||||
}
|
||||
unit_combo_->setToolTip(
|
||||
"All Federated False Origin and Model Transformation numbers "
|
||||
"are interpreted in this unit. Changing it after data has been "
|
||||
"entered re-interprets the numbers — same physical location "
|
||||
"expressed in the new unit.");
|
||||
form->addRow("Unit", unit_combo_);
|
||||
root->addWidget(group);
|
||||
}
|
||||
|
||||
// Federated False Origin
|
||||
{
|
||||
auto* group = new QGroupBox("Federated False Origin", this);
|
||||
group->setToolTip(
|
||||
"Nominate a point in the federation that becomes the new (0,0,0). "
|
||||
"Optional Z-rotation aligns north for the federation.");
|
||||
auto* form = new QFormLayout(group);
|
||||
|
||||
auto build_xyz = [this](QDoubleSpinBox*& sb) {
|
||||
sb = new QDoubleSpinBox();
|
||||
sb->setRange(-1e12, 1e12);
|
||||
sb->setDecimals(6);
|
||||
sb->setSingleStep(1.0);
|
||||
};
|
||||
build_xyz(xyz_x_);
|
||||
build_xyz(xyz_y_);
|
||||
build_xyz(xyz_z_);
|
||||
form->addRow("X", xyz_x_);
|
||||
form->addRow("Y", xyz_y_);
|
||||
form->addRow("Z", xyz_z_);
|
||||
|
||||
rz_deg_ = new QDoubleSpinBox();
|
||||
rz_deg_->setRange(-360.0, 360.0);
|
||||
rz_deg_->setDecimals(6);
|
||||
rz_deg_->setSingleStep(1.0);
|
||||
form->addRow("Z rotation (°)", rz_deg_);
|
||||
root->addWidget(group);
|
||||
}
|
||||
|
||||
auto* button_box = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
root->addWidget(button_box);
|
||||
connect(button_box, &QDialogButtonBox::accepted, this,
|
||||
&FederationSettingsDialog::onAccepted);
|
||||
connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
void FederationSettingsDialog::showEvent(QShowEvent* event) {
|
||||
syncFromFederation();
|
||||
QDialog::showEvent(event);
|
||||
}
|
||||
|
||||
void FederationSettingsDialog::syncFromFederation() {
|
||||
if (!federation_) return;
|
||||
|
||||
const auto& cfg = 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) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx >= 0) {
|
||||
unit_combo_->setCurrentIndex(idx);
|
||||
} else {
|
||||
// Federation has a unit that isn't in the common list — fall back to
|
||||
// the first entry. Saving keeps whatever the user picks; the
|
||||
// original federation unit is preserved unless they hit Ok.
|
||||
unit_combo_->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
const auto& origin = federation_->federatedFalseOrigin();
|
||||
xyz_x_->setValue(origin.xyz.x());
|
||||
xyz_y_->setValue(origin.xyz.y());
|
||||
xyz_z_->setValue(origin.xyz.z());
|
||||
rz_deg_->setValue(origin.rz_deg);
|
||||
}
|
||||
|
||||
void FederationSettingsDialog::onAccepted() {
|
||||
if (!federation_) { accept(); return; }
|
||||
|
||||
const QStringList data = unit_combo_->currentData().toStringList();
|
||||
FederationConfig cfg;
|
||||
if (data.size() == 2) {
|
||||
cfg.unit_prefix = data[0].toStdString();
|
||||
cfg.unit_name = data[1].toStdString();
|
||||
}
|
||||
federation_->setConfig(cfg);
|
||||
|
||||
FederatedFalseOrigin origin;
|
||||
origin.xyz = Eigen::Vector3d(xyz_x_->value(), xyz_y_->value(), xyz_z_->value());
|
||||
origin.rz_deg = rz_deg_->value();
|
||||
federation_->setFederatedFalseOrigin(origin);
|
||||
|
||||
accept();
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* 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 *
|
||||
* 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, *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FEDERATIONSETTINGSDIALOG_H
|
||||
#define FEDERATIONSETTINGSDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QComboBox;
|
||||
class QDoubleSpinBox;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class Federation;
|
||||
|
||||
// Edits federation-wide state: the display unit (used to interpret all
|
||||
// FederatedFalseOrigin / ModelTransformation numeric inputs) and the
|
||||
// FederatedFalseOrigin (XYZ + Z-axis rotation in that unit). On Ok,
|
||||
// calls Federation::setConfig + setFederatedFalseOrigin, which fire the
|
||||
// granular Federation signals and trigger the viewport to recompose.
|
||||
class FederationSettingsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FederationSettingsDialog(Federation* federation, QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
void syncFromFederation();
|
||||
void onAccepted();
|
||||
|
||||
Federation* federation_ = nullptr;
|
||||
|
||||
// The combobox encodes (prefix, name) pairs in user data; common length
|
||||
// units are listed. itemData(idx) returns a QStringList { prefix, name }.
|
||||
QComboBox* unit_combo_ = nullptr;
|
||||
QDoubleSpinBox* xyz_x_ = nullptr;
|
||||
QDoubleSpinBox* xyz_y_ = nullptr;
|
||||
QDoubleSpinBox* xyz_z_ = nullptr;
|
||||
QDoubleSpinBox* rz_deg_ = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,4 @@
|
||||
// This file was generated with the assistance of an AI coding tool.
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
@@ -17,195 +18,85 @@
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
#ifndef IFCINTERFACE_SHELL_MAINWINDOW_H
|
||||
#define IFCINTERFACE_SHELL_MAINWINDOW_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QMainWindow>
|
||||
#include <QTreeWidget>
|
||||
#include <QTableWidget>
|
||||
#include <QProgressBar>
|
||||
#include <QLabel>
|
||||
#include <QSplitter>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Measurement.h"
|
||||
#include "ViewportWindow.h"
|
||||
#include "SceneLoader.h"
|
||||
#include <QStringList>
|
||||
|
||||
class QLabel;
|
||||
class QDockWidget;
|
||||
class QProgressBar;
|
||||
class QStackedWidget;
|
||||
class QToolButton;
|
||||
class Federation;
|
||||
class SettingsWindow;
|
||||
class FederationSettingsDialog;
|
||||
class ModelTransformationDialog;
|
||||
class SceneLoader;
|
||||
namespace ifcinterface { class ElementRegistry; }
|
||||
namespace ifcinterface { class SessionState; }
|
||||
namespace ifcinterface::components { class TabBar; }
|
||||
namespace ifcinterface::modules::models { class ModelsPanelController; }
|
||||
namespace ifcinterface::modules::models { class ModelsPanel; }
|
||||
namespace ifcinterface::modules::models { class ModelsPanelView; }
|
||||
namespace ifcinterface::modules::project { class ProjectController; }
|
||||
namespace ifcinterface::modules::spatial_hierarchy { class SpatialHierarchyPanel; }
|
||||
namespace ifcinterface::modules::spatial_hierarchy { class SpatialHierarchyPanelView; }
|
||||
namespace ifcinterface::modules::properties { class PropertiesPanel; }
|
||||
namespace ifcinterface::modules::properties { class PropertiesPanelView; }
|
||||
namespace ifcinterface::modules::viewport { class ViewportController; }
|
||||
namespace ifcinterface::modules::viewport { class ViewportPanel; }
|
||||
|
||||
namespace ifcinterface::shell {
|
||||
|
||||
class MainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(QWidget* parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
void addFiles(const QStringList& paths);
|
||||
bool openFederation(const QString& path);
|
||||
void setPendingCamera(const QString& params);
|
||||
void setPendingBenchmark(int frames);
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void onFileOpen();
|
||||
void onDatabaseOpen();
|
||||
void onFederationNew();
|
||||
void onFederationOpen();
|
||||
bool onFederationSave();
|
||||
bool onFederationSaveAs();
|
||||
void onSetHomeView();
|
||||
void onGoHomeView();
|
||||
void onFileSettings();
|
||||
void onFederationSettings();
|
||||
void onModelTransformations();
|
||||
void onObjectPicked(uint32_t object_id);
|
||||
void onTreeSelectionChanged();
|
||||
void onTreeContextMenu(const QPoint& pos);
|
||||
|
||||
void onLoadStarted(uint32_t mid, QString display_name);
|
||||
void onLoadProgressChanged(int percent);
|
||||
void onSidecarElementsReady(uint32_t mid,
|
||||
std::vector<PackedElementInfo> elements,
|
||||
std::string string_table);
|
||||
void onLoadedFromSidecar(uint32_t mid, qint64 elapsed_ms);
|
||||
void onDataSourceReady(uint32_t mid);
|
||||
void onStreamedElementsReady(uint32_t mid, std::vector<ElementInfo> elements);
|
||||
void onLoadedFromStream(uint32_t mid, qint64 elapsed_ms);
|
||||
void onLoadCancelled(uint32_t mid);
|
||||
void onLoadError(uint32_t mid, QString message);
|
||||
void onAllLoadsFinished();
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
void setupMenus();
|
||||
void loadModelsFromPaths(const QStringList& paths,
|
||||
const QStringList& fed_ids);
|
||||
void clearScene();
|
||||
bool confirmDiscardIfDirty();
|
||||
void setupChrome();
|
||||
void setupRibbon();
|
||||
void setupViewport();
|
||||
void setupPanels();
|
||||
void setupStatus();
|
||||
void setupLoader();
|
||||
QWidget* buildHomeRibbonPage();
|
||||
QWidget* buildNavigateRibbonPage();
|
||||
QWidget* buildInspectRibbonPage();
|
||||
QWidget* buildPanelsRibbonPage();
|
||||
void updateWindowTitle();
|
||||
void populateProperties(uint32_t object_id);
|
||||
// Push the volume HUD + per-object volume labels for the current
|
||||
// selection. No-op when a measurement tool is active — that tool
|
||||
// owns the overlay state until it's exited.
|
||||
void updateVolumeReadout();
|
||||
void appendElementToTree(uint32_t model_id,
|
||||
uint32_t object_id,
|
||||
int ifc_id,
|
||||
int parent_ifc_id,
|
||||
const std::string& guid,
|
||||
const std::string& name,
|
||||
const std::string& type);
|
||||
void writeSidecarForModel(uint32_t mid);
|
||||
void removeModelUi(uint32_t mid);
|
||||
void removeModel(uint32_t mid);
|
||||
// Returns the model_id whose tree root is `item`, or 0 if `item` is not
|
||||
// a model root (i.e. an element row, group, or null).
|
||||
uint32_t modelIdForRoot(QTreeWidgetItem* item) const;
|
||||
// Returns the group_id whose tree item is `item`, or empty string when
|
||||
// `item` is null or is not a group item.
|
||||
QString groupIdForItem(QTreeWidgetItem* item) const;
|
||||
// Place / reparent a model root under its group (or at top level when
|
||||
// group_id is empty / unknown). Idempotent. No-op if there's no tree
|
||||
// root yet for `mid`.
|
||||
void reparentModelTreeRoot(uint32_t mid);
|
||||
// Place / reparent a group item under its parent group (or at top
|
||||
// level). Idempotent. No-op if there's no tree item for `group_id`.
|
||||
void reparentGroupTreeItem(const QString& group_id);
|
||||
// Lazily create the QTreeWidgetItem for `group_id` if not already in
|
||||
// group_tree_items_. Returns the item. Sets text + flags but does
|
||||
// not place it under a parent — call reparentGroupTreeItem afterwards.
|
||||
QTreeWidgetItem* ensureGroupTreeItem(const QString& group_id);
|
||||
// Recompute italic/grey on a group row from current effective visibility.
|
||||
void refreshGroupRowAppearance(const QString& group_id);
|
||||
// Walk descendants of `group_id` and re-push effective visibility to
|
||||
// the viewport for every model under it. When `group_id` is empty,
|
||||
// re-pushes every model in the federation.
|
||||
void applyVisibilityCascadeFromGroup(const QString& group_id);
|
||||
// Walk every group_id whose ancestor chain currently includes
|
||||
// `group_id` (inclusive).
|
||||
std::vector<QString> descendantGroupIds(const QString& group_id) const;
|
||||
// Push the federation's `visible` flag for `mid` onto the viewport.
|
||||
// No-op if `mid` is not in the federation map. Idempotent — safe to
|
||||
// call before the model is finalised on the viewport (hideModel is a
|
||||
// lookup-and-set on models_gpu_; missing entries are skipped).
|
||||
void applyModelVisibilityToViewport(uint32_t mid);
|
||||
void applyPendingBenchmark();
|
||||
QToolButton* makePanelToggle(const QString& text, QDockWidget* dock);
|
||||
|
||||
// Push a model's CoordinateOperation matrix to the viewport (or
|
||||
// identity, when the AppSettings toggle is off or the model has no
|
||||
// map conversion). No-op if the model isn't yet known to the loader
|
||||
// or its IFC file isn't available (sidecar-hit before data-source
|
||||
// load); the call retries on onDataSourceReady.
|
||||
void applyCoordinateOperationToViewport(uint32_t mid);
|
||||
|
||||
// Push a model's ModelTransformation (stage 4) matrix to the viewport,
|
||||
// composed from the federation's authoring intent + the model's units
|
||||
// + the active CoordinateOperation matrix. Identity when the model is
|
||||
// not in the federation.
|
||||
void applyModelTransformationToViewport(uint32_t mid);
|
||||
|
||||
// Push the federation-wide FederatedFalseOrigin (stage 3) matrix to
|
||||
// the viewport. Affects every loaded model.
|
||||
void applyFederatedFalseOriginToViewport();
|
||||
|
||||
// For an untitled federation whose FederatedFalseOrigin is still at
|
||||
// its default, derive a sensible origin from `mid`'s first instance
|
||||
// placement + georef and push it via Federation. Idempotent: a
|
||||
// non-default origin (user-edited, already guessed by a sibling load
|
||||
// in the same batch, or loaded from a saved .ifcfed) is left
|
||||
// untouched, so multi-file batches naturally anchor on whichever
|
||||
// model finishes first.
|
||||
void maybeGuessFederatedFalseOrigin(uint32_t mid);
|
||||
QString formatElapsed(qint64 ms) const;
|
||||
|
||||
ViewportWindow* viewport_ = nullptr;
|
||||
SceneLoader* loader_ = nullptr;
|
||||
Federation* federation_ = nullptr;
|
||||
SettingsWindow* settings_ = nullptr;
|
||||
FederationSettingsDialog* federation_settings_ = nullptr;
|
||||
ModelTransformationDialog* model_transformations_ = nullptr;
|
||||
QWidget* viewport_container_ = nullptr;
|
||||
QTreeWidget* element_tree_ = nullptr;
|
||||
QTableWidget* property_table_ = nullptr;
|
||||
QProgressBar* progress_bar_ = nullptr;
|
||||
QLabel* status_label_ = nullptr;
|
||||
QLabel* stats_label_ = nullptr;
|
||||
|
||||
// Per-model tree roots, keyed by model_id. May live at the top level
|
||||
// of element_tree_ or as a child of a group tree item.
|
||||
std::map<uint32_t, QTreeWidgetItem*> tree_roots_;
|
||||
// Per-group tree items, keyed by Federation group id.
|
||||
std::unordered_map<QString, QTreeWidgetItem*> group_tree_items_;
|
||||
|
||||
// Bidirectional federation_id <-> model_id map. Federation owns the
|
||||
// persistent ids; SceneLoader owns the runtime model_ids.
|
||||
std::unordered_map<QString, uint32_t> fed_id_to_model_id_;
|
||||
std::unordered_map<uint32_t, QString> model_id_to_fed_id_;
|
||||
|
||||
// Display-side element registry for tree + property lookup.
|
||||
std::unordered_map<uint32_t, ElementInfo> element_map_;
|
||||
std::unordered_map<uint32_t, QTreeWidgetItem*> tree_items_;
|
||||
// Scoped (model_id, ifc_id) -> object_id
|
||||
std::unordered_map<uint64_t, uint32_t> scoped_ifc_id_to_object_id_;
|
||||
|
||||
static uint64_t scopedKey(uint32_t model_id, int ifc_id) {
|
||||
return (static_cast<uint64_t>(model_id) << 32) | static_cast<uint32_t>(ifc_id);
|
||||
}
|
||||
|
||||
QString pending_camera_;
|
||||
int pending_benchmark_ = 0;
|
||||
|
||||
AreaMeasurement area_measurement_;
|
||||
LengthMeasurement length_measurement_;
|
||||
private:
|
||||
Federation* federation_ = nullptr;
|
||||
QLabel* status_mode_label_ = nullptr;
|
||||
QLabel* status_selection_label_ = nullptr;
|
||||
QLabel* status_perf_label_ = nullptr;
|
||||
QProgressBar* status_progress_bar_ = nullptr;
|
||||
ifcinterface::components::TabBar* ribbon_tabs_ = nullptr;
|
||||
QStackedWidget* ribbon_pages_ = nullptr;
|
||||
ifcinterface::modules::viewport::ViewportPanel* viewport_widget_ = nullptr;
|
||||
ifcinterface::modules::viewport::ViewportController* viewport_controller_ = nullptr;
|
||||
SceneLoader* loader_ = nullptr;
|
||||
ifcinterface::ElementRegistry* element_registry_ = nullptr;
|
||||
ifcinterface::SessionState* session_state_ = nullptr;
|
||||
ifcinterface::modules::models::ModelsPanel* models_panel_ = nullptr;
|
||||
ifcinterface::modules::spatial_hierarchy::SpatialHierarchyPanel* spatial_panel_ = nullptr;
|
||||
QDockWidget* layers_panel_ = nullptr;
|
||||
ifcinterface::modules::properties::PropertiesPanel* properties_panel_ = nullptr;
|
||||
QDockWidget* stored_views_panel_ = nullptr;
|
||||
QDockWidget* search_panel_ = nullptr;
|
||||
QDockWidget* spreadsheet_panel_ = nullptr;
|
||||
QDockWidget* audit_panel_ = nullptr;
|
||||
QDockWidget* clash_panel_ = nullptr;
|
||||
QDockWidget* issues_panel_ = nullptr;
|
||||
ifcinterface::modules::models::ModelsPanelController* models_controller_ = nullptr;
|
||||
ifcinterface::modules::models::ModelsPanelView* models_view_ = nullptr;
|
||||
ifcinterface::modules::project::ProjectController* project_controller_ = nullptr;
|
||||
ifcinterface::modules::spatial_hierarchy::SpatialHierarchyPanelView* spatial_view_ = nullptr;
|
||||
ifcinterface::modules::properties::PropertiesPanelView* properties_view_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
} // namespace ifcinterface::shell
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// This file was generated with the assistance of an AI coding tool.
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// This file was generated with the assistance of an AI coding tool.
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
@@ -17,8 +18,8 @@
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef IFCVIEWER_FULL_MEASUREMENT_H
|
||||
#define IFCVIEWER_FULL_MEASUREMENT_H
|
||||
#ifndef IFCINTERFACE_MEASUREMENT_H
|
||||
#define IFCINTERFACE_MEASUREMENT_H
|
||||
|
||||
#include <cstddef>
|
||||
#include "ViewportWindow.h"
|
||||
@@ -158,4 +159,4 @@ private:
|
||||
ViewportWindow::MeshLocalPick first_pick_{};
|
||||
};
|
||||
|
||||
#endif // IFCVIEWER_FULL_MEASUREMENT_H
|
||||
#endif // IFCINTERFACE_MEASUREMENT_H
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* 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 *
|
||||
* 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, *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include "ModelTransformationDialog.h"
|
||||
#include "Federation.h"
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QRadioButton>
|
||||
#include <QShowEvent>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace {
|
||||
|
||||
QString unitDisplay(const FederationConfig& cfg) {
|
||||
QString s;
|
||||
if (!cfg.unit_prefix.empty()) s += QString::fromStdString(cfg.unit_prefix) + " ";
|
||||
s += QString::fromStdString(cfg.unit_name);
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ModelTransformationDialog::ModelTransformationDialog(Federation* federation,
|
||||
QWidget* parent)
|
||||
: QDialog(parent), federation_(federation)
|
||||
{
|
||||
setWindowTitle("Model Transformation");
|
||||
setupUi();
|
||||
}
|
||||
|
||||
void ModelTransformationDialog::setupUi() {
|
||||
auto* root = new QVBoxLayout(this);
|
||||
|
||||
// Model picker.
|
||||
{
|
||||
auto* row = new QHBoxLayout();
|
||||
row->addWidget(new QLabel("Model:", this));
|
||||
model_combo_ = new QComboBox(this);
|
||||
row->addWidget(model_combo_, 1);
|
||||
root->addLayout(row);
|
||||
connect(model_combo_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &ModelTransformationDialog::onModelChanged);
|
||||
}
|
||||
|
||||
auto build_xyz = [](QDoubleSpinBox*& sb) {
|
||||
sb = new QDoubleSpinBox();
|
||||
sb->setRange(-1e12, 1e12);
|
||||
sb->setDecimals(6);
|
||||
sb->setSingleStep(1.0);
|
||||
};
|
||||
|
||||
// A-frame + A.
|
||||
{
|
||||
auto* group = new QGroupBox("Point A", this);
|
||||
group->setToolTip(
|
||||
"The model-side anchor point. ModelLocal expresses A in the "
|
||||
"model's project length unit, before its CoordinateOperation. "
|
||||
"ModelGlobal expresses A in the model's map unit, after its "
|
||||
"CoordinateOperation.");
|
||||
auto* form = new QFormLayout(group);
|
||||
|
||||
radio_local_ = new QRadioButton("ModelLocal", group);
|
||||
radio_global_ = new QRadioButton("ModelGlobal", group);
|
||||
radio_global_->setChecked(true);
|
||||
auto* af_group = new QButtonGroup(this);
|
||||
af_group->addButton(radio_local_);
|
||||
af_group->addButton(radio_global_);
|
||||
connect(radio_local_, &QRadioButton::toggled,
|
||||
this, &ModelTransformationDialog::onAFrameToggled);
|
||||
connect(radio_global_, &QRadioButton::toggled,
|
||||
this, &ModelTransformationDialog::onAFrameToggled);
|
||||
|
||||
auto* af_row = new QHBoxLayout();
|
||||
af_row->addWidget(radio_local_);
|
||||
af_row->addWidget(radio_global_);
|
||||
af_row->addStretch();
|
||||
form->addRow("Frame", af_row);
|
||||
|
||||
build_xyz(a_x_); build_xyz(a_y_); build_xyz(a_z_);
|
||||
form->addRow("X", a_x_);
|
||||
form->addRow("Y", a_y_);
|
||||
form->addRow("Z", a_z_);
|
||||
a_unit_label_ = new QLabel("(model unit)", group);
|
||||
form->addRow("Unit", a_unit_label_);
|
||||
|
||||
root->addWidget(group);
|
||||
}
|
||||
|
||||
// B.
|
||||
{
|
||||
auto* group = new QGroupBox("Point B (federation target)", this);
|
||||
group->setToolTip(
|
||||
"Where in the federation point A should land. In federation units.");
|
||||
auto* form = new QFormLayout(group);
|
||||
build_xyz(b_x_); build_xyz(b_y_); build_xyz(b_z_);
|
||||
form->addRow("X", b_x_);
|
||||
form->addRow("Y", b_y_);
|
||||
form->addRow("Z", b_z_);
|
||||
b_unit_label_ = new QLabel("(federation unit)", group);
|
||||
form->addRow("Unit", b_unit_label_);
|
||||
root->addWidget(group);
|
||||
}
|
||||
|
||||
// Rotation (intrinsic XYZ).
|
||||
{
|
||||
auto* group = new QGroupBox("Rotation (intrinsic XYZ, degrees)", this);
|
||||
group->setToolTip(
|
||||
"Composed as R = R_z(rz) · R_y(ry) · R_x(rx). Y-up models "
|
||||
"rotated to Z-up: rx = 90, ry = 0, rz = 0.");
|
||||
auto* form = new QFormLayout(group);
|
||||
rx_ = new QDoubleSpinBox(); rx_->setRange(-360, 360); rx_->setDecimals(6);
|
||||
ry_ = new QDoubleSpinBox(); ry_->setRange(-360, 360); ry_->setDecimals(6);
|
||||
rz_ = new QDoubleSpinBox(); rz_->setRange(-360, 360); rz_->setDecimals(6);
|
||||
form->addRow("rx (°)", rx_);
|
||||
form->addRow("ry (°)", ry_);
|
||||
form->addRow("rz (°)", rz_);
|
||||
root->addWidget(group);
|
||||
}
|
||||
|
||||
// Pivot.
|
||||
{
|
||||
auto* group = new QGroupBox("Rotation Pivot", this);
|
||||
group->setToolTip(
|
||||
"The point the rotation rotates around, in federation units. "
|
||||
"Set pivot = B to keep A landing on B regardless of rotation.");
|
||||
auto* form = new QFormLayout(group);
|
||||
build_xyz(pivot_x_); build_xyz(pivot_y_); build_xyz(pivot_z_);
|
||||
form->addRow("X", pivot_x_);
|
||||
form->addRow("Y", pivot_y_);
|
||||
form->addRow("Z", pivot_z_);
|
||||
pivot_unit_label_ = new QLabel("(federation unit)", group);
|
||||
form->addRow("Unit", pivot_unit_label_);
|
||||
root->addWidget(group);
|
||||
}
|
||||
|
||||
auto* button_box = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
root->addWidget(button_box);
|
||||
connect(button_box, &QDialogButtonBox::accepted, this,
|
||||
&ModelTransformationDialog::onAccepted);
|
||||
connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
void ModelTransformationDialog::showEvent(QShowEvent* event) {
|
||||
populateModelCombo();
|
||||
refreshUnitLabels();
|
||||
if (model_combo_->count() > 0) {
|
||||
syncFromModel(model_combo_->currentData().toString());
|
||||
}
|
||||
QDialog::showEvent(event);
|
||||
}
|
||||
|
||||
void ModelTransformationDialog::populateModelCombo() {
|
||||
model_combo_->blockSignals(true);
|
||||
QString prev_id = model_combo_->currentData().toString();
|
||||
model_combo_->clear();
|
||||
if (federation_) {
|
||||
for (const auto& m : federation_->models()) {
|
||||
QString label = m.display_name.isEmpty() ? m.id : m.display_name;
|
||||
model_combo_->addItem(label, m.id);
|
||||
}
|
||||
}
|
||||
int restore = -1;
|
||||
for (int i = 0; i < model_combo_->count(); ++i) {
|
||||
if (model_combo_->itemData(i).toString() == prev_id) { restore = i; break; }
|
||||
}
|
||||
if (restore >= 0) model_combo_->setCurrentIndex(restore);
|
||||
model_combo_->blockSignals(false);
|
||||
}
|
||||
|
||||
void ModelTransformationDialog::syncFromModel(const QString& fed_id) {
|
||||
if (!federation_) return;
|
||||
const Federation::Model* m = federation_->findById(fed_id);
|
||||
if (!m) return;
|
||||
const ModelTransformation& xf = m->model_transformation;
|
||||
|
||||
radio_local_->setChecked(xf.a_frame == AFrame::ModelLocal);
|
||||
radio_global_->setChecked(xf.a_frame == AFrame::ModelGlobal);
|
||||
|
||||
a_x_->setValue(xf.a.x()); a_y_->setValue(xf.a.y()); a_z_->setValue(xf.a.z());
|
||||
b_x_->setValue(xf.b.x()); b_y_->setValue(xf.b.y()); b_z_->setValue(xf.b.z());
|
||||
rx_->setValue(xf.rxyz_deg.x());
|
||||
ry_->setValue(xf.rxyz_deg.y());
|
||||
rz_->setValue(xf.rxyz_deg.z());
|
||||
pivot_x_->setValue(xf.pivot.x());
|
||||
pivot_y_->setValue(xf.pivot.y());
|
||||
pivot_z_->setValue(xf.pivot.z());
|
||||
}
|
||||
|
||||
void ModelTransformationDialog::refreshUnitLabels() {
|
||||
if (!federation_) return;
|
||||
const QString fed_unit = unitDisplay(federation_->config());
|
||||
b_unit_label_->setText("(federation: " + fed_unit + ")");
|
||||
pivot_unit_label_->setText("(federation: " + fed_unit + ")");
|
||||
onAFrameToggled();
|
||||
}
|
||||
|
||||
void ModelTransformationDialog::onModelChanged(int /*idx*/) {
|
||||
if (model_combo_->count() == 0) return;
|
||||
syncFromModel(model_combo_->currentData().toString());
|
||||
}
|
||||
|
||||
void ModelTransformationDialog::onAFrameToggled() {
|
||||
if (!a_unit_label_) return;
|
||||
if (radio_local_->isChecked()) {
|
||||
a_unit_label_->setText("(model project length unit, e.g. millimetre)");
|
||||
} else {
|
||||
a_unit_label_->setText("(model map unit, e.g. metre)");
|
||||
}
|
||||
}
|
||||
|
||||
void ModelTransformationDialog::onAccepted() {
|
||||
if (!federation_ || model_combo_->count() == 0) { accept(); return; }
|
||||
const QString fed_id = model_combo_->currentData().toString();
|
||||
|
||||
ModelTransformation xf;
|
||||
xf.a_frame = radio_local_->isChecked() ? AFrame::ModelLocal : AFrame::ModelGlobal;
|
||||
xf.a = Eigen::Vector3d(a_x_->value(), a_y_->value(), a_z_->value());
|
||||
xf.b = Eigen::Vector3d(b_x_->value(), b_y_->value(), b_z_->value());
|
||||
xf.rxyz_deg = Eigen::Vector3d(rx_->value(), ry_->value(), rz_->value());
|
||||
xf.pivot = Eigen::Vector3d(pivot_x_->value(), pivot_y_->value(), pivot_z_->value());
|
||||
|
||||
federation_->setModelTransformation(fed_id, xf);
|
||||
accept();
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* 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 *
|
||||
* 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, *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef MODELTRANSFORMATIONDIALOG_H
|
||||
#define MODELTRANSFORMATIONDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QComboBox;
|
||||
class QDoubleSpinBox;
|
||||
class QLabel;
|
||||
class QRadioButton;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class Federation;
|
||||
|
||||
// Edits one model's ModelTransformation (the per-model placement within
|
||||
// the federation: a_frame, a, b, rxyz Euler degrees, pivot). A combobox
|
||||
// at the top picks which model to edit. Switching models discards any
|
||||
// unsaved form edits — Ok saves the currently-visible model. On Ok,
|
||||
// calls Federation::setModelTransformation, which fires
|
||||
// modelTransformationChanged → MainWindow recomposes the viewport for
|
||||
// that model.
|
||||
class ModelTransformationDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModelTransformationDialog(Federation* federation, QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void onModelChanged(int idx);
|
||||
void onAFrameToggled();
|
||||
void onAccepted();
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
void populateModelCombo();
|
||||
void syncFromModel(const QString& fed_id);
|
||||
void refreshUnitLabels();
|
||||
|
||||
Federation* federation_ = nullptr;
|
||||
|
||||
QComboBox* model_combo_ = nullptr;
|
||||
|
||||
QRadioButton* radio_local_ = nullptr;
|
||||
QRadioButton* radio_global_ = nullptr;
|
||||
|
||||
QDoubleSpinBox* a_x_ = nullptr;
|
||||
QDoubleSpinBox* a_y_ = nullptr;
|
||||
QDoubleSpinBox* a_z_ = nullptr;
|
||||
QLabel* a_unit_label_ = nullptr;
|
||||
|
||||
QDoubleSpinBox* b_x_ = nullptr;
|
||||
QDoubleSpinBox* b_y_ = nullptr;
|
||||
QDoubleSpinBox* b_z_ = nullptr;
|
||||
QLabel* b_unit_label_ = nullptr;
|
||||
|
||||
QDoubleSpinBox* rx_ = nullptr;
|
||||
QDoubleSpinBox* ry_ = nullptr;
|
||||
QDoubleSpinBox* rz_ = nullptr;
|
||||
|
||||
QDoubleSpinBox* pivot_x_ = nullptr;
|
||||
QDoubleSpinBox* pivot_y_ = nullptr;
|
||||
QDoubleSpinBox* pivot_z_ = nullptr;
|
||||
QLabel* pivot_unit_label_ = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,171 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* 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 *
|
||||
* 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, *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include "SettingsWindow.h"
|
||||
#include "AppSettings.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QShowEvent>
|
||||
#include <QSpinBox>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
SettingsWindow::SettingsWindow(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle("Settings");
|
||||
setupUi();
|
||||
}
|
||||
|
||||
void SettingsWindow::setupUi() {
|
||||
auto* form = new QFormLayout();
|
||||
|
||||
geometry_library_edit_ = new QLineEdit(this);
|
||||
geometry_library_edit_->setMinimumWidth(280);
|
||||
form->addRow("Geometry Library", geometry_library_edit_);
|
||||
|
||||
show_stats_check_ = new QCheckBox(this);
|
||||
form->addRow("Show Performance Stats", show_stats_check_);
|
||||
|
||||
backface_culling_check_ = new QCheckBox(this);
|
||||
backface_culling_check_->setToolTip(
|
||||
"Skip triangles facing away from the camera. Big FPS win on "
|
||||
"closed solids; disable if you see holes in open geometry.");
|
||||
form->addRow("Backface Culling", backface_culling_check_);
|
||||
|
||||
void_limit_spin_ = new QSpinBox(this);
|
||||
void_limit_spin_->setRange(0, 100000);
|
||||
void_limit_spin_->setToolTip(
|
||||
"Skip elements with more openings (HasOpenings) than this. "
|
||||
"A handful of pathological elements can dominate boolean-subtraction "
|
||||
"time; dropping them keeps load times sane.");
|
||||
form->addRow("Void Limit", void_limit_spin_);
|
||||
|
||||
deflection_tolerance_spin_ = new QDoubleSpinBox(this);
|
||||
deflection_tolerance_spin_->setRange(0.000001, 1000.0);
|
||||
deflection_tolerance_spin_->setDecimals(6);
|
||||
deflection_tolerance_spin_->setSingleStep(0.001);
|
||||
deflection_tolerance_spin_->setToolTip(
|
||||
"Linear chord error between curved geometry and its triangulation, "
|
||||
"in model length units. Smaller = smoother curves but more "
|
||||
"triangles and slower load.");
|
||||
form->addRow("Deflection Tolerance", deflection_tolerance_spin_);
|
||||
|
||||
angular_tolerance_spin_ = new QDoubleSpinBox(this);
|
||||
angular_tolerance_spin_->setRange(0.000001, 3.141592);
|
||||
angular_tolerance_spin_->setDecimals(6);
|
||||
angular_tolerance_spin_->setSingleStep(0.05);
|
||||
angular_tolerance_spin_->setToolTip(
|
||||
"Maximum angle (radians) between adjacent facet normals on a "
|
||||
"curved surface. Smaller = smoother shading but more triangles.");
|
||||
form->addRow("Angular Tolerance", angular_tolerance_spin_);
|
||||
|
||||
min_pixel_radius_spin_ = new QDoubleSpinBox(this);
|
||||
min_pixel_radius_spin_->setRange(0.0, 100.0);
|
||||
min_pixel_radius_spin_->setDecimals(2);
|
||||
min_pixel_radius_spin_->setSingleStep(0.5);
|
||||
min_pixel_radius_spin_->setToolTip(
|
||||
"Minimum projected sphere radius (in pixels) for an instance to "
|
||||
"be drawn. Bigger = faster but more pop-in on small detail.");
|
||||
form->addRow("Min Pixel Radius", min_pixel_radius_spin_);
|
||||
|
||||
motion_min_pixel_radius_spin_ = new QDoubleSpinBox(this);
|
||||
motion_min_pixel_radius_spin_->setRange(0.0, 100.0);
|
||||
motion_min_pixel_radius_spin_->setDecimals(2);
|
||||
motion_min_pixel_radius_spin_->setSingleStep(1.0);
|
||||
motion_min_pixel_radius_spin_->setToolTip(
|
||||
"Aggressive cull threshold while the camera is moving. 0 = no "
|
||||
"motion boost (motion uses the same threshold as still frames). "
|
||||
"Big perceived FPS win on heavy scenes.");
|
||||
form->addRow("Motion Min Pixel Radius", motion_min_pixel_radius_spin_);
|
||||
|
||||
lod1_pixel_threshold_spin_ = new QDoubleSpinBox(this);
|
||||
lod1_pixel_threshold_spin_->setRange(0.0, 1000.0);
|
||||
lod1_pixel_threshold_spin_->setDecimals(1);
|
||||
lod1_pixel_threshold_spin_->setSingleStep(1.0);
|
||||
lod1_pixel_threshold_spin_->setToolTip(
|
||||
"Pixel radius below which an instance switches to its LOD1 "
|
||||
"representation. 0 disables LOD1 entirely (always draw LOD0).");
|
||||
form->addRow("LOD1 Pixel Threshold", lod1_pixel_threshold_spin_);
|
||||
|
||||
hiz_enabled_check_ = new QCheckBox(this);
|
||||
hiz_enabled_check_->setToolTip(
|
||||
"Enable HiZ (hierarchical Z) occlusion culling. Hides geometry "
|
||||
"behind opaque blockers based on a downsampled depth pyramid "
|
||||
"from the previous frame. Big perf win on dense interiors.");
|
||||
form->addRow("HiZ Occlusion", hiz_enabled_check_);
|
||||
|
||||
hiz_resolution_spin_ = new QSpinBox(this);
|
||||
hiz_resolution_spin_->setRange(64, 4096);
|
||||
hiz_resolution_spin_->setSingleStep(64);
|
||||
hiz_resolution_spin_->setToolTip(
|
||||
"Base HiZ pyramid width in texels (height tracks aspect). "
|
||||
"Bigger = tighter occlusion but more readback bandwidth. "
|
||||
"Changes take effect on next viewport reinitialization.");
|
||||
form->addRow("HiZ Resolution", hiz_resolution_spin_);
|
||||
|
||||
auto* button_box = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
|
||||
auto* root = new QVBoxLayout(this);
|
||||
root->addLayout(form);
|
||||
root->addWidget(button_box);
|
||||
|
||||
connect(button_box, &QDialogButtonBox::accepted, this, &SettingsWindow::onAccepted);
|
||||
connect(button_box, &QDialogButtonBox::rejected, this, &SettingsWindow::reject);
|
||||
}
|
||||
|
||||
void SettingsWindow::showEvent(QShowEvent* event) {
|
||||
// Re-sync widgets from the persisted settings every time the dialog is
|
||||
// shown, so a previous Cancel doesn't leave stale text in the field.
|
||||
syncFromSettings();
|
||||
QDialog::showEvent(event);
|
||||
}
|
||||
|
||||
void SettingsWindow::syncFromSettings() {
|
||||
geometry_library_edit_->setText(AppSettings::instance().geometryLibrary());
|
||||
show_stats_check_->setChecked(AppSettings::instance().showStats());
|
||||
backface_culling_check_->setChecked(AppSettings::instance().backfaceCulling());
|
||||
void_limit_spin_->setValue(AppSettings::instance().voidLimit());
|
||||
deflection_tolerance_spin_->setValue(AppSettings::instance().deflectionTolerance());
|
||||
angular_tolerance_spin_->setValue(AppSettings::instance().angularTolerance());
|
||||
min_pixel_radius_spin_->setValue(AppSettings::instance().minPixelRadius());
|
||||
motion_min_pixel_radius_spin_->setValue(AppSettings::instance().motionMinPixelRadius());
|
||||
lod1_pixel_threshold_spin_->setValue(AppSettings::instance().lod1PixelThreshold());
|
||||
hiz_enabled_check_->setChecked(AppSettings::instance().hizEnabled());
|
||||
hiz_resolution_spin_->setValue(AppSettings::instance().hizResolution());
|
||||
}
|
||||
|
||||
void SettingsWindow::onAccepted() {
|
||||
AppSettings::instance().setGeometryLibrary(geometry_library_edit_->text());
|
||||
AppSettings::instance().setShowStats(show_stats_check_->isChecked());
|
||||
AppSettings::instance().setBackfaceCulling(backface_culling_check_->isChecked());
|
||||
AppSettings::instance().setVoidLimit(void_limit_spin_->value());
|
||||
AppSettings::instance().setDeflectionTolerance(deflection_tolerance_spin_->value());
|
||||
AppSettings::instance().setAngularTolerance(angular_tolerance_spin_->value());
|
||||
AppSettings::instance().setMinPixelRadius(min_pixel_radius_spin_->value());
|
||||
AppSettings::instance().setMotionMinPixelRadius(motion_min_pixel_radius_spin_->value());
|
||||
AppSettings::instance().setLod1PixelThreshold(lod1_pixel_threshold_spin_->value());
|
||||
AppSettings::instance().setHizEnabled(hiz_enabled_check_->isChecked());
|
||||
AppSettings::instance().setHizResolution(hiz_resolution_spin_->value());
|
||||
accept();
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* 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 *
|
||||
* 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, *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef SETTINGSWINDOW_H
|
||||
#define SETTINGSWINDOW_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QCheckBox;
|
||||
class QDoubleSpinBox;
|
||||
class QLineEdit;
|
||||
class QShowEvent;
|
||||
class QSpinBox;
|
||||
|
||||
class SettingsWindow : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SettingsWindow(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void onAccepted();
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
void syncFromSettings();
|
||||
|
||||
QLineEdit* geometry_library_edit_ = nullptr;
|
||||
QCheckBox* show_stats_check_ = nullptr;
|
||||
QCheckBox* backface_culling_check_ = nullptr;
|
||||
QSpinBox* void_limit_spin_ = nullptr;
|
||||
QDoubleSpinBox* deflection_tolerance_spin_ = nullptr;
|
||||
QDoubleSpinBox* angular_tolerance_spin_ = nullptr;
|
||||
QDoubleSpinBox* min_pixel_radius_spin_ = nullptr;
|
||||
QDoubleSpinBox* motion_min_pixel_radius_spin_ = nullptr;
|
||||
QDoubleSpinBox* lod1_pixel_threshold_spin_ = nullptr;
|
||||
QSpinBox* hiz_resolution_spin_ = nullptr;
|
||||
QCheckBox* hiz_enabled_check_ = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1005 B After Width: | Height: | Size: 1005 B |
|
Before Width: | Height: | Size: 644 B After Width: | Height: | Size: 644 B |
|
Before Width: | Height: | Size: 227 B After Width: | Height: | Size: 227 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 562 B After Width: | Height: | Size: 562 B |
|
Before Width: | Height: | Size: 556 B After Width: | Height: | Size: 556 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 834 B After Width: | Height: | Size: 834 B |
|
Before Width: | Height: | Size: 437 B After Width: | Height: | Size: 437 B |
|
Before Width: | Height: | Size: 611 B After Width: | Height: | Size: 611 B |
|
Before Width: | Height: | Size: 688 B After Width: | Height: | Size: 688 B |
|
Before Width: | Height: | Size: 528 B After Width: | Height: | Size: 528 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 532 B |
|
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 465 B |
|
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 426 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 674 B After Width: | Height: | Size: 674 B |
|
Before Width: | Height: | Size: 942 B After Width: | Height: | Size: 942 B |
|
Before Width: | Height: | Size: 691 B After Width: | Height: | Size: 691 B |
|
Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 632 B |
|
Before Width: | Height: | Size: 651 B After Width: | Height: | Size: 651 B |
|
Before Width: | Height: | Size: 460 B After Width: | Height: | Size: 460 B |
|
Before Width: | Height: | Size: 700 B After Width: | Height: | Size: 700 B |
|
Before Width: | Height: | Size: 579 B After Width: | Height: | Size: 579 B |
|
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 426 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 435 B After Width: | Height: | Size: 435 B |
|
Before Width: | Height: | Size: 795 B After Width: | Height: | Size: 795 B |
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 995 B |
|
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |
|
Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 717 B |
|
Before Width: | Height: | Size: 1020 B After Width: | Height: | Size: 1020 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 875 B After Width: | Height: | Size: 875 B |
|
Before Width: | Height: | Size: 786 B After Width: | Height: | Size: 786 B |
|
Before Width: | Height: | Size: 533 B After Width: | Height: | Size: 533 B |
|
Before Width: | Height: | Size: 516 B After Width: | Height: | Size: 516 B |
|
Before Width: | Height: | Size: 545 B After Width: | Height: | Size: 545 B |
@@ -1,3 +1,4 @@
|
||||
// This file was generated with the assistance of an AI coding tool.
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
@@ -17,18 +18,40 @@
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "InterfaceSettings.h"
|
||||
#include "components/Style.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QFont>
|
||||
#include <QFontDatabase>
|
||||
#include <QSurfaceFormat>
|
||||
|
||||
namespace {
|
||||
|
||||
void installUiFont() {
|
||||
const int font_id = QFontDatabase::addApplicationFont(
|
||||
":/fonts/DMSans-VariableFont_opsz,wght.ttf");
|
||||
QString family;
|
||||
if (font_id >= 0) {
|
||||
const QStringList families = QFontDatabase::applicationFontFamilies(font_id);
|
||||
if (!families.isEmpty()) {
|
||||
family = families.front();
|
||||
}
|
||||
}
|
||||
if (!family.isEmpty()) {
|
||||
QApplication::setFont(QFont(family, 10));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
QApplication app(argc, argv);
|
||||
app.setApplicationName("IfcViewer");
|
||||
app.setOrganizationName("IfcOpenShell");
|
||||
|
||||
// Request OpenGL 4.5 Core globally
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setVersion(4, 5);
|
||||
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||
@@ -38,39 +61,21 @@ int main(int argc, char* argv[]) {
|
||||
QSurfaceFormat::setDefaultFormat(fmt);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("IfcOpenShell IFC Viewer");
|
||||
parser.setApplicationDescription("IfcOpenShell IFC viewer");
|
||||
parser.addHelpOption();
|
||||
parser.addPositionalArgument("files",
|
||||
"IFC file(s) and/or one .ifcfed federation to open",
|
||||
"[files...]");
|
||||
parser.addOption({{"c", "camera"},
|
||||
"Set camera: tx,ty,tz,dist,yaw,pitch", "params"});
|
||||
parser.addOption({{"b", "benchmark"},
|
||||
"Run N frames then print stats and exit", "frames"});
|
||||
parser.process(app);
|
||||
|
||||
MainWindow window;
|
||||
installUiFont();
|
||||
const auto applyStyle = [&app]() {
|
||||
app.setStyleSheet(ifcinterface::components::style::buildAppStyleSheet());
|
||||
};
|
||||
applyStyle();
|
||||
QObject::connect(&ifcinterface::InterfaceSettings::instance(),
|
||||
&ifcinterface::InterfaceSettings::themeChanged,
|
||||
&app,
|
||||
applyStyle);
|
||||
|
||||
ifcinterface::shell::MainWindow window;
|
||||
window.show();
|
||||
|
||||
auto args = parser.positionalArguments();
|
||||
QStringList file_args;
|
||||
QString fed_arg;
|
||||
for (const auto& a : args) {
|
||||
if (fed_arg.isEmpty() && a.endsWith(".ifcfed", Qt::CaseInsensitive)) {
|
||||
fed_arg = a;
|
||||
} else {
|
||||
file_args << a;
|
||||
}
|
||||
}
|
||||
if (!fed_arg.isEmpty()) window.openFederation(fed_arg);
|
||||
if (!file_args.isEmpty()) window.addFiles(file_args);
|
||||
|
||||
if (parser.isSet("camera")) {
|
||||
window.setPendingCamera(parser.value("camera"));
|
||||
}
|
||||
if (parser.isSet("benchmark")) {
|
||||
window.setPendingBenchmark(parser.value("benchmark").toInt());
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ void ModelsPanelController::bindLoader(SceneLoader* loader) {
|
||||
connect(loader, &SceneLoader::loadError, this,
|
||||
[this, host = host_](uint32_t /*mid*/, const QString& message) {
|
||||
session_state_->setStatusMessage("Error", message);
|
||||
QMessageBox::warning(host, "IfcInterfaceMockup", message);
|
||||
QMessageBox::warning(host, "IfcViewer", message);
|
||||
});
|
||||
connect(loader, &SceneLoader::allLoadsFinished, this,
|
||||
[this, loader]() {
|
||||