diff --git a/src/ifcgeom/IfcGeomIterator.h b/src/ifcgeom/IfcGeomIterator.h index b9c50730f1..78b0d0f6ad 100644 --- a/src/ifcgeom/IfcGeomIterator.h +++ b/src/ifcgeom/IfcGeomIterator.h @@ -655,6 +655,13 @@ namespace IfcGeom { return ret; } + /// Gets the native (Open Cascade) representation of the current geometrical entity. + BRepElement

* get_native() + { + // TODO: Test settings and throw + return current_shape_model; + } + const Element

* getObject(int id) { gp_Trsf trsf; diff --git a/src/ifcgeomserver/IfcGeomServer.cpp b/src/ifcgeomserver/IfcGeomServer.cpp index aa715d62fc..e1ec526770 100644 --- a/src/ifcgeomserver/IfcGeomServer.cpp +++ b/src/ifcgeomserver/IfcGeomServer.cpp @@ -44,6 +44,10 @@ #include #endif +#include +#include +#include + using namespace boost; template @@ -71,6 +75,17 @@ std::string sread(std::istream& s) { return str; } +template +std::string format_json(const T& t) { + return boost::lexical_cast(t); +} + +template <> +std::string format_json(const std::string& s) { + // NB: No escaping whatsoever. Only use alphanumeric values. + return "\"" + s + "\""; +} + static std::streambuf *stdout_orig, *stdout_redir; template @@ -199,10 +214,16 @@ public: WriteLog(const std::string& str) : Command(LOG), str(str) {}; }; +class EntityExtension { +public: + virtual void write_contents(std::ostream& s) = 0; +}; + class Entity : public Command { private: const IfcGeom::TriangulationElement* geom; bool append_line_data; + EntityExtension* eext_; protected: void read_content(std::istream& /*s*/) {} void write_content(std::ostream& s) { @@ -276,9 +297,12 @@ protected: material_indices.push_back(*it); } swrite(s, std::string((char*) material_indices.data(), material_indices.size() * sizeof(int32_t))); } + if (eext_) { + eext_->write_contents(s); + } } public: - Entity(const IfcGeom::TriangulationElement* geom) : Command(ENTITY), geom(geom), append_line_data(false) {}; + Entity(const IfcGeom::TriangulationElement* geom, EntityExtension* eext = 0) : Command(ENTITY), geom(geom), append_line_data(false), eext_(eext) {}; }; class Next : public Command { @@ -331,6 +355,102 @@ public: uint32_t value() const { return value_; } }; +static const std::string TOTAL_SURFACE_AREA = "TOTAL_SURFACE_AREA"; +static const std::string TOTAL_SHAPE_VOLUME = "TOTAL_SHAPE_VOLUME"; +static const std::string WALKABLE_SURFACE_AREA = "WALKABLE_SURFACE_AREA"; +static const double MAX_WALKABLE_SURFACE_ANGLE_DEGREES = 15.; + +class QuantityWriter : public EntityExtension { +private: + const IfcGeom::BRepElement* elem_; +public: + QuantityWriter(const IfcGeom::BRepElement* elem) : + elem_(elem) + {} + void write_contents(std::ostream& s) { + + double total_surface_area = 0.; + double total_shape_volume = 0.; + double walkable_surface_area = 0.; + + for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = elem_->geometry().begin(); it != elem_->geometry().end(); ++it) { + gp_GTrsf gtrsf = it->Placement(); + const gp_Trsf& o_trsf = elem_->transformation().data(); + gtrsf.PreMultiply(o_trsf); + const TopoDS_Shape& shp = it->Shape(); + const TopoDS_Shape moved_shape = IfcGeom::Kernel::apply_transformation(shp, gtrsf); + + { + GProp_GProps prop_area; + BRepGProp::SurfaceProperties(moved_shape, prop_area); + total_surface_area += prop_area.Mass(); + } + + { + GProp_GProps prop_volume; + BRepGProp::VolumeProperties(moved_shape, prop_volume); + total_shape_volume += prop_volume.Mass(); + } + + if (elem_->type() == "IfcSpace") { + TopExp_Explorer exp(moved_shape, TopAbs_FACE); + for (; exp.More(); exp.Next()) { + const TopoDS_Face& face = TopoDS::Face(exp.Current()); + Handle(Geom_Surface) surf = BRep_Tool::Surface(face); + + // Assume we can only walk on planar surfaces + if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) { + continue; + } + + BRepGProp_Face prop(face); + double u0, u1, v0, v1; + BRepTools::UVBounds(face, u0, u1, v0, v1); + gp_Pnt p; + gp_Vec normal_direction; + prop.Normal((u0 + u1) / 2., (v0 + v1) / 2., p, normal_direction); + + gp_Vec normal(0., 0., 0.); + if (normal_direction.Magnitude() > ALMOST_ZERO) { + normal = gp_Dir(normal_direction.XYZ()); + } + + if (normal.Angle(gp::DZ()) < (MAX_WALKABLE_SURFACE_ANGLE_DEGREES * M_PI / 180.0)) { + GProp_GProps prop_face; + BRepGProp::SurfaceProperties(face, prop_face); + walkable_surface_area += prop_face.Mass(); + } + } + } + } + + // TODO: Manual JSON formatting is always a bad idea + std::ostringstream ss; + ss.write("{", 1); + ss << format_json(TOTAL_SURFACE_AREA); + ss.write(":", 1); + ss << format_json(total_surface_area); + ss.write(",", 1); + ss << format_json(TOTAL_SHAPE_VOLUME); + ss.write(":", 1); + ss << format_json(total_shape_volume); + if (elem_->type() == "IfcSpace") { + ss.write(",", 1); + ss << format_json(WALKABLE_SURFACE_AREA); + ss.write(":", 1); + ss << format_json(walkable_surface_area); + } + ss.write("}", 1); + + // We do a 4-byte manual alignment + std::string payload = ss.str(); + s << payload; + if (payload.size() % 4) { + s << std::string(4 - (payload.size() % 4), ' '); + } + } +}; + int main () { // Redirect stdout to this stream, so that involuntary // writes to stdout do not interfere with our protocol. @@ -390,7 +510,8 @@ int main () { break; } const IfcGeom::TriangulationElement* geom = static_cast*>(iterator->get()); - Entity(geom).write(std::cout); + QuantityWriter eext(iterator->get_native()); + Entity(geom, &eext).write(std::cout); continue; } case NEXT: {