mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Interface mockup 16
This commit is contained in:
@@ -1690,16 +1690,6 @@ ViewportWindow::CameraState ViewportWindow::cameraState() const {
|
||||
void ViewportWindow::keyPressEvent(QKeyEvent* event) {
|
||||
const int key = event->key();
|
||||
|
||||
// Shift+F toggles FPS/fly mode. Checked before auto-repeat filtering so
|
||||
// a held Shift+F doesn't thrash between modes.
|
||||
if (key == Qt::Key_F
|
||||
&& (event->modifiers() & Qt::ShiftModifier)
|
||||
&& !event->isAutoRepeat()) {
|
||||
if (camera_mode_ == CameraMode::Fps) exitFpsMode();
|
||||
else enterFpsMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (camera_mode_ == CameraMode::Fps) {
|
||||
if (key == Qt::Key_Escape && !event->isAutoRepeat()) {
|
||||
exitFpsMode();
|
||||
@@ -1731,7 +1721,6 @@ void ViewportWindow::keyPressEvent(QKeyEvent* event) {
|
||||
}
|
||||
|
||||
// Plain F (no modifiers): focus camera on the currently selected object.
|
||||
// Shift+F is FPS-mode toggle and was handled above.
|
||||
if (key == Qt::Key_F
|
||||
&& event->modifiers() == Qt::NoModifier
|
||||
&& !event->isAutoRepeat()) {
|
||||
@@ -3678,14 +3667,16 @@ void ViewportWindow::handleMousePress(QMouseEvent* e) {
|
||||
// gesture. Tool-mode LMB defers pick handling to surfacePickedInTool
|
||||
// on release for tools that consume clicks (Area / Length); Volume is
|
||||
// passive and routes input through normal selection.
|
||||
const bool tool_consumes_clicks =
|
||||
(tool_mode_ == ToolMode::Area || tool_mode_ == ToolMode::Length);
|
||||
if (e->button() == Qt::LeftButton && !tool_consumes_clicks) {
|
||||
press_pick_id_ = pickObjectAt(e->pos().x(), e->pos().y());
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
box_select_start_pos_ = e->pos();
|
||||
box_select_current_pos_ = e->pos();
|
||||
box_select_press_mods_ = e->modifiers();
|
||||
box_select_armed_ = true;
|
||||
const bool tool_consumes_clicks =
|
||||
(tool_mode_ == ToolMode::Area || tool_mode_ == ToolMode::Length);
|
||||
if (!tool_consumes_clicks) {
|
||||
press_pick_id_ = pickObjectAt(e->pos().x(), e->pos().y());
|
||||
box_select_press_mods_ = e->modifiers();
|
||||
box_select_armed_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
void ViewportWindow::handleMouseRelease(QMouseEvent* e) {
|
||||
|
||||
@@ -314,6 +314,7 @@ public:
|
||||
void setModelCoordinateOperation(uint32_t model_id, const Eigen::Matrix4d& matrix_meters);
|
||||
void setModelTransformation(uint32_t model_id, const Eigen::Matrix4d& matrix_meters);
|
||||
void setBackgroundColor(const QColor& color);
|
||||
void enterFpsMode();
|
||||
|
||||
// Selection. The viewport owns a SelectionState that tracks both
|
||||
// the multi-set and the "active" (last single-clicked) id. External
|
||||
@@ -601,7 +602,6 @@ private:
|
||||
// way one long frame only produces a single catch-up step instead of
|
||||
// also missing a QTimer tick.
|
||||
enum class CameraMode { Orbit, Fps };
|
||||
void enterFpsMode();
|
||||
void exitFpsMode();
|
||||
void fpsIntegrate(); // called from render()
|
||||
void recenterFpsCursor();
|
||||
|
||||
@@ -80,6 +80,8 @@ set(INTERFACE_FILES
|
||||
${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}/../ifcviewer-full/Measurement.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../ifcviewer-full/Measurement.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/interface_resources.qrc
|
||||
)
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QShortcut>
|
||||
#include <QSignalBlocker>
|
||||
#include <QStackedWidget>
|
||||
#include <QStatusBar>
|
||||
@@ -91,6 +92,33 @@ void MainWindow::setupChrome() {
|
||||
setDockOptions(QMainWindow::AllowNestedDocks |
|
||||
QMainWindow::AllowTabbedDocks |
|
||||
QMainWindow::GroupedDragging);
|
||||
|
||||
auto bind_shortcut = [this](const QKeySequence& sequence, auto fn) {
|
||||
auto* shortcut = new QShortcut(sequence, this);
|
||||
shortcut->setContext(Qt::WindowShortcut);
|
||||
connect(shortcut, &QShortcut::activated, this, fn);
|
||||
};
|
||||
bind_shortcut(QKeySequence("Ctrl+Shift+L"), [this]() {
|
||||
viewport_controller_->toggleDistanceMode();
|
||||
});
|
||||
bind_shortcut(QKeySequence("Ctrl+Shift+A"), [this]() {
|
||||
viewport_controller_->toggleAreaMode();
|
||||
});
|
||||
bind_shortcut(QKeySequence("Ctrl+Shift+V"), [this]() {
|
||||
viewport_controller_->toggleVolumeMode();
|
||||
});
|
||||
bind_shortcut(QKeySequence(Qt::SHIFT | Qt::Key_F), [this]() {
|
||||
viewport_controller_->setFlyMode();
|
||||
});
|
||||
bind_shortcut(QKeySequence(Qt::Key_H), [this]() {
|
||||
viewport_controller_->hideSelectedElements();
|
||||
});
|
||||
bind_shortcut(QKeySequence(Qt::SHIFT | Qt::Key_H), [this]() {
|
||||
viewport_controller_->isolateSelectedElements();
|
||||
});
|
||||
bind_shortcut(QKeySequence(Qt::ALT | Qt::Key_H), [this]() {
|
||||
viewport_controller_->showAllElements();
|
||||
});
|
||||
updateWindowTitle();
|
||||
}
|
||||
|
||||
@@ -211,18 +239,14 @@ QWidget* MainWindow::buildNavigateRibbonPage() {
|
||||
viewport_widget_->viewport()->projectionOrtho() ? "Ortho" : "Perspective");
|
||||
});
|
||||
|
||||
auto* orbit_mode = components::buttons::makeButton("Orbit", ":/icons/rotate-camera-right.svg", this);
|
||||
connect(orbit_mode, &QToolButton::clicked, this, [this]() {
|
||||
session_state_->setStatusMessage("Mode", "Orbit mode active");
|
||||
});
|
||||
auto* fly_mode = components::buttons::makeButton("Fly", ":/icons/drone.svg", this);
|
||||
connect(fly_mode, &QToolButton::clicked, this, [this]() {
|
||||
session_state_->setStatusMessage("Mode", "Fly mode coming soon");
|
||||
viewport_controller_->setFlyMode();
|
||||
});
|
||||
|
||||
row->addWidget(components::buttons::makeButtonGroup("CAMERA", {set_home, go_home, view_all, view_selected}, this));
|
||||
row->addWidget(components::buttons::makeButtonGroup("ORIENTATION", {plan_view, front_view, side_view, align_object, projection_button}, this));
|
||||
row->addWidget(components::buttons::makeButtonGroup("MODE", {orbit_mode, fly_mode}, this));
|
||||
row->addWidget(components::buttons::makeButtonGroup("MODE", {fly_mode}, this));
|
||||
row->addStretch(1);
|
||||
return page;
|
||||
}
|
||||
@@ -236,15 +260,15 @@ QWidget* MainWindow::buildInspectRibbonPage() {
|
||||
|
||||
auto* hide_selected = components::buttons::makeButton("Hide", ":/icons/eye-closed.svg", this);
|
||||
connect(hide_selected, &QToolButton::clicked, this, [this]() {
|
||||
session_state_->setStatusMessage("Selection", "Hide selected coming soon");
|
||||
viewport_controller_->hideSelectedElements();
|
||||
});
|
||||
auto* isolate_selected = components::buttons::makeButton("Isolate", ":/icons/eye-solid.svg", this);
|
||||
connect(isolate_selected, &QToolButton::clicked, this, [this]() {
|
||||
session_state_->setStatusMessage("Selection", "Isolate selected coming soon");
|
||||
viewport_controller_->isolateSelectedElements();
|
||||
});
|
||||
auto* show_all = components::buttons::makeButton("Show All", ":/icons/eye.svg", this);
|
||||
connect(show_all, &QToolButton::clicked, this, [this]() {
|
||||
session_state_->setStatusMessage("Selection", "Show all coming soon");
|
||||
viewport_controller_->showAllElements();
|
||||
});
|
||||
auto* invert_selection = components::buttons::makeButton("Invert", ":/icons/intersect.svg", this);
|
||||
connect(invert_selection, &QToolButton::clicked, this, [this]() {
|
||||
@@ -253,15 +277,15 @@ QWidget* MainWindow::buildInspectRibbonPage() {
|
||||
|
||||
auto* distance = components::buttons::makeButton("Distance", ":/icons/select-edge3d.svg", this);
|
||||
connect(distance, &QToolButton::clicked, this, [this]() {
|
||||
session_state_->setStatusMessage("Measure", "Distance coming soon");
|
||||
viewport_controller_->toggleDistanceMode();
|
||||
});
|
||||
auto* area = components::buttons::makeButton("Area", ":/icons/select-face3d.svg", this);
|
||||
connect(area, &QToolButton::clicked, this, [this]() {
|
||||
session_state_->setStatusMessage("Measure", "Area coming soon");
|
||||
viewport_controller_->toggleAreaMode();
|
||||
});
|
||||
auto* volume = components::buttons::makeButton("Volume", ":/icons/select-point3d.svg", this);
|
||||
connect(volume, &QToolButton::clicked, this, [this]() {
|
||||
session_state_->setStatusMessage("Measure", "Volume coming soon");
|
||||
viewport_controller_->toggleVolumeMode();
|
||||
});
|
||||
|
||||
row->addWidget(components::buttons::makeButtonGroup("SELECTION", {hide_selected, isolate_selected, show_all, invert_selection}, this));
|
||||
|
||||
@@ -26,8 +26,13 @@
|
||||
#include "../../../ifcviewer/Federation.h"
|
||||
#include "../../../ifcviewer/SceneLoader.h"
|
||||
#include "../../../ifcviewer/ViewportWindow.h"
|
||||
#include "../../../ifcviewer/OverlayRenderer.h"
|
||||
#include "../../../ifcviewer-full/Measurement.h"
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <QVector3D>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ifcinterface::modules::viewport {
|
||||
|
||||
@@ -37,6 +42,8 @@ ViewportController::ViewportController(ifcinterface::SessionState* session_state
|
||||
: QObject(parent)
|
||||
, session_state_(session_state)
|
||||
, viewport_(viewport)
|
||||
, area_measurement_(std::make_unique<AreaMeasurement>())
|
||||
, length_measurement_(std::make_unique<LengthMeasurement>())
|
||||
{
|
||||
Federation* federation = session_state_->federation();
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
@@ -91,8 +98,60 @@ ViewportController::ViewportController(ifcinterface::SessionState* session_state
|
||||
applyModelVisibility(mid);
|
||||
maybeGuessFederatedFalseOrigin(mid);
|
||||
});
|
||||
connect(viewport_, &ViewportWindow::surfacePickedInTool, this,
|
||||
[this](int x, int y, int modifiers) {
|
||||
const bool alt = (modifiers & Qt::AltModifier) != 0;
|
||||
switch (viewport_->toolMode()) {
|
||||
case ViewportWindow::ToolMode::Area:
|
||||
area_measurement_->onPick(*viewport_, x, y, alt);
|
||||
viewport_->setHudText(QString("Area: %1 m² (%2 tris)")
|
||||
.arg(area_measurement_->totalArea(), 0, 'f', 4)
|
||||
.arg(area_measurement_->triangleCount()));
|
||||
break;
|
||||
case ViewportWindow::ToolMode::Length:
|
||||
length_measurement_->onPick(*viewport_, x, y, alt);
|
||||
break;
|
||||
case ViewportWindow::ToolMode::None:
|
||||
case ViewportWindow::ToolMode::Volume:
|
||||
break;
|
||||
}
|
||||
});
|
||||
connect(viewport_, &ViewportWindow::toolModeChanged, this,
|
||||
[this](ViewportWindow::ToolMode mode) {
|
||||
area_measurement_->clear(*viewport_);
|
||||
length_measurement_->clear(*viewport_);
|
||||
switch (mode) {
|
||||
case ViewportWindow::ToolMode::None:
|
||||
viewport_->setHudText(QString());
|
||||
viewport_->setOverlayLabels({});
|
||||
session_state_->setStatusMessage("Measure", "Measurement tool off");
|
||||
break;
|
||||
case ViewportWindow::ToolMode::Length:
|
||||
viewport_->setHudText("Length tool: click first point");
|
||||
session_state_->setStatusMessage("Measure", "Length tool: LMB add point, Backspace remove last, Esc exits");
|
||||
break;
|
||||
case ViewportWindow::ToolMode::Area:
|
||||
viewport_->setHudText("Area: 0.0000 m² (0 tris)");
|
||||
session_state_->setStatusMessage("Measure", "Area tool: LMB add, Alt+LMB single tri, click again to remove, Esc exits");
|
||||
break;
|
||||
case ViewportWindow::ToolMode::Volume:
|
||||
session_state_->setStatusMessage("Measure", "Volume tool: click / box-select objects, Esc exits");
|
||||
updateVolumeReadout();
|
||||
break;
|
||||
}
|
||||
});
|
||||
connect(viewport_, &ViewportWindow::toolBackspacePressed, this, [this]() {
|
||||
if (viewport_->toolMode() == ViewportWindow::ToolMode::Length) {
|
||||
length_measurement_->removeLastPoint(*viewport_);
|
||||
}
|
||||
});
|
||||
connect(viewport_, &ViewportWindow::objectPicked, this, [this](uint32_t) {
|
||||
updateVolumeReadout();
|
||||
});
|
||||
}
|
||||
|
||||
ViewportController::~ViewportController() = default;
|
||||
|
||||
void ViewportController::applyCoordinateOperation(uint32_t mid) {
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity();
|
||||
@@ -170,6 +229,72 @@ void ViewportController::goHomeView() {
|
||||
session_state_->setStatusMessage("Camera", "Home view restored");
|
||||
}
|
||||
|
||||
void ViewportController::setFlyMode() {
|
||||
viewport_->requestActivate();
|
||||
viewport_->enterFpsMode();
|
||||
session_state_->setStatusMessage("Mode", "Fly mode active");
|
||||
}
|
||||
|
||||
void ViewportController::toggleDistanceMode() {
|
||||
viewport_->toggleLengthTool();
|
||||
}
|
||||
|
||||
void ViewportController::toggleAreaMode() {
|
||||
viewport_->toggleAreaTool();
|
||||
}
|
||||
|
||||
void ViewportController::toggleVolumeMode() {
|
||||
viewport_->toggleVolumeTool();
|
||||
}
|
||||
|
||||
void ViewportController::hideSelectedElements() {
|
||||
viewport_->hideSelectedElements();
|
||||
}
|
||||
|
||||
void ViewportController::isolateSelectedElements() {
|
||||
viewport_->isolateSelectedElements();
|
||||
}
|
||||
|
||||
void ViewportController::showAllElements() {
|
||||
viewport_->showAllElements();
|
||||
}
|
||||
|
||||
void ViewportController::updateVolumeReadout() {
|
||||
if (viewport_->toolMode() != ViewportWindow::ToolMode::Volume) return;
|
||||
|
||||
const auto& sel = viewport_->selection().selectionIds();
|
||||
if (sel.empty()) {
|
||||
viewport_->setHudText(QString());
|
||||
viewport_->setOverlayLabels({});
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ids(sel.begin(), sel.end());
|
||||
const auto per_obj = volumesPerObject(*viewport_, ids);
|
||||
|
||||
double total = 0.0;
|
||||
std::vector<OverlayRenderer::Label> labels;
|
||||
labels.reserve(per_obj.size());
|
||||
for (const auto& [oid, v] : per_obj) {
|
||||
total += v;
|
||||
QVector3D mn, mx;
|
||||
if (!viewport_->computeObjectAabb(oid, mn, mx)) continue;
|
||||
OverlayRenderer::Label lbl;
|
||||
const QVector3D 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³";
|
||||
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"));
|
||||
viewport_->setOverlayLabels(labels);
|
||||
}
|
||||
|
||||
void ViewportController::maybeGuessFederatedFalseOrigin(uint32_t mid) {
|
||||
Federation* federation = session_state_->federation();
|
||||
SceneLoader* loader = session_state_->loader();
|
||||
|
||||
@@ -22,9 +22,12 @@
|
||||
#define IFCINTERFACE_PANELS_VIEWPORT_CONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
namespace ifcinterface { class SessionState; }
|
||||
class ViewportWindow;
|
||||
class AreaMeasurement;
|
||||
class LengthMeasurement;
|
||||
|
||||
namespace ifcinterface::modules::viewport {
|
||||
|
||||
@@ -35,19 +38,30 @@ public:
|
||||
explicit ViewportController(ifcinterface::SessionState* session_state,
|
||||
ViewportWindow* viewport,
|
||||
QObject* parent = nullptr);
|
||||
~ViewportController() override;
|
||||
|
||||
void applyFederatedFalseOrigin();
|
||||
void setHomeView();
|
||||
void goHomeView();
|
||||
void setFlyMode();
|
||||
void toggleDistanceMode();
|
||||
void toggleAreaMode();
|
||||
void toggleVolumeMode();
|
||||
void hideSelectedElements();
|
||||
void isolateSelectedElements();
|
||||
void showAllElements();
|
||||
|
||||
private:
|
||||
void applyCoordinateOperation(uint32_t mid);
|
||||
void applyModelTransformation(uint32_t mid);
|
||||
void applyModelVisibility(uint32_t mid);
|
||||
void maybeGuessFederatedFalseOrigin(uint32_t mid);
|
||||
void updateVolumeReadout();
|
||||
|
||||
ifcinterface::SessionState* session_state_ = nullptr;
|
||||
ViewportWindow* viewport_ = nullptr;
|
||||
std::unique_ptr<AreaMeasurement> area_measurement_;
|
||||
std::unique_ptr<LengthMeasurement> length_measurement_;
|
||||
};
|
||||
|
||||
} // namespace ifcinterface::modules::viewport
|
||||
|
||||
Reference in New Issue
Block a user