/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see . *
* *
********************************************************************************/
/********************************************************************************
* *
* This examples exposes the IfcOpenShell API through a command-based stdin *
* interface *
* *
********************************************************************************/
#include
#include
// NB: Streams are only re-opened as binary when compiled with MSVC currently.
// It is unclear what the correct behaviour would be compiled with e.g MinGW
#if defined(_MSC_VER)
#define SET_BINARY_STREAMS
#endif
#ifdef SET_BINARY_STREAMS
#include
#include
#endif
#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
#include "../ifcgeom/IfcGeomElement.h"
#include "../ifcparse/IfcFile.h"
#include "../ifcparse/IfcLogger.h"
#if USE_VLD
#include
#endif
#include
#include
#include
#include
template
union data_field {
char buffer[sizeof(T)];
T value;
};
template
T sread(std::istream& s) {
data_field data;
s.read(data.buffer, sizeof(T));
return data.value;
}
template <>
std::string sread(std::istream& s) {
int32_t len = sread(s);
char* buf = new char[len + 1];
s.read(buf, len);
buf[len] = 0;
while (len++ % 4) s.get();
std::string str(buf);
delete[] buf;
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
void swrite(std::ostream& s, T t) {
char buf[sizeof(T)];
memcpy(buf, &t, sizeof(T));
s.write(buf, sizeof(T));
}
template <>
void swrite(std::ostream& s, std::string t) {
int32_t len = (int32_t)t.size();
swrite(s, len);
s.write(t.c_str(), len);
while (len++ % 4) s.put(0);
}
class Command {
protected:
virtual void read_content(std::istream& s) = 0;
virtual void write_content(std::ostream& s) = 0;
int32_t iden;
int32_t len;
public:
void read(std::istream& s) {
len = sread(s);
read_content(s);
}
void write(std::ostream& s) {
std::cout.rdbuf(stdout_orig);
swrite(s, iden);
std::ostringstream oss;
write_content(oss);
swrite(s, oss.str());
s.flush();
std::cout.rdbuf(stdout_redir);
}
Command(int32_t iden) : iden(iden) {}
};
const int32_t HELLO = 0xff00;
const int32_t IFC_MODEL = HELLO + 1;
const int32_t GET = IFC_MODEL + 1;
const int32_t ENTITY = GET + 1;
const int32_t MORE = ENTITY + 1;
const int32_t NEXT = MORE + 1;
const int32_t BYE = NEXT + 1;
const int32_t GET_LOG = BYE + 1;
const int32_t LOG = GET_LOG + 1;
const int32_t DEFLECTION = LOG + 1;
const int32_t SETTING = DEFLECTION + 1;
class Hello : public Command {
private:
std::string str;
protected:
void read_content(std::istream& s) {
str = sread(s);
}
void write_content(std::ostream& s) {
swrite(s, str);
}
public:
const std::string& string() { return str; }
Hello() : Command(HELLO), str("IfcOpenShell-" IFCOPENSHELL_VERSION "-0") {}
};
class More : public Command {
private:
bool more;
protected:
void read_content(std::istream& s) {
more = sread(s) == 1;
}
void write_content(std::ostream& s) {
swrite(s, more ? 1 : 0);
}
public:
More(bool more) : Command(MORE), more(more) {}
};
class IfcModel : public Command {
private:
std::string str;
protected:
void read_content(std::istream& s) {
str = sread(s);
}
void write_content(std::ostream& s) {
swrite(s, str);
}
public:
const std::string& string() { return str; }
IfcModel() : Command(IFC_MODEL) {};
};
class Get : public Command {
protected:
void read_content(std::istream& /*s*/) {}
void write_content(std::ostream& /*s*/) {}
public:
Get() : Command(GET) {};
};
class GetLog : public Command {
protected:
void read_content(std::istream& /*s*/) {}
void write_content(std::ostream& /*s*/) {}
public:
GetLog() : Command(GET_LOG) {};
};
class WriteLog : public Command {
private:
std::string str;
protected:
void read_content(std::istream& s) {
str = sread(s);
}
void write_content(std::ostream& s) {
swrite(s, str);
}
public:
WriteLog(const std::string& str) : Command(LOG), str(str) {};
};
class EntityExtension {
protected:
bool trailing_, opened_;
std::stringstream json_;
template
void put_json(const std::string& k, T v) {
if (!opened_) {
json_ << "{";
opened_ = true;
}
if (trailing_) {
json_ << ",";
}
json_ << format_json(k) << ":" << format_json(v);
trailing_ = true;
}
public:
EntityExtension()
: trailing_(false)
, opened_(false)
{}
void write_contents(std::ostream& s) {
if (opened_) {
json_ << "}";
}
// We do a 4-byte manual alignment
std::string payload = json_.str();
s << payload;
if (payload.size() % 4) {
s << std::string(4 - (payload.size() % 4), ' ');
}
}
};
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) {
swrite(s, geom->id());
swrite(s, geom->guid());
swrite(s, geom->name());
swrite(s, geom->type());
swrite(s, geom->parent_id());
const std::vector& m = geom->transformation().matrix().data();
const double matrix_array[16] = {
m[0], m[3], m[6], m[ 9],
m[1], m[4], m[7], m[10],
m[2], m[5], m[8], m[11],
0, 0, 0, 1
};
swrite(s, std::string((char*)matrix_array, 16 * sizeof(double)));
// The first bit of the string is always the instance name of the representation.
const std::string& representation_id = geom->geometry().id();
const int integer_representation_id = atoi(representation_id.c_str());
swrite(s, (int32_t)integer_representation_id);
swrite(s, std::string((char*)geom->geometry().verts().data(), geom->geometry().verts().size() * sizeof(float)));
swrite(s, std::string((char*)geom->geometry().normals().data(), geom->geometry().normals().size() * sizeof(float)));
{
std::vector indices;
const std::vector& faces = geom->geometry().faces();
indices.reserve(faces.size());
for (std::vector::const_iterator it = faces.begin(); it != faces.end(); ++it) {
indices.push_back(*it);
}
swrite(s, std::string((char*) indices.data(), indices.size() * sizeof(int32_t)));
if (append_line_data) {
std::vector lines;
std::set faces_set (indices.begin(), indices.end());
const std::vector& edges = geom->geometry().edges();
for ( std::vector::const_iterator it = edges.begin(); it != edges.end(); ) {
const int32_t i1 = *(it++);
const int32_t i2 = *(it++);
if (faces_set.find(i1) != faces_set.end() || faces_set.find(i2) != faces_set.end()) {
continue;
}
lines.push_back(i1);
lines.push_back(i2);
}
swrite(s, std::string((char*) lines.data(), lines.size() * sizeof(int32_t)));
}
}
{ std::vector diffuse_color_array;
for (std::vector::const_iterator it = geom->geometry().materials().begin(); it != geom->geometry().materials().end(); ++it) {
const IfcGeom::Material& mat = *it;
if (mat.hasDiffuse()) {
const double* color = mat.diffuse();
diffuse_color_array.push_back(static_cast(color[0]));
diffuse_color_array.push_back(static_cast(color[1]));
diffuse_color_array.push_back(static_cast(color[2]));
} else {
diffuse_color_array.push_back(0.f);
diffuse_color_array.push_back(0.f);
diffuse_color_array.push_back(0.f);
}
if (mat.hasTransparency()) {
diffuse_color_array.push_back(static_cast(1. - mat.transparency()));
} else {
diffuse_color_array.push_back(1.f);
}
}
swrite(s, std::string((char*) diffuse_color_array.data(), diffuse_color_array.size() * sizeof(float))); }
{ std::vector material_indices;
for (std::vector::const_iterator it = geom->geometry().material_ids().begin(); it != geom->geometry().material_ids().end(); ++it) {
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, EntityExtension* eext = 0) : Command(ENTITY), geom(geom), append_line_data(false), eext_(eext) {};
};
class Next : public Command {
protected:
void read_content(std::istream& /*s*/) {}
void write_content(std::ostream& /*s*/) {}
public:
Next() : Command(NEXT) {};
};
class Bye : public Command {
protected:
void read_content(std::istream& /*s*/) {}
void write_content(std::ostream& /*s*/) {}
public:
Bye() : Command(BYE) {};
};
class Deflection : public Command {
private:
double deflection_;
protected:
void read_content(std::istream& s) {
deflection_ = sread(s);
}
void write_content(std::ostream& s) {
swrite(s, deflection_);
}
public:
Deflection(double d = 0.) : Command(DEFLECTION), deflection_(d) {};
double deflection() const { return deflection_; }
};
class Setting : public Command {
private:
uint32_t id_;
uint32_t value_;
protected:
void read_content(std::istream& s) {
id_ = sread(s);
value_ = sread(s);
}
void write_content(std::ostream& s) {
swrite(s, id_);
swrite(s, value_);
}
public:
Setting(uint32_t k = 0, uint32_t v = 0) : Command(SETTING), id_(k), value_(v) {};
uint32_t id() const { return id_; }
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 SURFACE_AREA_ALONG_X = "SURFACE_AREA_ALONG_X";
static const std::string SURFACE_AREA_ALONG_Y = "SURFACE_AREA_ALONG_Y";
static const std::string SURFACE_AREA_ALONG_Z = "SURFACE_AREA_ALONG_Z";
static const std::string WALKABLE_SURFACE_AREA = "WALKABLE_SURFACE_AREA";
class QuantityWriter_v0 : public EntityExtension {
private:
const IfcGeom::BRepElement* elem_;
public:
QuantityWriter_v0(const IfcGeom::BRepElement* elem) :
elem_(elem)
{
put_json(TOTAL_SURFACE_AREA, 0.);
put_json(TOTAL_SHAPE_VOLUME, 0.);
if (elem_->type() == "IfcSpace") {
put_json(WALKABLE_SURFACE_AREA, 0.);
}
}
};
class QuantityWriter_v1 : public EntityExtension {
private:
const IfcGeom::BRepElement* elem_;
public:
QuantityWriter_v1(const IfcGeom::BRepElement* elem) :
elem_(elem)
{
double a, b, c;
if (elem_->geometry().calculate_surface_area(a)) {
put_json(TOTAL_SURFACE_AREA, a);
}
if (elem_->geometry().calculate_volume(a)) {
put_json(TOTAL_SHAPE_VOLUME, a);
}
if (elem_->calculate_projected_surface_area(a, b, c)) {
put_json(SURFACE_AREA_ALONG_X, a);
put_json(SURFACE_AREA_ALONG_Y, b);
put_json(SURFACE_AREA_ALONG_Z, c);
}
}
};
int main () {
// Redirect stdout to this stream, so that involuntary
// writes to stdout do not interfere with our protocol.
std::ostringstream oss;
stdout_redir = oss.rdbuf();
stdout_orig = std::cout.rdbuf();
std::cout.rdbuf(stdout_redir);
bool emit_quantities = false;
#ifdef SET_BINARY_STREAMS
_setmode(_fileno(stdout), _O_BINARY);
std::cout.setf(std::ios_base::binary);
_setmode(_fileno(stdin), _O_BINARY);
std::cin.setf(std::ios_base::binary);
#endif
double deflection = 1.e-3;
bool has_more = false;
IfcGeom::Iterator* iterator = 0;
IfcParse::IfcFile* file = 0;
std::vector< std::pair > setting_pairs;
Hello().write(std::cout);
int exit_code = 0;
for (;;) {
const int32_t msg_type = sread(std::cin);
switch (msg_type) {
case IFC_MODEL: {
IfcModel m; m.read(std::cin);
std::string::size_type len = m.string().size();
char* data = new char[len];
memcpy(data, m.string().c_str(), len);
IfcGeom::IteratorSettings settings;
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false);
settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, false);
settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, true);
// settings.set(IfcGeom::IteratorSettings::INCLUDE_CURVES, true);
std::vector< std::pair >::const_iterator it = setting_pairs.begin();
for (; it != setting_pairs.end(); ++it) {
settings.set(it->first, it->second != 0);
if (it->first == IfcGeom::IteratorSettings::SEW_SHELLS && it->second) {
// Quantities (especially volume) can be emitted if there are proper
// topologically valid geometries being created.
emit_quantities = true;
}
}
settings.set_deflection_tolerance(deflection);
file = new IfcParse::IfcFile(data, (int)len);
iterator = new IfcGeom::Iterator(settings, file);
has_more = iterator->initialize();
More(has_more).write(std::cout);
continue;
}
case GET: {
Get g; g.read(std::cin);
if (!has_more) {
exit_code = 1;
break;
}
const IfcGeom::TriangulationElement* geom = static_cast*>(iterator->get());
std::unique_ptr eext;
if (emit_quantities) {
eext.reset(new QuantityWriter_v1(iterator->get_native()));
} else {
eext.reset(new QuantityWriter_v0(iterator->get_native()));
}
Entity(geom, eext.get()).write(std::cout);
continue;
}
case NEXT: {
Next n; n.read(std::cin);
has_more = iterator->next() != 0;
if (!has_more) {
delete file;
delete iterator;
file = 0;
iterator = 0;
}
More(has_more).write(std::cout);
continue;
}
case GET_LOG: {
GetLog gl; gl.read(std::cin);
WriteLog(Logger::GetLog()).write(std::cout);
continue;
}
case BYE: {
Bye().write(std::cout);
exit_code = 0;
break;
}
case DEFLECTION: {
Deflection d; d.read(std::cin);
if (!iterator) {
deflection = d.deflection();
continue;
} else {
exit_code = 1;
break;
}
}
case SETTING: {
Setting s; s.read(std::cin);
if (!iterator) {
setting_pairs.push_back(std::make_pair(s.id(), s.value()));
continue;
} else {
exit_code = 1;
break;
}
}
default:
exit_code = 1;
break;
}
break;
}
std::cout.rdbuf(stdout_orig);
return exit_code;
}