diff --git a/src/ifcviewer-full/FederationSettingsDialog.cpp b/src/ifcviewer-full/FederationSettingsDialog.cpp new file mode 100644 index 0000000000..aa55eddeba --- /dev/null +++ b/src/ifcviewer-full/FederationSettingsDialog.cpp @@ -0,0 +1,174 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include "FederationSettingsDialog.h" +#include "Federation.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(); +} diff --git a/src/ifcviewer-full/FederationSettingsDialog.h b/src/ifcviewer-full/FederationSettingsDialog.h new file mode 100644 index 0000000000..04cf8ebe3b --- /dev/null +++ b/src/ifcviewer-full/FederationSettingsDialog.h @@ -0,0 +1,61 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#ifndef FEDERATIONSETTINGSDIALOG_H +#define FEDERATIONSETTINGSDIALOG_H + +#include + +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 diff --git a/src/ifcviewer-full/MainWindow.cpp b/src/ifcviewer-full/MainWindow.cpp index a07ad82db8..0fd00530b1 100644 --- a/src/ifcviewer-full/MainWindow.cpp +++ b/src/ifcviewer-full/MainWindow.cpp @@ -20,6 +20,7 @@ #include "MainWindow.h" #include "AppSettings.h" #include "Federation.h" +#include "FederationSettingsDialog.h" #include "SettingsWindow.h" #include "LodBuilder.h" #include "SidecarCache.h" @@ -189,6 +190,8 @@ void MainWindow::setupMenus() { file_menu->addAction("Save Federation &As...", this, &MainWindow::onFederationSaveAs, QKeySequence::SaveAs); + file_menu->addAction("Federation Se&ttings...", + this, &MainWindow::onFederationSettings); file_menu->addSeparator(); file_menu->addAction("&Settings...", this, &MainWindow::onFileSettings); file_menu->addSeparator(); @@ -228,6 +231,15 @@ void MainWindow::onFileSettings() { settings_->raise(); } +void MainWindow::onFederationSettings() { + if (federation_settings_ == nullptr) { + federation_settings_ = new FederationSettingsDialog(federation_, this); + } + federation_settings_->open(); + federation_settings_->activateWindow(); + federation_settings_->raise(); +} + void MainWindow::addFiles(const QStringList& paths) { QStringList accepted_paths; QStringList accepted_fed_ids; diff --git a/src/ifcviewer-full/MainWindow.h b/src/ifcviewer-full/MainWindow.h index 86bfc3c00c..20a514c073 100644 --- a/src/ifcviewer-full/MainWindow.h +++ b/src/ifcviewer-full/MainWindow.h @@ -36,6 +36,7 @@ class Federation; class SettingsWindow; +class FederationSettingsDialog; class MainWindow : public QMainWindow { Q_OBJECT @@ -61,6 +62,7 @@ private slots: void onSetHomeView(); void onGoHomeView(); void onFileSettings(); + void onFederationSettings(); void onObjectPicked(uint32_t object_id); void onTreeSelectionChanged(); @@ -119,6 +121,7 @@ private: SceneLoader* loader_ = nullptr; Federation* federation_ = nullptr; SettingsWindow* settings_ = nullptr; + FederationSettingsDialog* federation_settings_ = nullptr; QWidget* viewport_container_ = nullptr; QTreeWidget* element_tree_ = nullptr; QTableWidget* property_table_ = nullptr;