mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
ifcviewer: F to frame selection from tree, debug coord dump
Tree -> viewport selection was already wired (onTreeSelectionChanged
calls setSelectedObjectId), but pressing F afterwards routed to the
focused tree widget rather than the viewport, so framing didn't fire.
Add a window-level View > Frame Selected QAction with Qt::Key_F that
delegates to ViewportWindow::focusOnSelectedObject — works regardless
of which child widget has focus. The viewport's own F handler stays
in place for when the viewport itself owns focus.
For debugging coordinate problems, add View > Print Selected Coords
(Ctrl+Shift+P) -> ViewportWindow::printSelectedObjectCoords, which
qInfo's:
- a sample vertex (first vertex of the selected mesh, decoded on
demand from the quantised VBO so no extra CPU storage is needed);
- placement_transformation (the per-instance matrix that maps the
sample vertex from mesh-local into the model's pre-georef frame);
- global = CoordinateOperation . placement_transformation (where
the IFC's own IfcCoordinateOperation has been folded in);
- the sample vertex transformed through both matrices.
The print is a no-op when nothing is selected or GL hasn't initialised.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -201,6 +201,17 @@ void MainWindow::setupMenus() {
|
||||
file_menu->addAction("&Quit", QKeySequence::Quit, qApp, &QApplication::quit);
|
||||
|
||||
auto* view_menu = menuBar()->addMenu("&View");
|
||||
// F frames the current selection. The viewport already binds F in its
|
||||
// own keyPressEvent for the case where it has focus; this duplicate at
|
||||
// window level is so the shortcut still fires when the tree, property
|
||||
// table, or any other child widget has keyboard focus.
|
||||
view_menu->addAction("&Frame Selected", this, [this]() {
|
||||
viewport_->focusOnSelectedObject();
|
||||
}, QKeySequence(Qt::Key_F));
|
||||
view_menu->addAction("Print Selected &Coords", this, [this]() {
|
||||
viewport_->printSelectedObjectCoords();
|
||||
}, QKeySequence("Ctrl+Shift+P"));
|
||||
view_menu->addSeparator();
|
||||
view_menu->addAction("Set &Home View", this, &MainWindow::onSetHomeView);
|
||||
view_menu->addAction("&Go to Home View", this, &MainWindow::onGoHomeView);
|
||||
}
|
||||
|
||||
@@ -3664,3 +3664,86 @@ void ViewportWindow::setModelTransformation(uint32_t model_id,
|
||||
it->second.model_transformation_meters = matrix_meters;
|
||||
recomposeAndUploadModel(model_id);
|
||||
}
|
||||
|
||||
void ViewportWindow::printSelectedObjectCoords() {
|
||||
if (selected_object_id_ == 0) {
|
||||
qInfo("printSelectedObjectCoords: no object selected");
|
||||
return;
|
||||
}
|
||||
if (!gl_initialized_) {
|
||||
qInfo("printSelectedObjectCoords: GL not initialised yet");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& kv : models_gpu_) {
|
||||
const ModelGpuData& m = kv.second;
|
||||
for (size_t i = 0; i < m.instances.size(); ++i) {
|
||||
const InstanceCpu& inst = m.instances[i];
|
||||
if (inst.object_id != selected_object_id_) continue;
|
||||
|
||||
qInfo("Selected object %u (model %u, mesh %u, instance %zu):",
|
||||
inst.object_id, inst.model_id, inst.mesh_id, i);
|
||||
|
||||
// Decode the first emitted vertex from the VBO. Per
|
||||
// InstancedGeometry.h: pos is 3 x uint16 at offset 0, normalised
|
||||
// to [0,1] and dequantised against the mesh's local AABB.
|
||||
float vx = 0, vy = 0, vz = 0;
|
||||
bool have_vert = false;
|
||||
if (inst.mesh_id < m.meshes.size()) {
|
||||
const MeshInfo& mi = m.meshes[inst.mesh_id];
|
||||
if (mi.vertex_count > 0) {
|
||||
context_->makeCurrent(this);
|
||||
uint16_t pos_u16[3] = {0, 0, 0};
|
||||
gl_->glGetNamedBufferSubData(
|
||||
m.vbo, mi.vbo_byte_offset, sizeof(pos_u16), pos_u16);
|
||||
auto lerp = [](float lo, float hi, float t) {
|
||||
return lo + t * (hi - lo);
|
||||
};
|
||||
vx = lerp(mi.local_aabb_min[0], mi.local_aabb_max[0],
|
||||
pos_u16[0] / 65535.0f);
|
||||
vy = lerp(mi.local_aabb_min[1], mi.local_aabb_max[1],
|
||||
pos_u16[1] / 65535.0f);
|
||||
vz = lerp(mi.local_aabb_min[2], mi.local_aabb_max[2],
|
||||
pos_u16[2] / 65535.0f);
|
||||
have_vert = true;
|
||||
}
|
||||
}
|
||||
if (have_vert) {
|
||||
qInfo(" vertex (mesh-local m): (%g, %g, %g)", vx, vy, vz);
|
||||
} else {
|
||||
qInfo(" vertex: (no vertex data)");
|
||||
}
|
||||
|
||||
using Mat4f = Eigen::Matrix<float, 4, 4, Eigen::ColMajor>;
|
||||
const Eigen::Matrix4d Pd =
|
||||
Eigen::Map<const Mat4f>(inst.placement_transformation).cast<double>();
|
||||
// global = CoordinateOperation · placement_transformation.
|
||||
// (FederatedFalseOrigin and ModelTransformation are user-side
|
||||
// tweaks; "global" here means the IFC's own georeferenced frame.)
|
||||
const Eigen::Matrix4d Gd = m.coordinate_operation_meters * Pd;
|
||||
|
||||
auto print_mat = [](const char* label, const Eigen::Matrix4d& M) {
|
||||
qInfo(" %s (m):", label);
|
||||
for (int r = 0; r < 4; ++r) {
|
||||
qInfo(" [% .9g % .9g % .9g % .9g]",
|
||||
M(r, 0), M(r, 1), M(r, 2), M(r, 3));
|
||||
}
|
||||
};
|
||||
print_mat("placement_transformation", Pd);
|
||||
print_mat("global (CoordinateOperation . placement)", Gd);
|
||||
|
||||
if (have_vert) {
|
||||
const Eigen::Vector4d vh(vx, vy, vz, 1.0);
|
||||
const Eigen::Vector3d after_p = (Pd * vh).head<3>();
|
||||
const Eigen::Vector3d after_g = (Gd * vh).head<3>();
|
||||
qInfo(" vertex after placement (m): (% .9g, % .9g, % .9g)",
|
||||
after_p.x(), after_p.y(), after_p.z());
|
||||
qInfo(" vertex after global (m): (% .9g, % .9g, % .9g)",
|
||||
after_g.x(), after_g.y(), after_g.z());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
qInfo("printSelectedObjectCoords: object_id %u not found in any model",
|
||||
selected_object_id_);
|
||||
}
|
||||
|
||||
@@ -177,6 +177,12 @@ public:
|
||||
void showModel(uint32_t model_id);
|
||||
void removeModel(uint32_t model_id);
|
||||
|
||||
// Debug helper: walk to the currently selected object's instance and
|
||||
// qInfo a sample vertex (decoded from the VBO), the placement
|
||||
// transformation matrix, and the global matrix
|
||||
// (CoordinateOperation · placement_transformation), in metres.
|
||||
void printSelectedObjectCoords();
|
||||
|
||||
// Federation pipeline: composed instance transform =
|
||||
// FederatedFalseOrigin · ModelTransformation · CoordinateOperation
|
||||
// · placement_transformation
|
||||
|
||||
Reference in New Issue
Block a user