diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 79bc01e01c..2e792f704c 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -378,6 +378,8 @@ int main(int argc, char** argv) { "Stores name and guid in a separate namespace as opposed to data-name, data-guid") ("svg-poly", "Uses the polygonal algorithm for hidden line rendering") + ("svg-project", + "Always enable hidden line rendering instead of only on elevations") ("door-arcs", "Draw door openings arcs for IfcDoor elements") ("section-height", po::value(§ion_height), "Specifies the cut section height for SVG 2D geometry.") @@ -965,6 +967,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())->setAlwaysProject(vmap.count("svg-project") > 0); 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 1a470f4cc3..cc8f3c621c 100644 --- a/src/serializers/SvgSerializer.cpp +++ b/src/serializers/SvgSerializer.cpp @@ -409,6 +409,29 @@ void SvgSerializer::write(const IfcGeom::BRepElement* brep_obj) { write(data); } +namespace { + class hlr_writer { + const TopoDS_Shape& shape_; + + public: + hlr_writer(const TopoDS_Shape& shape) : shape_(shape) + {} + + void operator()(boost::blank&) const { + throw std::runtime_error(""); + } + + void operator()(Handle(HLRBRep_Algo)& algo) const { + algo->Add(shape_); + } + + void operator()(Handle(HLRBRep_PolyAlgo)& algo) const { + BRepMesh_IncrementalMesh(shape_, 0.10); + algo->Load(shape_); + } + }; +} + void SvgSerializer::write(const geometry_data& data) { std::vector section_heights_storage; const std::vector* section_heights_used = §ion_heights_storage; @@ -543,7 +566,7 @@ void SvgSerializer::write(const geometry_data& data) { IfcUtil::IfcBaseEntity* storey = nullptr; std::string drawing_name; - bool use_hlr = false; + bool use_hlr = always_project_; // @todo use visitor // horizontal_plan, horizontal_plan_at_element, vertical_section @@ -556,8 +579,10 @@ void SvgSerializer::write(const geometry_data& data) { range.first = -std::numeric_limits::infinity(); } projection_direction = gp::DZ(); + projection_plane = gp_Pln(gp_Ax3(gp_Pnt(0, 0, cut_z), gp_Dir(0, 0, 1), gp_Dir(1, 0, 0))); } else if (variant.which() == 1) { projection_direction = gp::DZ(); + projection_plane = gp_Pln(gp_Ax3(gp_Pnt(0, 0, cut_z), gp_Dir(0, 0, 1), gp_Dir(1, 0, 0))); } else if (variant.which() == 2) { const auto& section = boost::get(variant); projection_direction = section.plane.Axis().Direction(); @@ -568,7 +593,7 @@ void SvgSerializer::write(const geometry_data& data) { auto& compound_to_use = is_floor_plan_ ? compound : compound_unmirrored; - if (use_hlr && (hlr_poly || hlr_brep)) { + if (use_hlr) { // && (hlr.which())) { // Check if any of the bounding box points is on the correct side of the plane Bnd_Box bb; @@ -601,12 +626,22 @@ void SvgSerializer::write(const geometry_data& data) { } if (any) { - if (hlr_brep) { - hlr_brep->Add(compound_to_use); + if (is_floor_plan_ && storey) { + if (storey_hlr.find(storey) == storey_hlr.end()) { + if (use_hlr_poly_) { + storey_hlr[storey] = new HLRBRep_PolyAlgo; + } else { + storey_hlr[storey] = new HLRBRep_Algo; + } + } + hlr_writer vis(compound_to_use); + boost::apply_visitor(vis, storey_hlr[storey]); + // this is tricky, how can we change start_path()? Always include section/projection params? + // storey_hlr[] } - if (hlr_poly) { - BRepMesh_IncrementalMesh(compound_to_use, 0.10); - hlr_poly->Load(compound_to_use); + else { + hlr_writer vis(compound_to_use); + boost::apply_visitor(vis, hlr); } } } @@ -952,6 +987,88 @@ std::array, 3> SvgSerializer::resize() { return m; } +namespace { + class hlr_calc { + private: + const HLRAlgo_Projector& projector_; + + public: + hlr_calc(const HLRAlgo_Projector& projector) : projector_(projector) + {} + + TopoDS_Shape operator()(boost::blank&) const { + throw std::runtime_error(""); + } + + TopoDS_Shape operator()(Handle(HLRBRep_Algo)& algo) { + algo->Projector(projector_); + algo->Update(); + algo->Hide(); + HLRBRep_HLRToShape hlr_shapes(algo); + return hlr_shapes.VCompound(); + } + + TopoDS_Shape operator()(Handle(HLRBRep_PolyAlgo)& algo) { + algo->Projector(projector_); + algo->Update(); + HLRBRep_PolyHLRToShape hlr_shapes; + hlr_shapes.Update(algo); + return hlr_shapes.VCompound(); + } + }; +} + +void SvgSerializer::draw_hlr(const gp_Pln& pln, const drawing_key& drawing_name) { + gp_Trsf trsf; + trsf.SetTransformation(pln.Position()); + HLRAlgo_Projector projector(trsf, false, 1.); + + hlr_calc vis(projector); + TopoDS_Shape hlr_compound_unmirrored = boost::apply_visitor(vis, drawing_name.first ? this->storey_hlr[drawing_name.first] : hlr); + + 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. + + TopoDS_Shape hlr_compound; + if (drawing_name.first == nullptr) { + 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(); + 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); + } + + } +} + 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 @@ -963,6 +1080,10 @@ void SvgSerializer::resetScale() { } void SvgSerializer::finalize() { + for (auto& p : storey_hlr) { + draw_hlr(drawing_metadata[{p.first, ""}].pln_3d, { p.first, "" }); + } + auto m = resize(); // Update the paper space scale matrices @@ -1032,7 +1153,7 @@ void SvgSerializer::finalize() { is_floor_plan_ = false; for (auto& sd : *deferred_section_data_) { - bool use_hlr = false; + bool use_hlr = true; std::string drawing_name; if (sd.which() == 2) { const auto& section = boost::get(sd); @@ -1042,9 +1163,9 @@ void SvgSerializer::finalize() { if (use_hlr) { if (use_hlr_poly_) { - hlr_poly = new HLRBRep_PolyAlgo; + hlr = new HLRBRep_PolyAlgo; } else { - hlr_brep = new HLRBRep_Algo; + hlr = new HLRBRep_Algo; } } @@ -1055,58 +1176,9 @@ void SvgSerializer::finalize() { if (use_hlr) { const auto& section = boost::get(sd); - - gp_Trsf trsf; - trsf.SetTransformation(section.plane.Position()); - HLRAlgo_Projector projector(trsf, false, 1.); - - TopoDS_Shape hlr_compound_unmirrored; + const auto& ax = section.plane.Position(); - if (use_hlr_poly_) { - hlr_poly->Projector(projector); - - hlr_poly->Update(); - HLRBRep_PolyHLRToShape hlr_shapes; - hlr_shapes.Update(hlr_poly); - hlr_compound_unmirrored = hlr_shapes.VCompound(); - } else { - hlr_brep->Projector(projector); - - hlr_brep->Update(); - hlr_brep->Hide(); - HLRBRep_HLRToShape hlr_shapes(hlr_brep); - hlr_compound_unmirrored = hlr_shapes.VCompound(); - } - - 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(section.plane, drawing_name, "class=\"projection\""); - for (; exp.More(); exp.Next()) { - TopoDS_Wire w; - B.MakeWire(w); - B.Add(w, exp.Current()); - write(po, w); - } - - } + draw_hlr(ax, { nullptr, drawing_name }); } auto m3 = resize(); @@ -1116,8 +1188,8 @@ void SvgSerializer::finalize() { resetScale(); - if (hlr_brep) hlr_brep.Nullify(); - if (hlr_poly) hlr_poly.Nullify(); + // @todo does this probably call Nullify() + hlr = boost::blank(); } } diff --git a/src/serializers/SvgSerializer.h b/src/serializers/SvgSerializer.h index f1b3740ad6..e806789840 100644 --- a/src/serializers/SvgSerializer.h +++ b/src/serializers/SvgSerializer.h @@ -114,6 +114,12 @@ struct drawing_meta { std::array, 3> matrix_3; }; +typedef boost::variant< + boost::blank, + Handle(HLRBRep_Algo), + Handle(HLRBRep_PolyAlgo) +> hlr_t; + class SvgSerializer : public GeometrySerializer { public: typedef std::pair > path_object; @@ -128,12 +134,13 @@ protected: bool with_section_heights_from_storey_, rescale, print_space_names_, print_space_areas_; bool draw_door_arcs_, is_floor_plan_; bool auto_section_, auto_elevation_; - bool use_namespace_, use_hlr_poly_; + bool use_namespace_, use_hlr_poly_, always_project_; IfcParse::IfcFile* file; IfcUtil::IfcBaseEntity* storey_; std::multimap paths; std::map drawing_metadata; + std::map storey_hlr; float_item_list xcoords, ycoords, radii; size_t xcoords_begin, ycoords_begin, radii_begin; @@ -142,10 +149,14 @@ protected: std::list element_buffer_; - Handle(HLRBRep_Algo) hlr_brep; - Handle(HLRBRep_PolyAlgo) hlr_poly; + hlr_t hlr; + // Handle(HLRBRep_Algo) hlr_brep; + // Handle(HLRBRep_PolyAlgo) hlr_poly; std::string namespace_prefix_; + + void draw_hlr(const gp_Pln& pln, const drawing_key& drawing_name); + public: SvgSerializer(const std::string& out_filename, const SerializerSettings& settings) : GeometrySerializer(settings) @@ -164,6 +175,7 @@ public: , auto_elevation_(false) , use_namespace_(false) , use_hlr_poly_(false) + , always_project_(false) , file(0) , storey_(0) , xcoords_begin(0) @@ -221,6 +233,10 @@ public: use_hlr_poly_ = b; } + void setAlwaysProject(bool b) { + always_project_ = b; + } + void setScale(double s) { scale_ = s; } void setDrawingCenter(double x, double y) { center_x_ = x; center_y_ = y;