diff --git a/src/serializers/SvgSerializer.cpp b/src/serializers/SvgSerializer.cpp index 43788ffe84..571e9fa1e6 100644 --- a/src/serializers/SvgSerializer.cpp +++ b/src/serializers/SvgSerializer.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -297,8 +298,8 @@ SvgSerializer::path_object& SvgSerializer::start_path(IfcUtil::IfcBaseEntity* st void SvgSerializer::write(const IfcGeom::BRepElement* o) { - std::vector> section_heights_storage; - const std::vector>* section_heights_used = §ion_heights_storage; + std::vector, IfcUtil::IfcBaseEntity*>> section_heights_storage; + const std::vector, IfcUtil::IfcBaseEntity*>>* section_heights_used = §ion_heights_storage; if (section_heights) { section_heights_used = section_heights.get_ptr(); @@ -309,7 +310,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) const IfcGeom::ElementSettings& settings = o->geometry().settings(); double e = *p->product()->get("Elevation"); double storey_elevation = e * settings.unit_magnitude(); - section_heights_storage.push_back({ storey_elevation + 1. , p->product() }); + section_heights_storage.push_back({ {storey_elevation, +1.} , p->product() }); } catch (...) { continue; } @@ -427,8 +428,22 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) } } - for (auto& pair : *section_heights_used) { - auto cut_z = pair.first; + bool emitted = false; + + for (auto sit = section_heights_used->begin(); sit != section_heights_used->end(); ++sit) { + const auto& pair = *sit; + + // Elev + offset + auto cut_z = pair.first.first + pair.first.second; + + // Elev .. Elev(next) + std::pair range{ pair.first.first, std::numeric_limits::infinity() }; + if (sit == section_heights_used->begin()) { + range.first = -range.second; + } + if (sit + 1 != section_heights_used->end()) { + range.second = (sit + 1)->first.first; + } auto storey = pair.second; TopoDS_Iterator it(compound); @@ -464,10 +479,77 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) cut_z = zmin + 1.; } + if (o->type() == "IfcAnnotation" && ((zmax - zmin) < 1.e-5) && zmin >= range.first && zmin <= range.second) { + if (po == nullptr) { + po = &start_path(storey, nameElement(storey, o)); + } + + TopExp_Explorer exp(subshape, TopAbs_EDGE, TopAbs_FACE); + for (; exp.More(); exp.Next()) { + const auto& e = TopoDS::Edge(exp.Current()); + TopoDS_Vertex v0, v1; + TopExp::Vertices(e, v0, v1); + gp_Pnt p0 = BRep_Tool::Pnt(v0); + gp_Pnt p1 = BRep_Tool::Pnt(v1); + // @todo should we take the average parameter value instead? + gp_XYZ center = (p0.XYZ() + p1.XYZ()) / 2.; + BRep_Builder B; + TopoDS_Wire W; + B.MakeWire(W); + B.Add(W, e); + write(*po, W); + + 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(" "); + std::vector labels{}; + + GProp_GProps prop; + BRepGProp::LinearProperties(e, prop); + const double area = prop.Mass(); + std::stringstream ss; + ss << std::setprecision(2) << std::fixed << std::showpoint << area; + labels.push_back(ss.str() + "m"); + + for (auto lit = labels.begin(); lit != labels.end(); ++lit) { + const auto& l = *lit; + double dy = labels.begin() == lit + ? 0.35 - (labels.size() - 1.) / 2. + : 1.0; // <- dy is relative to the previous text element, so + // always 1 for successive spans. + path.add("(dy)); + path.add("em\">"); + path.add(l); + path.add(""); + } + path.add(""); + po->second.push_back(path); + } + continue; + } + + if (subshape.ShapeType() > TopAbs_FACE) { + // Except for annotations we only emit solids and surfaces to SVG. + emitted = true; + continue; + } + // No intersection with bounding box, fail early if (zmin > cut_z || zmax < cut_z) continue; - po = &start_path(storey, nameElement(storey, o)); + emitted = true; + + if (po == nullptr) { + po = &start_path(storey, nameElement(storey, o)); + } // Create a horizontal cross section 1 meter above the bottom point of the shape const gp_Pln pln(gp_Pnt(0, 0, cut_z), gp::DZ()); @@ -605,6 +687,10 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) write(*po, annotation); } } + + if (!emitted) { + Logger::Warning("Element not written to SVG due to section heights", o->product()); + } } void SvgSerializer::setBoundingRectangle(double width, double height) { @@ -674,7 +760,23 @@ void SvgSerializer::finalize() { } void SvgSerializer::writeHeader() { - svg_file << "\n"; + svg_file << "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"; } namespace { @@ -759,7 +861,7 @@ void SvgSerializer::setFile(IfcParse::IfcFile* f) { void SvgSerializer::setSectionHeight(double h, IfcUtil::IfcBaseEntity* storey) { section_heights.emplace(); - section_heights->push_back({ h, storey }); + section_heights->push_back({ {h, 0.}, storey }); } void SvgSerializer::setSectionHeightsFromStoreys(double offset) { @@ -778,10 +880,10 @@ void SvgSerializer::setSectionHeightsFromStoreys(double offset) { Logger::Error(e); continue; } - section_heights->push_back({ elev * lu + offset , (IfcUtil::IfcBaseEntity*)s }); + section_heights->push_back({ {elev * lu, offset} , (IfcUtil::IfcBaseEntity*)s }); } } } else { - section_heights->push_back({ std::numeric_limits::quiet_NaN(), nullptr }); + section_heights->push_back({ {std::numeric_limits::quiet_NaN(), 0.}, nullptr }); } } diff --git a/src/serializers/SvgSerializer.h b/src/serializers/SvgSerializer.h index 2e23ac624a..5b623ed843 100644 --- a/src/serializers/SvgSerializer.h +++ b/src/serializers/SvgSerializer.h @@ -72,7 +72,7 @@ public: protected: std::ofstream svg_file; double xmin, ymin, xmax, ymax, width, height; - boost::optional>> section_heights; + boost::optional, IfcUtil::IfcBaseEntity*>>> section_heights; bool rescale, print_space_names_, print_space_areas_, draw_door_arcs_, with_section_heights_from_storey_; std::multimap paths; std::vector< boost::shared_ptr > xcoords;