/********************************************************************************
* *
* 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 . *
* *
********************************************************************************/
// Georeferencing matrix math over plain numbers: no IFC file, no schema
// dispatch, no Qt.
//
// Split out of geolocation.h/geolocation.cpp for the same reason as
// unit_convert: those pull in ../ifcparse/express.h to read an
// IfcMapConversion out of a model, but IfcViewerCore — and through it the
// Emscripten build — needs to compose the resulting matrices without
// linking IfcParse. Reading the parameters out of an IFC stays in
// geolocation.h; turning them into matrices lives here.
#ifndef GEOLOCATION_TRANSFORM_H
#define GEOLOCATION_TRANSFORM_H
#include
struct HelmertTransformation {
double e = 0.0; // eastings offset
double n = 0.0; // northings offset
double h = 0.0; // orthogonal-height offset
double xaa = 1.0; // X-axis abscissa (cos of grid-rotation angle)
double xao = 0.0; // X-axis ordinate (sin of grid-rotation angle)
double scale = 1.0; // unit scale (project unit -> map unit)
double factor_x = 1.0; // combined scale factor along X
double factor_y = 1.0; // combined scale factor along Y
double factor_z = 1.0; // combined scale factor along Z
};
// Apply a Helmert transformation to a 4x4 local matrix.
Eigen::Matrix4d local_to_global(const Eigen::Matrix4d& matrix,
const HelmertTransformation& params);
// Build the Helmert transformation as a meter-input / meter-output 4x4 matrix
// directly from parsed parameters, bypassing auto_local_to_global's normalisation
// step. Used by callers that want a single per-model georef matrix to compose
// with placement matrices at upload time.
//
// Result has shape:
// [ R_z(theta) · diag(fx, fy, fz) | (e, n, h) · u_m ]
// [ 0 | 1 ]
//
// `map_unit_to_meters` is derived by the caller from the IFC project length
// unit and the authoritative IfcMapConversion.Scale. Since this matrix takes
// meter inputs from the geometry iterator, Scale is represented by that unit
// conversion and is not applied again in the linear block. The caller composes
// any IfcGeometricRepresentationContext WCS on the right:
// G = helmert_meters_from_parameters(...) · inv(wcs_meters)
// (where wcs_meters has its translation column converted from project units
// to meters via calculate_unit_scale).
//
// Unlike auto_local_to_global, this preserves IfcMapConversionScaled.FactorX/Y/Z
// in the rotation block, so they apply correctly to placement translations
// when composing per-model.
Eigen::Matrix4d helmert_meters_from_parameters(const HelmertTransformation& params,
double map_unit_to_meters);
// "How do I rotate project east to get to grid east?" — i.e. -atan2(xao, xaa)
// converted to degrees, anticlockwise positive. Mirrors
// ifcopenshell.util.geolocation.xaxis2angle.
double x_axis_to_angle_deg(double xaa, double xao);
#endif // GEOLOCATION_TRANSFORM_H