diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index fcf7afc7f6..059aebd45c 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -416,6 +416,8 @@ int main(int argc, char** argv) { "Uses the polygonal algorithm for hidden line rendering") ("svg-prefilter", "Prefilter faces and shapes before feeding to HLR algorithm") + ("svg-segment-projection", + "Segment result of projection wrt original products") ("svg-write-poly", "Approximate every curve as polygonal in SVG output") ("svg-project", @@ -1085,6 +1087,7 @@ int main(int argc, char** argv) { static_cast(serializer.get())->setUseNamespace(vmap.count("svg-xmlns") > 0); static_cast(serializer.get())->setUseHlrPoly(vmap.count("svg-poly") > 0); static_cast(serializer.get())->setUsePrefiltering(vmap.count("svg-prefilter") > 0); + static_cast(serializer.get())->setSegmentProjection(vmap.count("svg-segment-projection") > 0); static_cast(serializer.get())->setPolygonal(vmap.count("svg-write-poly") > 0); static_cast(serializer.get())->setAlwaysProject(vmap.count("svg-project") > 0); static_cast(serializer.get())->setWithoutStoreys(vmap.count("svg-without-storeys") > 0); diff --git a/src/serializers/SvgSerializer.cpp b/src/serializers/SvgSerializer.cpp index b11db7287c..92a58485a5 100644 --- a/src/serializers/SvgSerializer.cpp +++ b/src/serializers/SvgSerializer.cpp @@ -1149,14 +1149,14 @@ void SvgSerializer::write(const geometry_data& data) { if (storey) { auto it = storey_hlr.find(storey); if (it == storey_hlr.end()) { - it = storey_hlr.insert({ storey, hlr_t(use_prefiltering_, use_hlr_poly_, projection_plane) }).first; + it = storey_hlr.insert({ storey, hlr_t(use_prefiltering_, use_hlr_poly_, segment_projection_, projection_plane) }).first; } - it->second.add(*compound_to_hlr); + it->second.add(*compound_to_hlr, data.product); } else { Logger::Warning("Unable to invoke HLR due to absence of storey containment", data.product); } } else if (hlr) { - hlr->add(*compound_to_hlr); + hlr->add(*compound_to_hlr, data.product); } } } @@ -1699,55 +1699,66 @@ std::array, 3> SvgSerializer::resize() { } void SvgSerializer::draw_hlr(const gp_Pln& pln, const drawing_key& drawing_name) { - TopoDS_Shape hlr_compound_unmirrored = (drawing_name.first ? this->storey_hlr.find(drawing_name.first)->second : *hlr).build(); + auto hlr_items = (drawing_name.first ? this->storey_hlr.find(drawing_name.first)->second : *hlr).build(); - 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())); - } + for (auto& p : hlr_items) { + const TopoDS_Shape& hlr_compound_unmirrored = p.second; - // 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. - - TopoDS_Shape hlr_compound; - if (drawing_name.first == nullptr) { - gp_Trsf trsf_mirror; - if (!mirror_y_) { - trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY())); + 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())); } - if (mirror_x_) { - gp_Trsf mirror_x; - mirror_x.SetMirror(gp_Ax2(gp::Origin(), gp::DX())); - trsf_mirror.PreMultiply(mirror_x); + + // 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. + + TopoDS_Shape hlr_compound; + if (drawing_name.first == nullptr) { + gp_Trsf trsf_mirror; + if (!mirror_y_) { + trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY())); + } + if (mirror_x_) { + gp_Trsf mirror_x; + mirror_x.SetMirror(gp_Ax2(gp::Origin(), gp::DX())); + trsf_mirror.PreMultiply(mirror_x); + } + BRepBuilderAPI_Transform make_transform_mirror(hlr_compound_unmirrored, trsf_mirror, true); + make_transform_mirror.Build(); + hlr_compound = make_transform_mirror.Shape(); + } else { + // In case of building storey-based floor plan the mirroring has already + // been taken into account before projection. + hlr_compound = hlr_compound_unmirrored; } - BRepBuilderAPI_Transform make_transform_mirror(hlr_compound_unmirrored, trsf_mirror, true); - make_transform_mirror.Build(); - hlr_compound = make_transform_mirror.Shape(); - } else { - // In case of building storey-based floor plan the mirroring has already - // been taken into account before projection. - hlr_compound = hlr_compound_unmirrored; - } - exp.Init(hlr_compound, TopAbs_EDGE); - BRep_Builder B; - path_object* po; - if (drawing_name.first) { - po = &start_path(pln, drawing_name.first, "class=\"projection\""); - } else { - po = &start_path(pln, drawing_name.second, "class=\"projection\""); - } - for (; exp.More(); exp.Next()) { - TopoDS_Wire w; - B.MakeWire(w); - B.Add(w, exp.Current()); - write(*po, w); - } + exp.Init(hlr_compound, TopAbs_EDGE); + BRep_Builder B; + path_object* po; + std::string name; + if (p.first) { + name = nameElement(p.first); + boost::replace_all(name, "class=\"", "class=\"projection "); + } else { + name = "class=\"projection\""; + } + if (drawing_name.first) { + po = &start_path(pln, drawing_name.first, name); + } else { + po = &start_path(pln, drawing_name.second, name); + } + for (; exp.More(); exp.Next()) { + TopoDS_Wire w; + B.MakeWire(w); + B.Add(w, exp.Current()); + write(*po, w); + } + } } } @@ -1812,18 +1823,19 @@ void SvgSerializer::addTextAnnotations(const drawing_key& k) { v.Transform(trsf_view); auto svg_name = nameElement(ann); - 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); - } 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 font_size; std::vector tokens; boost::split(tokens, name, boost::is_any_of("_")); @@ -1987,7 +1999,7 @@ void SvgSerializer::finalize() { // @todo do we have always have pln here? if (use_hlr && pln) { - hlr = new hlr_t(use_prefiltering_, use_hlr_poly_, *pln); + hlr = new hlr_t(use_prefiltering_, use_hlr_poly_, segment_projection_, *pln); } section_data_ = std::vector{ sd }; diff --git a/src/serializers/SvgSerializer.h b/src/serializers/SvgSerializer.h index 6cd806737c..3a44563ddc 100644 --- a/src/serializers/SvgSerializer.h +++ b/src/serializers/SvgSerializer.h @@ -205,31 +205,52 @@ namespace { class hlr_calc { private: const HLRAlgo_Projector& projector_; + const std::list>* product_shapes_ = nullptr; public: - typedef TopoDS_Shape result_type; + typedef std::list> result_type; hlr_calc(const HLRAlgo_Projector& projector) : projector_(projector) {} - TopoDS_Shape operator()(boost::blank&) const { + void set_product_shape(const std::list>* product_shapes) { + product_shapes_ = product_shapes; + } + + result_type operator()(boost::blank&) const { throw std::runtime_error(""); } - TopoDS_Shape operator()(opencascade::handle& algo) { + result_type operator()(opencascade::handle& algo) { algo->Projector(projector_); algo->Update(); algo->Hide(); HLRBRep_HLRToShape hlr_shapes(algo); - return occt_join(hlr_shapes.OutLineVCompound(), hlr_shapes.VCompound()); + if (product_shapes_) { + std::list> r; + for (auto& p : *product_shapes_) { + r.push_back({ p.first, occt_join(hlr_shapes.OutLineVCompound(p.second), hlr_shapes.VCompound(p.second)) }); + } + return r; + } else { + return { {nullptr, occt_join(hlr_shapes.OutLineVCompound(), hlr_shapes.VCompound())}}; + } } - TopoDS_Shape operator()(opencascade::handle& algo) { + result_type operator()(opencascade::handle& algo) { algo->Projector(projector_); algo->Update(); HLRBRep_PolyHLRToShape hlr_shapes; hlr_shapes.Update(algo); - return occt_join(hlr_shapes.OutLineVCompound(), hlr_shapes.VCompound()); + if (product_shapes_) { + std::list> r; + for (auto& p : *product_shapes_) { + r.push_back({ p.first, occt_join(hlr_shapes.OutLineVCompound(p.second), hlr_shapes.VCompound(p.second)) }); + } + return r; + } else { + return { {nullptr, occt_join(hlr_shapes.OutLineVCompound(), hlr_shapes.VCompound()) } }; + } } }; @@ -240,13 +261,13 @@ namespace { gp_XYZ dxyz, xdir, ydir; public: - std::list::const_iterator item; + TopoDS_Shape* item; TopoDS_Face face; bool is_convex; // @note copying the BRepTopAdaptor_FClass2d didn't work so it's a pointer BRepTopAdaptor_FClass2d* fclass; - face_info(std::list::const_iterator it, const TopoDS_Face& fa) + face_info(TopoDS_Shape* it, const TopoDS_Face& fa) : item(it) , face(fa) , fclass(nullptr) @@ -334,17 +355,19 @@ namespace { hlr_brep_or_poly_t engine_; bool use_prefiltering_; bool use_hlr_poly_; + bool segment_projection_; gp_Ax1 view_direction_; HLRAlgo_Projector projector_; std::multimap large_ortho_faces_; - std::list items_; + std::list> items_; public: - prefiltered_hlr(bool use_prefiltering, bool use_hlr_poly, const gp_Pln& view_direction) + prefiltered_hlr(bool use_prefiltering, bool use_hlr_poly, bool segment_projection, const gp_Pln& view_direction) : use_prefiltering_(use_prefiltering) , use_hlr_poly_(use_hlr_poly) + , segment_projection_(segment_projection) // @nb negative z in accordance with occt projector convention (and opengl) , view_direction_(view_direction.Axis()) { @@ -359,7 +382,7 @@ namespace { projector_ = HLRAlgo_Projector(trsf, false, 1.); } - bool is_obscured_(std::list::const_iterator sit) { + bool is_obscured_(TopoDS_Shape* sit) { const TopoDS_Shape& s = *sit; double min_d = std::numeric_limits::infinity(); @@ -393,9 +416,9 @@ namespace { return false; } - void add(const TopoDS_Shape& s) { + void add(const TopoDS_Shape& s, const IfcUtil::IfcBaseEntity* product) { if (!use_prefiltering_) { - items_.insert(items_.end(), s); + items_.insert(items_.end(), {product, s}); return; } @@ -434,7 +457,7 @@ namespace { Logger::Notice("Included " + std::to_string(n_faces_included) + " faces out of " + std::to_string(n_total) + " after prefiltering"); - auto it = items_.insert(items_.end(), C); + auto it = items_.insert(items_.end(), { product, C }); { TopExp_Explorer exp(C, TopAbs_FACE); @@ -458,7 +481,7 @@ namespace { auto d = -(pnt.XYZ() - view_direction_.Location().XYZ()).Dot(view_direction_.Direction().XYZ()); if (d > 1.e-5) { - large_ortho_faces_.insert({ d, face_info(it, face) }); + large_ortho_faces_.insert({ d, face_info(&it->second, face) }); } } } @@ -467,15 +490,15 @@ namespace { } } } else { - items_.insert(items_.end(), s); + items_.insert(items_.end(), { product, s }); } } - TopoDS_Shape build() { + std::list> build() { size_t n_included = 0; for (auto it = items_.begin(); it != items_.end(); ++it) { - if (!use_prefiltering_ || !is_obscured_(it)) { - hlr_writer vis(*it); + if (!use_prefiltering_ || !is_obscured_(&it->second)) { + hlr_writer vis(it->second); boost::apply_visitor(vis, engine_); n_included++; } @@ -483,7 +506,11 @@ namespace { if (use_prefiltering_) { Logger::Notice("Included " + std::to_string(n_included) + " elements out of " + std::to_string(items_.size()) + " after prefiltering"); } + hlr_calc vis(projector_); + if (true) { + vis.set_product_shape(&items_); + } return boost::apply_visitor(vis, engine_); } }; @@ -517,7 +544,7 @@ protected: storey_height_display_types storey_height_display_; bool draw_door_arcs_, is_floor_plan_; bool auto_section_, auto_elevation_; - bool use_namespace_, use_hlr_poly_, use_prefiltering_, always_project_, polygonal_; + bool use_namespace_, use_hlr_poly_, use_prefiltering_, segment_projection_, always_project_, polygonal_; bool emit_building_storeys_; bool no_css_; bool unify_inputs_; @@ -570,6 +597,7 @@ public: , use_namespace_(false) , use_hlr_poly_(false) , use_prefiltering_(false) + , segment_projection_(false) , always_project_(false) , polygonal_(false) , emit_building_storeys_(true) @@ -657,6 +685,14 @@ public: return use_prefiltering_; } + void setSegmentProjection(bool b) { + segment_projection_ = b; + } + + bool getSegmentProjection() const { + return segment_projection_; + } + void setPolygonal(bool b) { polygonal_ = b; }