Work towards v1.0 data model with encapsulated weak_ptr as basis for instances

This commit is contained in:
Thomas Krijnen
2026-01-04 10:40:02 +01:00
parent f09ca658f1
commit 7098beb819
210 changed files with 28269 additions and 26471 deletions
+281 -286
View File
@@ -97,7 +97,7 @@ bool SvgSerializer::ready() {
return true;
}
void SvgSerializer::write(path_object& p, const TopoDS_Shape& comp_or_wire, boost::optional<std::vector<double>> dash_array) {
void SvgSerializer::write(path_object& p, const TopoDS_Shape& comp_or_wire, std::optional<std::vector<double>> dash_array) {
/* ShapeFix_Wire fix;
Handle(ShapeExtend_WireData) data = new ShapeExtend_WireData;
for (TopExp_Explorer edges(result, TopAbs_EDGE); edges.More(); edges.Next()) {
@@ -361,7 +361,7 @@ void SvgSerializer::write(path_object& p, const TopoDS_Shape& comp_or_wire, boos
}
}
SvgSerializer::path_object& SvgSerializer::start_path(const gp_Pln& pln, const IfcUtil::IfcBaseEntity* storey, const std::string& id) {
SvgSerializer::path_object& SvgSerializer::start_path(const gp_Pln& pln, const express::Base& storey, const std::string& id) {
auto key = std::make_pair(std::make_pair(storey, ""), path_object());
SvgSerializer::path_object& p = paths.insert(key)->second;
drawing_metadata[key.first].pln_3d = pln;
@@ -370,7 +370,7 @@ SvgSerializer::path_object& SvgSerializer::start_path(const gp_Pln& pln, const I
}
SvgSerializer::path_object& SvgSerializer::start_path(const gp_Pln& pln, const std::string& drawing_name, const std::string& id) {
auto key = std::make_pair(std::make_pair(nullptr, drawing_name), path_object());
auto key = std::make_pair(std::make_pair(express::Base{}, drawing_name), path_object());
SvgSerializer::path_object& p = paths.insert(key)->second;
drawing_metadata[key.first].pln_3d = pln;
p.first = id;
@@ -378,11 +378,11 @@ SvgSerializer::path_object& SvgSerializer::start_path(const gp_Pln& pln, const s
}
namespace {
boost::optional<std::pair<const IfcUtil::IfcBaseEntity*, double>> storey_elevation_from_element(const IfcGeom::BRepElement* o) {
std::optional<std::pair<express::Base, double>> storey_elevation_from_element(const IfcGeom::BRepElement* o) {
for (const auto& p : o->parents()) {
if (p->type() == "IfcBuildingStorey") {
try {
double e = p->product()->get("Elevation");
double e = p->product().get("Elevation");
double storey_elevation = e * o->geometry().settings().get<ifcopenshell::geometry::settings::LengthUnit>().get();
return std::make_pair(p->product(), storey_elevation);
} catch (...) {
@@ -391,12 +391,12 @@ namespace {
break;
}
}
return boost::none;
return std::nullopt;
}
typedef std::pair<std::array<double, 3>, std::array<double, 3>> box_t;
boost::optional<TopoDS_Edge> edge_from_compound(TopoDS_Shape& compound) {
std::optional<TopoDS_Edge> edge_from_compound(TopoDS_Shape& compound) {
TopoDS_Iterator it(compound);
if (it.More()) {
TopoDS_Shape wire = it.Value();
@@ -412,7 +412,7 @@ namespace {
}
}
}
return boost::none;
return std::nullopt;
}
class almost {
@@ -433,7 +433,7 @@ namespace {
}
};
boost::optional<box_t> box_from_compound(TopoDS_Shape& compound) {
std::optional<box_t> box_from_compound(TopoDS_Shape& compound) {
/*
// in v0.8 apparently we don't get a solid/shell anymore because
// we no longer use PrimAPI, but rather resolve the box to an
@@ -446,17 +446,17 @@ namespace {
shell = TopoDS::Shell(exp.Current());
exp.Next();
if (exp.More()) {
return boost::none;
return std::nullopt;
}
}
else {
return boost::none;
return std::nullopt;
}
*/
auto& shell = compound;
if (IfcGeom::util::count(shell, TopAbs_FACE) != 6) {
return boost::none;
return std::nullopt;
}
TopExp_Explorer it(shell, TopAbs_FACE);
@@ -464,16 +464,16 @@ namespace {
const auto& face = TopoDS::Face(it.Current());
auto surf = BRep_Tool::Surface(face);
if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
return boost::none;
return std::nullopt;
}
auto pln = Handle(Geom_Plane)::DownCast(surf);
auto dz = std::abs(pln->Position().Direction().Z());
if (almost(0.) != dz && almost(1.) != dz) {
return boost::none;
return std::nullopt;
}
auto dy = std::abs(pln->Position().Direction().Y());
if (almost(0.) != dy && almost(1.) != dy) {
return boost::none;
return std::nullopt;
}
}
@@ -491,27 +491,27 @@ namespace {
};
template <typename It>
void enumerate_string_properties(const IfcUtil::IfcBaseEntity* product, It output_it) {
auto rels = product->get_inverse("IsDefinedBy");
for (auto& rel : *rels) {
if (rel->declaration().is("IfcRelDefinesByProperties")) {
auto pset = ((IfcUtil::IfcBaseClass*) ((IfcUtil::IfcBaseEntity*) rel)->get("RelatingPropertyDefinition"))->as<IfcUtil::IfcBaseEntity>();
if (!pset->declaration().is("IfcPropertySet")) {
void enumerate_string_properties(const express::Base& product, It output_it) {
auto rels = product.as<express::Entity>().get_inverse("IsDefinedBy");
for (auto& rel : rels) {
if (rel.declaration().is("IfcRelDefinesByProperties")) {
auto pset = ((express::Base)rel.get("RelatingPropertyDefinition")).as<express::Entity>();
if (!pset.declaration().is("IfcPropertySet")) {
continue;
}
std::string pset_name;
if (!pset->get("Name").isNull()) {
pset_name = (std::string) pset->get("Name");
if (!pset.get("Name").isNull()) {
pset_name = (std::string) pset.get("Name");
}
aggregate_of_instance::ptr props = pset->get("HasProperties");
for (auto& prop : *props) {
if (prop->declaration().is("IfcPropertySingleValue")) {
std::string name = ((IfcUtil::IfcBaseEntity*) prop)->get("Name");
if (((IfcUtil::IfcBaseEntity*) prop)->get("NominalValue").isNull()) {
std::vector<express::Base> props = pset.get("HasProperties");
for (auto& prop : props) {
if (prop.declaration().is("IfcPropertySingleValue")) {
std::string name = prop.as<express::Entity>().get("Name");
if (prop.as<express::Entity>().get("NominalValue").isNull()) {
continue;
}
IfcUtil::IfcBaseClass* v = ((IfcUtil::IfcBaseEntity*) prop)->get("NominalValue");
auto value = v->get_attribute_value(0);
express::Base v = prop.as<express::Entity>().get("NominalValue");
auto value = v.get_attribute_value(0);
if (value.type() == IfcUtil::Argument_STRING) {
std::string v_str = value;
*output_it++ = string_property{ pset_name, name, v_str };
@@ -524,25 +524,24 @@ namespace {
}
namespace {
boost::optional<std::string> get_curve_style_name(IfcUtil::IfcBaseEntity* item) {
auto refs = item->get_inverse("StyledByItem");
for (auto& ref : *refs) {
if (ref->declaration().is("IfcStyledItem")) {
aggregate_of_instance::ptr styles = ((IfcUtil::IfcBaseEntity*)ref)->get("Styles");
for (auto& s_ : *styles) {
auto s = (IfcUtil::IfcBaseEntity*) s_;
std::vector<IfcUtil::IfcBaseEntity*> pss;
if (s->declaration().is("IfcPresentationStyleAssignment")) {
aggregate_of_instance::ptr pstyles = s->get("Styles");
for (auto& ssss : *pstyles) {
pss.push_back((IfcUtil::IfcBaseEntity*) ssss);
std::optional<std::string> get_curve_style_name(const express::Base& item) {
auto refs = item.as<express::Entity>().get_inverse("StyledByItem");
for (auto& ref : refs) {
if (ref.declaration().is("IfcStyledItem")) {
std::vector<express::Base> styles = ref.as<express::Entity>().get("Styles");
for (auto& s : styles) {
std::vector<express::Entity> pss;
if (s.declaration().is("IfcPresentationStyleAssignment")) {
std::vector<express::Base> pstyles = s.as<express::Entity>().get("Styles");
for (auto& ssss : pstyles) {
pss.push_back(ssss.as<express::Entity>());
}
} else {
pss.push_back(s);
pss.push_back(s.as<express::Entity>());
}
for (auto& ps : pss) {
if (ps->declaration().is("IfcCurveStyle")) {
auto arg = ps->get("Name");
if (ps.declaration().is("IfcCurveStyle")) {
auto arg = ps.get("Name");
if (!arg.isNull()) {
return (std::string) arg;
}
@@ -551,18 +550,18 @@ namespace {
}
}
}
return boost::none;
return std::nullopt;
}
}
void SvgSerializer::write(const IfcGeom::BRepElement* brep_obj) {
boost::optional<std::string> object_type;
if (!brep_obj->product()->get("ObjectType").isNull()) {
object_type = static_cast<std::string>(brep_obj->product()->get("ObjectType"));
std::optional<std::string> object_type;
if (!brep_obj->product().get("ObjectType").isNull()) {
object_type = static_cast<std::string>(brep_obj->product().get("ObjectType"));
}
std::vector<boost::optional<std::vector<double>>> dash_arrays;
std::vector<std::optional<std::vector<double>>> dash_arrays;
auto itm = brep_obj->geometry().as_compound();
TopoDS_Shape compound_local = ((ifcopenshell::geometry::OpenCascadeShape*)itm)->shape();
@@ -571,9 +570,9 @@ void SvgSerializer::write(const IfcGeom::BRepElement* brep_obj) {
for (auto& x : brep_obj->geometry()) {
dash_arrays.emplace_back();
boost::optional<std::string> curve_style_name;
std::optional<std::string> curve_style_name;
if (file) {
auto item = (IfcUtil::IfcBaseEntity*) this->file->instance_by_id(x.ItemId());
auto item = this->file->instance_by_id(x.ItemId());
curve_style_name = get_curve_style_name(item);
}
@@ -621,11 +620,11 @@ void SvgSerializer::write(const IfcGeom::BRepElement* brep_obj) {
auto compound_unmirrored = make_transform_global.Shape();
if (is_section || is_elevation) {
boost::optional<double> scale;
boost::optional<std::pair<double, double>> size;
std::optional<double> scale;
std::optional<std::pair<double, double>> size;
auto e = edge_from_compound(compound_unmirrored);
boost::optional<gp_Pln> pln;
std::optional<gp_Pln> pln;
if (e) {
TopoDS_Edge global_edge = TopoDS::Edge(e->Moved(trsf));
double u0, u1;
@@ -638,7 +637,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement* brep_obj) {
pln = gp_Pln(gp_Ax3(P, N, V));
}
}
else if (boost::optional<box_t> b = box_from_compound(compound_local)) {
else if (std::optional<box_t> b = box_from_compound(compound_local)) {
pln = gp_Pln().Transformed(trsf);
size = std::make_pair(
b->second[0] - b->first[0],
@@ -726,7 +725,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement* brep_obj) {
}
auto p = storey_elevation_from_element(brep_obj);
const IfcUtil::IfcBaseEntity* storey = p ? p->first : nullptr;
auto storey = p ? p->first : express::Base{};
double elev = p ? p->second : std::numeric_limits<double>::quiet_NaN();
// @todo is it correct to call nameElement() here with a single storey (what if this element spans multiple?)
@@ -792,7 +791,7 @@ void SvgSerializer::write(const geometry_data& data) {
const std::vector<section_data>* section_heights_used = &section_heights_storage;
if (section_data_) {
section_heights_used = section_data_.get_ptr();
section_heights_used = section_data_ ? std::addressof(*section_data_) : nullptr;
} else {
if (data.storey) {
section_heights_storage.push_back(horizontal_plan{ data.storey, data.storey_elevation, +1. });
@@ -836,25 +835,25 @@ void SvgSerializer::write(const geometry_data& data) {
TopoDS_Wire annotation;
if (is_floor_plan_ && draw_door_arcs_ && data.product->declaration().is("IfcDoor")) {
if (is_floor_plan_ && draw_door_arcs_ && data.product.declaration().is("IfcDoor")) {
boost::optional<std::string> operation_type;
std::optional<std::string> operation_type;
try {
aggregate_of_instance::ptr rels;
if (data.product->declaration().schema()->name() == "IFC2X3") {
rels = data.product->get_inverse("IsDefinedBy");
std::vector<express::Entity> rels;
if (data.product.declaration().schema()->name() == "IFC2X3") {
rels = data.product.as<express::Entity>().get_inverse("IsDefinedBy");
} else {
// Damn you, IFC
rels = data.product->get_inverse("IsTypedBy");
rels = data.product.as<express::Entity>().get_inverse("IsTypedBy");
}
for (auto& rel : *rels) {
if (rel->declaration().name() == "IfcRelDefinesByType") {
IfcUtil::IfcBaseClass* ty = ((IfcUtil::IfcBaseEntity*)rel)->get("RelatingType");
const std::string& ty_entity_name = ty->declaration().name();
for (auto& rel : rels) {
if (rel.declaration().name() == "IfcRelDefinesByType") {
express::Base ty = rel.as<express::Entity>().get("RelatingType");
const std::string& ty_entity_name = ty.declaration().name();
// Damn you, IFC
if (ty_entity_name == "IfcDoorStyle" || ty_entity_name == "IfcDoorType") {
operation_type = (std::string)((IfcUtil::IfcBaseEntity*)ty)->get("OperationType");
operation_type = ty.as<express::Entity>().get("OperationType");
}
}
}
@@ -936,7 +935,7 @@ void SvgSerializer::write(const geometry_data& data) {
gp_Vec projection_direction;
gp_Pln projection_plane;
const IfcUtil::IfcBaseEntity* storey = nullptr;
express::Base storey;
std::string drawing_name;
bool use_hlr = always_project_;
@@ -996,7 +995,7 @@ void SvgSerializer::write(const geometry_data& data) {
}
// Exclude annotations, spaces and grids from HLR
if (any_in_front && !data.product->declaration().is("IfcAnnotation") && !data.product->declaration().is("IfcSpace") && !data.product->declaration().is("IfcGrid")) {
if (any_in_front && !data.product.declaration().is("IfcAnnotation") && !data.product.declaration().is("IfcSpace") && !data.product.declaration().is("IfcGrid")) {
TopoDS_Shape* compound_to_hlr = &compound_to_use;
TopoDS_Shape subtracted_shape;
@@ -1004,9 +1003,9 @@ void SvgSerializer::write(const geometry_data& data) {
bool should_subtract = false;
if (subtraction_settings_ == ON_SLABS_AT_FLOORPLANS) {
should_subtract = data.product->declaration().is("IfcSlab") && is_floor_plan_;
should_subtract = data.product.declaration().is("IfcSlab") && is_floor_plan_;
} else if (subtraction_settings_ == ON_SLABS_AND_WALLS) {
should_subtract = data.product->declaration().is("IfcSlab") || data.product->declaration().is("IfcWall");
should_subtract = data.product.declaration().is("IfcSlab") || data.product.declaration().is("IfcWall");
} else if (subtraction_settings_ == ALWAYS) {
should_subtract = true;
}
@@ -1118,7 +1117,7 @@ void SvgSerializer::write(const geometry_data& data) {
}
TopoDS_Compound profile_edges;
if (profile_threshold_ != -1 && !(data.product->declaration().is("IfcWall") || data.product->declaration().is("IfcSlab"))) {
if (profile_threshold_ != -1 && !(data.product.declaration().is("IfcWall") || data.product.declaration().is("IfcSlab"))) {
TopTools_IndexedDataMapOfShapeListOfShape map;
TopExp::MapShapesAndAncestors(*compound_to_hlr, TopAbs_EDGE, TopAbs_FACE, map);
if (map.Extent() > profile_threshold_) {
@@ -1277,7 +1276,7 @@ void SvgSerializer::write(const geometry_data& data) {
auto proj = projection_direction ^ bbdif ^ projection_direction;
std::string object_type;
auto ot_arg = data.product->get("ObjectType");
auto ot_arg = data.product.as<express::Entity>().get("ObjectType");
if (!ot_arg.isNull()) {
object_type = (std::string) ot_arg;
object_type.erase(std::remove_if(object_type.begin(), object_type.end(), [](char c) { return !std::isalnum(c); }), object_type.end());
@@ -1287,7 +1286,7 @@ void SvgSerializer::write(const geometry_data& data) {
auto xyz_global = gp_Pnt().Transformed(data.trsf);
int state = infront_or_behind(projection_plane, xyz_global);
if (data.product->declaration().is("IfcAnnotation") && // is an Annotation
if (data.product.declaration().is("IfcAnnotation") && // is an Annotation
(proj.Magnitude() > 1.e-5) && // when projected onto the view has a length
(is_floor_plan_
? (zmin >= range.first && zmin < (range.second - 1.e-5)) // the Z-coords are within the range of the building storey,
@@ -1457,7 +1456,7 @@ void SvgSerializer::write(const geometry_data& data) {
TopoDS_Wire wire = TopoDS::Wire(wires->Value(i));
if (wire.Closed() && (print_space_names_ || print_space_areas_) && data.product->declaration().is("IfcSpace")) {
if (wire.Closed() && (print_space_names_ || print_space_areas_) && data.product.declaration().is("IfcSpace")) {
// we explicitly specify the surface here, to later on
// simplify the projection from {x,y,z} to {u, v} because
// we know we can simply discard z.
@@ -1475,12 +1474,12 @@ void SvgSerializer::write(const geometry_data& data) {
}
if (file && data.product->declaration().is("IfcBuildingStorey") && storey_height_display_ != SH_NONE && wires->Length() == 1 && IfcGeom::util::count(wire, TopAbs_EDGE) == 1) {
if (file && data.product.declaration().is("IfcBuildingStorey") && storey_height_display_ != SH_NONE && wires->Length() == 1 && IfcGeom::util::count(wire, TopAbs_EDGE) == 1) {
std::string elev_str;
const double lu = file->getUnit("LENGTHUNIT").second;
auto a = data.product->get("Elevation");
auto a = data.product.as<express::Entity>().get("Elevation");
if (!a.isNull()) {
double elev = a;
@@ -1523,7 +1522,7 @@ void SvgSerializer::write(const geometry_data& data) {
auto d = (p1.XYZ() - p0.XYZ());
d.Normalize();
const double shll = storey_height_line_length_.get_value_or(2.);
const double shll = storey_height_line_length_.value_or(2.);
d *= shll;
gp_Pnt p1x(p0.XYZ() + d);
@@ -1579,7 +1578,7 @@ void SvgSerializer::write(const geometry_data& data) {
std::pair<const gp_Pnt*, const gp_Pnt*> furthest_points = { nullptr, nullptr };
double furthest_points_distance = 0.;
boost::optional<gp_Pnt> center_point;
std::optional<gp_Pnt> center_point;
BRepTopAdaptor_FClass2d fcls(largest_closed_wire_face, BRep_Tool::Tolerance(largest_closed_wire_face));
@@ -1625,8 +1624,8 @@ void SvgSerializer::write(const geometry_data& data) {
if (print_space_names_) {
labels.push_back(data.ifc_name);
}
if (print_space_names_ && data.product->declaration().is("IfcSpace")) {
auto attr = data.product->get("LongName");
if (print_space_names_ && data.product.declaration().is("IfcSpace")) {
auto attr = data.product.as<express::Entity>().get("LongName");
if (!attr.isNull()) {
std::string long_name = attr;
if (!long_name.empty()) {
@@ -1708,8 +1707,8 @@ std::array<std::array<double, 3>, 3> SvgSerializer::resize() {
cy = offset_2d_->second;
} else if (scale_) {
sc = (*scale_) * 1000;
cx = (xmax + xmin) / 2. * sc - size_->first * center_x_.get_value_or(0.5);
cy = (ymax + ymin) / 2. * sc - size_->second * center_y_.get_value_or(0.5);
cx = (xmax + xmin) / 2. * sc - size_->first * center_x_.value_or(0.5);
cy = (ymax + ymin) / 2. * sc - size_->second * center_y_.value_or(0.5);
} else {
if (calculated_scale_) {
sc = *calculated_scale_;
@@ -1772,7 +1771,7 @@ void SvgSerializer::draw_hlr(const gp_Pln& pln, const drawing_key& drawing_name)
// not on the TopoDS_Shape input.
TopoDS_Shape hlr_compound;
if (drawing_name.first == nullptr) {
if (!drawing_name.first) {
gp_Trsf trsf_mirror;
if (!mirror_y_) {
trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY()));
@@ -1830,7 +1829,7 @@ void SvgSerializer::resetScale() {
void SvgSerializer::addTextAnnotations(const drawing_key& k) {
auto& meta = drawing_metadata[k];
boost::optional<std::pair<double, double>> range;
std::optional<std::pair<double, double>> range;
if (k.first && section_data_) {
for (auto& sd : *section_data_) {
@@ -1843,130 +1842,128 @@ void SvgSerializer::addTextAnnotations(const drawing_key& k) {
}
}
aggregate_of_instance::ptr annotations;
std::vector<express::Base> annotations;
if (file) {
annotations = file->instances_by_type("IfcAnnotation");
}
if (annotations) {
for (auto& ann_ : *annotations) {
auto ann = (IfcUtil::IfcBaseEntity*) ann_;
for (auto& ann_ : annotations) {
auto ann = ann_.as<express::Entity>();
auto ot = ann->get("ObjectType");
auto nm = ann->get("Name");
auto ds = ann->get("Description");
auto pl = ann->get("ObjectPlacement");
auto ot = ann.get("ObjectType");
auto nm = ann.get("Name");
auto ds = ann.get("Description");
auto pl = ann.get("ObjectPlacement");
if (!ot.isNull() && !nm.isNull() && !ds.isNull() && !pl.isNull()) {
auto object_type = (std::string) ot;
auto name = (std::string) nm;
auto desc = (std::string) ds;
if (!ot.isNull() && !nm.isNull() && !ds.isNull() && !pl.isNull()) {
auto object_type = (std::string) ot;
auto name = (std::string) nm;
auto desc = (std::string) ds;
if (object_type == "Text") {
auto mapping = ifcopenshell::geometry::impl::mapping_implementations().construct(file, geometry_settings_);
auto item = mapping->map(pl);
auto matrix = ifcopenshell::geometry::taxonomy::cast<ifcopenshell::geometry::taxonomy::matrix4>(item);
delete mapping;
if (item) {
gp_Trsf trsf;
auto& m = matrix->ccomponents();
trsf.SetValues(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
);
if (object_type == "Text") {
auto mapping = ifcopenshell::geometry::impl::mapping_implementations().construct(file, geometry_settings_);
auto item = mapping->map(pl);
auto matrix = ifcopenshell::geometry::taxonomy::cast<ifcopenshell::geometry::taxonomy::matrix4>(item);
delete mapping;
if (item) {
gp_Trsf trsf;
auto& m = matrix->ccomponents();
trsf.SetValues(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
);
#ifdef TAXONOMY_USE_NAKED_PTR
delete matrix;
delete matrix;
#endif
auto v = gp_Pnt(trsf.TranslationPart());
auto v = gp_Pnt(trsf.TranslationPart());
auto z_local = gp::DZ().Transformed(trsf);
auto view_dir = z_local.Dot(meta.pln_3d.Axis().Direction());
auto z_local = gp::DZ().Transformed(trsf);
auto view_dir = z_local.Dot(meta.pln_3d.Axis().Direction());
if ((!range || (v.Z() >= range->first && v.Z() < range->second)) && view_dir > 0.99) {
if ((!range || (v.Z() >= range->first && v.Z() < range->second)) && view_dir > 0.99) {
gp_Trsf trsf_view;
trsf_view.SetTransformation(gp::XOY(), meta.pln_3d.Position());
v.Transform(trsf_view);
gp_Trsf trsf_view;
trsf_view.SetTransformation(gp::XOY(), meta.pln_3d.Position());
v.Transform(trsf_view);
auto svg_name = nameElement(ann);
auto svg_name = nameElement(ann);
if (object_type.size()) {
// postfix the object_type for CSS matching
boost::replace_all(svg_name, "class=\"IfcAnnotation\"", "class=\"IfcAnnotation " + object_type + "\"");
}
path_object* po;
if (k.first) {
po = &start_path(meta.pln_3d, k.first, svg_name);
} else {
po = &start_path(meta.pln_3d, k.second, svg_name);
}
boost::optional<double> font_size;
std::vector<std::string> tokens;
boost::split(tokens, name, boost::is_any_of("_"));
if (tokens.size() == 2) {
try {
font_size = boost::lexical_cast<double>(tokens.back());
}
catch (...) {}
}
// @todo column or row?
double z_rotation = gp::DX().Transformed(trsf).AngleWithRef(
meta.pln_3d.Position().XDirection(),
meta.pln_3d.Position().Direction()
);
z_rotation *= 180. / M_PI;
auto y = -v.Y();
util::string_buffer path;
// dominant-baseline="central" is not well supported in IE.
// so we add a 0.35 offset to the dy of the tspans
path.add(" <text text-anchor=\"left\" x=\"");
xcoords.push_back(path.add(v.X()));
path.add("\" y=\"");
ycoords.push_back(path.add(y));
path.add("\" transform=\"rotate(");
path.add(z_rotation);
path.add(" ");
xcoords.push_back(path.add(v.X()));
path.add(" ");
ycoords.push_back(path.add(y));
path.add(")\"");
if (font_size) {
path.add(" font-size=\"");
path.add(*font_size);
path.add("\"");
}
path.add(">");
std::vector<std::string> labels{ desc };
for (auto lit = labels.begin(); lit != labels.end(); ++lit) {
auto l = *lit;
IfcUtil::escape_xml(l);
double dy = labels.begin() == lit
? 0.0 // align bottom
: 1.0; // <- dy is relative to the previous text element, so
// always 1 for successive spans.
path.add("<tspan x=\"");
xcoords.push_back(path.add(v.X()));
path.add("\" dy=\"");
path.add(boost::lexical_cast<std::string>(dy));
path.add("em\">");
path.add(l);
path.add("</tspan>");
}
path.add("</text>");
po->second.push_back(path);
if (object_type.size()) {
// postfix the object_type for CSS matching
boost::replace_all(svg_name, "class=\"IfcAnnotation\"", "class=\"IfcAnnotation " + object_type + "\"");
}
path_object* po;
if (k.first) {
po = &start_path(meta.pln_3d, k.first, svg_name);
} else {
po = &start_path(meta.pln_3d, k.second, svg_name);
}
std::optional<double> font_size;
std::vector<std::string> tokens;
boost::split(tokens, name, boost::is_any_of("_"));
if (tokens.size() == 2) {
try {
font_size = boost::lexical_cast<double>(tokens.back());
}
catch (...) {}
}
// @todo column or row?
double z_rotation = gp::DX().Transformed(trsf).AngleWithRef(
meta.pln_3d.Position().XDirection(),
meta.pln_3d.Position().Direction()
);
z_rotation *= 180. / M_PI;
auto y = -v.Y();
util::string_buffer path;
// dominant-baseline="central" is not well supported in IE.
// so we add a 0.35 offset to the dy of the tspans
path.add(" <text text-anchor=\"left\" x=\"");
xcoords.push_back(path.add(v.X()));
path.add("\" y=\"");
ycoords.push_back(path.add(y));
path.add("\" transform=\"rotate(");
path.add(z_rotation);
path.add(" ");
xcoords.push_back(path.add(v.X()));
path.add(" ");
ycoords.push_back(path.add(y));
path.add(")\"");
if (font_size) {
path.add(" font-size=\"");
path.add(*font_size);
path.add("\"");
}
path.add(">");
std::vector<std::string> labels{ desc };
for (auto lit = labels.begin(); lit != labels.end(); ++lit) {
auto l = *lit;
IfcUtil::escape_xml(l);
double dy = labels.begin() == lit
? 0.0 // align bottom
: 1.0; // <- dy is relative to the previous text element, so
// always 1 for successive spans.
path.add("<tspan x=\"");
xcoords.push_back(path.add(v.X()));
path.add("\" dy=\"");
path.add(boost::lexical_cast<std::string>(dy));
path.add("em\">");
path.add(l);
path.add("</tspan>");
}
path.add("</text>");
po->second.push_back(path);
}
}
}
@@ -1992,7 +1989,7 @@ void SvgSerializer::finalize() {
drawing_metadata[p.first].matrix_3 = m;
}
if (!deferred_section_data_.is_initialized() && (auto_section_ || auto_elevation_)) {
if (!deferred_section_data_ && (auto_section_ || auto_elevation_)) {
deferred_section_data_.emplace();
}
@@ -2078,64 +2075,62 @@ void SvgSerializer::finalize() {
const auto& section = boost::get<vertical_section>(sd);
const auto& ax = section.plane.Position();
draw_hlr(ax, { nullptr, drawing_name });
draw_hlr(ax, { express::Base{}, drawing_name });
}
addTextAnnotations({ nullptr, drawing_name });
addTextAnnotations({express::Base{}, drawing_name});
if (file && storey_height_display_ != SH_NONE && pln && std::abs(pln->Position().Direction().Z()) < 1.e-5) {
auto storeys = file->instances_by_type("IfcBuildingStorey");
if (storeys) {
const double lu = file->getUnit("LENGTHUNIT").second;
for (auto& s : *storeys) {
auto storey = (IfcUtil::IfcBaseEntity*) s;
auto a = storey->get("Elevation");
if (!a.isNull()) {
double elev = a;
elev *= lu;
auto svg_name = nameElement(storey);
const double lu = file->getUnit("LENGTHUNIT").second;
for (auto& s : storeys) {
auto storey = s.as<express::Entity>();
auto a = storey.get("Elevation");
if (!a.isNull()) {
double elev = a;
elev *= lu;
auto svg_name = nameElement(storey);
gp_Pln elev_pln(gp_Ax3(gp_Pnt(0, 0, elev), gp::DZ(), gp::DX()));
//, pln->Position().XDirection()));
// auto ref_y = pln->Position().YDirection().XYZ().Dot(pln->Position().Location().XYZ());
gp_Pln elev_pln(gp_Ax3(gp_Pnt(0, 0, elev), gp::DZ(), gp::DX()));
//, pln->Position().XDirection()));
// auto ref_y = pln->Position().YDirection().XYZ().Dot(pln->Position().Location().XYZ());
double x0, y0, z0, x1, y1, z1;
bnd_.Get(x0, y0, z0, x1, y1, z1);
double x0, y0, z0, x1, y1, z1;
bnd_.Get(x0, y0, z0, x1, y1, z1);
// @todo this is a hack in order to get the auto elevations (which are 0.1 offset from
// the global bounding box) to include the storey height symbols.
x0 -= 0.2;
y0 -= 0.2;
z0 -= 0.2;
// @todo this is a hack in order to get the auto elevations (which are 0.1 offset from
// the global bounding box) to include the storey height symbols.
x0 -= 0.2;
y0 -= 0.2;
z0 -= 0.2;
x1 += 0.2;
y1 += 0.2;
z1 += 0.2;
x1 += 0.2;
y1 += 0.2;
z1 += 0.2;
const double shll = storey_height_line_length_.get_value_or(2.);
const double shll = storey_height_line_length_.value_or(2.);
BRepBuilderAPI_MakeFace mf(elev_pln, x0 - shll, x1 + shll, y0 - shll, y1 + shll);
gp_Trsf trsf;
TopoDS_Compound C;
BRep_Builder B;
B.MakeCompound(C);
B.Add(C, mf.Face());
std::string name;
auto a2 = storey->get("Name");
if (!a2.isNull()) {
name = (std::string) a2;
}
write(geometry_data{
C,{boost::none},trsf,storey,storey,elev,name,nameElement(storey)
});
BRepBuilderAPI_MakeFace mf(elev_pln, x0 - shll, x1 + shll, y0 - shll, y1 + shll);
gp_Trsf trsf;
TopoDS_Compound C;
BRep_Builder B;
B.MakeCompound(C);
B.Add(C, mf.Face());
std::string name;
auto a2 = storey.get("Name");
if (!a2.isNull()) {
name = (std::string) a2;
}
write(geometry_data{
C,{std::nullopt},trsf,storey,storey,elev,name,nameElement(storey)
});
}
}
}
auto m3 = resize();
auto k = std::make_pair(nullptr, drawing_name);
auto k = std::make_pair(express::Base{}, drawing_name);
drawing_metadata[k].matrix_3 = m3;
resetScale();
@@ -2146,7 +2141,7 @@ void SvgSerializer::finalize() {
std::multimap<drawing_key, path_object, storey_sorter>::const_iterator it;
boost::optional<drawing_key> previous;
std::optional<drawing_key> previous;
for (it = paths.begin(); it != paths.end(); ++it) {
if (!previous || it->first != *previous) {
if (previous) {
@@ -2269,7 +2264,7 @@ return oss.str();
}
}
std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* storey, const IfcGeom::Element* elem) {
std::string SvgSerializer::nameElement(express::Base storey, const IfcGeom::Element* elem) {
auto n = elem->name();
IfcUtil::escape_xml(n);
@@ -2281,26 +2276,28 @@ std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* storey, con
});
}
std::string SvgSerializer::idElement(const IfcUtil::IfcBaseEntity* elem) {
const std::string type = elem->declaration().is("IfcBuildingStorey") ? "storey" : "product";
std::string SvgSerializer::idElement(express::Base elem_) {
auto elem = elem_.as<express::Entity>();
const std::string type = elem.declaration().is("IfcBuildingStorey") ? "storey" : "product";
const std::string name =
(settings().get<ifcopenshell::geometry::settings::UseElementGuids>().get()
? static_cast<std::string>(elem->get("GlobalId"))
: ((settings().get<ifcopenshell::geometry::settings::UseElementNames>().get() && !elem->get("Name").isNull()))
? static_cast<std::string>(elem->get("Name"))
? static_cast<std::string>(elem.get("GlobalId"))
: ((settings().get<ifcopenshell::geometry::settings::UseElementNames>().get() && !elem.get("Name").isNull()))
? static_cast<std::string>(elem.get("Name"))
: (settings().get<ifcopenshell::geometry::settings::UseElementStepIds>().get())
? ("id-" + boost::lexical_cast<std::string>(elem->id()))
: IfcParse::IfcGlobalId(elem->get("GlobalId")).formatted());
? ("id-" + boost::lexical_cast<std::string>(elem.id()))
: IfcParse::IfcGlobalId(elem.get("GlobalId")).formatted());
return type + "-" + name;
}
std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* elem) {
if (elem == 0) { return ""; }
std::string SvgSerializer::nameElement(express::Base elem_) {
auto elem = elem_.as<express::Entity>();
if (!elem) { return ""; }
const std::string& entity = elem->declaration().name();
const std::string& entity = elem.declaration().name();
std::string ifc_name;
if (!elem->get("Name").isNull()) {
ifc_name = (std::string) elem->get("Name");
if (!elem.get("Name").isNull()) {
ifc_name = (std::string) elem.get("Name");
IfcUtil::escape_xml(ifc_name);
}
@@ -2308,7 +2305,7 @@ std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* elem) {
{"id", idElement(elem)},
{"class", entity},
{namespace_prefix_ + "name", ifc_name},
{namespace_prefix_ + "guid", elem->get("GlobalId")}
{namespace_prefix_ + "guid", elem.get("GlobalId")}
});
}
@@ -2316,30 +2313,28 @@ void SvgSerializer::setFile(IfcParse::IfcFile* f) {
file = f;
auto storeys = f->instances_by_type("IfcBuildingStorey");
if (!storeys || storeys->size() == 0) {
if (storeys.empty()) {
auto mapping = ifcopenshell::geometry::impl::mapping_implementations().construct(file, geometry_settings_);
std::vector<const IfcParse::declaration*> to_derive_from;
to_derive_from.push_back(f->schema()->declaration_by_name("IfcBuilding"));
to_derive_from.push_back(f->schema()->declaration_by_name("IfcSite"));
for (auto it = to_derive_from.begin(); it != to_derive_from.end(); ++it) {
aggregate_of_instance::ptr insts = f->instances_by_type(*it);
if (insts) {
for (auto jt = insts->begin(); jt != insts->end(); ++jt) {
IfcUtil::IfcBaseEntity* product = (IfcUtil::IfcBaseEntity*) *jt;
if (!product->get("ObjectPlacement").isNull()) {
auto item = mapping->map(product->get("ObjectPlacement"));
auto matrix = ifcopenshell::geometry::taxonomy::cast<ifcopenshell::geometry::taxonomy::matrix4>(item);
gp_Trsf trsf;
if (matrix) {
// @todo shouldn't this take into account configurable section height?
setSectionHeight(matrix->translation_part()(2) + 1.);
auto insts = f->instances_by_type(*it);
for (auto& inst : insts) {
auto product = inst.as<express::Entity>();
if (!product.get("ObjectPlacement").isNull()) {
auto item = mapping->map(product.get("ObjectPlacement"));
auto matrix = ifcopenshell::geometry::taxonomy::cast<ifcopenshell::geometry::taxonomy::matrix4>(item);
gp_Trsf trsf;
if (matrix) {
// @todo shouldn't this take into account configurable section height?
setSectionHeight(matrix->translation_part()(2) + 1.);
#ifdef TAXONOMY_USE_NAKED_PTR
delete matrix;
delete matrix;
#endif
Logger::Warning("No building storeys encountered, used for reference:", product);
return;
}
Logger::Warning("No building storeys encountered, used for reference:", product);
return;
}
}
}
@@ -2351,7 +2346,7 @@ void SvgSerializer::setFile(IfcParse::IfcFile* f) {
}
}
void SvgSerializer::setSectionHeight(double h, const IfcUtil::IfcBaseEntity* storey) {
void SvgSerializer::setSectionHeight(double h, express::Base storey) {
section_data_.emplace();
section_data_->push_back(horizontal_plan{ storey, h, 0., std::numeric_limits<double>::infinity() });
}
@@ -2365,23 +2360,23 @@ void SvgSerializer::setSectionHeightsFromStoreys(double offset) {
section_data_.emplace();
auto storeys = file->instances_by_type("IfcBuildingStorey");
const double lu = file->getUnit("LENGTHUNIT").second;
if (storeys && storeys->size() > 0) {
for (auto& s : *storeys) {
auto attr_value = ((IfcUtil::IfcBaseEntity*)s)->get("Elevation");
if (!attr_value.isNull()) {
double elev;
try {
elev = attr_value;
} catch (std::exception& e) {
Logger::Error(e);
continue;
}
if (!section_data_->empty()) {
boost::get<horizontal_plan>(section_data_->back()).next_elevation = elev * lu;
}
section_data_->push_back(horizontal_plan{ (IfcUtil::IfcBaseEntity*)s, elev * lu, offset, std::numeric_limits<double>::infinity() });
}
}
if (!storeys.empty()) {
for (auto& s : storeys) {
auto attr_value = s.as<express::Entity>().get("Elevation");
if (!attr_value.isNull()) {
double elev;
try {
elev = attr_value;
} catch (std::exception& e) {
Logger::Error(e);
continue;
}
if (!section_data_->empty()) {
boost::get<horizontal_plan>(section_data_->back()).next_elevation = elev * lu;
}
section_data_->push_back(horizontal_plan{s, elev * lu, offset, std::numeric_limits<double>::infinity()});
}
}
} else {
section_data_->push_back(horizontal_plan_at_element{});
}