From de5ce28a78305955c46a76fbbe82a1c0c9c9a125 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Wed, 30 Dec 2020 14:25:08 +0100 Subject: [PATCH] --auto-section and --auto-elevation flags --- src/ifcconvert/IfcConvert.cpp | 10 ++ src/serializers/SvgSerializer.cpp | 176 ++++++++++++++++++++++++------ src/serializers/SvgSerializer.h | 20 +++- 3 files changed, 169 insertions(+), 37 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index c75ce4ffd0..45b45f2489 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -370,6 +370,10 @@ int main(int argc, char** argv) { "Element at which vertical cross sections should be created") ("elevation-ref", po::value(&elevation_ref), "Element at which vertical elevations should be created") + ("auto-section", + "Creates SVG cross section drawings automatically based on model extents") + ("auto-elevation", + "Creates SVG elevation drawings automatically based on model extents") ("door-arcs", "Draw door openings arcs for IfcDoor elements") ("section-height", po::value(§ion_height), "Specifies the cut section height for SVG 2D geometry.") @@ -946,6 +950,12 @@ int main(int argc, char** argv) { if (vmap.count("elevation-ref")) { static_cast(serializer.get())->setElevationRef(elevation_ref); } + if (vmap.count("auto-section")) { + static_cast(serializer.get())->setAutoSection(true); + } + if (vmap.count("auto-elevation")) { + static_cast(serializer.get())->setAutoElevation(true); + } if (relative_center_x && relative_center_y) { static_cast(serializer.get())->setDrawingCenter(*relative_center_x, *relative_center_y); } diff --git a/src/serializers/SvgSerializer.cpp b/src/serializers/SvgSerializer.cpp index 35c7259522..34162ba013 100644 --- a/src/serializers/SvgSerializer.cpp +++ b/src/serializers/SvgSerializer.cpp @@ -397,7 +397,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement* brep_obj) { double elev = p ? p->second : std::numeric_limits::quiet_NaN(); geometry_data data{ compound_local, trsf, brep_obj->product(), storey, elev, brep_obj->name(), nameElement(storey, brep_obj) }; - if (buffer_elements_) { + if (auto_section_ || auto_elevation_ || section_ref_ || elevation_ref_) { element_buffer_.push_back(data); } @@ -533,6 +533,7 @@ void SvgSerializer::write(const geometry_data& data) { std::pair range; gp_Vec projection_direction; + gp_Pln projection_plane; IfcUtil::IfcBaseEntity* storey = nullptr; std::string drawing_name; @@ -555,6 +556,7 @@ void SvgSerializer::write(const geometry_data& data) { } else if (variant.which() == 2) { const auto& section = boost::get(variant); projection_direction = section.plane.Axis().Direction(); + projection_plane = section.plane; drawing_name = section.name; use_hlr = section.with_projection; } @@ -562,7 +564,40 @@ void SvgSerializer::write(const geometry_data& data) { auto& compound_to_use = is_floor_plan_ ? compound : compound_unmirrored; if (use_hlr && hlr) { - hlr->Add(compound_to_use); + + // Check if any of the bounding box points is on the correct side of the plane + Bnd_Box bb; + try { + BRepBndLib::Add(compound_to_use, bb); + } + catch (const Standard_Failure&) {} + + if (bb.IsVoid()) { + continue; + } + + double xs[2], ys[2], zs[2]; + bb.Get(xs[0], ys[0], zs[0], xs[1], ys[1], zs[1]); + + bool any = false; + + for (int i = 0; i < 8; ++i) { + gp_Pnt p(xs[(i & 1) == 1], ys[(i & 2) == 2], zs[(i & 4) == 4]); + auto d = (p.XYZ() - projection_plane.Location().XYZ()).Dot(projection_plane.Axis().Direction().XYZ()); + int state; + if (std::abs(d) < 1.e-5) { + state = 0; + } else { + state = d < 0. ? -1 : 1; + } + if (state == 1) { + any = true; + } + } + + if (any) { + hlr->Add(compound_to_use); + } } TopoDS_Iterator it(compound_to_use); @@ -865,13 +900,16 @@ void SvgSerializer::resize() { sc = (*scale_) * 1000; cx = (xmax + xmin) / 2. * sc - width * center_x_.get_value_or(0.5); cy = (ymax + ymin) / 2. * sc - height * center_y_.get_value_or(0.5); - } else { + } + else { if (calculated_scale_) { sc = *calculated_scale_; - } else { + } + else { if (dx / width > dy / height) { sc = width / dx; - } else { + } + else { sc = height / dy; } calculated_scale_ = sc; @@ -893,8 +931,12 @@ void SvgSerializer::resize() { (*it)->value() *= sc; } } +} +void SvgSerializer::resetScale() { // reset the bounding box, as a subsequent drawing (elevation, section) will be centered, but use the same scale. + // this is a separate call now as we first need to read drawing extents for automatically positioning sections and + // elevations xmin = +std::numeric_limits::infinity(); ymin = +std::numeric_limits::infinity(); xmax = -std::numeric_limits::infinity(); @@ -904,6 +946,63 @@ void SvgSerializer::resize() { void SvgSerializer::finalize() { resize(); + if (!deferred_section_data_.is_initialized() && (auto_section_ || auto_elevation_)) { + deferred_section_data_.emplace(); + } + + // @nb keep in mind Y-axis is negated in these 6 definitions to account + // for coordinate system differences. + if (auto_section_) { + { + gp_Pln pln(gp_Ax3( + gp_Pnt((xmin + xmax) / 2., (ymin + ymax) / 2., 0.), + gp_Dir(-1, 0, 0), + gp_Dir(0, -1, 0))); + deferred_section_data_->push_back(vertical_section{ pln , "Section North South", false }); + } + { + gp_Pln pln(gp_Ax3( + gp_Pnt((xmin + xmax) / 2., (ymin + ymax) / -2., 0.), + gp_Dir(0, -1, 0), + gp_Dir(-1, 0, 0))); + deferred_section_data_->push_back(vertical_section{ pln , "Section East West", true }); + } + } + + if (auto_elevation_) { + { + gp_Pln pln(gp_Ax3( + gp_Pnt((xmin + xmax) / 2., -(ymin - 1.e-1), 0.), + gp_Dir(0, -1, 0), + gp_Dir(-1, 0, 0))); + deferred_section_data_->push_back(vertical_section{ pln , "Elevation South", true }); + } + { + gp_Pln pln(gp_Ax3( + gp_Pnt(xmax + 1.e-1, (ymin + ymax) / -2., 0.), + gp_Dir(-1, 0, 0), + gp_Dir(0, -1, 0))); + deferred_section_data_->push_back(vertical_section{ pln , "Elevation East", true }); + } + { + gp_Pln pln(gp_Ax3( + gp_Pnt((xmin + xmax) / 2., -(ymax + 1.e-1), 0.), + gp_Dir(0, -1, 0), + gp_Dir(1, 0, 0))); + deferred_section_data_->push_back(vertical_section{ pln , "Elevation North", true }); + } + { + gp_Pln pln(gp_Ax3( + gp_Pnt(xmin - 1.e-1, (ymin + ymax) / -2., 0.), + gp_Dir(-1, 0, 0), + gp_Dir(0, -1, 0))); + deferred_section_data_->push_back(vertical_section{ pln , "Elevation West", true }); + } + } + + resetScale(); + + if (deferred_section_data_ && deferred_section_data_->size() && element_buffer_.size()) { // Draw door arcs only on floor plans. @@ -922,7 +1021,7 @@ void SvgSerializer::finalize() { hlr = new HLRBRep_Algo; } - *section_data_ = { sd }; + section_data_ = std::vector{ sd }; for (auto& e : element_buffer_) { write(e); } @@ -939,35 +1038,40 @@ void SvgSerializer::finalize() { HLRBRep_HLRToShape hlr_shapes(hlr); auto hlr_compound_unmirrored = hlr_shapes.VCompound(); - // Compound 3D curves for mirroring to work - ShapeFix_Edge sfe; - TopExp_Explorer exp(hlr_compound_unmirrored, TopAbs_EDGE); - for (; exp.More(); exp.Next()) { - sfe.FixAddCurve3d(TopoDS::Edge(exp.Current())); + if (!hlr_compound_unmirrored.IsNull()) { + + // Compound 3D curves for mirroring to work + ShapeFix_Edge sfe; + TopExp_Explorer exp(hlr_compound_unmirrored, TopAbs_EDGE); + for (; exp.More(); exp.Next()) { + sfe.FixAddCurve3d(TopoDS::Edge(exp.Current())); + } + + // Mirror to match SVG coord system. + // @todo this is very wasteful. We better do the Y-mirror in the SVG writing and + // not on the TopoDS_Shape input. + + gp_Trsf trsf_mirror; + trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY())); + BRepBuilderAPI_Transform make_transform_mirror(hlr_compound_unmirrored, trsf_mirror, true); + make_transform_mirror.Build(); + auto hlr_compound = make_transform_mirror.Shape(); + + exp.Init(hlr_compound, TopAbs_EDGE); + BRep_Builder B; + auto& po = start_path(drawing_name, "class=\"projection\""); + for (; exp.More(); exp.Next()) { + TopoDS_Wire w; + B.MakeWire(w); + B.Add(w, exp.Current()); + write(po, w); + } + } - - // Mirror to match SVG coord system. - // @todo this is very wasteful. We better do the Y-mirror in the SVG writing and - // not on the TopoDS_Shape input. - - gp_Trsf trsf_mirror; - trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY())); - BRepBuilderAPI_Transform make_transform_mirror(hlr_compound_unmirrored, trsf_mirror, true); - make_transform_mirror.Build(); - auto hlr_compound = make_transform_mirror.Shape(); - - exp.Init(hlr_compound, TopAbs_EDGE); - BRep_Builder B; - auto& po = start_path(drawing_name, "class=\"projection\""); - for (; exp.More(); exp.Next()) { - TopoDS_Wire w; - B.MakeWire(w); - B.Add(w, exp.Current()); - write(po, w); - } } resize(); + resetScale(); if (use_hlr) { hlr.Nullify(); @@ -987,7 +1091,9 @@ void SvgSerializer::finalize() { if (it->first.first) { svg_file << " first.first) << ">\n"; } else { - svg_file << " first.second << "\" class=\"section\">\n"; + auto n = it->first.second; + IfcUtil::escape_xml(n); + svg_file << " \n"; } } svg_file << " second.first << ">\n"; @@ -1071,10 +1177,13 @@ return oss.str(); } std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* storey, const IfcGeom::Element* elem) { + auto n = elem->name(); + IfcUtil::escape_xml(n); + return nameElement_({ {"id", with_section_heights_from_storey_ ? object_id(storey, elem) : GeometrySerializer::object_id(elem)}, {"class", elem->type()}, - {"data-name", elem->name()}, + {"data-name", n}, {"data-guid", elem->guid()} }); } @@ -1099,6 +1208,7 @@ std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* elem) { std::string ifc_name; if (!elem->get("Name")->isNull()) { ifc_name = (std::string) *elem->get("Name"); + IfcUtil::escape_xml(ifc_name); } return nameElement_({ diff --git a/src/serializers/SvgSerializer.h b/src/serializers/SvgSerializer.h index b69e1feb71..178e336e42 100644 --- a/src/serializers/SvgSerializer.h +++ b/src/serializers/SvgSerializer.h @@ -119,7 +119,8 @@ protected: boost::optional scale_, calculated_scale_, center_x_, center_y_; bool with_section_heights_from_storey_, rescale, print_space_names_, print_space_areas_; - bool draw_door_arcs_, buffer_elements_, is_floor_plan_; + bool draw_door_arcs_, is_floor_plan_; + bool auto_section_, auto_elevation_; IfcParse::IfcFile* file; IfcUtil::IfcBaseEntity* storey_; @@ -146,8 +147,9 @@ public: , print_space_names_(false) , print_space_areas_(false) , draw_door_arcs_(false) - , buffer_elements_(false) , is_floor_plan_(true) + , auto_section_(false) + , auto_elevation_(false) , file(0) , storey_(0) , xcoords_begin(0) @@ -176,15 +178,25 @@ public: void setPrintSpaceNames(bool b) { print_space_names_ = b; } void setPrintSpaceAreas(bool b) { print_space_areas_ = b; } void setDrawDoorArcs(bool b) { draw_door_arcs_ = b; } + void resize(); + void resetScale(); + void setSectionRef(const boost::optional& s) { section_ref_ = s; - buffer_elements_ = true; } void setElevationRef(const boost::optional& s) { elevation_ref_ = s; - buffer_elements_ = true; } + + void setAutoSection(bool b) { + auto_section_ = b; + } + + void setAutoElevation(bool b) { + auto_elevation_ = b; + } + void setScale(double s) { scale_ = s; } void setDrawingCenter(double x, double y) { center_x_ = x; center_y_ = y;