mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 19:07:57 +00:00
ifcviewer: auto-guess FederatedFalseOrigin on first model added
When the user adds a model into a fresh, untitled federation that still
has the default (0,0,0, no rotation) FederatedFalseOrigin, derive an
origin from the first instance's placement_transformation (lifted
through CoordinateOperation when enabled) and the helmert grid-north
baked into ModelGeoref::coordinate_operation_meters. Multi-file batches
naturally settle: whichever load finishes first anchors the federation,
the rest see a non-default origin and skip. Saved .ifcfeds keep their
authoritative origin.
Adds Placement.{h,cpp} (port of util/placement.py — a2p,
get_axis2placement, get_local_placement) so Geolocation no longer needs
its own anonymous getAxis2Placement, and xaxis2angleDeg in Geolocation
mirroring util/geolocation.xaxis2angle.
SceneLoader captures the first instance's placement_transformation from
either the sidecar's InstanceCpu[0] or the streamer's first
InstanceChunk, so the guess works on both load paths without re-reading
the IFC.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
********************************************************************************/
|
||||
|
||||
#include "Geolocation.h"
|
||||
#include "Placement.h"
|
||||
|
||||
#include "../ifcparse/express.h"
|
||||
#include "../ifcparse/file.h"
|
||||
@@ -30,59 +31,6 @@
|
||||
|
||||
namespace {
|
||||
|
||||
// IfcAxis2Placement3D / IfcAxis2PlacementLinear -> column-major 4x4 matrix.
|
||||
// Mirrors ifcopenshell.util.placement.a2p + get_axis2placement, but only the
|
||||
// branches needed for IfcGeometricRepresentationContext.WorldCoordinateSystem.
|
||||
std::optional<Eigen::Matrix4d> getAxis2Placement(express::Base placement) {
|
||||
if (!placement) return std::nullopt;
|
||||
const auto& decl = placement.declaration();
|
||||
if (!(decl.is("IfcAxis2Placement3D") || decl.is("IfcAxis2PlacementLinear"))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto entity = placement.as<express::Entity>();
|
||||
|
||||
Eigen::Vector3d z(0.0, 0.0, 1.0);
|
||||
Eigen::Vector3d x(1.0, 0.0, 0.0);
|
||||
|
||||
auto axis_attr = entity.get("Axis");
|
||||
if (!axis_attr.isNull()) {
|
||||
express::Base axis = axis_attr;
|
||||
std::vector<double> dr =
|
||||
axis.as<express::Entity>().get("DirectionRatios");
|
||||
if (dr.size() >= 3) z = Eigen::Vector3d(dr[0], dr[1], dr[2]);
|
||||
}
|
||||
|
||||
auto refdir_attr = entity.get("RefDirection");
|
||||
if (!refdir_attr.isNull()) {
|
||||
express::Base refdir = refdir_attr;
|
||||
std::vector<double> dr =
|
||||
refdir.as<express::Entity>().get("DirectionRatios");
|
||||
if (dr.size() >= 3) x = Eigen::Vector3d(dr[0], dr[1], dr[2]);
|
||||
}
|
||||
|
||||
auto loc_attr = entity.get("Location");
|
||||
if (loc_attr.isNull()) return std::nullopt;
|
||||
express::Base location = loc_attr;
|
||||
auto coords_attr = location.as<express::Entity>().get("Coordinates");
|
||||
if (coords_attr.isNull()) return std::nullopt;
|
||||
std::vector<double> coords = coords_attr;
|
||||
if (coords.size() < 3) return std::nullopt;
|
||||
|
||||
Eigen::Vector3d xn = x.normalized();
|
||||
Eigen::Vector3d zn = z.normalized();
|
||||
Eigen::Vector3d yn = zn.cross(xn).normalized();
|
||||
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
m.block<3, 1>(0, 0) = xn;
|
||||
m.block<3, 1>(0, 1) = yn;
|
||||
m.block<3, 1>(0, 2) = zn;
|
||||
m(0, 3) = coords[0];
|
||||
m(1, 3) = coords[1];
|
||||
m(2, 3) = coords[2];
|
||||
return m;
|
||||
}
|
||||
|
||||
// Read a numeric NominalValue out of an IfcPropertySingleValue. IFC2X3
|
||||
// ePSet_MapConversion stores eastings/northings/scale as IfcLengthMeasure or
|
||||
// IfcReal wrapped inside IfcValue (a SELECT) — get_attribute_value(0) peels
|
||||
@@ -223,6 +171,10 @@ std::optional<Eigen::Matrix4d> getWcs(ifcopenshell::file* ifc_file) {
|
||||
}
|
||||
}
|
||||
if (!found) return std::nullopt;
|
||||
const auto& decl = wcs.declaration();
|
||||
if (!(decl.is("IfcAxis2Placement3D") || decl.is("IfcAxis2PlacementLinear"))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return getAxis2Placement(wcs);
|
||||
}
|
||||
|
||||
@@ -311,3 +263,8 @@ std::optional<express::Base> getMapUnit(ifcopenshell::file* ifc_file) {
|
||||
if (mu_attr.isNull()) return std::nullopt;
|
||||
return (express::Base) mu_attr;
|
||||
}
|
||||
|
||||
double xaxis2angleDeg(double xaa, double xao) {
|
||||
constexpr double kPi = 3.14159265358979323846;
|
||||
return -std::atan2(xao, xaa) * (180.0 / kPi);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user