mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 09:08:40 +00:00
--section-height-from-storeys
This commit is contained in:
@@ -325,6 +325,7 @@ int main(int argc, char** argv) {
|
|||||||
#endif
|
#endif
|
||||||
short precision;
|
short precision;
|
||||||
double section_height;
|
double section_height;
|
||||||
|
|
||||||
po::options_description serializer_options("Serialization options");
|
po::options_description serializer_options("Serialization options");
|
||||||
serializer_options.add_options()
|
serializer_options.add_options()
|
||||||
#ifdef HAVE_ICU
|
#ifdef HAVE_ICU
|
||||||
@@ -337,7 +338,8 @@ int main(int argc, char** argv) {
|
|||||||
"output will be scaled. Only used when converting to SVG.")
|
"output will be scaled. Only used when converting to SVG.")
|
||||||
("section-height", po::value<double>(§ion_height),
|
("section-height", po::value<double>(§ion_height),
|
||||||
"Specifies the cut section height for SVG 2D geometry.")
|
"Specifies the cut section height for SVG 2D geometry.")
|
||||||
("use-element-names",
|
("section-height-from-storeys", "Derives section height from storey elevation. Use --section-height to override default offset of 1")
|
||||||
|
("use-element-names",
|
||||||
"Use entity names instead of unique IDs for naming elements upon serialization. "
|
"Use entity names instead of unique IDs for naming elements upon serialization. "
|
||||||
"Applicable for OBJ, DAE, and SVG output.")
|
"Applicable for OBJ, DAE, and SVG output.")
|
||||||
("use-element-guids",
|
("use-element-guids",
|
||||||
@@ -695,19 +697,6 @@ int main(int argc, char** argv) {
|
|||||||
} else if (output_extension == SVG) {
|
} else if (output_extension == SVG) {
|
||||||
settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
|
settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
|
||||||
serializer = boost::make_shared<SvgSerializer>(IfcUtil::path::to_utf8(output_temp_filename), settings);
|
serializer = boost::make_shared<SvgSerializer>(IfcUtil::path::to_utf8(output_temp_filename), settings);
|
||||||
if (vmap.count("section-height") != 0) {
|
|
||||||
Logger::Notice("Overriding section height");
|
|
||||||
static_cast<SvgSerializer*>(serializer.get())->setSectionHeight(section_height);
|
|
||||||
}
|
|
||||||
if (vmap.count("print-space-names") != 0) {
|
|
||||||
static_cast<SvgSerializer*>(serializer.get())->setPrintSpaceNames(true);
|
|
||||||
}
|
|
||||||
if (vmap.count("print-space-areas") != 0) {
|
|
||||||
static_cast<SvgSerializer*>(serializer.get())->setPrintSpaceAreas(true);
|
|
||||||
}
|
|
||||||
if (bounding_width.is_initialized() && bounding_height.is_initialized()) {
|
|
||||||
static_cast<SvgSerializer*>(serializer.get())->setBoundingRectangle(bounding_width.get(), bounding_height.get());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
cerr_ << "[Error] Unknown output filename extension '" << output_extension << "'\n";
|
cerr_ << "[Error] Unknown output filename extension '" << output_extension << "'\n";
|
||||||
write_log(!quiet);
|
write_log(!quiet);
|
||||||
@@ -823,9 +812,31 @@ int main(int argc, char** argv) {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer->setFile(context_iterator.file());
|
serializer->setFile(context_iterator.file());
|
||||||
|
|
||||||
if (convert_back_units) {
|
if (output_extension == SVG) {
|
||||||
|
if (vmap.count("section-height-from-storeys") != 0) {
|
||||||
|
if (vmap.count("section-height")) {
|
||||||
|
static_cast<SvgSerializer*>(serializer.get())->setSectionHeightsFromStoreys(section_height);
|
||||||
|
} else {
|
||||||
|
static_cast<SvgSerializer*>(serializer.get())->setSectionHeightsFromStoreys();
|
||||||
|
}
|
||||||
|
} else if (vmap.count("section-height") != 0) {
|
||||||
|
Logger::Notice("Overriding section height");
|
||||||
|
static_cast<SvgSerializer*>(serializer.get())->setSectionHeight(section_height);
|
||||||
|
}
|
||||||
|
if (vmap.count("print-space-names") != 0) {
|
||||||
|
static_cast<SvgSerializer*>(serializer.get())->setPrintSpaceNames(true);
|
||||||
|
}
|
||||||
|
if (vmap.count("print-space-areas") != 0) {
|
||||||
|
static_cast<SvgSerializer*>(serializer.get())->setPrintSpaceAreas(true);
|
||||||
|
}
|
||||||
|
if (bounding_width.is_initialized() && bounding_height.is_initialized()) {
|
||||||
|
static_cast<SvgSerializer*>(serializer.get())->setBoundingRectangle(bounding_width.get(), bounding_height.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (convert_back_units) {
|
||||||
serializer->setUnitNameAndMagnitude(context_iterator.unit_name(), static_cast<float>(context_iterator.unit_magnitude()));
|
serializer->setUnitNameAndMagnitude(context_iterator.unit_name(), static_cast<float>(context_iterator.unit_magnitude()));
|
||||||
} else {
|
} else {
|
||||||
serializer->setUnitNameAndMagnitude("METER", 1.0f);
|
serializer->setUnitNameAndMagnitude("METER", 1.0f);
|
||||||
|
|||||||
+216
-182
@@ -290,211 +290,217 @@ SvgSerializer::path_object& SvgSerializer::start_path(IfcUtil::IfcBaseEntity* st
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <Bnd_Box.hxx>
|
||||||
|
#include <BRepBndLib.hxx>
|
||||||
|
|
||||||
void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||||
{
|
{
|
||||||
IfcUtil::IfcBaseEntity* storey = storey_;
|
std::vector<std::pair<double, IfcUtil::IfcBaseEntity*>> section_heights_storage;
|
||||||
boost::optional<double> storey_elevation = boost::none;
|
const std::vector<std::pair<double, IfcUtil::IfcBaseEntity*>>* section_heights_used = §ion_heights_storage;
|
||||||
|
|
||||||
for (const auto& p : o->parents()) {
|
if (section_heights) {
|
||||||
if (p->type() == "IfcBuildingStorey") {
|
section_heights_used = section_heights.get_ptr();
|
||||||
try {
|
} else {
|
||||||
const IfcGeom::ElementSettings& settings = o->geometry().settings();
|
for (const auto& p : o->parents()) {
|
||||||
double e = *p->product()->get("Elevation");
|
if (p->type() == "IfcBuildingStorey") {
|
||||||
storey_elevation = e * settings.unit_magnitude();
|
try {
|
||||||
} catch (...) {
|
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() });
|
||||||
|
} catch (...) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section_heights_storage.empty()) {
|
||||||
|
Logger::Warning("No global section height and unable to determine building storey for:", o->product());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& pair : *section_heights_used) {
|
||||||
|
auto cut_z = pair.first;
|
||||||
|
auto storey = pair.second;
|
||||||
|
|
||||||
|
// SVG has a coordinate system with the origin in the *upper*-left corner
|
||||||
|
// therefore we mirror the shape along the XZ-plane.
|
||||||
|
TopoDS_Shape compound = o->geometry().as_compound();
|
||||||
|
gp_Trsf trsf;
|
||||||
|
trsf.SetMirror(gp_Ax2(gp::Origin(), gp::DY()));
|
||||||
|
BRepBuilderAPI_Transform make_transform(compound, trsf);
|
||||||
|
make_transform.Build();
|
||||||
|
// When determinant < 0, copy is implied and the input is not mutated.
|
||||||
|
compound = make_transform.Shape();
|
||||||
|
|
||||||
|
TopoDS_Iterator it(compound);
|
||||||
|
|
||||||
|
TopoDS_Face largest_closed_wire_face;
|
||||||
|
double largest_closed_wire_area = 0.;
|
||||||
|
path_object* po;
|
||||||
|
|
||||||
|
// Iterate over components of compound to have better chance of matching section edges to closed wires
|
||||||
|
for (; it.More(); it.Next()) {
|
||||||
|
|
||||||
|
const TopoDS_Shape& subshape = it.Value();
|
||||||
|
|
||||||
|
Bnd_Box bb;
|
||||||
|
BRepBndLib::Add(it.Value(), bb);
|
||||||
|
|
||||||
|
// Empty geometry
|
||||||
|
if (bb.IsVoid()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
storey = p->product();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// With a global section height, building storeys are not a requirement.
|
double x1, y1, zmin, x2, y2, zmax;
|
||||||
if (!storey && !section_height) {
|
bb.Get(x1, y1, zmin, x2, y2, zmax);
|
||||||
Logger::Warning("No global section height and unable to determine building storey for:", o->product());
|
|
||||||
return;
|
// 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
|
||||||
|
|
||||||
path_object& p = start_path(storey, nameElement(o));
|
if (std::isnan(cut_z)) {
|
||||||
|
cut_z = zmin + 1.;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No intersection with bounding box, fail early
|
||||||
|
if (zmin > cut_z || zmax < cut_z) continue;
|
||||||
|
|
||||||
// SVG has a coordinate system with the origin in the *upper*-left corner
|
po = &start_path(storey, nameElement(o));
|
||||||
// therefore we mirror the shape along the XZ-plane.
|
|
||||||
TopoDS_Shape compound = o->geometry().as_compound();
|
|
||||||
gp_Trsf trsf;
|
|
||||||
trsf.SetMirror(gp_Ax2(gp::Origin(), gp::DY()));
|
|
||||||
BRepBuilderAPI_Transform make_transform(compound, trsf);
|
|
||||||
make_transform.Build();
|
|
||||||
// When determinant < 0, copy is implied and the input is not mutated.
|
|
||||||
compound = make_transform.Shape();
|
|
||||||
|
|
||||||
TopoDS_Iterator it(compound);
|
// 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());
|
||||||
|
TopoDS_Shape result = BRepAlgoAPI_Section(subshape, pln);
|
||||||
|
|
||||||
TopoDS_Face largest_closed_wire_face;
|
Handle(TopTools_HSequenceOfShape) edges = new TopTools_HSequenceOfShape();
|
||||||
double largest_closed_wire_area = 0.;
|
Handle(TopTools_HSequenceOfShape) wires = new TopTools_HSequenceOfShape();
|
||||||
|
{
|
||||||
|
TopExp_Explorer exp(result, TopAbs_EDGE);
|
||||||
|
for (; exp.More(); exp.Next()) {
|
||||||
|
edges->Append(exp.Current());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ShapeAnalysis_FreeBounds::ConnectEdgesToWires(edges, 1e-5, false, wires);
|
||||||
|
|
||||||
// Iterate over components of compound to have better chance of matching section edges to closed wires
|
gp_Pnt prev;
|
||||||
for (; it.More(); it.Next()) {
|
|
||||||
|
|
||||||
const TopoDS_Shape& subshape = it.Value();
|
|
||||||
|
|
||||||
const double inf = std::numeric_limits<double>::infinity();
|
for (int i = 1; i <= wires->Length(); ++i) {
|
||||||
double zmin = inf;
|
const TopoDS_Wire& wire = TopoDS::Wire(wires->Value(i));
|
||||||
double zmax = -inf;
|
if (wire.Closed() && (print_space_names_ || print_space_areas_) && o->type() == "IfcSpace") {
|
||||||
{TopExp_Explorer exp(subshape, TopAbs_VERTEX);
|
// we explicitly specify the surface here, to later on
|
||||||
for (; exp.More(); exp.Next()) {
|
// simplify the projection from {x,y,z} to {u, v} because
|
||||||
const TopoDS_Vertex& vertex = TopoDS::Vertex(exp.Current());
|
// we know we can simply discard z.
|
||||||
gp_Pnt pnt = BRep_Tool::Pnt(vertex);
|
BRepBuilderAPI_MakeFace mf(pln, wire);
|
||||||
if (pnt.Z() < zmin) { zmin = pnt.Z(); }
|
if (mf.IsDone()) {
|
||||||
if (pnt.Z() > zmax) { zmax = pnt.Z(); }
|
TopoDS_Face f = mf.Face();
|
||||||
}}
|
GProp_GProps prop;
|
||||||
|
BRepGProp::SurfaceProperties(f, prop);
|
||||||
// Empty geometry, no vertices encountered
|
const double area = prop.Mass();
|
||||||
if (zmin == inf) continue;
|
if (area > largest_closed_wire_area) {
|
||||||
|
largest_closed_wire_face = f;
|
||||||
// Determine slicing plane z coordinate, priority:
|
largest_closed_wire_area = area;
|
||||||
// 1) explicitly set global section height
|
}
|
||||||
// 2) containing building storey elevation + 1m
|
}
|
||||||
// 3) zmin (from geometry bounding box) + 1m
|
|
||||||
double cut_z;
|
}
|
||||||
if (section_height) {
|
write(*po, wire);
|
||||||
cut_z = section_height.get();
|
}
|
||||||
} else if (storey_elevation && !(zmin > *storey_elevation || zmax < *storey_elevation)) {
|
|
||||||
cut_z = storey_elevation.get() + 1.;
|
|
||||||
} else {
|
|
||||||
cut_z = zmin + 1.;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No intersection with bounding box, fail early
|
if (!largest_closed_wire_face.IsNull()) {
|
||||||
if (zmin > cut_z || zmax < cut_z) continue;
|
std::vector<gp_Pnt> points;
|
||||||
|
TopExp_Explorer exp(largest_closed_wire_face, TopAbs_VERTEX);
|
||||||
// Create a horizontal cross section 1 meter above the bottom point of the shape
|
for (; exp.More(); exp.Next()) {
|
||||||
const gp_Pln pln(gp_Pnt(0, 0, cut_z), gp::DZ());
|
if (exp.Current().Orientation() == TopAbs_FORWARD) {
|
||||||
TopoDS_Shape result = BRepAlgoAPI_Section(subshape, pln);
|
const TopoDS_Vertex& v = TopoDS::Vertex(exp.Current());
|
||||||
|
points.push_back(BRep_Tool::Pnt(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Handle(TopTools_HSequenceOfShape) edges = new TopTools_HSequenceOfShape();
|
// we brute force the largest distance between pairs of points where
|
||||||
Handle(TopTools_HSequenceOfShape) wires = new TopTools_HSequenceOfShape();
|
// the center is contained in the face.
|
||||||
{TopExp_Explorer exp(result, TopAbs_EDGE);
|
|
||||||
for (; exp.More(); exp.Next()) {
|
|
||||||
edges->Append(exp.Current());
|
|
||||||
}}
|
|
||||||
ShapeAnalysis_FreeBounds::ConnectEdgesToWires(edges, 1e-5, false, wires);
|
|
||||||
|
|
||||||
gp_Pnt prev;
|
std::pair<const gp_Pnt*, const gp_Pnt*> furthest_points = { nullptr, nullptr };
|
||||||
|
double furthest_points_distance = 0.;
|
||||||
|
boost::optional<gp_Pnt> center_point;
|
||||||
|
|
||||||
for (int i = 1; i <= wires->Length(); ++i) {
|
BRepTopAdaptor_FClass2d fcls(largest_closed_wire_face, BRep_Tool::Tolerance(largest_closed_wire_face));
|
||||||
const TopoDS_Wire& wire = TopoDS::Wire(wires->Value(i));
|
|
||||||
if (wire.Closed() && (print_space_names_ || print_space_areas_) && o->type() == "IfcSpace") {
|
for (size_t i = 0; i < points.size(); ++i) {
|
||||||
// we explicitly specify the surface here, to later on
|
for (size_t j = 0; j < i; ++j) {
|
||||||
// simplify the projection from {x,y,z} to {u, v} because
|
const gp_Pnt& pa = points[i];
|
||||||
// we know we can simply discard z.
|
const gp_Pnt& pb = points[j];
|
||||||
BRepBuilderAPI_MakeFace mf(pln, wire);
|
// Since the text is always displayed horizontally,
|
||||||
if (mf.IsDone()) {
|
// the distance is not simply euclidian, but we
|
||||||
TopoDS_Face f = mf.Face();
|
// favour the x-component;
|
||||||
|
const double d = std::sqrt(
|
||||||
|
10 * ((pa.X() - pb.X()) * (pa.X() - pb.X())) +
|
||||||
|
1 * ((pa.Y() - pb.Y()) * (pa.Y() - pb.Y()))
|
||||||
|
);
|
||||||
|
if (d > furthest_points_distance) {
|
||||||
|
gp_Pnt p3d((pa.XYZ() + pb.XYZ()) / 2.);
|
||||||
|
gp_Pnt2d p2d(p3d.X(), p3d.Y());
|
||||||
|
|
||||||
|
if (fcls.Perform(p2d) == TopAbs_IN) {
|
||||||
|
furthest_points = { &pa, &pb };
|
||||||
|
furthest_points_distance = d;
|
||||||
|
center_point = p3d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (center_point) {
|
||||||
|
std::vector<std::string> labels;
|
||||||
|
if (print_space_names_) {
|
||||||
|
labels.push_back(o->name());
|
||||||
|
}
|
||||||
|
if (print_space_names_ && o->type() == "IfcSpace") {
|
||||||
|
auto attr = o->product()->get("LongName");
|
||||||
|
if (!attr->isNull()) {
|
||||||
|
std::string long_name = *attr;
|
||||||
|
if (!long_name.empty()) {
|
||||||
|
labels.insert(labels.begin(), long_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (print_space_areas_) {
|
||||||
GProp_GProps prop;
|
GProp_GProps prop;
|
||||||
BRepGProp::SurfaceProperties(f, prop);
|
BRepGProp::SurfaceProperties(largest_closed_wire_face, prop);
|
||||||
const double area = prop.Mass();
|
const double area = prop.Mass();
|
||||||
if (area > largest_closed_wire_area) {
|
std::stringstream ss;
|
||||||
largest_closed_wire_face = f;
|
ss << std::setprecision(2) << std::fixed << std::showpoint << area;
|
||||||
largest_closed_wire_area = area;
|
labels.push_back(ss.str() + "m²");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
write(p, wire);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!largest_closed_wire_face.IsNull()) {
|
util::string_buffer path;
|
||||||
std::vector<gp_Pnt> points;
|
// dominant-baseline="central" is not well supported in IE.
|
||||||
TopExp_Explorer exp(largest_closed_wire_face, TopAbs_VERTEX);
|
// so we add a 0.35 offset to the dy of the tspans
|
||||||
for (; exp.More(); exp.Next()) {
|
path.add(" <text text-anchor=\"middle\" x=\"");
|
||||||
if (exp.Current().Orientation() == TopAbs_FORWARD) {
|
|
||||||
const TopoDS_Vertex& v = TopoDS::Vertex(exp.Current());
|
|
||||||
points.push_back(BRep_Tool::Pnt(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we brute force the largest distance between pairs of points where
|
|
||||||
// the center is contained in the face.
|
|
||||||
|
|
||||||
std::pair<const gp_Pnt*, const gp_Pnt*> furthest_points = { nullptr, nullptr };
|
|
||||||
double furthest_points_distance = 0.;
|
|
||||||
boost::optional<gp_Pnt> center_point;
|
|
||||||
|
|
||||||
BRepTopAdaptor_FClass2d fcls(largest_closed_wire_face, BRep_Tool::Tolerance(largest_closed_wire_face));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < points.size(); ++i) {
|
|
||||||
for (size_t j = 0; j < i; ++j) {
|
|
||||||
const gp_Pnt& pa = points[i];
|
|
||||||
const gp_Pnt& pb = points[j];
|
|
||||||
// Since the text is always displayed horizontally,
|
|
||||||
// the distance is not simply euclidian, but we
|
|
||||||
// favour the x-component;
|
|
||||||
const double d = std::sqrt(
|
|
||||||
10 * ((pa.X() - pb.X()) * (pa.X() - pb.X())) +
|
|
||||||
1 * ((pa.Y() - pb.Y()) * (pa.Y() - pb.Y()))
|
|
||||||
);
|
|
||||||
if (d > furthest_points_distance) {
|
|
||||||
gp_Pnt p3d((pa.XYZ() + pb.XYZ()) / 2.);
|
|
||||||
gp_Pnt2d p2d(p3d.X(), p3d.Y());
|
|
||||||
|
|
||||||
if (fcls.Perform(p2d) == TopAbs_IN) {
|
|
||||||
furthest_points = { &pa, &pb };
|
|
||||||
furthest_points_distance = d;
|
|
||||||
center_point = p3d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (center_point) {
|
|
||||||
std::vector<std::string> labels;
|
|
||||||
if (print_space_names_) {
|
|
||||||
labels.push_back(o->name());
|
|
||||||
}
|
|
||||||
if (print_space_names_ && o->type() == "IfcSpace") {
|
|
||||||
auto attr = o->product()->get("LongName");
|
|
||||||
if (!attr->isNull()) {
|
|
||||||
std::string long_name = *attr;
|
|
||||||
if (!long_name.empty()) {
|
|
||||||
labels.insert(labels.begin(), long_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (print_space_areas_) {
|
|
||||||
GProp_GProps prop;
|
|
||||||
BRepGProp::SurfaceProperties(largest_closed_wire_face, prop);
|
|
||||||
const double area = prop.Mass();
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::setprecision(2) << std::fixed << std::showpoint << area;
|
|
||||||
labels.push_back(ss.str() + "m²");
|
|
||||||
}
|
|
||||||
|
|
||||||
path_object& po = p;
|
|
||||||
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(" <text text-anchor=\"middle\" x=\"");
|
|
||||||
xcoords.push_back(path.add(center_point->X()));
|
|
||||||
path.add("\" y=\"");
|
|
||||||
ycoords.push_back(path.add(center_point->Y()));
|
|
||||||
path.add("\">");
|
|
||||||
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("<tspan x=\"");
|
|
||||||
xcoords.push_back(path.add(center_point->X()));
|
xcoords.push_back(path.add(center_point->X()));
|
||||||
path.add("\" dy=\"");
|
path.add("\" y=\"");
|
||||||
path.add(boost::lexical_cast<std::string>(dy));
|
ycoords.push_back(path.add(center_point->Y()));
|
||||||
path.add("em\">");
|
path.add("\">");
|
||||||
path.add(l);
|
for (auto lit = labels.begin(); lit != labels.end(); ++lit) {
|
||||||
path.add("</tspan>");
|
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("<tspan x=\"");
|
||||||
|
xcoords.push_back(path.add(center_point->X()));
|
||||||
|
path.add("\" dy=\"");
|
||||||
|
path.add(boost::lexical_cast<std::string>(dy));
|
||||||
|
path.add("em\">");
|
||||||
|
path.add(l);
|
||||||
|
path.add("</tspan>");
|
||||||
|
}
|
||||||
|
path.add("</text>");
|
||||||
|
po->second.push_back(path);
|
||||||
}
|
}
|
||||||
path.add("</text>");
|
|
||||||
po.second.push_back(path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -635,3 +641,31 @@ void SvgSerializer::setFile(IfcParse::IfcFile* f) {
|
|||||||
Logger::Warning("No building storeys encountered, output might be invalid or missing");
|
Logger::Warning("No building storeys encountered, output might be invalid or missing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SvgSerializer::setSectionHeight(double h, IfcUtil::IfcBaseEntity* storey) {
|
||||||
|
section_heights.emplace();
|
||||||
|
section_heights->push_back({ h, storey });
|
||||||
|
}
|
||||||
|
|
||||||
|
void SvgSerializer::setSectionHeightsFromStoreys(double offset) {
|
||||||
|
section_heights.emplace();
|
||||||
|
auto storeys = file->instances_by_type("IfcBuildingStorey");
|
||||||
|
const double lu = file->getUnit("LENGTHUNIT").second;
|
||||||
|
if (storeys && storeys->size() > 0) {
|
||||||
|
for (auto& s : *storeys) {
|
||||||
|
auto attr_value = ((IfcUtil::IfcBaseEntity*)s)->get("Elevation");
|
||||||
|
if (!attr_value->isNull()) {
|
||||||
|
double elev;
|
||||||
|
try {
|
||||||
|
elev = *attr_value;
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
Logger::Error(e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
section_heights->push_back({ elev * lu + offset , (IfcUtil::IfcBaseEntity*)s });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
section_heights->push_back({ std::numeric_limits<double>::quiet_NaN(), nullptr });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
std::ofstream svg_file;
|
std::ofstream svg_file;
|
||||||
double xmin, ymin, xmax, ymax, width, height;
|
double xmin, ymin, xmax, ymax, width, height;
|
||||||
boost::optional<double> section_height;
|
boost::optional<std::vector<std::pair<double, IfcUtil::IfcBaseEntity*>>> section_heights;
|
||||||
bool rescale, print_space_names_, print_space_areas_;
|
bool rescale, print_space_names_, print_space_areas_;
|
||||||
std::multimap<IfcUtil::IfcBaseEntity*, path_object, storey_sorter> paths;
|
std::multimap<IfcUtil::IfcBaseEntity*, path_object, storey_sorter> paths;
|
||||||
std::vector< boost::shared_ptr<util::string_buffer::float_item> > xcoords;
|
std::vector< boost::shared_ptr<util::string_buffer::float_item> > xcoords;
|
||||||
@@ -99,7 +99,8 @@ public:
|
|||||||
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
|
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
|
||||||
void setFile(IfcParse::IfcFile* f);
|
void setFile(IfcParse::IfcFile* f);
|
||||||
void setBoundingRectangle(double width, double height);
|
void setBoundingRectangle(double width, double height);
|
||||||
void setSectionHeight(double h, IfcUtil::IfcBaseEntity* storey = 0) { section_height = h; storey_ = storey; }
|
void setSectionHeight(double h, IfcUtil::IfcBaseEntity* storey = 0);
|
||||||
|
void setSectionHeightsFromStoreys(double offset=1.);
|
||||||
void setPrintSpaceNames(bool b) { print_space_names_ = b; }
|
void setPrintSpaceNames(bool b) { print_space_names_ = b; }
|
||||||
void setPrintSpaceAreas(bool b) { print_space_areas_ = b; }
|
void setPrintSpaceAreas(bool b) { print_space_areas_ = b; }
|
||||||
std::string nameElement(const IfcGeom::Element<real_t>* elem);
|
std::string nameElement(const IfcGeom::Element<real_t>* elem);
|
||||||
|
|||||||
Reference in New Issue
Block a user