mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 14:23:53 +00:00
Style
This commit is contained in:
@@ -34,7 +34,7 @@ Stack
|
|||||||
primitives used in instrumentation.
|
primitives used in instrumentation.
|
||||||
* **IfcOpenShell C++ libs** (IfcParse / IfcGeom) for IFC parsing and
|
* **IfcOpenShell C++ libs** (IfcParse / IfcGeom) for IFC parsing and
|
||||||
geometry generation, plus **helpers** for the schema-agnostic
|
geometry generation, plus **helpers** for the schema-agnostic
|
||||||
helpers (``Unit``, ``Geolocation``, ``Placement``).
|
helpers (``unit``, ``geolocation``, ``placement``).
|
||||||
* **Eigen3** for 4×4 matrices and small linear algebra.
|
* **Eigen3** for 4×4 matrices and small linear algebra.
|
||||||
* **meshoptimizer** at sidecar-build time only — decimates each
|
* **meshoptimizer** at sidecar-build time only — decimates each
|
||||||
unique mesh into an LOD1 slice. Not pulled at runtime.
|
unique mesh into an LOD1 slice. Not pulled at runtime.
|
||||||
|
|||||||
@@ -23,9 +23,9 @@
|
|||||||
|
|
||||||
#include "../../SessionState.h"
|
#include "../../SessionState.h"
|
||||||
#include "../../../ifcviewer/Federation.h"
|
#include "../../../ifcviewer/Federation.h"
|
||||||
#include "../../../ifcutil/Geolocation.h"
|
#include "../../../helpers/geolocation.h"
|
||||||
#include "../../../ifcviewer/SceneLoader.h"
|
#include "../../../ifcviewer/SceneLoader.h"
|
||||||
#include "../../../ifcutil/Unit.h"
|
#include "../../../helpers/unit.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
@@ -89,8 +89,8 @@ QString formatNamedUnit(const express::Base& unit) {
|
|||||||
text += QString::fromStdString(prefix) + " ";
|
text += QString::fromStdString(prefix) + " ";
|
||||||
}
|
}
|
||||||
text += QString::fromStdString(name);
|
text += QString::fromStdString(name);
|
||||||
auto symbol_it = kUnitSymbols.find(name);
|
auto symbol_it = UNIT_SYMBOLS.find(name);
|
||||||
if (symbol_it != kUnitSymbols.end()) {
|
if (symbol_it != UNIT_SYMBOLS.end()) {
|
||||||
text += " (" + QString::fromStdString(symbol_it->second) + ")";
|
text += " (" + QString::fromStdString(symbol_it->second) + ")";
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
@@ -100,8 +100,8 @@ QString formatNamedUnit(const express::Base& unit) {
|
|||||||
if (name_attr.isNull()) return "—";
|
if (name_attr.isNull()) return "—";
|
||||||
const std::string name = static_cast<std::string>(name_attr);
|
const std::string name = static_cast<std::string>(name_attr);
|
||||||
QString text = QString::fromStdString(name);
|
QString text = QString::fromStdString(name);
|
||||||
auto symbol_it = kUnitSymbols.find(name);
|
auto symbol_it = UNIT_SYMBOLS.find(name);
|
||||||
if (symbol_it != kUnitSymbols.end()) {
|
if (symbol_it != UNIT_SYMBOLS.end()) {
|
||||||
text += " (" + QString::fromStdString(symbol_it->second) + ")";
|
text += " (" + QString::fromStdString(symbol_it->second) + ")";
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
@@ -119,7 +119,7 @@ std::optional<QString> coordinateOperationType(ifcopenshell::file* ifc_file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ifc_file->schema()->name() == "IFC2X3") {
|
if (ifc_file->schema()->name() == "IFC2X3") {
|
||||||
if (getHelmertTransformationParameters(ifc_file)) {
|
if (get_helmert_transformation_parameters(ifc_file)) {
|
||||||
return QStringLiteral("ePSet_MapConversion");
|
return QStringLiteral("ePSet_MapConversion");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,10 +129,10 @@ std::optional<QString> coordinateOperationType(ifcopenshell::file* ifc_file) {
|
|||||||
SelectedModelGeorefState stateFromLiveFile(ifcopenshell::file* ifc_file) {
|
SelectedModelGeorefState stateFromLiveFile(ifcopenshell::file* ifc_file) {
|
||||||
if (!ifc_file) return unknownState("Not available yet", "No data source");
|
if (!ifc_file) return unknownState("Not available yet", "No data source");
|
||||||
|
|
||||||
const auto params = getHelmertTransformationParameters(ifc_file);
|
const auto params = get_helmert_transformation_parameters(ifc_file);
|
||||||
const auto coordop_type = coordinateOperationType(ifc_file);
|
const auto coordop_type = coordinateOperationType(ifc_file);
|
||||||
const auto project_unit_value = getProjectUnit(ifc_file, "LENGTHUNIT");
|
const auto project_unit_value = get_project_unit(ifc_file, "LENGTHUNIT");
|
||||||
const auto map_unit_value = getMapUnit(ifc_file);
|
const auto map_unit_value = get_map_unit(ifc_file);
|
||||||
const QString project_unit = project_unit_value ? formatNamedUnit(*project_unit_value) : QString("—");
|
const QString project_unit = project_unit_value ? formatNamedUnit(*project_unit_value) : QString("—");
|
||||||
const QString map_unit = map_unit_value ? formatNamedUnit(*map_unit_value) : project_unit;
|
const QString map_unit = map_unit_value ? formatNamedUnit(*map_unit_value) : project_unit;
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ SelectedModelGeorefState stateFromLiveFile(ifcopenshell::file* ifc_file) {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
const double rotation_dd = xaxis2angleDeg(params->xaa, params->xao);
|
const double rotation_dd = x_axis_to_angle_deg(params->xaa, params->xao);
|
||||||
return {
|
return {
|
||||||
"Yes",
|
"Yes",
|
||||||
coordop_type.value_or("Unknown"),
|
coordop_type.value_or("Unknown"),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
# #
|
# #
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
message("Running CMakeLists.txt in /src/ifcutil")
|
message("Running CMakeLists.txt in /src/helpers")
|
||||||
|
|
||||||
# Schema-agnostic IFC helpers ported from ifcopenshell.util.*. Pure
|
# Schema-agnostic IFC helpers ported from ifcopenshell.util.*. Pure
|
||||||
# IfcParse + Eigen, no Qt, no IfcGeom, no viewer/renderer deps — so unit
|
# IfcParse + Eigen, no Qt, no IfcGeom, no viewer/renderer deps — so unit
|
||||||
@@ -44,8 +44,8 @@ set_target_properties(helpers PROPERTIES
|
|||||||
)
|
)
|
||||||
|
|
||||||
# PUBLIC: callers (IfcViewer, test_federation, future tools) get
|
# PUBLIC: callers (IfcViewer, test_federation, future tools) get
|
||||||
# `#include "Unit.h"` etc. via this directory automatically, no
|
# `#include "unit.h"` etc. via this directory automatically, no
|
||||||
# `#include "../helpers/Unit.h"` ugliness at call sites.
|
# `#include "../helpers/unit.h"` ugliness at call sites.
|
||||||
target_include_directories(helpers PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(helpers PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
target_link_libraries(helpers PUBLIC
|
target_link_libraries(helpers PUBLIC
|
||||||
|
|||||||
+21
-21
@@ -17,8 +17,8 @@
|
|||||||
* *
|
* *
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
#include "Geolocation.h"
|
#include "geolocation.h"
|
||||||
#include "Placement.h"
|
#include "placement.h"
|
||||||
|
|
||||||
#include "../ifcparse/express.h"
|
#include "../ifcparse/express.h"
|
||||||
#include "../ifcparse/file.h"
|
#include "../ifcparse/file.h"
|
||||||
@@ -35,7 +35,7 @@ namespace {
|
|||||||
// ePSet_MapConversion stores eastings/northings/scale as IfcLengthMeasure or
|
// ePSet_MapConversion stores eastings/northings/scale as IfcLengthMeasure or
|
||||||
// IfcReal wrapped inside IfcValue (a SELECT) — get_attribute_value(0) peels
|
// IfcReal wrapped inside IfcValue (a SELECT) — get_attribute_value(0) peels
|
||||||
// the wrapper. Returns nullopt if the value is missing or non-numeric.
|
// the wrapper. Returns nullopt if the value is missing or non-numeric.
|
||||||
std::optional<double> readPropertyValueDouble(const express::Base& property) {
|
std::optional<double> read_property_value_double(const express::Base& property) {
|
||||||
if (!property.declaration().is("IfcPropertySingleValue")) return std::nullopt;
|
if (!property.declaration().is("IfcPropertySingleValue")) return std::nullopt;
|
||||||
auto pe = property.as<express::Entity>();
|
auto pe = property.as<express::Entity>();
|
||||||
auto nv = pe.get("NominalValue");
|
auto nv = pe.get("NominalValue");
|
||||||
@@ -53,7 +53,7 @@ std::optional<double> readPropertyValueDouble(const express::Base& property) {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::optional<HelmertTransformation>
|
std::optional<HelmertTransformation>
|
||||||
getHelmertTransformationParameters(ifcopenshell::file* ifc_file) {
|
get_helmert_transformation_parameters(ifcopenshell::file* ifc_file) {
|
||||||
HelmertTransformation p;
|
HelmertTransformation p;
|
||||||
const std::string schema_name = ifc_file->schema()->name();
|
const std::string schema_name = ifc_file->schema()->name();
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ getHelmertTransformationParameters(ifcopenshell::file* ifc_file) {
|
|||||||
auto pname_attr = pe.get("Name");
|
auto pname_attr = pe.get("Name");
|
||||||
if (pname_attr.isNull()) continue;
|
if (pname_attr.isNull()) continue;
|
||||||
std::string pname = pname_attr;
|
std::string pname = pname_attr;
|
||||||
auto value = readPropertyValueDouble(prop);
|
auto value = read_property_value_double(prop);
|
||||||
if (!value) continue;
|
if (!value) continue;
|
||||||
if (pname == "Eastings") p.e = *value;
|
if (pname == "Eastings") p.e = *value;
|
||||||
else if (pname == "Northings") p.n = *value;
|
else if (pname == "Northings") p.n = *value;
|
||||||
@@ -153,7 +153,7 @@ getHelmertTransformationParameters(ifcopenshell::file* ifc_file) {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Eigen::Matrix4d> getWcs(ifcopenshell::file* ifc_file) {
|
std::optional<Eigen::Matrix4d> get_wcs(ifcopenshell::file* ifc_file) {
|
||||||
auto contexts = ifc_file->instances_by_type_excl_subtypes(
|
auto contexts = ifc_file->instances_by_type_excl_subtypes(
|
||||||
"IfcGeometricRepresentationContext");
|
"IfcGeometricRepresentationContext");
|
||||||
express::Base wcs;
|
express::Base wcs;
|
||||||
@@ -175,11 +175,11 @@ std::optional<Eigen::Matrix4d> getWcs(ifcopenshell::file* ifc_file) {
|
|||||||
if (!(decl.is("IfcAxis2Placement3D") || decl.is("IfcAxis2PlacementLinear"))) {
|
if (!(decl.is("IfcAxis2Placement3D") || decl.is("IfcAxis2PlacementLinear"))) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
return getAxis2Placement(wcs);
|
return get_axis2_placement(wcs);
|
||||||
}
|
}
|
||||||
|
|
||||||
Eigen::Matrix4d local2global(const Eigen::Matrix4d& matrix,
|
Eigen::Matrix4d local_to_global(const Eigen::Matrix4d& matrix,
|
||||||
const HelmertTransformation& p) {
|
const HelmertTransformation& p) {
|
||||||
const double theta = std::atan2(p.xao, p.xaa);
|
const double theta = std::atan2(p.xao, p.xaa);
|
||||||
const double c = std::cos(theta);
|
const double c = std::cos(theta);
|
||||||
const double s = std::sin(theta);
|
const double s = std::sin(theta);
|
||||||
@@ -208,17 +208,17 @@ Eigen::Matrix4d local2global(const Eigen::Matrix4d& matrix,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Eigen::Matrix4d autoLocal2Global(ifcopenshell::file* ifc_file,
|
Eigen::Matrix4d auto_local_to_global(ifcopenshell::file* ifc_file,
|
||||||
const Eigen::Matrix4d& matrix,
|
const Eigen::Matrix4d& matrix,
|
||||||
bool should_return_in_map_units) {
|
bool should_return_in_map_units) {
|
||||||
auto params = getHelmertTransformationParameters(ifc_file);
|
auto params = get_helmert_transformation_parameters(ifc_file);
|
||||||
if (!params) return matrix;
|
if (!params) return matrix;
|
||||||
|
|
||||||
Eigen::Matrix4d m = matrix;
|
Eigen::Matrix4d m = matrix;
|
||||||
if (auto wcs = getWcs(ifc_file)) {
|
if (auto wcs = get_wcs(ifc_file)) {
|
||||||
m = wcs->inverse() * m;
|
m = wcs->inverse() * m;
|
||||||
}
|
}
|
||||||
Eigen::Matrix4d result = local2global(m, *params);
|
Eigen::Matrix4d result = local_to_global(m, *params);
|
||||||
if (!should_return_in_map_units) {
|
if (!should_return_in_map_units) {
|
||||||
result(0, 3) /= params->scale;
|
result(0, 3) /= params->scale;
|
||||||
result(1, 3) /= params->scale;
|
result(1, 3) /= params->scale;
|
||||||
@@ -227,8 +227,8 @@ Eigen::Matrix4d autoLocal2Global(ifcopenshell::file* ifc_file,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Eigen::Matrix4d helmertMetersFromParameters(const HelmertTransformation& p,
|
Eigen::Matrix4d helmert_meters_from_parameters(const HelmertTransformation& p,
|
||||||
double map_unit_to_meters) {
|
double map_unit_to_meters) {
|
||||||
const double theta = std::atan2(p.xao, p.xaa);
|
const double theta = std::atan2(p.xao, p.xaa);
|
||||||
const double c = std::cos(theta);
|
const double c = std::cos(theta);
|
||||||
const double s = std::sin(theta);
|
const double s = std::sin(theta);
|
||||||
@@ -247,7 +247,7 @@ Eigen::Matrix4d helmertMetersFromParameters(const HelmertTransformation& p,
|
|||||||
return M;
|
return M;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<express::Base> getMapUnit(ifcopenshell::file* ifc_file) {
|
std::optional<express::Base> get_map_unit(ifcopenshell::file* ifc_file) {
|
||||||
std::vector<express::Base> coordops;
|
std::vector<express::Base> coordops;
|
||||||
try {
|
try {
|
||||||
coordops = ifc_file->instances_by_type("IfcCoordinateOperation");
|
coordops = ifc_file->instances_by_type("IfcCoordinateOperation");
|
||||||
@@ -264,7 +264,7 @@ std::optional<express::Base> getMapUnit(ifcopenshell::file* ifc_file) {
|
|||||||
return (express::Base) mu_attr;
|
return (express::Base) mu_attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
double xaxis2angleDeg(double xaa, double xao) {
|
double x_axis_to_angle_deg(double xaa, double xao) {
|
||||||
constexpr double kPi = 3.14159265358979323846;
|
constexpr double PI = 3.14159265358979323846;
|
||||||
return -std::atan2(xao, xaa) * (180.0 / kPi);
|
return -std::atan2(xao, xaa) * (180.0 / PI);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-16
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
// Port of selected helpers from
|
// Port of selected helpers from
|
||||||
// src/ifcopenshell-python/ifcopenshell/util/geolocation.py — primarily
|
// src/ifcopenshell-python/ifcopenshell/util/geolocation.py — primarily
|
||||||
// auto_local2global, which builds a 4x4 matrix that lifts an element's local
|
// auto_local_to_global, which builds a 4x4 matrix that lifts an element's local
|
||||||
// transform into the model's global (georeferenced) frame. The python utils
|
// transform into the model's global (georeferenced) frame. The python utils
|
||||||
// are expected to be ported to C++ in their own module later; this file is
|
// are expected to be ported to C++ in their own module later; this file is
|
||||||
// the temporary home until that lands.
|
// the temporary home until that lands.
|
||||||
@@ -52,28 +52,28 @@ struct HelmertTransformation {
|
|||||||
// IfcProject.ePSet_MapConversion property set in IFC2X3. Returns nullopt
|
// IfcProject.ePSet_MapConversion property set in IFC2X3. Returns nullopt
|
||||||
// when the model has no map conversion.
|
// when the model has no map conversion.
|
||||||
std::optional<HelmertTransformation>
|
std::optional<HelmertTransformation>
|
||||||
getHelmertTransformationParameters(ifcopenshell::file* ifc_file);
|
get_helmert_transformation_parameters(ifcopenshell::file* ifc_file);
|
||||||
|
|
||||||
// Read the IfcGeometricRepresentationContext.WorldCoordinateSystem (preferring
|
// Read the IfcGeometricRepresentationContext.WorldCoordinateSystem (preferring
|
||||||
// the "Model" context) as a 4x4 matrix. Returns nullopt when the model has
|
// the "Model" context) as a 4x4 matrix. Returns nullopt when the model has
|
||||||
// no parseable WCS.
|
// no parseable WCS.
|
||||||
std::optional<Eigen::Matrix4d> getWcs(ifcopenshell::file* ifc_file);
|
std::optional<Eigen::Matrix4d> get_wcs(ifcopenshell::file* ifc_file);
|
||||||
|
|
||||||
// Apply a Helmert transformation to a 4x4 local matrix.
|
// Apply a Helmert transformation to a 4x4 local matrix.
|
||||||
Eigen::Matrix4d local2global(const Eigen::Matrix4d& matrix,
|
Eigen::Matrix4d local_to_global(const Eigen::Matrix4d& matrix,
|
||||||
const HelmertTransformation& params);
|
const HelmertTransformation& params);
|
||||||
|
|
||||||
// Lift a 4x4 local matrix into global (map) coordinates using the IFC model's
|
// Lift a 4x4 local matrix into global (map) coordinates using the IFC model's
|
||||||
// georeferencing data. When no map conversion is present the matrix is
|
// georeferencing data. When no map conversion is present the matrix is
|
||||||
// returned unchanged. When should_return_in_map_units is false, the
|
// returned unchanged. When should_return_in_map_units is false, the
|
||||||
// translation column is divided by the map scale so the result is expressed
|
// translation column is divided by the map scale so the result is expressed
|
||||||
// in project length units.
|
// in project length units.
|
||||||
Eigen::Matrix4d autoLocal2Global(ifcopenshell::file* ifc_file,
|
Eigen::Matrix4d auto_local_to_global(ifcopenshell::file* ifc_file,
|
||||||
const Eigen::Matrix4d& matrix,
|
const Eigen::Matrix4d& matrix,
|
||||||
bool should_return_in_map_units = true);
|
bool should_return_in_map_units = true);
|
||||||
|
|
||||||
// Build the Helmert transformation as a meter-input / meter-output 4x4 matrix
|
// Build the Helmert transformation as a meter-input / meter-output 4x4 matrix
|
||||||
// directly from parsed parameters, bypassing autoLocal2Global's normalisation
|
// 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
|
// step. Used by callers that want a single per-model georef matrix to compose
|
||||||
// with placement matrices at upload time.
|
// with placement matrices at upload time.
|
||||||
//
|
//
|
||||||
@@ -86,26 +86,26 @@ Eigen::Matrix4d autoLocal2Global(ifcopenshell::file* ifc_file,
|
|||||||
// meter inputs from the geometry iterator, Scale is represented by that unit
|
// 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
|
// conversion and is not applied again in the linear block. The caller composes
|
||||||
// any IfcGeometricRepresentationContext WCS on the right:
|
// any IfcGeometricRepresentationContext WCS on the right:
|
||||||
// G = helmertMetersFromParameters(...) · inv(wcs_meters)
|
// G = helmert_meters_from_parameters(...) · inv(wcs_meters)
|
||||||
// (where wcs_meters has its translation column converted from project units
|
// (where wcs_meters has its translation column converted from project units
|
||||||
// to meters via calculateUnitScale).
|
// to meters via calculate_unit_scale).
|
||||||
//
|
//
|
||||||
// Unlike autoLocal2Global, this preserves IfcMapConversionScaled.FactorX/Y/Z
|
// Unlike auto_local_to_global, this preserves IfcMapConversionScaled.FactorX/Y/Z
|
||||||
// in the rotation block, so they apply correctly to placement translations
|
// in the rotation block, so they apply correctly to placement translations
|
||||||
// when composing per-model.
|
// when composing per-model.
|
||||||
Eigen::Matrix4d helmertMetersFromParameters(const HelmertTransformation& params,
|
Eigen::Matrix4d helmert_meters_from_parameters(const HelmertTransformation& params,
|
||||||
double map_unit_to_meters);
|
double map_unit_to_meters);
|
||||||
|
|
||||||
// IfcCoordinateOperation.TargetCRS.MapUnit (the IfcNamedUnit), if present.
|
// IfcCoordinateOperation.TargetCRS.MapUnit (the IfcNamedUnit), if present.
|
||||||
// Returns nullopt for IFC2X3, models without an IfcCoordinateOperation, or
|
// Returns nullopt for IFC2X3, models without an IfcCoordinateOperation, or
|
||||||
// when MapUnit is absent on the IfcProjectedCRS. This is retained for UI /
|
// when MapUnit is absent on the IfcProjectedCRS. This is retained for UI /
|
||||||
// metadata inspection; transform composition derives map unit scale from
|
// metadata inspection; transform composition derives map unit scale from
|
||||||
// IfcMapConversion.Scale instead.
|
// IfcMapConversion.Scale instead.
|
||||||
std::optional<express::Base> getMapUnit(ifcopenshell::file* ifc_file);
|
std::optional<express::Base> get_map_unit(ifcopenshell::file* ifc_file);
|
||||||
|
|
||||||
// "How do I rotate project east to get to grid east?" — i.e. -atan2(xao, xaa)
|
// "How do I rotate project east to get to grid east?" — i.e. -atan2(xao, xaa)
|
||||||
// converted to degrees, anticlockwise positive. Mirrors
|
// converted to degrees, anticlockwise positive. Mirrors
|
||||||
// ifcopenshell.util.geolocation.xaxis2angle.
|
// ifcopenshell.util.geolocation.xaxis2angle.
|
||||||
double xaxis2angleDeg(double xaa, double xao);
|
double x_axis_to_angle_deg(double xaa, double xao);
|
||||||
|
|
||||||
#endif // GEOLOCATION_H
|
#endif // GEOLOCATION_H
|
||||||
|
|||||||
+20
-20
@@ -17,7 +17,7 @@
|
|||||||
* *
|
* *
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
#include "Placement.h"
|
#include "placement.h"
|
||||||
|
|
||||||
#include "../ifcparse/instance_data.h"
|
#include "../ifcparse/instance_data.h"
|
||||||
#include "../ifcparse/schema.h"
|
#include "../ifcparse/schema.h"
|
||||||
@@ -26,13 +26,13 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Eigen::Vector3d safeNormalize(const Eigen::Vector3d& v,
|
Eigen::Vector3d safe_normalize(const Eigen::Vector3d& v,
|
||||||
const Eigen::Vector3d& fallback) {
|
const Eigen::Vector3d& fallback) {
|
||||||
const double n = v.norm();
|
const double n = v.norm();
|
||||||
return (n > 0.0) ? Eigen::Vector3d(v / n) : fallback;
|
return (n > 0.0) ? Eigen::Vector3d(v / n) : fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<double> readDirectionRatios(const express::Base& dir) {
|
std::vector<double> read_direction_ratios(const express::Base& dir) {
|
||||||
if (!dir) return {};
|
if (!dir) return {};
|
||||||
auto attr = dir.as<express::Entity>().get("DirectionRatios");
|
auto attr = dir.as<express::Entity>().get("DirectionRatios");
|
||||||
if (attr.isNull()) return {};
|
if (attr.isNull()) return {};
|
||||||
@@ -41,12 +41,12 @@ std::vector<double> readDirectionRatios(const express::Base& dir) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Eigen::Matrix4d a2p(const Eigen::Vector3d& origin,
|
Eigen::Matrix4d axes_to_placement(const Eigen::Vector3d& origin,
|
||||||
const Eigen::Vector3d& z,
|
const Eigen::Vector3d& z,
|
||||||
const Eigen::Vector3d& x) {
|
const Eigen::Vector3d& x) {
|
||||||
const Eigen::Vector3d xn = safeNormalize(x, Eigen::Vector3d::UnitX());
|
const Eigen::Vector3d xn = safe_normalize(x, Eigen::Vector3d::UnitX());
|
||||||
const Eigen::Vector3d zn = safeNormalize(z, Eigen::Vector3d::UnitZ());
|
const Eigen::Vector3d zn = safe_normalize(z, Eigen::Vector3d::UnitZ());
|
||||||
const Eigen::Vector3d yn = safeNormalize(zn.cross(xn), Eigen::Vector3d::UnitY());
|
const Eigen::Vector3d yn = safe_normalize(zn.cross(xn), Eigen::Vector3d::UnitY());
|
||||||
|
|
||||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||||
m.block<3, 1>(0, 0) = xn;
|
m.block<3, 1>(0, 0) = xn;
|
||||||
@@ -56,7 +56,7 @@ Eigen::Matrix4d a2p(const Eigen::Vector3d& origin,
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
Eigen::Matrix4d getAxis2Placement(const express::Base& placement) {
|
Eigen::Matrix4d get_axis2_placement(const express::Base& placement) {
|
||||||
if (!placement) return Eigen::Matrix4d::Identity();
|
if (!placement) return Eigen::Matrix4d::Identity();
|
||||||
const auto& decl = placement.declaration();
|
const auto& decl = placement.declaration();
|
||||||
auto entity = placement.as<express::Entity>();
|
auto entity = placement.as<express::Entity>();
|
||||||
@@ -68,12 +68,12 @@ Eigen::Matrix4d getAxis2Placement(const express::Base& placement) {
|
|||||||
if (decl.is("IfcAxis2Placement3D") || decl.is("IfcAxis2PlacementLinear")) {
|
if (decl.is("IfcAxis2Placement3D") || decl.is("IfcAxis2PlacementLinear")) {
|
||||||
auto axis_attr = entity.get("Axis");
|
auto axis_attr = entity.get("Axis");
|
||||||
if (!axis_attr.isNull()) {
|
if (!axis_attr.isNull()) {
|
||||||
auto dr = readDirectionRatios((express::Base) axis_attr);
|
auto dr = read_direction_ratios((express::Base) axis_attr);
|
||||||
if (dr.size() >= 3) z = Eigen::Vector3d(dr[0], dr[1], dr[2]);
|
if (dr.size() >= 3) z = Eigen::Vector3d(dr[0], dr[1], dr[2]);
|
||||||
}
|
}
|
||||||
auto refdir_attr = entity.get("RefDirection");
|
auto refdir_attr = entity.get("RefDirection");
|
||||||
if (!refdir_attr.isNull()) {
|
if (!refdir_attr.isNull()) {
|
||||||
auto dr = readDirectionRatios((express::Base) refdir_attr);
|
auto dr = read_direction_ratios((express::Base) refdir_attr);
|
||||||
if (dr.size() >= 3) x = Eigen::Vector3d(dr[0], dr[1], dr[2]);
|
if (dr.size() >= 3) x = Eigen::Vector3d(dr[0], dr[1], dr[2]);
|
||||||
}
|
}
|
||||||
auto loc_attr = entity.get("Location");
|
auto loc_attr = entity.get("Location");
|
||||||
@@ -86,7 +86,7 @@ Eigen::Matrix4d getAxis2Placement(const express::Base& placement) {
|
|||||||
} else if (decl.is("IfcAxis2Placement2D")) {
|
} else if (decl.is("IfcAxis2Placement2D")) {
|
||||||
auto refdir_attr = entity.get("RefDirection");
|
auto refdir_attr = entity.get("RefDirection");
|
||||||
if (!refdir_attr.isNull()) {
|
if (!refdir_attr.isNull()) {
|
||||||
auto dr = readDirectionRatios((express::Base) refdir_attr);
|
auto dr = read_direction_ratios((express::Base) refdir_attr);
|
||||||
if (dr.size() >= 1) {
|
if (dr.size() >= 1) {
|
||||||
x = Eigen::Vector3d(dr.size() > 0 ? dr[0] : 1.0,
|
x = Eigen::Vector3d(dr.size() > 0 ? dr[0] : 1.0,
|
||||||
dr.size() > 1 ? dr[1] : 0.0,
|
dr.size() > 1 ? dr[1] : 0.0,
|
||||||
@@ -106,7 +106,7 @@ Eigen::Matrix4d getAxis2Placement(const express::Base& placement) {
|
|||||||
} else if (decl.is("IfcAxis1Placement")) {
|
} else if (decl.is("IfcAxis1Placement")) {
|
||||||
auto axis_attr = entity.get("Axis");
|
auto axis_attr = entity.get("Axis");
|
||||||
if (!axis_attr.isNull()) {
|
if (!axis_attr.isNull()) {
|
||||||
auto dr = readDirectionRatios((express::Base) axis_attr);
|
auto dr = read_direction_ratios((express::Base) axis_attr);
|
||||||
if (dr.size() >= 3) z = Eigen::Vector3d(dr[0], dr[1], dr[2]);
|
if (dr.size() >= 3) z = Eigen::Vector3d(dr[0], dr[1], dr[2]);
|
||||||
}
|
}
|
||||||
auto loc_attr = entity.get("Location");
|
auto loc_attr = entity.get("Location");
|
||||||
@@ -120,10 +120,10 @@ Eigen::Matrix4d getAxis2Placement(const express::Base& placement) {
|
|||||||
return Eigen::Matrix4d::Identity();
|
return Eigen::Matrix4d::Identity();
|
||||||
}
|
}
|
||||||
|
|
||||||
return a2p(o, z, x);
|
return axes_to_placement(o, z, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
Eigen::Matrix4d getLocalPlacement(const express::Base& placement) {
|
Eigen::Matrix4d get_local_placement(const express::Base& placement) {
|
||||||
if (!placement) return Eigen::Matrix4d::Identity();
|
if (!placement) return Eigen::Matrix4d::Identity();
|
||||||
const auto& decl = placement.declaration();
|
const auto& decl = placement.declaration();
|
||||||
|
|
||||||
@@ -132,13 +132,13 @@ Eigen::Matrix4d getLocalPlacement(const express::Base& placement) {
|
|||||||
Eigen::Matrix4d parent = Eigen::Matrix4d::Identity();
|
Eigen::Matrix4d parent = Eigen::Matrix4d::Identity();
|
||||||
auto rel_attr = entity.get("PlacementRelTo");
|
auto rel_attr = entity.get("PlacementRelTo");
|
||||||
if (!rel_attr.isNull()) {
|
if (!rel_attr.isNull()) {
|
||||||
parent = getLocalPlacement((express::Base) rel_attr);
|
parent = get_local_placement((express::Base) rel_attr);
|
||||||
}
|
}
|
||||||
auto rp_attr = entity.get("RelativePlacement");
|
auto rp_attr = entity.get("RelativePlacement");
|
||||||
if (rp_attr.isNull()) return parent;
|
if (rp_attr.isNull()) return parent;
|
||||||
return parent * getAxis2Placement((express::Base) rp_attr);
|
return parent * get_axis2_placement((express::Base) rp_attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// IfcAxis2Placement* / IfcAxis1Placement passed in directly.
|
// IfcAxis2Placement* / IfcAxis1Placement passed in directly.
|
||||||
return getAxis2Placement(placement);
|
return get_axis2_placement(placement);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,19 +32,19 @@
|
|||||||
// Build a 4x4 placement matrix from an origin + Z + X axis triple, mirroring
|
// Build a 4x4 placement matrix from an origin + Z + X axis triple, mirroring
|
||||||
// ifcopenshell.util.placement.a2p. The Y axis is derived as Z × X. Inputs
|
// ifcopenshell.util.placement.a2p. The Y axis is derived as Z × X. Inputs
|
||||||
// don't need to be unit; vectors are renormalised internally.
|
// don't need to be unit; vectors are renormalised internally.
|
||||||
Eigen::Matrix4d a2p(const Eigen::Vector3d& origin,
|
Eigen::Matrix4d axes_to_placement(const Eigen::Vector3d& origin,
|
||||||
const Eigen::Vector3d& z,
|
const Eigen::Vector3d& z,
|
||||||
const Eigen::Vector3d& x);
|
const Eigen::Vector3d& x);
|
||||||
|
|
||||||
// IfcAxis2Placement{2D,3D,Linear} / IfcAxis1Placement -> 4x4 matrix. Mirrors
|
// IfcAxis2Placement{2D,3D,Linear} / IfcAxis1Placement -> 4x4 matrix. Mirrors
|
||||||
// ifcopenshell.util.placement.get_axis2placement. Returns identity for null
|
// ifcopenshell.util.placement.get_axis2placement. Returns identity for null
|
||||||
// or unparseable inputs. Translation is in the IFC's project length unit.
|
// or unparseable inputs. Translation is in the IFC's project length unit.
|
||||||
Eigen::Matrix4d getAxis2Placement(const express::Base& placement);
|
Eigen::Matrix4d get_axis2_placement(const express::Base& placement);
|
||||||
|
|
||||||
// Resolve an IfcLocalPlacement (or an IfcAxis2Placement* directly) into a
|
// Resolve an IfcLocalPlacement (or an IfcAxis2Placement* directly) into a
|
||||||
// 4x4 matrix in the IFC project's length unit, walking the PlacementRelTo
|
// 4x4 matrix in the IFC project's length unit, walking the PlacementRelTo
|
||||||
// chain. Mirrors ifcopenshell.util.placement.get_local_placement. Returns
|
// chain. Mirrors ifcopenshell.util.placement.get_local_placement. Returns
|
||||||
// identity for a null input.
|
// identity for a null input.
|
||||||
Eigen::Matrix4d getLocalPlacement(const express::Base& placement);
|
Eigen::Matrix4d get_local_placement(const express::Base& placement);
|
||||||
|
|
||||||
#endif // PLACEMENT_H
|
#endif // PLACEMENT_H
|
||||||
|
|||||||
+44
-44
@@ -17,7 +17,7 @@
|
|||||||
* *
|
* *
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
#include "Unit.h"
|
#include "unit.h"
|
||||||
|
|
||||||
#include "../ifcparse/file.h"
|
#include "../ifcparse/file.h"
|
||||||
#include "../ifcparse/instance_data.h"
|
#include "../ifcparse/instance_data.h"
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
const std::unordered_map<std::string, double> kSiPrefixes = {
|
const std::unordered_map<std::string, double> SI_PREFIXES = {
|
||||||
{ "EXA", 1e18 },
|
{ "EXA", 1e18 },
|
||||||
{ "PETA", 1e15 },
|
{ "PETA", 1e15 },
|
||||||
{ "TERA", 1e12 },
|
{ "TERA", 1e12 },
|
||||||
@@ -45,7 +45,7 @@ const std::unordered_map<std::string, double> kSiPrefixes = {
|
|||||||
{ "ATTO", 1e-18 },
|
{ "ATTO", 1e-18 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::unordered_map<std::string, std::string> kSiPrefixSymbols = {
|
const std::unordered_map<std::string, std::string> SI_PREFIX_SYMBOLS = {
|
||||||
{ "EXA", "E" },
|
{ "EXA", "E" },
|
||||||
{ "PETA", "P" },
|
{ "PETA", "P" },
|
||||||
{ "TERA", "T" },
|
{ "TERA", "T" },
|
||||||
@@ -64,7 +64,7 @@ const std::unordered_map<std::string, std::string> kSiPrefixSymbols = {
|
|||||||
{ "ATTO", "a" },
|
{ "ATTO", "a" },
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::unordered_map<std::string, double> kSiConversions = {
|
const std::unordered_map<std::string, double> SI_CONVERSIONS = {
|
||||||
{ "thou", 0.0000254 },
|
{ "thou", 0.0000254 },
|
||||||
{ "inch", 0.0254 },
|
{ "inch", 0.0254 },
|
||||||
{ "foot", 0.3048 },
|
{ "foot", 0.3048 },
|
||||||
@@ -105,7 +105,7 @@ const std::unordered_map<std::string, double> kSiConversions = {
|
|||||||
{ "fahrenheit", 1.8 },
|
{ "fahrenheit", 1.8 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::unordered_map<std::string, std::string> kImperialTypes = {
|
const std::unordered_map<std::string, std::string> IMPERIAL_TYPES = {
|
||||||
{ "thou", "LENGTHUNIT" }, { "inch", "LENGTHUNIT" }, { "foot", "LENGTHUNIT" },
|
{ "thou", "LENGTHUNIT" }, { "inch", "LENGTHUNIT" }, { "foot", "LENGTHUNIT" },
|
||||||
{ "yard", "LENGTHUNIT" }, { "mile", "LENGTHUNIT" },
|
{ "yard", "LENGTHUNIT" }, { "mile", "LENGTHUNIT" },
|
||||||
{ "square thou", "AREAUNIT" }, { "square inch", "AREAUNIT" },
|
{ "square thou", "AREAUNIT" }, { "square inch", "AREAUNIT" },
|
||||||
@@ -127,7 +127,7 @@ const std::unordered_map<std::string, std::string> kImperialTypes = {
|
|||||||
{ "fahrenheit", "THERMODYNAMICTEMPERATUREUNIT" },
|
{ "fahrenheit", "THERMODYNAMICTEMPERATUREUNIT" },
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::unordered_map<std::string, std::string> kUnitSymbols = {
|
const std::unordered_map<std::string, std::string> UNIT_SYMBOLS = {
|
||||||
// SI base / derived
|
// SI base / derived
|
||||||
{ "CUBIC_METRE", "m3" },
|
{ "CUBIC_METRE", "m3" },
|
||||||
{ "GRAM", "g" },
|
{ "GRAM", "g" },
|
||||||
@@ -162,7 +162,7 @@ const std::unordered_map<std::string, std::string> kUnitSymbols = {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string toLower(const std::string& s) {
|
std::string to_lower(const std::string& s) {
|
||||||
std::string r;
|
std::string r;
|
||||||
r.resize(s.size());
|
r.resize(s.size());
|
||||||
std::transform(s.begin(), s.end(), r.begin(),
|
std::transform(s.begin(), s.end(), r.begin(),
|
||||||
@@ -171,7 +171,7 @@ std::string toLower(const std::string& s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pull an enumeration string off an attribute_value, or "" if null/invalid.
|
// Pull an enumeration string off an attribute_value, or "" if null/invalid.
|
||||||
std::string enumString(const attribute_value& av) {
|
std::string enum_string(const attribute_value& av) {
|
||||||
if (av.isNull()) return {};
|
if (av.isNull()) return {};
|
||||||
if (av.type() != ifcopenshell::Argument_ENUMERATION) return {};
|
if (av.type() != ifcopenshell::Argument_ENUMERATION) return {};
|
||||||
enumeration_reference er = av;
|
enumeration_reference er = av;
|
||||||
@@ -180,13 +180,13 @@ std::string enumString(const attribute_value& av) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
double getPrefixMultiplier(const std::string& prefix) {
|
double get_prefix_multiplier(const std::string& prefix) {
|
||||||
if (prefix.empty()) return 1.0;
|
if (prefix.empty()) return 1.0;
|
||||||
auto it = kSiPrefixes.find(prefix);
|
auto it = SI_PREFIXES.find(prefix);
|
||||||
return (it == kSiPrefixes.end()) ? 1.0 : it->second;
|
return (it == SI_PREFIXES.end()) ? 1.0 : it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<double> siScaleFromNamedUnit(express::Base unit) {
|
std::optional<double> si_scale_from_named_unit(express::Base unit) {
|
||||||
double scale = 1.0;
|
double scale = 1.0;
|
||||||
while (unit && unit.declaration().is("IfcConversionBasedUnit")) {
|
while (unit && unit.declaration().is("IfcConversionBasedUnit")) {
|
||||||
auto e = unit.as<express::Entity>();
|
auto e = unit.as<express::Entity>();
|
||||||
@@ -195,8 +195,8 @@ std::optional<double> siScaleFromNamedUnit(express::Base unit) {
|
|||||||
std::string name;
|
std::string name;
|
||||||
auto name_attr = e.get("Name");
|
auto name_attr = e.get("Name");
|
||||||
if (!name_attr.isNull()) name = (std::string) name_attr;
|
if (!name_attr.isNull()) name = (std::string) name_attr;
|
||||||
if (auto it = kSiConversions.find(toLower(name));
|
if (auto it = SI_CONVERSIONS.find(to_lower(name));
|
||||||
it != kSiConversions.end()) {
|
it != SI_CONVERSIONS.end()) {
|
||||||
return scale * it->second;
|
return scale * it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,15 +217,15 @@ std::optional<double> siScaleFromNamedUnit(express::Base unit) {
|
|||||||
|
|
||||||
if (unit && unit.declaration().is("IfcSIUnit")) {
|
if (unit && unit.declaration().is("IfcSIUnit")) {
|
||||||
auto e = unit.as<express::Entity>();
|
auto e = unit.as<express::Entity>();
|
||||||
const std::string prefix = enumString(e.get("Prefix"));
|
const std::string prefix = enum_string(e.get("Prefix"));
|
||||||
const std::string name = enumString(e.get("Name"));
|
const std::string name = enum_string(e.get("Name"));
|
||||||
double m = getPrefixMultiplier(prefix);
|
double m = get_prefix_multiplier(prefix);
|
||||||
// SQUARE_/CUBIC_-prefixed SI names: prefix multiplier squared/cubed.
|
// SQUARE_/CUBIC_-prefixed SI names: prefix multiplier squared/cubed.
|
||||||
if (name.find("SQUARE") != std::string::npos) {
|
if (name.find("SQUARE") != std::string::npos) {
|
||||||
m *= getPrefixMultiplier(prefix);
|
m *= get_prefix_multiplier(prefix);
|
||||||
} else if (name.find("CUBIC") != std::string::npos) {
|
} else if (name.find("CUBIC") != std::string::npos) {
|
||||||
m *= getPrefixMultiplier(prefix);
|
m *= get_prefix_multiplier(prefix);
|
||||||
m *= getPrefixMultiplier(prefix);
|
m *= get_prefix_multiplier(prefix);
|
||||||
}
|
}
|
||||||
return scale * m;
|
return scale * m;
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ std::optional<double> siScaleFromNamedUnit(express::Base unit) {
|
|||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<express::Base> getUnitAssignment(ifcopenshell::file* ifc_file) {
|
std::optional<express::Base> get_unit_assignment(ifcopenshell::file* ifc_file) {
|
||||||
auto projects = ifc_file->instances_by_type("IfcProject");
|
auto projects = ifc_file->instances_by_type("IfcProject");
|
||||||
if (projects.empty()) return std::nullopt;
|
if (projects.empty()) return std::nullopt;
|
||||||
auto ua_attr = projects[0].as<express::Entity>().get("UnitsInContext");
|
auto ua_attr = projects[0].as<express::Entity>().get("UnitsInContext");
|
||||||
@@ -246,9 +246,9 @@ std::optional<express::Base> getUnitAssignment(ifcopenshell::file* ifc_file) {
|
|||||||
return (express::Base) ua_attr;
|
return (express::Base) ua_attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<express::Base> getProjectUnit(ifcopenshell::file* ifc_file,
|
std::optional<express::Base> get_project_unit(ifcopenshell::file* ifc_file,
|
||||||
const std::string& unit_type) {
|
const std::string& unit_type) {
|
||||||
auto ua = getUnitAssignment(ifc_file);
|
auto ua = get_unit_assignment(ifc_file);
|
||||||
if (!ua) return std::nullopt;
|
if (!ua) return std::nullopt;
|
||||||
auto units_attr = ua->as<express::Entity>().get("Units");
|
auto units_attr = ua->as<express::Entity>().get("Units");
|
||||||
if (units_attr.isNull()) return std::nullopt;
|
if (units_attr.isNull()) return std::nullopt;
|
||||||
@@ -260,60 +260,60 @@ std::optional<express::Base> getProjectUnit(ifcopenshell::file* ifc_file,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto ut = unit.as<express::Entity>().get("UnitType");
|
auto ut = unit.as<express::Entity>().get("UnitType");
|
||||||
if (enumString(ut) == unit_type) return unit;
|
if (enum_string(ut) == unit_type) return unit;
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
double calculateUnitScale(ifcopenshell::file* ifc_file,
|
double calculate_unit_scale(ifcopenshell::file* ifc_file,
|
||||||
const std::string& unit_type) {
|
const std::string& unit_type) {
|
||||||
auto unit = getProjectUnit(ifc_file, unit_type);
|
auto unit = get_project_unit(ifc_file, unit_type);
|
||||||
if (!unit) return 1.0;
|
if (!unit) return 1.0;
|
||||||
auto scale = siScaleFromNamedUnit(*unit);
|
auto scale = si_scale_from_named_unit(*unit);
|
||||||
return scale.value_or(1.0);
|
return scale.value_or(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
double convert(double value,
|
double convert(double value,
|
||||||
const std::string& from_prefix, const std::string& from_unit,
|
const std::string& from_prefix, const std::string& from_unit,
|
||||||
const std::string& to_prefix, const std::string& to_unit) {
|
const std::string& to_prefix, const std::string& to_unit) {
|
||||||
const std::string fl = toLower(from_unit);
|
const std::string fl = to_lower(from_unit);
|
||||||
const std::string tl = toLower(to_unit);
|
const std::string tl = to_lower(to_unit);
|
||||||
|
|
||||||
if (auto it = kSiConversions.find(fl); it != kSiConversions.end()) {
|
if (auto it = SI_CONVERSIONS.find(fl); it != SI_CONVERSIONS.end()) {
|
||||||
value *= it->second;
|
value *= it->second;
|
||||||
} else if (!from_prefix.empty()) {
|
} else if (!from_prefix.empty()) {
|
||||||
value *= getPrefixMultiplier(from_prefix);
|
value *= get_prefix_multiplier(from_prefix);
|
||||||
if (from_unit.find("SQUARE") != std::string::npos) {
|
if (from_unit.find("SQUARE") != std::string::npos) {
|
||||||
value *= getPrefixMultiplier(from_prefix);
|
value *= get_prefix_multiplier(from_prefix);
|
||||||
} else if (from_unit.find("CUBIC") != std::string::npos) {
|
} else if (from_unit.find("CUBIC") != std::string::npos) {
|
||||||
value *= getPrefixMultiplier(from_prefix);
|
value *= get_prefix_multiplier(from_prefix);
|
||||||
value *= getPrefixMultiplier(from_prefix);
|
value *= get_prefix_multiplier(from_prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto it = kSiConversions.find(tl); it != kSiConversions.end()) {
|
if (auto it = SI_CONVERSIONS.find(tl); it != SI_CONVERSIONS.end()) {
|
||||||
return value * (1.0 / it->second);
|
return value * (1.0 / it->second);
|
||||||
} else if (!to_prefix.empty()) {
|
} else if (!to_prefix.empty()) {
|
||||||
value *= 1.0 / getPrefixMultiplier(to_prefix);
|
value *= 1.0 / get_prefix_multiplier(to_prefix);
|
||||||
// NB: python ifcopenshell.util.unit.convert checks `from_unit` (not
|
// NB: python ifcopenshell.util.unit.convert checks `from_unit` (not
|
||||||
// `to_unit`) here. Mirrored for parity — from_unit and to_unit are
|
// `to_unit`) here. Mirrored for parity — from_unit and to_unit are
|
||||||
// always the same dimension in valid calls, so behaviour is the same.
|
// always the same dimension in valid calls, so behaviour is the same.
|
||||||
if (from_unit.find("SQUARE") != std::string::npos) {
|
if (from_unit.find("SQUARE") != std::string::npos) {
|
||||||
value *= 1.0 / getPrefixMultiplier(to_prefix);
|
value *= 1.0 / get_prefix_multiplier(to_prefix);
|
||||||
} else if (from_unit.find("CUBIC") != std::string::npos) {
|
} else if (from_unit.find("CUBIC") != std::string::npos) {
|
||||||
value *= 1.0 / getPrefixMultiplier(to_prefix);
|
value *= 1.0 / get_prefix_multiplier(to_prefix);
|
||||||
value *= 1.0 / getPrefixMultiplier(to_prefix);
|
value *= 1.0 / get_prefix_multiplier(to_prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
double convertUnit(double value, express::Base from_unit, express::Base to_unit) {
|
double convert_unit(double value, express::Base from_unit, express::Base to_unit) {
|
||||||
auto pull = [](express::Base u, std::string& prefix, std::string& name) {
|
auto pull = [](express::Base u, std::string& prefix, std::string& name) {
|
||||||
auto e = u.as<express::Entity>();
|
auto e = u.as<express::Entity>();
|
||||||
if (u.declaration().is("IfcSIUnit")) {
|
if (u.declaration().is("IfcSIUnit")) {
|
||||||
prefix = enumString(e.get("Prefix"));
|
prefix = enum_string(e.get("Prefix"));
|
||||||
name = enumString(e.get("Name"));
|
name = enum_string(e.get("Name"));
|
||||||
} else {
|
} else {
|
||||||
// IfcConversionBasedUnit / IfcContextDependentUnit: no Prefix,
|
// IfcConversionBasedUnit / IfcContextDependentUnit: no Prefix,
|
||||||
// Name is a string attribute.
|
// Name is a string attribute.
|
||||||
|
|||||||
+14
-14
@@ -35,44 +35,44 @@ namespace ifcopenshell { class file; }
|
|||||||
|
|
||||||
// SI prefix multipliers, e.g. "MILLI" -> 1e-3. Empty key not present;
|
// SI prefix multipliers, e.g. "MILLI" -> 1e-3. Empty key not present;
|
||||||
// callers should pass an empty prefix string for "no prefix".
|
// callers should pass an empty prefix string for "no prefix".
|
||||||
extern const std::unordered_map<std::string, double> kSiPrefixes;
|
extern const std::unordered_map<std::string, double> SI_PREFIXES;
|
||||||
|
|
||||||
// SI prefix display symbols, e.g. "MILLI" -> "m".
|
// SI prefix display symbols, e.g. "MILLI" -> "m".
|
||||||
extern const std::unordered_map<std::string, std::string> kSiPrefixSymbols;
|
extern const std::unordered_map<std::string, std::string> SI_PREFIX_SYMBOLS;
|
||||||
|
|
||||||
// Conversion-based unit name (lowercase, IFC convention) -> SI base scale.
|
// Conversion-based unit name (lowercase, IFC convention) -> SI base scale.
|
||||||
// e.g. "foot" -> 0.3048, "square foot" -> 0.09290304.
|
// e.g. "foot" -> 0.3048, "square foot" -> 0.09290304.
|
||||||
extern const std::unordered_map<std::string, double> kSiConversions;
|
extern const std::unordered_map<std::string, double> SI_CONVERSIONS;
|
||||||
|
|
||||||
// Conversion-based unit name -> IFC unit type, e.g. "foot" -> "LENGTHUNIT".
|
// Conversion-based unit name -> IFC unit type, e.g. "foot" -> "LENGTHUNIT".
|
||||||
extern const std::unordered_map<std::string, std::string> kImperialTypes;
|
extern const std::unordered_map<std::string, std::string> IMPERIAL_TYPES;
|
||||||
|
|
||||||
// Display symbol per unit name. Covers IfcSIUnit names ("METRE" -> "m") and
|
// Display symbol per unit name. Covers IfcSIUnit names ("METRE" -> "m") and
|
||||||
// IfcConversionBasedUnit names ("foot" -> "ft").
|
// IfcConversionBasedUnit names ("foot" -> "ft").
|
||||||
extern const std::unordered_map<std::string, std::string> kUnitSymbols;
|
extern const std::unordered_map<std::string, std::string> UNIT_SYMBOLS;
|
||||||
|
|
||||||
// Returns the multiplier for an SI prefix. Empty string returns 1.0.
|
// Returns the multiplier for an SI prefix. Empty string returns 1.0.
|
||||||
double getPrefixMultiplier(const std::string& prefix);
|
double get_prefix_multiplier(const std::string& prefix);
|
||||||
|
|
||||||
// Returns the SI scale for an IfcNamedUnit such that
|
// Returns the SI scale for an IfcNamedUnit such that
|
||||||
// value_in_unit * scale == value_in_si_base
|
// value_in_unit * scale == value_in_si_base
|
||||||
// Walks IfcConversionBasedUnit chains down to IfcSIUnit. Returns nullopt
|
// Walks IfcConversionBasedUnit chains down to IfcSIUnit. Returns nullopt
|
||||||
// when the chain bottoms out in IfcContextDependentUnit (cannot convert).
|
// when the chain bottoms out in IfcContextDependentUnit (cannot convert).
|
||||||
std::optional<double> siScaleFromNamedUnit(express::Base named_unit);
|
std::optional<double> si_scale_from_named_unit(express::Base named_unit);
|
||||||
|
|
||||||
// IfcProject.UnitsInContext (the IfcUnitAssignment). Returns nullopt if
|
// IfcProject.UnitsInContext (the IfcUnitAssignment). Returns nullopt if
|
||||||
// the file has no project or no assignment.
|
// the file has no project or no assignment.
|
||||||
std::optional<express::Base> getUnitAssignment(ifcopenshell::file* ifc_file);
|
std::optional<express::Base> get_unit_assignment(ifcopenshell::file* ifc_file);
|
||||||
|
|
||||||
// First unit in the project's IfcUnitAssignment matching `unit_type`
|
// First unit in the project's IfcUnitAssignment matching `unit_type`
|
||||||
// (e.g. "LENGTHUNIT"). Returns nullopt if not found.
|
// (e.g. "LENGTHUNIT"). Returns nullopt if not found.
|
||||||
std::optional<express::Base> getProjectUnit(ifcopenshell::file* ifc_file,
|
std::optional<express::Base> get_project_unit(ifcopenshell::file* ifc_file,
|
||||||
const std::string& unit_type);
|
const std::string& unit_type);
|
||||||
|
|
||||||
// Project unit -> SI base scale (e.g. project in mm => 0.001). Defaults
|
// Project unit -> SI base scale (e.g. project in mm => 0.001). Defaults
|
||||||
// to 1.0 when no project unit of the requested type is set.
|
// to 1.0 when no project unit of the requested type is set.
|
||||||
double calculateUnitScale(ifcopenshell::file* ifc_file,
|
double calculate_unit_scale(ifcopenshell::file* ifc_file,
|
||||||
const std::string& unit_type = "LENGTHUNIT");
|
const std::string& unit_type = "LENGTHUNIT");
|
||||||
|
|
||||||
// Convert between two units identified by name + optional SI prefix.
|
// Convert between two units identified by name + optional SI prefix.
|
||||||
// SQUARE_/CUBIC_ prefixed SI names get the prefix multiplier squared/cubed
|
// SQUARE_/CUBIC_ prefixed SI names get the prefix multiplier squared/cubed
|
||||||
@@ -83,7 +83,7 @@ double convert(double value,
|
|||||||
|
|
||||||
// Convert between two IfcNamedUnit entities. Pulls Name and Prefix off each
|
// Convert between two IfcNamedUnit entities. Pulls Name and Prefix off each
|
||||||
// and delegates to convert(). IfcConversionBasedUnit names that don't appear
|
// and delegates to convert(). IfcConversionBasedUnit names that don't appear
|
||||||
// in kSiConversions return the value unchanged.
|
// in SI_CONVERSIONS return the value unchanged.
|
||||||
double convertUnit(double value, express::Base from_unit, express::Base to_unit);
|
double convert_unit(double value, express::Base from_unit, express::Base to_unit);
|
||||||
|
|
||||||
#endif // UNIT_H
|
#endif // UNIT_H
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
#include "Federation.h"
|
#include "Federation.h"
|
||||||
#include "Geolocation.h"
|
#include "geolocation.h"
|
||||||
#include "Unit.h"
|
#include "unit.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -103,18 +103,18 @@ ModelGeoref computeModelGeoref(ifcopenshell::file* ifc_file) {
|
|||||||
if (!ifc_file) return out;
|
if (!ifc_file) return out;
|
||||||
|
|
||||||
out.units.project_length_to_meters =
|
out.units.project_length_to_meters =
|
||||||
calculateUnitScale(ifc_file, "LENGTHUNIT");
|
calculate_unit_scale(ifc_file, "LENGTHUNIT");
|
||||||
|
|
||||||
auto params = getHelmertTransformationParameters(ifc_file);
|
auto params = get_helmert_transformation_parameters(ifc_file);
|
||||||
const double scale = (params && params->scale != 0.0) ? params->scale : 1.0;
|
const double scale = (params && params->scale != 0.0) ? params->scale : 1.0;
|
||||||
out.units.map_unit_to_meters = out.units.project_length_to_meters / scale;
|
out.units.map_unit_to_meters = out.units.project_length_to_meters / scale;
|
||||||
if (!params) return out;
|
if (!params) return out;
|
||||||
|
|
||||||
Eigen::Matrix4d helmert =
|
Eigen::Matrix4d helmert =
|
||||||
helmertMetersFromParameters(*params, out.units.map_unit_to_meters);
|
helmert_meters_from_parameters(*params, out.units.map_unit_to_meters);
|
||||||
|
|
||||||
if (auto wcs = getWcs(ifc_file)) {
|
if (auto wcs = get_wcs(ifc_file)) {
|
||||||
// getWcs returns the WCS in project units (translation in project
|
// get_wcs returns the WCS in project units (translation in project
|
||||||
// length units). Convert translation to metres before inverting.
|
// length units). Convert translation to metres before inverting.
|
||||||
Eigen::Matrix4d wcs_m = *wcs;
|
Eigen::Matrix4d wcs_m = *wcs;
|
||||||
wcs_m(0, 3) *= out.units.project_length_to_meters;
|
wcs_m(0, 3) *= out.units.project_length_to_meters;
|
||||||
@@ -147,11 +147,11 @@ guessFederatedFalseOrigin(const Eigen::Vector3d& first_geometry_point_m,
|
|||||||
out.xyz = t_m * u_fed_inv;
|
out.xyz = t_m * u_fed_inv;
|
||||||
|
|
||||||
// Rotation: helmert grid-north baked into coordinate_operation_meters.
|
// Rotation: helmert grid-north baked into coordinate_operation_meters.
|
||||||
// helmertMetersFromParameters built that block as R_z(theta)·diag(fx,fy,fz)
|
// helmert_meters_from_parameters built that block as R_z(theta)·diag(fx,fy,fz)
|
||||||
// with theta = atan2(xao, xaa); xaxis2angle is `-theta` in degrees.
|
// with theta = atan2(xao, xaa); x_axis_to_angle_deg is `-theta` in degrees.
|
||||||
if (use_coord_op) {
|
if (use_coord_op) {
|
||||||
const Eigen::Matrix4d& M = georef.coordinate_operation_meters;
|
const Eigen::Matrix4d& M = georef.coordinate_operation_meters;
|
||||||
out.rz_deg = xaxis2angleDeg(M(0, 0), M(1, 0));
|
out.rz_deg = x_axis_to_angle_deg(M(0, 0), M(1, 0));
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ struct ModelTransformation {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Per-model unit scales captured at load time. project_length_to_meters comes
|
// Per-model unit scales captured at load time. project_length_to_meters comes
|
||||||
// from calculateUnitScale(file, "LENGTHUNIT"). map_unit_to_meters is derived
|
// from calculate_unit_scale(file, "LENGTHUNIT"). map_unit_to_meters is derived
|
||||||
// from IfcMapConversion.Scale as project_length_to_meters / Scale; the
|
// from IfcMapConversion.Scale as project_length_to_meters / Scale; the
|
||||||
// IfcProjectedCRS.MapUnit named unit is metadata and does not affect the
|
// IfcProjectedCRS.MapUnit named unit is metadata and does not affect the
|
||||||
// transform composition.
|
// transform composition.
|
||||||
@@ -161,7 +161,7 @@ Eigen::Matrix4d composeFederatedFalseOrigin(const FederatedFalseOrigin&,
|
|||||||
|
|
||||||
// Compose ModelTransformation into a 4x4 matrix in metres.
|
// Compose ModelTransformation into a 4x4 matrix in metres.
|
||||||
// `coordinate_operation_meters` is the model's CoordinateOperation matrix
|
// `coordinate_operation_meters` is the model's CoordinateOperation matrix
|
||||||
// (e.g. helmertMetersFromParameters · inv(wcs_meters)) — needed to lift
|
// (e.g. helmert_meters_from_parameters · inv(wcs_meters)) — needed to lift
|
||||||
// `a` into metres when a_frame == ModelLocal. Pass identity when the
|
// `a` into metres when a_frame == ModelLocal. Pass identity when the
|
||||||
// CoordinateOperation is disabled or absent.
|
// CoordinateOperation is disabled or absent.
|
||||||
Eigen::Matrix4d composeModelTransformation(const ModelTransformation&,
|
Eigen::Matrix4d composeModelTransformation(const ModelTransformation&,
|
||||||
|
|||||||
@@ -158,9 +158,9 @@ target_link_libraries(test_federation PRIVATE
|
|||||||
Qt${QT_VERSION}::Gui # Federation::HomeView uses QVector3D from QtGui
|
Qt${QT_VERSION}::Gui # Federation::HomeView uses QVector3D from QtGui
|
||||||
Qt${QT_VERSION}::Test # QSignalSpy
|
Qt${QT_VERSION}::Test # QSignalSpy
|
||||||
Eigen3::Eigen # Federation.h: composed matrices use Eigen
|
Eigen3::Eigen # Federation.h: composed matrices use Eigen
|
||||||
# helpers provides Unit::convert + Geolocation helpers
|
# helpers provides unit conversion and geolocation helpers
|
||||||
# (helmertMetersFromParameters, getWcs, getMapUnit) + Placement
|
# (helmert_meters_from_parameters, get_wcs, get_map_unit) + Placement
|
||||||
# (getAxis2Placement, called from Geolocation::getWcs). Linking the
|
# (get_axis2_placement, called from geolocation::get_wcs). Linking the
|
||||||
# static lib avoids re-compiling those .cpp files here and pulls
|
# static lib avoids re-compiling those .cpp files here and pulls
|
||||||
# the src/helpers include dir + IfcParse transitively.
|
# the src/helpers include dir + IfcParse transitively.
|
||||||
helpers
|
helpers
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
#include "Federation.h"
|
#include "Federation.h"
|
||||||
#include "Geolocation.h"
|
#include "geolocation.h"
|
||||||
|
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
@@ -467,7 +467,7 @@ TEST_CASE("helmert metres transform consumes Scale through map unit scale",
|
|||||||
|
|
||||||
const double project_length_to_meters = 0.001;
|
const double project_length_to_meters = 0.001;
|
||||||
const double map_unit_to_meters = project_length_to_meters / params.scale;
|
const double map_unit_to_meters = project_length_to_meters / params.scale;
|
||||||
Eigen::Matrix4d M = helmertMetersFromParameters(params, map_unit_to_meters);
|
Eigen::Matrix4d M = helmert_meters_from_parameters(params, map_unit_to_meters);
|
||||||
|
|
||||||
Eigen::Vector4d local_m(10.0, 20.0, 0.0, 1.0);
|
Eigen::Vector4d local_m(10.0, 20.0, 0.0, 1.0);
|
||||||
Eigen::Vector4d global_m = M * local_m;
|
Eigen::Vector4d global_m = M * local_m;
|
||||||
|
|||||||
Reference in New Issue
Block a user