ifcviewer: cache per-model georef in SceneLoader

Adds ModelGeoref { ModelUnits units; Eigen::Matrix4d stage2_meters; bool
has_stage2; } and computeModelGeoref(file*) in Federation.{h,cpp}.  The
helper reads the project length unit, IfcProjectedCRS.MapUnit, helmert
parameters and WCS, and reduces them to a metres-in/metres-out stage 2
matrix using the existing Geolocation + Unit primitives.  When the model
has no IfcMapConversion it returns an identity stage_2 with has_stage2
== false, so the upload pipeline can branch cheaply.

SceneLoader::Model gains a cached ModelGeoref; SceneLoader::modelGeoref
(uint32_t mid) computes lazily on first call (returns nullptr when the
IFC file isn't available yet — happens on the sidecar-hit path before
the data-source thread populates the streamer) and serves from cache
afterwards.

Not yet consumed by the upload pipeline; that's the next commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-05-01 17:31:08 +10:00
parent 5386c9ec69
commit bea4e38e65
5 changed files with 87 additions and 3 deletions
+40
View File
@@ -18,6 +18,7 @@
********************************************************************************/
#include "Federation.h"
#include "Geolocation.h"
#include "Unit.h"
#include <QDir>
@@ -95,6 +96,45 @@ Eigen::Matrix4d composeFederationOrigin(const FederationOrigin& origin,
return Rz4 * translation4(-xyz_m);
}
ModelGeoref computeModelGeoref(ifcopenshell::file* ifc_file) {
ModelGeoref out;
if (!ifc_file) return out;
out.units.project_length_to_meters =
calculateUnitScale(ifc_file, "LENGTHUNIT");
if (auto map_unit = getMapUnit(ifc_file)) {
if (auto s = siScaleFromNamedUnit(*map_unit)) {
out.units.map_unit_to_meters = *s;
} else {
out.units.map_unit_to_meters = out.units.project_length_to_meters;
}
} else {
// No MapUnit on the IfcProjectedCRS — fall back to project length unit.
out.units.map_unit_to_meters = out.units.project_length_to_meters;
}
auto params = getHelmertTransformationParameters(ifc_file);
if (!params) return out;
Eigen::Matrix4d helmert =
helmertMetersFromParameters(*params, out.units.map_unit_to_meters);
if (auto wcs = getWcs(ifc_file)) {
// getWcs returns the WCS in project units (translation in project
// length units). Convert translation to metres before inverting.
Eigen::Matrix4d wcs_m = *wcs;
wcs_m(0, 3) *= out.units.project_length_to_meters;
wcs_m(1, 3) *= out.units.project_length_to_meters;
wcs_m(2, 3) *= out.units.project_length_to_meters;
out.stage2_meters = helmert * wcs_m.inverse();
} else {
out.stage2_meters = helmert;
}
out.has_stage2 = true;
return out;
}
Eigen::Matrix4d composeModelTransform(const ModelTransform& xf,
const FederationConfig& fed_cfg,
const ModelUnits& model_units,