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: {