From 3e9210d29f45af5d85bcd88eae8e93559607153a Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sun, 20 Sep 2020 15:08:52 +0200 Subject: [PATCH] support for section and elevation references as annotation lines --- cmake/CMakeLists.txt | 2 +- src/ifcconvert/IfcConvert.cpp | 11 + src/serializers/SvgSerializer.cpp | 368 +++++++++++++++++++++++------- src/serializers/SvgSerializer.h | 86 ++++++- 4 files changed, 374 insertions(+), 93 deletions(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index d151525a6c..d1a65b85cc 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -228,7 +228,7 @@ ENDIF() SET(OPENCASCADE_LIBRARY_NAMES TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO - TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset + TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset TKHLR ) IF("${OCC_LIBRARY_DIR}" STREQUAL "") diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 357c2a652d..ce1cfd7900 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -343,6 +343,7 @@ int main(int argc, char** argv) { short precision; double section_height; std::string svg_scale; + std::string section_ref, elevation_ref; po::options_description serializer_options("Serialization options"); serializer_options.add_options() @@ -357,6 +358,10 @@ int main(int argc, char** argv) { ("scale", po::value(&svg_scale), "Interprets SVG bounds in mm, centers layout and draw elements to scale. " "Only used when converting to SVG. Example 1:100.") + ("section-ref", po::value(§ion_ref), + "Element at which vertical cross sections should be created") + ("elevation-ref", po::value(&elevation_ref), + "Element at which vertical elevations should be created") ("door-arcs", "Draw door openings arcs for IfcDoor elements") ("section-height", po::value(§ion_height), "Specifies the cut section height for SVG 2D geometry.") @@ -908,6 +913,12 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } } + if (vmap.count("section-ref")) { + static_cast(serializer.get())->setSectionRef(section_ref); + } + if (vmap.count("elevation-ref")) { + static_cast(serializer.get())->setElevationRef(elevation_ref); + } } if (convert_back_units) { diff --git a/src/serializers/SvgSerializer.cpp b/src/serializers/SvgSerializer.cpp index 952700eada..bfc5f6c57d 100644 --- a/src/serializers/SvgSerializer.cpp +++ b/src/serializers/SvgSerializer.cpp @@ -291,68 +291,154 @@ void SvgSerializer::write(path_object& p, const TopoDS_Wire& wire) { } SvgSerializer::path_object& SvgSerializer::start_path(IfcUtil::IfcBaseEntity* storey, const std::string& id) { - SvgSerializer::path_object& p = paths.insert(std::make_pair(storey, path_object()))->second; + auto key = std::make_pair(std::make_pair(storey, ""), path_object()); + SvgSerializer::path_object& p = paths.insert(key)->second; p.first = id; return p; } -void SvgSerializer::write(const IfcGeom::BRepElement* o) -{ - std::vector, IfcUtil::IfcBaseEntity*>> section_heights_storage; - const std::vector, IfcUtil::IfcBaseEntity*>>* section_heights_used = §ion_heights_storage; +SvgSerializer::path_object& SvgSerializer::start_path(const std::string& drawing_name, const std::string& id) { + auto key = std::make_pair(std::make_pair(nullptr, drawing_name), path_object()); + SvgSerializer::path_object& p = paths.insert(key)->second; + p.first = id; + return p; +} - if (section_heights) { - section_heights_used = section_heights.get_ptr(); - } else { +namespace { + boost::optional> storey_elevation_from_element(const IfcGeom::BRepElement* o) { for (const auto& p : o->parents()) { if (p->type() == "IfcBuildingStorey") { try { 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() }); + return std::make_pair(p->product(), storey_elevation); } catch (...) { continue; } break; } } + return boost::none; + } - if (section_heights_storage.empty()) { - Logger::Warning("No global section height and unable to determine building storey for:", o->product()); + boost::optional edge_from_compound(TopoDS_Shape& compound) { + TopoDS_Iterator it(compound); + if (it.More()) { + TopoDS_Shape wire = it.Value(); + it.Next(); + if (!it.More() && wire.ShapeType() == TopAbs_WIRE) { + TopoDS_Iterator jt(wire); + if (jt.More()) { + TopoDS_Shape edge = jt.Value(); + jt.Next(); + if (!jt.More() && edge.ShapeType() == TopAbs_EDGE) { + return TopoDS::Edge(edge); + } + } + } + } + return boost::none; + } +} + +void SvgSerializer::write(const IfcGeom::BRepElement* brep_obj) { + + boost::optional object_type; + if (!brep_obj->product()->get("ObjectType")->isNull()) { + object_type = static_cast(*brep_obj->product()->get("ObjectType")); + } + + TopoDS_Shape compound_local = brep_obj->geometry().as_compound(); + const gp_Trsf& trsf = brep_obj->transformation().data(); + + const bool is_section = (section_ref_ && object_type && *section_ref_ == *object_type); + const bool is_elevation = (elevation_ref_ && object_type && *elevation_ref_ == *object_type); + + if (is_section || is_elevation) { + auto e = edge_from_compound(compound_local); + if (e) { + TopoDS_Edge global_edge = TopoDS::Edge(e->Moved(trsf)); + double u0, u1; + auto crv = BRep_Tool::Curve(global_edge, u0, u1); + if (crv->DynamicType() == STANDARD_TYPE(Geom_Line)) { + gp_Pnt P; + gp_Vec V; + crv->D1((u0 + u1) / 2., P, V); + auto N = gp::DZ().Crossed(V); + gp_Pln pln(gp_Ax3(P, N, V)); + if (!deferred_section_data_) { + deferred_section_data_.emplace(); + } + std::string name = brep_obj->name(); + if (name.empty()) { + name = boost::lexical_cast(brep_obj->id()); + } + if (is_section) { + deferred_section_data_->push_back(vertical_section{ pln , "Section " + name, false }); + } + if (is_elevation) { + deferred_section_data_->push_back(vertical_section{ pln , "Elevation " + name, true }); + } + } + } + return; + } + + auto p = storey_elevation_from_element(brep_obj); + IfcUtil::IfcBaseEntity* storey = p ? p->first : nullptr; + 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_) { + element_buffer_.push_back(data); + } + + write(data); +} + +void SvgSerializer::write(const geometry_data& data) { + std::vector section_heights_storage; + const std::vector* section_heights_used = §ion_heights_storage; + + if (section_data_) { + section_heights_used = section_data_.get_ptr(); + } else { + if (data.storey) { + section_heights_storage.push_back(horizontal_plan{ data.storey, data.storey_elevation, +1. }); + } else { + Logger::Warning("No global section height and unable to determine building storey for:", data.product); return; } } - TopoDS_Shape compound_local = o->geometry().as_compound(); - const gp_Trsf& trsf = o->transformation().data(); - BRepBuilderAPI_Transform make_transform_global(compound_local, trsf, true); + BRepBuilderAPI_Transform make_transform_global(data.compound_local, data.trsf, true); make_transform_global.Build(); // (When determinant < 0, copy is implied and the input is not mutated.) - auto compound = make_transform_global.Shape(); + auto compound_unmirrored = make_transform_global.Shape(); // SVG has a coordinate system with the origin in the *upper*-left corner // therefore we mirror the shape along the XZ-plane. gp_Trsf trsf_mirror; trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY())); - BRepBuilderAPI_Transform make_transform_mirror(compound, trsf_mirror, true); + BRepBuilderAPI_Transform make_transform_mirror(compound_unmirrored, trsf_mirror, true); make_transform_mirror.Build(); // (When determinant < 0, copy is implied and the input is not mutated.) - compound = make_transform_mirror.Shape(); + auto compound = make_transform_mirror.Shape(); TopoDS_Wire annotation; - if (draw_door_arcs_ && o->product()->declaration().is("IfcDoor")) { + if (draw_door_arcs_ && data.product->declaration().is("IfcDoor")) { boost::optional operation_type; try { IfcEntityList::ptr rels; - if (o->product()->declaration().schema()->name() == "IFC2X3") { - rels = o->product()->get_inverse("IsDefinedBy"); + if (data.product->declaration().schema()->name() == "IFC2X3") { + rels = data.product->get_inverse("IsDefinedBy"); } else { // Damn you, IFC - rels = o->product()->get_inverse("IsTypedBy"); + rels = data.product->get_inverse("IsTypedBy"); } for (auto& rel : *rels) { if (rel->declaration().name() == "IfcRelDefinesByType") { @@ -372,7 +458,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) const bool is_left = *operation_type == "SINGLE_SWING_LEFT"; Bnd_Box bb; - BRepBndLib::Add(compound_local, bb); + BRepBndLib::Add(data.compound_local, bb); if (bb.IsVoid()) { return; @@ -405,9 +491,9 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) make_transform_mirror.Perform(edge_global, true); auto edge_global_mirrored = make_transform_mirror.Shape(); - center.Transform(trsf); - p1.Transform(trsf); - p2.Transform(trsf); + center.Transform(data.trsf); + p1.Transform(data.trsf); + p2.Transform(data.trsf); center.Transform(trsf_mirror); p1.Transform(trsf_mirror); p2.Transform(trsf_mirror); @@ -431,20 +517,44 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) bool emitted = false; for (auto sit = section_heights_used->begin(); sit != section_heights_used->end(); ++sit) { - const auto& pair = *sit; + const auto& variant = *sit; // Elev + offset - auto cut_z = pair.first.first + pair.first.second; + double cut_z = std::numeric_limits::infinity(); // Elev .. Elev(next) - std::pair range{ pair.first.first, std::numeric_limits::infinity() }; - if (sit == section_heights_used->begin()) { - range.first = -range.second; + std::pair range; + + gp_Vec projection_direction; + + IfcUtil::IfcBaseEntity* storey = nullptr; + std::string drawing_name; + + bool use_hlr = false; + + // @todo use visitor + // horizontal_plan, horizontal_plan_at_element, vertical_section + if (variant.which() == 0) { + const auto& plan = boost::get(variant); + storey = plan.storey; + cut_z = plan.elevation + plan.offset; + range = { plan.elevation, plan.next_elevation }; + if (sit == section_heights_used->begin()) { + range.first = -std::numeric_limits::infinity(); + } + projection_direction = gp::DZ(); + } else if (variant.which() == 1) { + projection_direction = gp::DZ(); + } else if (variant.which() == 2) { + const auto& section = boost::get(variant); + projection_direction = section.plane.Axis().Direction(); + drawing_name = section.name; + use_hlr = section.with_projection; + } + + if (use_hlr && hlr) { + hlr->Add(compound_unmirrored); } - if (sit + 1 != section_heights_used->end()) { - range.second = (sit + 1)->first.first; - } - auto storey = pair.second; TopoDS_Iterator it(compound); @@ -456,7 +566,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) for (; it.More(); it.Next()) { const TopoDS_Shape& subshape = it.Value(); - + Bnd_Box bb; try { BRepBndLib::Add(it.Value(), bb); @@ -469,19 +579,28 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) double x1, y1, zmin, x2, y2, zmax; bb.Get(x1, y1, zmin, x2, y2, zmax); - + // Determine slicing plane z coordinate, priority: // 1) explicitly set global section height // 2) containing building storey elevation + 1m // 3) zmin (from geometry bounding box) + 1m - if (std::isnan(cut_z)) { + if (variant.which() == 1) { cut_z = zmin + 1.; } - - if (o->type() == "IfcAnnotation" && ((zmax - zmin) < 1.e-5) && zmin >= range.first && zmin <= range.second) { + + gp_Vec bbmin(x1, y1, zmin); + gp_Vec bbmax(x2, y2, zmax); + auto bbdif = bbmax - bbmin; + auto proj = projection_direction ^ bbdif ^ projection_direction; + + if (data.product->declaration().is("IfcAnnotation") && (proj.Magnitude() > 1.e-5) && zmin >= range.first && zmin <= range.second) { if (po == nullptr) { - po = &start_path(storey, nameElement(storey, o)); + if (storey) { + po = &start_path(storey, data.svg_name); + } else { + po = &start_path(drawing_name, data.svg_name); + } } TopExp_Explorer exp(subshape, TopAbs_EDGE, TopAbs_FACE); @@ -499,6 +618,9 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) 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 @@ -543,18 +665,32 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) } // No intersection with bounding box, fail early - if (zmin > cut_z || zmax < cut_z) continue; + if (variant.which() < 2) { + if (zmin > cut_z || zmax < cut_z) continue; + } emitted = true; if (po == nullptr) { - po = &start_path(storey, nameElement(storey, o)); + po = &start_path(storey, data.svg_name); } // 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()); + gp_Pln pln; + if (variant.which() < 2) { + pln = gp_Pln(gp_Pnt(0, 0, cut_z), gp::DZ()); + } else { + const auto& section = boost::get(variant); + pln = section.plane; + } TopoDS_Shape result = BRepAlgoAPI_Section(subshape, pln); + if (variant.which() == 2) { + gp_Trsf trsf; + trsf.SetTransformation(gp::XOY(), pln.Position()); + result.Move(trsf); + } + Handle(TopTools_HSequenceOfShape) edges = new TopTools_HSequenceOfShape(); Handle(TopTools_HSequenceOfShape) wires = new TopTools_HSequenceOfShape(); { @@ -569,7 +705,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) for (int i = 1; i <= wires->Length(); ++i) { const TopoDS_Wire& wire = TopoDS::Wire(wires->Value(i)); - if (wire.Closed() && (print_space_names_ || print_space_areas_) && o->type() == "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. @@ -636,10 +772,10 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) if (center_point) { std::vector labels; if (print_space_names_) { - labels.push_back(o->name()); + labels.push_back(data.ifc_name); } - if (print_space_names_ && o->type() == "IfcSpace") { - auto attr = o->product()->get("LongName"); + if (print_space_names_ && data.product->declaration().is("IfcSpace")) { + auto attr = data.product->get("LongName"); if (!attr->isNull()) { std::string long_name = *attr; if (!long_name.empty()) { @@ -689,7 +825,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement* o) } if (!emitted) { - Logger::Warning("Element not written to SVG due to section heights", o->product()); + Logger::Warning("Element not written to SVG due to section heights", data.product); } } @@ -699,8 +835,7 @@ void SvgSerializer::setBoundingRectangle(double width, double height) { this->rescale = true; } -void SvgSerializer::finalize() { - +void SvgSerializer::resize() { if (rescale) { // Scale the resulting image to a bounding rectangle specified by command line arguments const double dx = xmax - xmin; @@ -712,40 +847,107 @@ void SvgSerializer::finalize() { cx = (xmax + xmin) / 2. * sc - width / 2.; cy = (ymax + ymin) / 2. * sc - height / 2.; } else { - if (dx / width > dy / height) { - sc = width / dx; + if (calculated_scale_) { + sc = *calculated_scale_; } else { - sc = height / dy; + if (dx / width > dy / height) { + sc = width / dx; + } else { + sc = height / dy; + } + calculated_scale_ = sc; } cx = xmin * sc; cy = ymin * sc; } - {std::vector< boost::shared_ptr >::const_iterator it; - for (it = xcoords.begin(); it != xcoords.end(); ++it) { + float_item_list::const_iterator it; + for (it = xcoords.begin() + xcoords_begin; it != xcoords.end(); ++it, ++xcoords_begin) { double& v = (*it)->value(); v = v * sc - cx; } - for (it = ycoords.begin(); it != ycoords.end(); ++it) { + for (it = ycoords.begin() + ycoords_begin; it != ycoords.end(); ++it, ++ycoords_begin) { double& v = (*it)->value(); v = v * sc - cy; } - for (it = radii.begin(); it != radii.end(); ++it) { + for (it = radii.begin() + radii_begin; it != radii.end(); ++it, ++radii_begin) { (*it)->value() *= sc; - }} + } } - std::multimap::const_iterator it; + // reset the bounding box, as a subsequent drawing (elevation, section) will be centered, but use the same scale. + xmin = +std::numeric_limits::infinity(); + ymin = +std::numeric_limits::infinity(); + xmax = -std::numeric_limits::infinity(); + ymax = -std::numeric_limits::infinity(); +} - IfcUtil::IfcBaseEntity* previous = 0; - bool first = true; +void SvgSerializer::finalize() { + resize(); + + if (deferred_section_data_ && deferred_section_data_->size() && element_buffer_.size()) { + for (auto& sd : *deferred_section_data_) { + bool use_hlr = false; + std::string drawing_name; + if (sd.which() == 2) { + const auto& section = boost::get(sd); + use_hlr = section.with_projection; + drawing_name = section.name; + } + + if (use_hlr) { + hlr = new HLRBRep_Algo; + } + + *section_data_ = { sd }; + for (auto& e : element_buffer_) { + write(e); + } + + if (use_hlr) { + const auto& section = boost::get(sd); + gp_Ax2 transform = section.plane.Position().Ax2(); + HLRAlgo_Projector projector(transform); + hlr->Projector(projector); + + hlr->Update(); + hlr->Hide(); + + HLRBRep_HLRToShape hlr_shapes(hlr); + auto compound = hlr_shapes.VCompound(); + TopExp_Explorer exp(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(); + + if (use_hlr) { + hlr.Nullify(); + } + } + } + + std::multimap::const_iterator it; + + boost::optional previous; for (it = paths.begin(); it != paths.end(); ++it) { - if (it->first != previous || first) { - if (!first) { + if (!previous || it->first != *previous) { + if (previous) { svg_file << " \n"; } std::ostringstream oss; - svg_file << " first) << ">\n"; + if (it->first.first) { + svg_file << " first.first) << ">\n"; + } else { + svg_file << " first.second << "\" class=\"section\">\n"; + } } svg_file << " second.first << ">\n"; std::vector::const_iterator jt; @@ -754,10 +956,9 @@ void SvgSerializer::finalize() { } svg_file << " \n"; previous = it->first; - first = false; } - if (!first) { + if (previous) { svg_file << " \n"; } svg_file << "" << std::endl; @@ -803,11 +1004,11 @@ void SvgSerializer::writeHeader() { namespace { std::string nameElement_(const std::vector >& attrs) { std::ostringstream oss; - for (auto& a : attrs) { - // @todo while we're at it might as well implement escaping - oss << a.first << "=\"" << a.second << "\" "; - } - return oss.str(); +for (auto& a : attrs) { + // @todo while we're at it might as well implement escaping + oss << a.first << "=\"" << a.second << "\" "; +} +return oss.str(); } } @@ -817,7 +1018,7 @@ std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* storey, con {"class", elem->type()}, {"data-name", elem->name()}, {"data-guid", elem->guid()} - }); + }); } std::string SvgSerializer::idElement(const IfcUtil::IfcBaseEntity* elem) { @@ -843,11 +1044,11 @@ std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* elem) { } return nameElement_({ - {"id", idElement(elem)}, - {"class", entity}, + {"id", idElement(elem)}, + {"class", entity}, {"data-name", ifc_name}, {"data-guid", *elem->get("GlobalId")} - }); + }); } void SvgSerializer::setFile(IfcParse::IfcFile* f) { @@ -855,9 +1056,9 @@ void SvgSerializer::setFile(IfcParse::IfcFile* f) { auto storeys = f->instances_by_type("IfcBuildingStorey"); if (!storeys || storeys->size() == 0) { - + IfcGeom::Kernel kernel(f); - + std::vector 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")); @@ -883,13 +1084,13 @@ void SvgSerializer::setFile(IfcParse::IfcFile* f) { } void SvgSerializer::setSectionHeight(double h, IfcUtil::IfcBaseEntity* storey) { - section_heights.emplace(); - section_heights->push_back({ {h, 0.}, storey }); + section_data_.emplace(); + section_data_->push_back(horizontal_plan{ storey, h, 0., std::numeric_limits::infinity() }); } void SvgSerializer::setSectionHeightsFromStoreys(double offset) { with_section_heights_from_storey_ = true; - section_heights.emplace(); + section_data_.emplace(); auto storeys = file->instances_by_type("IfcBuildingStorey"); const double lu = file->getUnit("LENGTHUNIT").second; if (storeys && storeys->size() > 0) { @@ -903,10 +1104,13 @@ void SvgSerializer::setSectionHeightsFromStoreys(double offset) { Logger::Error(e); continue; } - section_heights->push_back({ {elev * lu, offset} , (IfcUtil::IfcBaseEntity*)s }); + if (!section_data_->empty()) { + boost::get(section_data_->back()).next_elevation = elev * lu; + } + section_data_->push_back(horizontal_plan{ (IfcUtil::IfcBaseEntity*)s, elev * lu, offset, std::numeric_limits::infinity() }); } } } else { - section_heights->push_back({ {std::numeric_limits::quiet_NaN(), 0.}, nullptr }); + section_data_->push_back(horizontal_plan_at_element{}); } } diff --git a/src/serializers/SvgSerializer.h b/src/serializers/SvgSerializer.h index 9ace5e748a..4eab966fcd 100644 --- a/src/serializers/SvgSerializer.h +++ b/src/serializers/SvgSerializer.h @@ -27,12 +27,28 @@ #include "../ifcparse/utils.h" +#include +#include + #include #include #include +typedef std::pair drawing_key; + struct storey_sorter { - bool operator()(IfcUtil::IfcBaseEntity* a, IfcUtil::IfcBaseEntity* b) const { + bool operator()(const drawing_key& ad, const drawing_key& bd) const { + if (ad.first == nullptr && bd.first != nullptr) { + return true; + } else if (bd.first == nullptr && ad.first != nullptr) { + return true; + } else if (ad.first == nullptr && bd.first == nullptr) { + return std::less()(ad.second, bd.second); + } + + auto a = ad.first; + auto b = bd.first; + const bool a_is_storey = a->declaration().is("IfcBuildingStorey"); const bool b_is_storey = b->declaration().is("IfcBuildingStorey"); if (a_is_storey && b_is_storey) { @@ -66,21 +82,52 @@ struct storey_sorter { } }; +struct horizontal_plan { + IfcUtil::IfcBaseEntity* storey; + double elevation, offset, next_elevation; +}; + +struct horizontal_plan_at_element {}; + +struct vertical_section { + gp_Pln plane; + std::string name; + bool with_projection; +}; + +typedef boost::variant section_data; + +struct geometry_data { + TopoDS_Shape compound_local; + gp_Trsf trsf; + IfcUtil::IfcBaseEntity* product; + IfcUtil::IfcBaseEntity* storey; + double storey_elevation; + std::string ifc_name, svg_name; +}; + class SvgSerializer : public GeometrySerializer { public: typedef std::pair > path_object; + typedef std::vector< boost::shared_ptr > float_item_list; protected: std::ofstream svg_file; double xmin, ymin, xmax, ymax, width, height; - boost::optional, IfcUtil::IfcBaseEntity*>>> section_heights; - boost::optional scale_; - 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; - std::vector< boost::shared_ptr > ycoords; - std::vector< boost::shared_ptr > radii; + boost::optional> section_data_; + boost::optional> deferred_section_data_; + boost::optional scale_, calculated_scale_; + bool rescale, print_space_names_, print_space_areas_, draw_door_arcs_, with_section_heights_from_storey_, buffer_elements_; + std::multimap paths; + + float_item_list xcoords, ycoords, radii; + size_t xcoords_begin, ycoords_begin, radii_begin; + + boost::optional section_ref_, elevation_ref_; IfcParse::IfcFile* file; IfcUtil::IfcBaseEntity* storey_; + std::list element_buffer_; + + Handle(HLRBRep_Algo) hlr; public: SvgSerializer(const std::string& out_filename, const SerializerSettings& settings) : GeometrySerializer(settings) @@ -94,8 +141,12 @@ public: , print_space_names_(false) , print_space_areas_(false) , draw_door_arcs_(false) + , buffer_elements_(false) , file(0) , storey_(0) + , xcoords_begin(0) + , ycoords_begin(0) + , radii_begin(0) {} void addXCoordinate(const boost::shared_ptr& fi) { xcoords.push_back(fi); } void addYCoordinate(const boost::shared_ptr& fi) { ycoords.push_back(fi); } @@ -106,8 +157,10 @@ public: void write(const IfcGeom::TriangulationElement* /*o*/) {} void write(const IfcGeom::BRepElement* o); void write(path_object& p, const TopoDS_Wire& wire); + void write(const geometry_data& data); path_object& start_path(IfcUtil::IfcBaseEntity* storey, const std::string& id); - bool isTesselated() const { return false; } + path_object& start_path(const std::string& drawing_name, const std::string& id); + bool isTesselated() const { return false; } void finalize(); void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {} void setFile(IfcParse::IfcFile* f); @@ -117,12 +170,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 setSectionRef(const boost::optional& s) { + section_ref_ = s; + buffer_elements_ = true; + } + void setElevationRef(const boost::optional& s) { + elevation_ref_ = s; + buffer_elements_ = true; + } void setScale(double s) { scale_ = s; } std::string nameElement(const IfcUtil::IfcBaseEntity* storey, const IfcGeom::Element* elem); std::string nameElement(const IfcUtil::IfcBaseEntity* elem); std::string idElement(const IfcUtil::IfcBaseEntity* elem); std::string object_id(const IfcUtil::IfcBaseEntity* storey, const IfcGeom::Element* o) { - return idElement(storey) + "-" + GeometrySerializer::object_id(o); + if (storey) { + return idElement(storey) + "-" + GeometrySerializer::object_id(o); + } else { + return GeometrySerializer::object_id(o); + } } };