Modify gitignore

This commit is contained in:
johltn
2020-10-25 10:44:57 +01:00
parent 25a5b435a6
commit f3282003f4
209 changed files with 163282 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
################################################################################
# #
# 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 <http://www.gnu.org/licenses/>. #
# #
################################################################################
foreach(max_year RANGE 2014 2030)
set(max_sdk "$ENV{ADSK_3DSMAX_SDK_${max_year}}")
if (NOT "${max_sdk}" STREQUAL "")
INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS} ${ICU_INCLUDE_DIR}
${Boost_INCLUDE_DIRS} ${max_sdk}/include
)
# All recent versions of 3ds Max (2014 and newer) are 64-bit only so assume lib/x64 directory
LINK_DIRECTORIES(${LINK_DIRECTORIES} ${IfcOpenShell_BINARY_DIR} ${OCC_LIBRARY_DIR} ${OPENCOLLADA_LIBRARY_DIR}
${ICU_LIBRARY_DIR} ${Boost_LIBRARY_DIRS} ${max_sdk}/lib/x64/Release
)
ADD_LIBRARY(IfcMax_${max_year} SHARED IfcMax.h IfcMax.cpp)
# TODO: find the minimal subset of 3dsmax libraries to reference
TARGET_LINK_LIBRARIES(IfcMax_${max_year} ${IFCOPENSHELL_LIBRARIES} Comctl32.lib zlibdll.lib bmm.lib core.lib CustDlg.lib edmodel.lib expr.lib
flt.lib geom.lib gfx.lib gup.lib imageViewers.lib ManipSys.lib maxnet.lib Maxscrpt.lib
maxutil.lib MenuMan.lib menus.lib mesh.lib MNMath.lib Paramblk2.lib particle.lib Poly.lib RenderUtil.lib
tessint.lib viewfile.lib ${OPENCASCADE_LIBRARIES}
)
SET_TARGET_PROPERTIES(IfcMax_${max_year} PROPERTIES SUFFIX ".dli")
INSTALL(TARGETS IfcMax_${max_year} RUNTIME DESTINATION ${BINDIR})
endif()
endforeach()
+327
View File
@@ -0,0 +1,327 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include <map>
#include <set>
#include <stdmat.h>
#include <istdplug.h>
#include "IfcMax.h"
#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
#include "../ifcgeom_schema_agnostic/IfcGeomMaterial.h"
#include "../ifcgeom/IfcGeomElement.h"
static const int NUM_MATERIAL_SLOTS = 24;
BOOL WINAPI DllMain(HINSTANCE /*hinstDLL*/, ULONG /*fdwReason*/, LPVOID /*lpvReserved*/) {
static int controlsInit = false;
if (!controlsInit) {
controlsInit = true;
InitCommonControls();
}
return TRUE;
}
static class IFCImpClassDesc :public ClassDesc {
public:
int IsPublic() { return 1; }
void * Create(BOOL /*loading = FALSE*/) { return new IFCImp; }
// TODO Delete() function?
const TCHAR * ClassName() { return _T("IFCImp"); }
SClass_ID SuperClassID() { return SCENE_IMPORT_CLASS_ID; }
Class_ID ClassID() { return Class_ID(0x3f230dbf, 0x5b3015c2); }
const TCHAR* Category() { return _T("Chrutilities"); }
} IFCImpDesc;
#define DLLEXPORT __declspec(dllexport)
extern "C" {
DLLEXPORT const TCHAR* LibDescription() {
return _T("IfcOpenShell IFC Importer");
}
DLLEXPORT int LibNumberClasses() { return 1; }
DLLEXPORT ClassDesc* LibClassDesc(int i) {
return i == 0 ? &IFCImpDesc : 0;
}
DLLEXPORT ULONG LibVersion() {
return VERSION_3DSMAX;
}
} // extern "C"
int IFCImp::ExtCount() { return 1; }
const TCHAR * IFCImp::Ext(int n) {
return n == 0 ? _T("IFC") : _T("");
}
const TCHAR * IFCImp::LongDesc() {
return _T("IfcOpenShell IFC Importer for 3ds Max");
}
const TCHAR * IFCImp::ShortDesc() {
return _T("Industry Foundation Classes");
}
const TCHAR * IFCImp::AuthorName() {
return _T("Thomas Krijnen");
}
const TCHAR * IFCImp::CopyrightMessage() {
return _T("Copyright (c) 2011-2016 IfcOpenShell");
}
const TCHAR * IFCImp::OtherMessage1() {
return _T("");
}
const TCHAR * IFCImp::OtherMessage2() {
return _T("");
}
unsigned int IFCImp::Version() {
return 12;
}
// TODO Use this in IFCImp::ShowAbout() if/when wanted
//static BOOL CALLBACK AboutBoxDlgProc(HWND /*hWnd*/, UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lParam*/) {
// return TRUE;
//}
void IFCImp::ShowAbout(HWND /*hWnd*/) {}
DWORD WINAPI fn(LPVOID /*arg*/) { return 0; }
#if MAX_RELEASE > 14000
# define S(x) (TSTR::FromCStr(x.c_str()))
#elif defined(_UNICODE)
# define S(x) (WStr(x.c_str()))
#else
# define S(x) (CStr(x.c_str()))
#endif
static Mtl* FindMaterialByName(MtlBaseLib* library, const std::string& material_name) {
TSTR mat_name = S(material_name);
const int mat_index = library->FindMtlByName(mat_name);
Mtl* m = 0;
if (mat_index != -1) {
m = static_cast<Mtl*>((*library)[mat_index]);
}
return m;
}
static Mtl* FindOrCreateMaterial(MtlBaseLib* library, Interface* max_interface, int& slot, const IfcGeom::Material& material) {
Mtl* m = FindMaterialByName(library, material.name());
if (m == 0) {
StdMat2* stdm = NewDefaultStdMat();
const TimeValue t = -1;
if (material.hasDiffuse()) {
const double* diffuse = material.diffuse();
stdm->SetDiffuse(Color(diffuse[0], diffuse[1], diffuse[2]),t);
}
if (material.hasSpecular()) {
const double* specular = material.specular();
stdm->SetSpecular(Color(specular[0], specular[1], specular[2]),t);
}
if (material.hasSpecularity()) {
stdm->SetShininess((float)material.specularity(), t);
}
if (material.hasTransparency()) {
stdm->SetOpacity(1.0f - (float)material.transparency(), t);
}
m = stdm;
m->SetName(S(material.name()));
library->Add(m);
if (slot < NUM_MATERIAL_SLOTS) {
max_interface->PutMtlToMtlEditor(m,slot++);
}
}
return m;
}
static Mtl* ComposeMultiMaterial(std::map<std::vector<std::string>, Mtl*>& multi_mats, MtlBaseLib* library,
Interface* max_interface, int& slot, const std::vector<IfcGeom::Material>& materials,
const std::string& object_type, const std::vector<int>& material_ids)
{
std::vector<std::string> material_names;
bool needs_default = std::find(material_ids.begin(), material_ids.end(), -1) != material_ids.end();
if (needs_default) {
material_names.push_back(object_type);
}
for (auto it = materials.begin(); it != materials.end(); ++it) {
material_names.push_back(it->name());
}
Mtl* default_material = 0;
if (needs_default) {
default_material = FindMaterialByName(library, object_type);
if (default_material == 0) {
default_material = NewDefaultStdMat();
default_material->SetName(S(object_type));
library->Add(default_material);
if (slot < NUM_MATERIAL_SLOTS) {
max_interface->PutMtlToMtlEditor(default_material, slot++);
}
}
}
if (material_names.size() == 1) {
if (needs_default) {
return default_material;
} else {
return FindOrCreateMaterial(library, max_interface, slot, *materials.begin());
}
}
std::map<std::vector<std::string>, Mtl*>::const_iterator i = multi_mats.find(material_names);
if (i != multi_mats.end()) {
return i->second;
}
MultiMtl* multi_mat = NewDefaultMultiMtl();
multi_mat->SetNumSubMtls((int)material_names.size());
int mtl_id = 0;
if (needs_default) {
multi_mat->SetSubMtlAndName(mtl_id ++, default_material, default_material->GetName());
}
for (auto it = materials.begin(); it != materials.end(); ++it) {
Mtl* mtl = FindOrCreateMaterial(library, max_interface, slot, *it);
multi_mat->SetSubMtl(mtl_id ++, mtl);
}
library->Add(multi_mat);
if (slot < NUM_MATERIAL_SLOTS) {
max_interface->PutMtlToMtlEditor(multi_mat,slot++);
}
multi_mats.insert(std::pair<std::vector<std::string>, Mtl*>(material_names, multi_mat));
return multi_mat;
}
int IFCImp::DoImport(const TCHAR *name, ImpInterface *impitfc, Interface *itfc, BOOL /*suppressPrompts*/) {
IfcGeom::IteratorSettings settings;
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false);
settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, true);
settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, true);
#ifdef _UNICODE
int fn_buffer_size = WideCharToMultiByte(CP_UTF8, 0, name, -1, 0, 0, 0, 0);
char* fn_mb = new char[fn_buffer_size];
WideCharToMultiByte(CP_UTF8, 0, name, -1, fn_mb, fn_buffer_size, 0, 0);
#else
const char* fn_mb = name;
#endif
IfcParse::IfcFile file(fn_mb);
IfcGeom::Iterator<float> iterator(settings, &file);
delete fn_mb;
if (!iterator.initialize()) return false;
itfc->ProgressStart(_T("Importing file..."), TRUE, fn, NULL);
MtlBaseLib* mats = itfc->GetSceneMtls();
int slot = mats->Count();
std::map<std::vector<std::string>, Mtl*> material_cache;
do{
const IfcGeom::TriangulationElement<float>* o = static_cast<const IfcGeom::TriangulationElement<float>*>(iterator.get());
TSTR o_type = S(o->type());
TSTR o_guid = S(o->guid());
Mtl *m = ComposeMultiMaterial(material_cache, mats, itfc, slot, o->geometry().materials(), o->type(), o->geometry().material_ids());
TriObject* tri = CreateNewTriObject();
const int numVerts = (int)o->geometry().verts().size()/3;
tri->mesh.setNumVerts(numVerts);
for( int i = 0; i < numVerts; i ++ ) {
tri->mesh.setVert(i,o->geometry().verts()[3*i+0],o->geometry().verts()[3*i+1],o->geometry().verts()[3*i+2]);
}
const int numFaces = (int)o->geometry().faces().size()/3;
tri->mesh.setNumFaces(numFaces);
bool needs_default = std::find(o->geometry().material_ids().begin(), o->geometry().material_ids().end(), -1) != o->geometry().material_ids().end();
typedef std::pair<int, int> edge_t;
std::set<edge_t> face_boundaries;
for(std::vector<int>::const_iterator it = o->geometry().edges().begin(); it != o->geometry().edges().end();) {
const int v1 = *it++;
const int v2 = *it++;
const edge_t e((std::min)(v1, v2), (std::max)(v1, v2));
face_boundaries.insert(e);
}
for( int i = 0; i < numFaces; i ++ ) {
const int v1 = o->geometry().faces()[3*i+0];
const int v2 = o->geometry().faces()[3*i+1];
const int v3 = o->geometry().faces()[3*i+2];
const edge_t e1((std::min)(v1, v2), (std::max)(v1, v2));
const edge_t e2((std::min)(v2, v3), (std::max)(v2, v3));
const edge_t e3((std::min)(v3, v1), (std::max)(v3, v1));
const bool b1 = face_boundaries.find(e1) != face_boundaries.end();
const bool b2 = face_boundaries.find(e2) != face_boundaries.end();
const bool b3 = face_boundaries.find(e3) != face_boundaries.end();
tri->mesh.faces[i].setVerts(v1, v2, v3);
tri->mesh.faces[i].setEdgeVisFlags(b1, b2, b3);
MtlID mtlid = (MtlID)o->geometry().material_ids()[i];
if (needs_default) {
mtlid ++;
}
tri->mesh.faces[i].setMatID(mtlid);
}
tri->mesh.buildNormals();
// Either use this or undefine the FACESETS_AS_COMPOUND option in IfcGeom.h to have
// properly oriented normals. Using only the line below will result in a consistent
// orientation of normals across shells, but not always oriented towards the
// outside.
// tri->mesh.UnifyNormals(false);
tri->mesh.BuildStripsAndEdges();
tri->mesh.InvalidateTopologyCache();
tri->mesh.InvalidateGeomCache();
ImpNode* node = impitfc->CreateNode();
node->Reference(tri);
node->SetName(o_guid);
node->GetINode()->Hide(o->type() == "IfcOpeningElement" || o->type() == "IfcSpace");
if (m) {
node->GetINode()->SetMtl(m);
}
const std::vector<float>& matrix_data = o->transformation().matrix().data();
node->SetTransform(0,Matrix3 ( Point3(matrix_data[0],matrix_data[1],matrix_data[2]),Point3(matrix_data[3],matrix_data[4],matrix_data[5]),
Point3(matrix_data[6],matrix_data[7],matrix_data[8]),Point3(matrix_data[9],matrix_data[10],matrix_data[11]) ));
impitfc->AddNodeToScene(node);
itfc->ProgressUpdate(iterator.progress(), true, _T(""));
} while (iterator.next());
itfc->ProgressEnd();
return true;
}
+43
View File
@@ -0,0 +1,43 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef IFCMAX_H
#define IFCMAX_H
#include "Max.h"
extern ClassDesc* GetIFCImpDesc();
class IFCImp : public SceneImport
{
public:
int ExtCount(); // = 1
const TCHAR * Ext(int n); // = "IFC"
const TCHAR * LongDesc(); // = "IfcOpenShell IFC Importer for 3ds Max"
const TCHAR * ShortDesc(); // = "Industry Foundation Classes"
const TCHAR * AuthorName(); // = "Thomas Krijnen"
const TCHAR * CopyrightMessage(); // = "Copyright (c) 2011-2016 IfcOpenShell"
const TCHAR * OtherMessage1(); // = ""
const TCHAR * OtherMessage2(); // = ""
unsigned int Version(); // = 12
void ShowAbout(HWND hWnd);
int DoImport(const TCHAR *name,ImpInterface *ei,Interface *i, BOOL suppressPrompts);
};
#endif
@@ -0,0 +1,555 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifdef WITH_OPENCOLLADA
#include "ColladaSerializer.h"
#include <boost/foreach.hpp>
#include <COLLADASWPrimitves.h>
#include <COLLADASWSource.h>
#include <COLLADASWScene.h>
#include <COLLADASWNode.h>
#include <COLLADASWInstanceGeometry.h>
#include <COLLADASWBaseInputElement.h>
#include <COLLADASWAsset.h>
#include <string>
#include <cmath>
#include "../ifcparse/utils.h"
static std::string& collada_id(std::string& s)
{
IfcUtil::sanitate_material_name(s);
IfcUtil::escape_xml(s);
return s;
}
void ColladaSerializer::ColladaExporter::ColladaGeometries::addFloatSource(const std::string& mesh_id,
const std::string& suffix, const std::vector<real_t>& floats, const char* coords /* = "XYZ" */)
{
COLLADASW::FloatSource source(mSW);
source.setId(mesh_id + suffix);
source.setArrayId(mesh_id + suffix + COLLADASW::LibraryGeometries::ARRAY_ID_SUFFIX);
const size_t num_elems = strlen(coords);
source.setAccessorStride(static_cast<unsigned long>(num_elems));
source.setAccessorCount(static_cast<unsigned long>(floats.size() / num_elems));
for (size_t i = 0; i < num_elems; ++i) {
source.getParameterNameList().push_back(std::string(1, coords[i]));
}
source.prepareToAppendValues();
for (std::vector<real_t>::const_iterator it = floats.begin(); it != floats.end(); ++it) {
source.appendValues(*it);
}
source.finish();
}
void ColladaSerializer::ColladaExporter::ColladaGeometries::write(
const std::string &mesh_id, const std::string &/**<@todo 'default_material_name' unused, remove? */,
const std::vector<real_t>& positions, const std::vector<real_t>& normals,
const std::vector<int>& faces, const std::vector<int>& edges,
const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& /**<@todo 'materials' unused, remove? */,
const std::vector<real_t>& uvs, const std::vector<std::string>& material_references)
{
openMesh(mesh_id);
// The normals vector can be empty for example when the WELD_VERTICES setting is used.
// IfcOpenShell does not provide them with multiple face normals collapsed into a single vertex.
const bool has_normals = !normals.empty();
const bool has_uvs = !uvs.empty();
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::POSITIONS_SOURCE_ID_SUFFIX, positions);
if (has_normals) {
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, normals);
if (has_uvs) {
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::TEXCOORDS_SOURCE_ID_SUFFIX, uvs, "UV");
}
}
COLLADASW::VerticesElement vertices(mSW);
vertices.setId(mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX );
vertices.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::POSITION, "#" + mesh_id + COLLADASW::LibraryGeometries::POSITIONS_SOURCE_ID_SUFFIX));
vertices.add();
std::vector<int>::const_iterator index_range_start = faces.begin();
std::vector<int>::const_iterator material_it = material_ids.begin();
int previous_material_id = -1;
for (std::vector<int>::const_iterator it = faces.begin(); !faces.empty(); it += 3) {
int current_material_id = 0;
if (material_it != material_ids.end()) {
// In order for the last range of equal material ids to be output as well, this loop iterates
// one element past the end of the vector. This needs to be observed when incrementing.
current_material_id = *(material_it++);
}
const size_t num_triangles = std::distance(index_range_start, it) / 3;
if ((previous_material_id != current_material_id && num_triangles > 0) || (it == faces.end())) {
COLLADASW::Triangles triangles(mSW);
std::string material_name = material_references[previous_material_id];
triangles.setMaterial(material_name);
triangles.setCount((unsigned long)num_triangles);
int offset = 0;
triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::VERTEX,"#" + mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX, offset++));
if (has_normals) {
triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::NORMAL,"#" + mesh_id + COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, offset++));
}
if (has_uvs) {
triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::TEXCOORD,"#" + mesh_id + COLLADASW::LibraryGeometries::TEXCOORDS_SOURCE_ID_SUFFIX, offset++));
}
triangles.prepareToAppendValues();
for (std::vector<int>::const_iterator jt = index_range_start; jt != it; ++jt) {
const int idx = *jt;
if (has_normals && has_uvs) {
triangles.appendValues(idx, idx, idx);
} else if(has_normals) {
triangles.appendValues(idx, idx);
} else {
triangles.appendValues(idx);
}
}
triangles.finish();
index_range_start = it;
}
previous_material_id = current_material_id;
if (it == faces.end()) {
break;
}
}
std::set<int> faces_set (faces.begin(), faces.end());
typedef std::vector< std::pair<int, std::vector<unsigned long> > > linelist_t;
linelist_t linelist;
int num_lines = 0;
for ( std::vector<int>::const_iterator it = edges.begin(); it != edges.end(); ++num_lines) {
const int i1 = *(it++);
const int i2 = *(it++);
if (faces_set.find(i1) != faces_set.end() || faces_set.find(i2) != faces_set.end()) {
continue;
}
const int current_material_id = *(material_it++);
if ((previous_material_id != current_material_id) || (num_lines == 0)) {
linelist.resize(linelist.size() + 1);
}
linelist.rbegin()->second.push_back(i1);
linelist.rbegin()->second.push_back(i2);
}
for (linelist_t::const_iterator it = linelist.begin(); it != linelist.end(); ++it) {
COLLADASW::Lines lines(mSW);
lines.setMaterial(material_references[it->first]);
lines.setCount((unsigned long)it->second.size());
int offset = 0;
lines.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::VERTEX, "#" + mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX, offset));
lines.prepareToAppendValues();
lines.appendValues(it->second);
lines.finish();
}
closeMesh();
closeGeometry();
}
void ColladaSerializer::ColladaExporter::ColladaGeometries::close() {
closeLibrary();
}
void ColladaSerializer::ColladaExporter::ColladaScene::add(
const std::string& node_id, const std::string& node_name, const std::string& geom_name,
const std::vector<std::string>& material_ids, const IfcGeom::Transformation<real_t>& transformation)
{
if (!scene_opened) {
openVisualScene(scene_id);
scene_opened = true;
}
COLLADASW::Node node(mSW);
node.setNodeId(node_id);
node.setNodeName(node_name);
node.setType(COLLADASW::Node::NODE);
// The matrix attribute of an entity is basically a 4x3 representation of its ObjectPlacement.
// Note that this placement is absolute, ie it is multiplied with all parent placements.
IfcGeom::Transformation<real_t>* relative_trsf = 0;
const IfcGeom::Transformation<real_t>* transformation_towrite = &transformation;
// If this is not the first parent, get the relative placement
if (parentNodes.size() > 0)
{
relative_trsf = new IfcGeom::Transformation<real_t>(matrixStack.top().multiplied(transformation));
transformation_towrite = relative_trsf;
}
const std::vector<real_t>& posmatrix = transformation_towrite->matrix().data();
double matrix_array[4][4] = {
{ (double)posmatrix[0], (double)posmatrix[3], (double)posmatrix[6], (double)posmatrix[9] },
{ (double)posmatrix[1], (double)posmatrix[4], (double)posmatrix[7], (double)posmatrix[10] },
{ (double)posmatrix[2], (double)posmatrix[5], (double)posmatrix[8], (double)posmatrix[11] },
{ 0, 0, 0, 1 }
};
delete relative_trsf;
node.start();
node.addMatrix(matrix_array);
COLLADASW::InstanceGeometry instanceGeometry(mSW);
instanceGeometry.setUrl("#" + geom_name);
BOOST_FOREACH(const std::string &material_name, material_ids) {
// Unescape to avoid double escaping beucase OpenCollada's material URI parameter escapes XML internally
std::string unescaped = material_name;
IfcUtil::unescape_xml(unescaped);
COLLADASW::InstanceMaterial material(material_name, "#" + unescaped);
instanceGeometry.getBindMaterial().getInstanceMaterialList().push_back(material);
}
instanceGeometry.add();
node.end();
}
void ColladaSerializer::ColladaExporter::ColladaScene::addParent(const IfcGeom::Element<real_t>& parent){
//we open the visual scene tag if it's not.
if (!scene_opened) {
openVisualScene(scene_id);
scene_opened = true;
}
const IfcGeom::Transformation<real_t>& parent_trsf = parent.transformation();
IfcGeom::Transformation<real_t>* relative_trsf = 0;
const IfcGeom::Transformation<real_t>* transformation_towrite = &parent_trsf;
// If this is not the first parent, get the relative placement
if (parentNodes.size() > 0)
{
relative_trsf = new IfcGeom::Transformation<real_t>(matrixStack.top().multiplied(parent_trsf));
transformation_towrite = relative_trsf;
}
const std::vector<real_t>& parentMatrix = transformation_towrite->matrix().data();
double matrix_array[4][4] = {
{ (double)parentMatrix[0], (double)parentMatrix[3], (double)parentMatrix[6], (double)parentMatrix[9] },
{ (double)parentMatrix[1], (double)parentMatrix[4], (double)parentMatrix[7], (double)parentMatrix[10] },
{ (double)parentMatrix[2], (double)parentMatrix[5], (double)parentMatrix[8], (double)parentMatrix[11] },
{ 0, 0, 0, 1 }
};
std::string name = serializer->object_id(&parent);
collada_id(name);
COLLADASW::Node *current_node;
current_node = new COLLADASW::Node(mSW);
current_node->setNodeId(name);
/// @todo redundant information using ID as both ID and Name, maybe omit Name or allow specifying what would be used as the name
current_node->setNodeName(name);
current_node->setType(COLLADASW::Node::NODE);
current_node->start();
current_node->addMatrix(matrix_array);
// Add the node to the parent stack
matrixStack.push(parent_trsf.inverted());
parentNodes.push(current_node);
serializer->parentStackId.push(parent.id());
}
void ColladaSerializer::ColladaExporter::ColladaScene::closeParent()
{
// Get the top element
COLLADASW::Node *current_node = parentNodes.top();
// Close the node
current_node->end();
// Remove it from the stack
parentNodes.pop();
matrixStack.pop();
serializer->parentStackId.pop();
// Free the memory
delete current_node;
current_node = NULL;
}
void ColladaSerializer::ColladaExporter::ColladaScene::write() {
if (scene_opened) {
closeVisualScene();
closeLibrary();
COLLADASW::Scene scene (mSW, COLLADASW::URI ("#" + scene_id));
scene.add();
}
}
void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::write(
const IfcGeom::Material &material, const std::string &material_uri)
{
openEffect(material_uri + "-fx");
COLLADASW::EffectProfile effect(mSW);
effect.setShaderType(COLLADASW::EffectProfile::LAMBERT);
if (material.hasDiffuse()) {
const double* diffuse = material.diffuse();
effect.setDiffuse(COLLADASW::ColorOrTexture(COLLADASW::Color(diffuse[0],diffuse[1],diffuse[2])));
}
if (material.hasSpecular()) {
const double* specular = material.specular();
effect.setSpecular(COLLADASW::ColorOrTexture(COLLADASW::Color(specular[0],specular[1],specular[2])));
}
if (material.hasSpecularity()) {
effect.setShininess(material.specularity());
}
if (material.hasTransparency()) {
const double transparency = material.transparency();
if (transparency > 0) {
// The default opacity mode for Collada is A_ONE, which apparently indicates a
// transparency value of 1 to be fully opaque. Hence transparency is inverted.
effect.setTransparency(1.0 - transparency);
}
}
addEffectProfile(effect);
closeEffect();
}
void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::close() {
closeLibrary();
}
void ColladaSerializer::ColladaExporter::ColladaMaterials::add(const IfcGeom::Material& material) {
if (!contains(material)) {
std::string material_name = (serializer->settings().get(SerializerSettings::USE_MATERIAL_NAMES)
? material.original_name() : material.name());
if (material_name.empty()) {
material_name = "missing-material-" + material.name();
}
collada_id(material_name);
effects.write(material, material_name);
materials.push_back(material);
material_uris.push_back(material_name);
}
}
std::string ColladaSerializer::ColladaExporter::ColladaMaterials::getMaterialUri(const IfcGeom::Material& material) {
std::vector<IfcGeom::Material>::iterator it = std::find(materials.begin(), materials.end(), material);
ptrdiff_t index = std::distance(materials.begin(), it);
return material_uris.at(index);
}
bool ColladaSerializer::ColladaExporter::ColladaMaterials::contains(const IfcGeom::Material& material) {
return std::find(materials.begin(), materials.end(), material) != materials.end();
}
void ColladaSerializer::ColladaExporter::ColladaMaterials::write() {
effects.close();
BOOST_FOREACH(const IfcGeom::Material& material, materials) {
std::string material_name = getMaterialUri(material);
openMaterial(material_name);
// Unescape to avoid double escaping beucase OpenCollada's addInstanceEffect escapes XML internally
IfcUtil::unescape_xml(material_name);
addInstanceEffect("#" + material_name + "-fx");
closeMaterial();
}
closeLibrary();
}
void ColladaSerializer::ColladaExporter::startDocument(const std::string& unit_name, float unit_magnitude) {
stream.startDocument();
COLLADASW::Asset asset(&stream);
asset.getContributor().mAuthoringTool = std::string("IfcOpenShell ") + IFCOPENSHELL_VERSION;
asset.setUnit(unit_name, unit_magnitude);
asset.setUpAxisType(COLLADASW::Asset::Z_UP);
asset.add();
}
void ColladaSerializer::ColladaExporter::write(const IfcGeom::TriangulationElement<real_t>* o)
{
const IfcGeom::Representation::Triangulation<real_t>& mesh = o->geometry();
std::string name = serializer->object_id(o);
collada_id(name);
std::string representation_id = "representation-" + o->geometry().id();
collada_id(representation_id);
std::vector<std::string> material_references;
BOOST_FOREACH(const IfcGeom::Material& material, mesh.materials()) {
materials.add(material);
std::string material_name = materials.getMaterialUri(material);
material_references.push_back(material_name);
}
DeferredObject deferred(name, representation_id, o->type(), o->transformation(), mesh.verts(), mesh.normals(),
mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials(), material_references, mesh.uvs());
if (serializer->settings().get(SerializerSettings::USE_ELEMENT_HIERARCHY)) {
deferred.parents() = o->parents();
}
deferreds.push_back(deferred);
}
std::string ColladaSerializer::differentiateSlabTypes(const IfcUtil::IfcBaseEntity* slab)
{
auto value = slab->get("PredefinedType");
if (value->isNull()) {
return "_Unknown";
}
const std::string str_value = *value;
std::string result;
if (str_value == "FLOOR") {
result = "_Floor";
} else if (str_value == "ROOF") {
result = "_Roof";
} else if (str_value == "LANDING") {
result = "_Landing";
} else if (str_value == "BASESLAB") {
result = "_BaseSlab";
} else if (str_value == "NOTDEFINED") {
result = "_NotDefined";
} else {
auto otype = slab->get("ObjectType");
if (otype->isNull()) {
result = "_Unknown";
} else {
result = (std::string) *otype;
}
}
return result;
}
std::string ColladaSerializer::object_id(const IfcGeom::Element<real_t>* o) /*override*/
{
if (settings_.get(SerializerSettings::USE_ELEMENT_TYPES)) {
const std::string slabSuffix = (o->product() && o->product()->declaration().name() == "IfcSlab")
? differentiateSlabTypes(o->product())
: "";
return o->type() + slabSuffix;
}
return GeometrySerializer::object_id(o);
}
void ColladaSerializer::ColladaExporter::endDocument() {
// In fact due the XML based nature of Collada and its dependency on library nodes,
// only at this point all objects are written to the stream.
materials.write();
bool use_hierarchy = serializer->settings().get(SerializerSettings::USE_ELEMENT_HIERARCHY);
std::set<std::string> geometries_written;
//if the setting USE_ELEMENT_HIERARCHY is in use, we sort the deferreds objects by their parents.
if (use_hierarchy) {
std::sort(deferreds.begin(), deferreds.end());
}
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
if (geometries_written.find(it->representation_id) != geometries_written.end()) {
continue;
}
geometries_written.insert(it->representation_id);
geometries.write(it->representation_id, it->type, it->vertices, it->normals, it->faces, it->edges,
it->material_ids, it->materials, it->uvs, it->material_references);
}
geometries.close();
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it){
const std::string object_name = it->unique_id;
if (use_hierarchy)
{
size_t parentsNumber = it->parents_.size();
bool finished = false;
// If we have no parent in the stack and the object has no parent, nothing to do : skip the loop
if (parentsNumber == 0 && serializer->parentStackId.size() == 0) { finished = true; }
while (!finished)
{
// If we need to add a parent
if (serializer->parentStackId.size() <= parentsNumber)
{
if (serializer->parentStackId.empty()) { scene.addParent(*(it->parents_.at(0))); }
else
{
size_t diff = parentsNumber - serializer->parentStackId.size();
// If we have the wrong parent in the list
if (serializer->parentStackId.top() != it->parents_.at(parentsNumber - diff - 1)->id()) {
scene.closeParent();
} else {
// So far we have the right parents, we just need to add the missing ones
for (size_t i = parentsNumber - diff; i < parentsNumber; i++) { scene.addParent(*(it->parents_.at(i))); }
// if diff == 0, we can leave the loop. In fact we have the right number of parents, and the last one is ok
if (diff == 0) { finished = true; }
}
}
} else {
// Close the finished nodes. After this we get the first case (serializer->parentStackId.size() <= parentsNumber)
while (serializer->parentStackId.size() > parentsNumber) { scene.closeParent(); }
}
}
}
/// @todo redundant information using ID as both ID and Name, maybe omit Name or allow specifying what would be used as the name
scene.add(object_name, object_name, it->representation_id, it->material_references, it->transformation);
}
//close the remaining parent tags.
while (serializer->parentStackId.size() > 0) { scene.closeParent(); }
scene.write();
stream.endDocument();
}
bool ColladaSerializer::ready() {
return true;
}
void ColladaSerializer::writeHeader() {
exporter.startDocument(unit_name, unit_magnitude);
}
void ColladaSerializer::write(const IfcGeom::TriangulationElement<real_t>* o) {
exporter.write(o);
}
void ColladaSerializer::finalize() {
exporter.endDocument();
}
#endif
+250
View File
@@ -0,0 +1,250 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifdef WITH_OPENCOLLADA
#ifndef COLLADASERIALIZER_H
#define COLLADASERIALIZER_H
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4201 4512)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#endif
#include <COLLADASWStreamWriter.h>
#include <COLLADASWNode.h>
#include <COLLADASWLibraryGeometries.h>
#include <COLLADASWLibraryVisualScenes.h>
#include <COLLADASWLibraryEffects.h>
#include <COLLADASWLibraryMaterials.h>
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
#include "../serializers/GeometrySerializer.h"
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
class ColladaSerializer : public GeometrySerializer
{
// TODO The vast amount of implement details of ColladaSerializer could be hidden to the cpp file.
private:
std::stack<int> parentStackId;
class ColladaExporter
{
private:
class ColladaGeometries : public COLLADASW::LibraryGeometries
{
ColladaGeometries(const ColladaGeometries&); //N/A
ColladaGeometries& operator =(const ColladaGeometries&); //N/A
public:
explicit ColladaGeometries(COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer)
: COLLADASW::LibraryGeometries(&stream)
, serializer(_serializer)
{}
void addFloatSource(const std::string& mesh_id, const std::string& suffix,
const std::vector<real_t>& floats, const char* coords = "XYZ");
/// @todo pass simply DeferredObject?
void write(
const std::string &mesh_id, const std::string &default_material_name,
const std::vector<real_t>& positions, const std::vector<real_t>& normals,
const std::vector<int>& faces, const std::vector<int>& edges,
const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials,
const std::vector<real_t>& uvs, const std::vector<std::string>& material_references);
void close();
ColladaSerializer *serializer;
};
class ColladaScene : public COLLADASW::LibraryVisualScenes
{
private:
ColladaScene(const ColladaScene&); //N/A
ColladaScene& operator =(const ColladaScene&); //N/A
const std::string scene_id;
bool scene_opened;
std::stack<COLLADASW::Node*> parentNodes;
std::stack<IfcGeom::Transformation<real_t> > matrixStack;
public:
ColladaScene(const std::string& scene_id, COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer)
: COLLADASW::LibraryVisualScenes(&stream)
, scene_id(scene_id)
, scene_opened(false)
, serializer(_serializer)
{}
void add(const std::string& node_id, const std::string& node_name, const std::string& geom_name,
const std::vector<std::string>& material_ids, const IfcGeom::Transformation<real_t>& matrix);
void addParent(const IfcGeom::Element<real_t>& parent);
void closeParent();
COLLADASW::Node* GetDirectParent();
void write();
ColladaSerializer *serializer;
};
class ColladaMaterials : public COLLADASW::LibraryMaterials
{
ColladaMaterials(const ColladaMaterials&); //N/A
ColladaMaterials& operator =(const ColladaMaterials&); //N/A
private:
class ColladaEffects : public COLLADASW::LibraryEffects
{
ColladaEffects(const ColladaEffects&); //N/A
ColladaEffects& operator =(const ColladaEffects&); //N/A
public:
explicit ColladaEffects(COLLADASW::StreamWriter& stream)
: COLLADASW::LibraryEffects(&stream)
{}
void write(const IfcGeom::Material &material, const std::string &material_uri);
void close();
ColladaSerializer *serializer;
};
std::vector<IfcGeom::Material> materials;
std::vector<std::string> material_uris;
public:
explicit ColladaMaterials(COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer)
: COLLADASW::LibraryMaterials(&stream)
, serializer(_serializer)
, effects(stream)
{}
void add(const IfcGeom::Material& material);
std::string getMaterialUri(const IfcGeom::Material& material);
bool contains(const IfcGeom::Material& material);
void write();
ColladaSerializer *serializer;
ColladaEffects effects;
};
class DeferredObject {
friend bool operator < (const DeferredObject& def_obj1, const DeferredObject& def_obj2) {
size_t size = (def_obj1.parents_.size() < def_obj2.parents_.size() ? def_obj1.parents_.size() : def_obj2.parents_.size());
size_t cpt = 0;
// Skip the shared parents
while (cpt < size && *(def_obj1.parents_.at(cpt)) == *(def_obj2.parents_.at(cpt))) {
cpt++;
}
// If a parent list container the other one
if (cpt >= size) {
return def_obj1.parents_.size() < def_obj2.parents_.size();
} else {
return *(def_obj1.parents_.at(cpt)) < *(def_obj2.parents_.at(cpt));
}
}
public:
std::string unique_id, representation_id, type;
IfcGeom::Transformation<real_t> transformation;
std::vector<real_t> vertices;
std::vector<real_t> normals;
std::vector<int> faces;
std::vector<int> edges;
std::vector<int> material_ids;
std::vector<IfcGeom::Material> materials;
std::vector<std::string> material_references;
std::vector<real_t> uvs;
std::vector<const IfcGeom::Element<real_t>*> parents_;
DeferredObject(const std::string& unique_id, const std::string& representation_id, const std::string& type, const IfcGeom::Transformation<real_t>& transformation,
const std::vector<real_t>& vertices, const std::vector<real_t>& normals, const std::vector<int>& faces,
const std::vector<int>& edges, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials,
const std::vector<std::string>& material_references, const std::vector<real_t>& uvs)
: unique_id(unique_id)
, representation_id(representation_id)
, type(type)
, transformation(transformation)
, vertices(vertices)
, normals(normals)
, faces(faces)
, edges(edges)
, material_ids(material_ids)
, materials(materials)
, material_references(material_references)
, uvs(uvs)
{}
std::vector<const IfcGeom::Element<real_t>*>& parents() { return parents_; }
const std::vector<const IfcGeom::Element<real_t>*>& parents() const { return parents_; }
};
COLLADABU::NativeString filename;
COLLADASW::StreamWriter stream;
ColladaScene scene;
public:
/// @param double_precision Whether to use "double precision" (up to 16 decimals) or not (6 or 7 decimals).
ColladaExporter(const std::string& scene_name, const std::string& fn, ColladaSerializer *_serializer,
bool double_precision)
: filename(fn)
, stream(COLLADASW::NativeString(filename.c_str(), COLLADASW::NativeString::ENCODING_UTF8), double_precision)
, scene(scene_name, stream, _serializer)
, materials(stream, _serializer)
, geometries(stream, _serializer)
, serializer(_serializer)
{
}
ColladaMaterials materials;
ColladaGeometries geometries;
ColladaSerializer *serializer;
std::vector<DeferredObject> deferreds;
virtual ~ColladaExporter() {}
void startDocument(const std::string& unit_name, float unit_magnitude);
void write(const IfcGeom::TriangulationElement<real_t>* o);
void endDocument();
};
ColladaExporter exporter;
std::string unit_name;
float unit_magnitude;
public:
ColladaSerializer(const std::string& dae_filename, const SerializerSettings& settings)
: GeometrySerializer(settings)
, exporter("IfcOpenShell", dae_filename, this, settings.precision >= 15)
{
exporter.serializer = this;
exporter.materials.serializer = this;
exporter.materials.effects.serializer = this;
exporter.geometries.serializer = this;
}
bool ready();
void writeHeader();
void write(const IfcGeom::TriangulationElement<real_t>* o);
void write(const IfcGeom::BRepElement<real_t>* /*o*/) {}
void finalize();
bool isTesselated() const { return true; }
void setUnitNameAndMagnitude(const std::string& name, float magnitude) {
unit_name = name;
unit_magnitude = magnitude;
}
void setFile(IfcParse::IfcFile*) {}
std::string object_id(const IfcGeom::Element<real_t>* o) /*override*/;
private:
static std::string differentiateSlabTypes(const IfcUtil::IfcBaseEntity* slab);
};
#endif
#endif
@@ -0,0 +1,96 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef GEOMETRYSERIALIZER_H
#define GEOMETRYSERIALIZER_H
#ifdef IFCCONVERT_DOUBLE_PRECISION
typedef double real_t;
#else
typedef float real_t;
#endif
#include "../serializers/Serializer.h"
#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
#include "../ifcgeom/IfcGeomElement.h"
class SerializerSettings : public IfcGeom::IteratorSettings
{
public:
enum Setting
{
/// Use entity names instead of unique IDs for naming elements.
/// Applicable for OBJ, DAE, and SVG output.
USE_ELEMENT_NAMES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 1),
/// Use entity GUIDs instead of unique IDs for naming elements.
/// Applicable for OBJ, DAE, and SVG output.
USE_ELEMENT_GUIDS = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 2),
/// Use material names instead of unique IDs for naming materials.
/// Applicable for OBJ and DAE output.
USE_MATERIAL_NAMES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 3),
/// Use element types instead of unique IDs for naming elements.
/// Applicable for DAE output.
USE_ELEMENT_TYPES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 4),
/// Order the elements using their IfcBuildingStorey parent
/// Applicable for DAE output
USE_ELEMENT_HIERARCHY = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 5),
/// Use step ids for naming elements.
/// Applicable for OBJ, DAE, and SVG output.
USE_ELEMENT_STEPIDS = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 6),
/// Number of different setting flags.
NUM_SETTINGS = 6
};
SerializerSettings()
: precision(DEFAULT_PRECISION) { }
/// Sets the precision used to format floating-point values, 15 by default.
/// Use a negative value to use the system's default precision (should be 6 typically).
short precision;
enum { DEFAULT_PRECISION = 15 };
};
class GeometrySerializer : public Serializer {
public:
GeometrySerializer(const SerializerSettings& settings) : settings_(settings) {}
virtual ~GeometrySerializer() {}
virtual bool isTesselated() const = 0;
virtual void write(const IfcGeom::TriangulationElement<real_t>* o) = 0;
virtual void write(const IfcGeom::BRepElement<real_t>* o) = 0;
virtual void setUnitNameAndMagnitude(const std::string& name, float magnitude) = 0;
const SerializerSettings& settings() const { return settings_; }
SerializerSettings& settings() { return settings_; }
/// Returns ID for the object depending on the used setting.
virtual std::string object_id(const IfcGeom::Element<real_t>* o)
{
if (settings_.get(SerializerSettings::USE_ELEMENT_GUIDS)) return o->guid();
if (settings_.get(SerializerSettings::USE_ELEMENT_NAMES)) return o->name();
if (settings_.get(SerializerSettings::USE_ELEMENT_STEPIDS)) return "id-" + boost::lexical_cast<std::string>(o->id());
return o->unique_id();
}
protected:
SerializerSettings settings_;
};
#endif
+351
View File
@@ -0,0 +1,351 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifdef WITH_GLTF
#include "GltfSerializer.h"
#include "../ifcparse/utils.h"
#include <iterator>
static const uint32_t GLTF = 0x46546C67U;
static const uint32_t JSON = 0x4E4F534A;
static const uint32_t BIN = 0x004E4942;
static const uint32_t CT_BYTE = 5120;
static const uint32_t CT_UNSIGNED_BYTE = 5121;
static const uint32_t CT_SHORT = 5122;
static const uint32_t CT_UNSIGNED_SHORT = 5123;
static const uint32_t CT_UNSIGNED_INT = 5125;
static const uint32_t CT_FLOAT = 5126;
static const uint32_t PRIM_POINTS = 0;
static const uint32_t PRIM_LINES = 1;
static const uint32_t PRIM_LINE_LOOP = 2;
static const uint32_t PRIM_LINE_STRIP = 3;
static const uint32_t PRIM_TRIANGLES = 4;
static const uint32_t PRIM_TRIANGLE_STRIP = 5;
static const uint32_t PRIM_TRIANGLE_FAN = 6;
GltfSerializer::GltfSerializer(const std::string& filename, const SerializerSettings& settings)
: GeometrySerializer(settings)
, filename_(filename)
, tmp_filename1_(filename + ".indices.tmp")
, tmp_filename2_(filename + ".vertices.tmp")
, fstream_(IfcUtil::path::from_utf8(filename).c_str(), std::ios_base::binary)
, tmp_fstream1_(IfcUtil::path::from_utf8(tmp_filename1_).c_str(), std::ios_base::binary)
, tmp_fstream2_(IfcUtil::path::from_utf8(tmp_filename2_).c_str(), std::ios_base::binary)
{}
GltfSerializer::~GltfSerializer() {
tmp_fstream1_.close();
tmp_fstream2_.close();
IfcUtil::path::delete_file(tmp_filename1_);
IfcUtil::path::delete_file(tmp_filename2_);
}
bool GltfSerializer::ready() {
return fstream_.is_open() && tmp_fstream1_.is_open() && tmp_fstream2_.is_open();
}
void GltfSerializer::writeHeader() {
json_["asset"]["generator"] = "IfcOpenShell IfcConvert " IFCOPENSHELL_VERSION;
json_["asset"]["version"] = "2.0";
json_["scene"] = 0;
node_array_ = json::array();
json_["accessors"] = json::array();
json_["scenes"] = json::array();
json_["nodes"] = json::array();
json_["meshes"] = json::array();
json_["materials"] = json::array();
}
int GltfSerializer::writeMaterial(const IfcGeom::Material& style) {
auto it = materials_.find(style.name());
if (it != materials_.end()) {
return it->second;
}
int idx = json_["materials"].size();
materials_[style.name()] = idx;
std::array<double, 4> base;
base.fill(1.0);
if (style.hasDiffuse()) {
for (int i = 0; i < 3; ++i) {
base[i] = style.diffuse()[i];
}
}
if (style.hasTransparency()) {
base[3] = 1. - style.transparency();
}
json_["materials"].push_back({ {"pbrMetallicRoughness", {{"baseColorFactor", base}, {"metallicFactor", 0}}} });
if (style.hasTransparency() && style.transparency() > 1.e-9) {
json_["materials"].back()["alphaMode"] = "BLEND";
}
return idx;
}
template <size_t N>
struct stride_name { static const char* const value; };
template <>
const char* const stride_name<1U>::value = "SCALAR";
template <>
const char* const stride_name<3U>::value = "VEC3";
template <typename T>
struct component_type { static const uint32_t value; };
template <>
const uint32_t component_type<int>::value = CT_UNSIGNED_INT;
template <>
const uint32_t component_type<float>::value = CT_FLOAT;
template <size_t N, typename It>
size_t write_accessor(json& j, std::ofstream& ofs, It begin, It end) {
auto num = std::distance(begin, end) / N;
json accessor = json::object();
accessor["bufferView"] = N == 1 ? 0 : 1;
accessor["byteOffset"] = (size_t)ofs.tellp();
accessor["componentType"] = component_type<typename It::value_type>::value;
accessor["count"] = num;
std::array<typename It::value_type, N> min, max;
min.fill(std::numeric_limits<typename It::value_type>::max());
max.fill(std::numeric_limits<typename It::value_type>::lowest());
for (auto it = begin; it != end; it += N) {
for (size_t i = 0; i < N; ++i) {
const float& v = *(it + i);
if (v < min[i]) {
min[i] = v;
}
if (v > max[i]) {
max[i] = v;
}
}
}
accessor["min"] = min;
accessor["max"] = max;
accessor["type"] = stride_name<N>::value;
ofs.write((const char*)&*begin, sizeof(typename It::value_type) * num * N);
j["accessors"].push_back(accessor);
return j["accessors"].size() - 1;
}
void GltfSerializer::write(const IfcGeom::TriangulationElement<real_t>* o) {
if (o->geometry().material_ids().empty()) {
return;
}
node_array_.push_back(json_["nodes"].size());
const std::vector<double>& m = o->transformation().matrix().data();
// nb: note that this contains the Y-UP transform as well.
const std::array<double, 16> matrix_flat = {
m[0], m[ 2], -m[ 1], 0,
m[3], m[ 5], -m[ 4], 0,
m[6], m[ 8], -m[ 7], 0,
m[9], m[11], -m[10], 1
};
static const std::array<double, 16> identity_matrix = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
json node;
if (matrix_flat != identity_matrix) {
// glTF validator complains about identity matrices
node["matrix"] = matrix_flat;
}
node["name"] = object_id(o);
int current_mesh_index;
// See if this mesh has already been processed
auto it = meshes_.find(o->geometry().id());
if (it == meshes_.end()) {
auto mid1 = o->geometry().material_ids().begin();
auto mid0 = mid1;
std::vector<int>::const_iterator fid0;
int stride;
int primitive_type;
if (!o->geometry().faces().empty()) {
stride = 3;
fid0 = o->geometry().faces().begin();
primitive_type = PRIM_TRIANGLES;
} else {
stride = 2;
fid0 = o->geometry().edges().begin();
primitive_type = PRIM_LINES;
}
json mesh;
mesh["name"] = o->geometry().id();
while (true) {
// In glTF we need to decompose a mesh into several primitives
// with a constant material. In the triangulations coming from
// IfcOpenShell the materials are encoded in an additional set
// of indices. Therefore we loop over the material indices to
// find equal ranges of materials. Triangle indices then need
// to be updated to reference the vertices only for the current
// material.
mid1++;
if ((mid1 == o->geometry().material_ids().end()) || (*mid1 != *mid0)) {
auto n = std::distance(mid0, mid1);
auto fid1 = fid0 + n * stride;
auto idx_range = std::minmax_element(fid0, fid1);
const auto& idx_begin = *idx_range.first;
const auto& idx_end = *idx_range.second + 1;
std::vector<int> idx_transformed;
idx_transformed.reserve((n * stride));
std::transform(fid0, fid1, std::back_inserter(idx_transformed), [idx_begin](int i) {
return i - idx_begin;
});
json primitive = json::object();
primitive["indices"] = write_accessor<1U>(json_, tmp_fstream1_, idx_transformed.begin(), idx_transformed.end());
auto vbegin = o->geometry().verts().begin();
std::vector<float> vf(vbegin + idx_begin * 3, vbegin + idx_end * 3);
primitive["attributes"]["POSITION"] = write_accessor<3U>(json_, tmp_fstream2_, vf.begin(), vf.end());
if (o->geometry().normals().size()) {
auto nbegin = o->geometry().normals().begin();
std::vector<float> nf(nbegin + idx_begin * 3, nbegin + idx_end * 3);
primitive["attributes"]["NORMAL"] = write_accessor<3U>(json_, tmp_fstream2_, nf.begin(), nf.end());
}
primitive["material"] = writeMaterial(o->geometry().materials()[*mid0]);
primitive["mode"] = primitive_type;
mesh["primitives"].push_back(primitive);
if (mid1 == o->geometry().material_ids().end()) {
break;
}
mid0 = mid1;
fid0 = fid1;
}
}
json_["meshes"].push_back(mesh);
meshes_[o->geometry().id()] = current_mesh_index = json_["meshes"].size() - 1;
} else {
current_mesh_index = it->second;
}
node["mesh"] = current_mesh_index;
json_["nodes"].push_back(node);
}
template <uint32_t>
struct padding_char { static const char value; };
template <>
const char padding_char<JSON>::value = ' ';
template <>
const char padding_char<BIN>::value = '\x00';
uint32_t padding_for(uint32_t length) {
return ((4 - (length % 4)) % 4);
}
template <uint32_t iden>
void write_padding(std::ostream& fs, uint32_t N) {
uint32_t padding = padding_for(N);
for (uint32_t i = 0; i < padding; ++i) {
fs.put(padding_char<iden>::value);
}
}
template <uint32_t iden>
void write_header(std::ostream& fs, uint32_t N) {
uint32_t padding = padding_for(N);
uint32_t header[] = { N + padding, iden };
fs.write((const char*)header, sizeof(header));
}
template <uint32_t iden, typename It>
void write_block(std::ostream& fs, It begin, It end) {
uint32_t N = std::distance(begin, end);
write_header<iden>(fs, N);
fs.write((const char*)&*begin, N);
write_padding<iden>(fs, N);
}
void GltfSerializer::finalize() {
tmp_fstream1_.close();
tmp_fstream2_.close();
std::vector<char> binary_contents;
// nb: uint32_t is the max buffer size in glTF
uint32_t indices_length, binary_length;
{
std::ifstream ifs(IfcUtil::path::from_utf8(tmp_filename1_).c_str(), std::ios::binary);
ifs.ignore(std::numeric_limits<std::streamsize>::max());
indices_length = ifs.gcount();
}
{
std::ifstream ifs(IfcUtil::path::from_utf8(tmp_filename2_).c_str(), std::ios::binary);
ifs.ignore(std::numeric_limits<std::streamsize>::max());
binary_length = indices_length + ifs.gcount();
}
json scene_0;
scene_0["nodes"] = node_array_;
json_["scenes"].push_back(scene_0);
json_["bufferViews"].push_back({ {"buffer", 0}, { "byteLength", indices_length } });
json_["bufferViews"].push_back({ {"buffer", 0}, {"byteStride", 12}, { "byteOffset", indices_length }, { "byteLength", binary_length - indices_length } });
json_["buffers"].push_back({ {"byteLength", binary_length} });
std::string json_contents = json_.dump();
uint32_t json_length = (uint32_t) json_contents.size();
uint32_t header[] = { GLTF, 2U, 12 + 8 + json_length + padding_for(json_length) + 8 + binary_length + padding_for(binary_length) };
fstream_.write((const char*)header, sizeof(header));
write_block<JSON>(fstream_, json_contents.begin(), json_contents.end());
write_header<BIN>(fstream_, binary_length);
{
std::ifstream ifs(IfcUtil::path::from_utf8(tmp_filename1_).c_str(), std::ios::binary);
fstream_ << ifs.rdbuf();
}
{
std::ifstream ifs(IfcUtil::path::from_utf8(tmp_filename2_).c_str(), std::ios::binary);
fstream_ << ifs.rdbuf();
}
write_padding<BIN>(fstream_, binary_length);
}
#endif
+55
View File
@@ -0,0 +1,55 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef GLTFSERIALIZER_H
#define GLTFSERIALIZER_H
#ifdef WITH_GLTF
#include "../serializers/GeometrySerializer.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
#include <map>
class GltfSerializer : public GeometrySerializer {
private:
std::string filename_, tmp_filename1_, tmp_filename2_;
std::ofstream fstream_, tmp_fstream1_, tmp_fstream2_;
std::map<std::string, int> materials_, meshes_;
json json_, node_array_;
int writeMaterial(const IfcGeom::Material& style);
public:
GltfSerializer(const std::string& filename, const SerializerSettings& settings);
virtual ~GltfSerializer();
bool ready();
void writeHeader();
void write(const IfcGeom::TriangulationElement<real_t>* o);
void write(const IfcGeom::BRepElement<real_t>* /*o*/) {}
void finalize();
bool isTesselated() const { return true; }
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
void setFile(IfcParse::IfcFile*) {}
};
#endif
#endif
+64
View File
@@ -0,0 +1,64 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef IGESSERIALIZER_H
#define IGESSERIALIZER_H
#include "OpenCascadeBasedSerializer.h"
#include "../ifcparse/IfcLogger.h"
#include <IGESControl_Writer.hxx>
#ifndef HAVE_CONFIG_H
/// @note this is brittle, but apparently the only way to differentiate OCCT
/// from OCE. In the latter including this header fails for some versions.
#include <Interface_Static.hxx>
#endif
class IgesSerializer : public OpenCascadeBasedSerializer
{
private:
IGESControl_Writer writer;
public:
/// @note IGESControl_Controller::Init() must be called prior to instantiating IgesSerializer.
/// See http://tracker.dev.opencascade.org/view.php?id=23679 for more information.
IgesSerializer(const std::string& out_filename, const SerializerSettings& settings)
: OpenCascadeBasedSerializer(out_filename, settings)
{}
virtual ~IgesSerializer() {}
void writeShape(const std::string&, const TopoDS_Shape& shape) {
writer.AddShape(shape);
}
void finalize() {
writer.Write(out_filename.c_str());
}
void setUnitNameAndMagnitude(const std::string& /*name*/, float magnitude) {
const char* symbol = getSymbolForUnitMagnitude(magnitude);
if (symbol) {
#ifdef HAVE_CONFIG_H
Logger::Warning("Setting IGES units not supported on OCE");
#else
Interface_Static::SetCVal("xstep.cascade.unit", symbol);
Interface_Static::SetCVal("write.iges.unit", symbol);
#endif
}
}
};
#endif
@@ -0,0 +1,66 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include "OpenCascadeBasedSerializer.h"
#include "../ifcparse/utils.h"
#include <string>
#include <fstream>
#include <cstdio>
#include <Standard_Version.hxx>
#include <BRepBuilderAPI_Transform.hxx>
bool OpenCascadeBasedSerializer::ready() {
std::ofstream test_file(IfcUtil::path::from_utf8(out_filename).c_str(), std::ios_base::binary);
bool succeeded = test_file.is_open();
test_file.close();
IfcUtil::path::delete_file(out_filename);
return succeeded;
}
void OpenCascadeBasedSerializer::write(const IfcGeom::BRepElement<real_t>* o) {
TopoDS_Shape compound = o->geometry().as_compound();
gp_Trsf trsf = o->transformation().data();
const IfcGeom::ElementSettings& settings = o->geometry().settings();
if (settings.get(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS) && settings.unit_magnitude() != 1.0) {
trsf.SetTranslationPart(trsf.TranslationPart() / settings.unit_magnitude());
}
writeShape(object_id(o), compound.Moved(trsf));
}
#define RATHER_SMALL (1e-3)
#define APPROXIMATELY_THE_SAME(a,b) (fabs(a-b) < RATHER_SMALL)
const char* OpenCascadeBasedSerializer::getSymbolForUnitMagnitude(float mag) {
if (APPROXIMATELY_THE_SAME(mag, 0.001f)) {
return "MM";
} else if (APPROXIMATELY_THE_SAME(mag, 0.01f)) {
return "CM";
} else if (APPROXIMATELY_THE_SAME(mag, 1.0f)) {
return "M";
} else if (APPROXIMATELY_THE_SAME(mag, 0.3048f)) {
return "FT";
} else if (APPROXIMATELY_THE_SAME(mag, 0.0254f)) {
return "INCH";
} else {
return 0;
}
}
@@ -0,0 +1,48 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef OPENCASCADEBASEDSERIALIZER_H
#define OPENCASCADEBASEDSERIALIZER_H
#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
#include "../serializers/GeometrySerializer.h"
class OpenCascadeBasedSerializer : public GeometrySerializer {
OpenCascadeBasedSerializer(const OpenCascadeBasedSerializer&); //N/A
OpenCascadeBasedSerializer& operator =(const OpenCascadeBasedSerializer&); //N/A
protected:
const std::string out_filename;
const char* getSymbolForUnitMagnitude(float mag);
public:
explicit OpenCascadeBasedSerializer(const std::string& out_filename, const SerializerSettings& settings)
: GeometrySerializer(settings)
, out_filename(out_filename)
{}
virtual ~OpenCascadeBasedSerializer() {}
void writeHeader() {}
bool ready();
virtual void writeShape(const std::string& name, const TopoDS_Shape& shape) = 0;
void write(const IfcGeom::TriangulationElement<real_t>* /*o*/) {}
void write(const IfcGeom::BRepElement<real_t>* o);
bool isTesselated() const { return false; }
void setFile(IfcParse::IfcFile*) {}
};
#endif
+35
View File
@@ -0,0 +1,35 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef SERIALIZER_H
#define SERIALIZER_H
#include "../ifcparse/IfcFile.h"
class Serializer {
public:
virtual ~Serializer() {}
virtual bool ready() = 0;
virtual void writeHeader() = 0;
virtual void finalize() = 0;
virtual void setFile(IfcParse::IfcFile*) = 0;
};
#endif
+61
View File
@@ -0,0 +1,61 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef STEPSERIALIZER_H
#define STEPSERIALIZER_H
#include <STEPControl_Writer.hxx>
#include <Interface_Static.hxx>
#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
#include "../serializers/OpenCascadeBasedSerializer.h"
class StepSerializer : public OpenCascadeBasedSerializer
{
private:
STEPControl_Writer writer;
public:
explicit StepSerializer(const std::string& out_filename, const SerializerSettings& settings)
: OpenCascadeBasedSerializer(out_filename, settings)
{}
virtual ~StepSerializer() {}
void writeShape(const std::string& name, const TopoDS_Shape& shape) {
std::stringstream ss;
std::streambuf *sb = std::cout.rdbuf(ss.rdbuf());
Interface_Static::SetCVal("write.step.product.name", name.c_str());
writer.Transfer(shape, STEPControl_AsIs);
std::cout.rdbuf(sb);
}
void finalize() {
std::stringstream ss;
std::streambuf *sb = std::cout.rdbuf(ss.rdbuf());
writer.Write(out_filename.c_str());
std::cout.rdbuf(sb);
}
void setUnitNameAndMagnitude(const std::string& /*name*/, float magnitude) {
const char* symbol = getSymbolForUnitMagnitude(magnitude);
if (symbol) {
Interface_Static::SetCVal("xstep.cascade.unit", symbol);
Interface_Static::SetCVal("write.step.unit", symbol);
}
}
};
#endif
+912
View File
@@ -0,0 +1,912 @@
/********************************************************************************
* *
* Copyright 2015 IfcOpenShell and ROOT B.V. *
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include <string>
#include <fstream>
#include <cstdio>
#include <limits>
#include <algorithm>
#include <gp_Pln.hxx>
#include <gp_Trsf.hxx>
#include <gp_Circ.hxx>
#include <gp_Elips.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopExp_Explorer.hxx>
#include <BRep_Tool.hxx>
#include <BRepAlgo_Section.hxx>
#include <BRepTools.hxx>
#include <BRepAlgoAPI_Section.hxx>
#include <ShapeAnalysis_FreeBounds.hxx>
#include <TopTools_HSequenceOfShape.hxx>
#include <TopExp.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <GCPnts_QuasiUniformDeflection.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Line.hxx>
#include <Geom_Plane.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <gp_Ax22d.hxx>
#include <Standard_Version.hxx>
#include <GeomAPI.hxx>
#include <TopoDS_Wire.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <GProp_GProps.hxx>
#include <BRepGProp.hxx>
#include <BRepTopAdaptor_FClass2d.hxx>
#include <Bnd_Box.hxx>
#include <BRep_Builder.hxx>
#include <BRepBndLib.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include "../ifcparse/IfcGlobalId.h"
#include "SvgSerializer.h"
const double PI2 = M_PI * 2.;
bool SvgSerializer::ready() {
return true;
}
void SvgSerializer::write(path_object& p, const TopoDS_Wire& wire) {
/* ShapeFix_Wire fix;
Handle(ShapeExtend_WireData) data = new ShapeExtend_WireData;
for (TopExp_Explorer edges(result, TopAbs_EDGE); edges.More(); edges.Next()) {
data->Add(edges.Current());
}
fix.Load(data);
fix.FixReorder();
fix.FixConnected();
const TopoDS_Wire fixed_wire = fix.Wire(); */
bool first = true;
util::string_buffer path;
for (TopExp_Explorer edges(wire, TopAbs_EDGE); edges.More(); edges.Next()) {
const TopoDS_Edge& edge = TopoDS::Edge(edges.Current());
double u1, u2;
Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, u1, u2);
Handle(Geom2d_Curve) curve2d;
if (curve.IsNull()) {
TopLoc_Location loc;
Handle_Geom_Surface surf;
BRep_Tool::CurveOnSurface(edge, curve2d, surf, loc, u1, u2);
if (curve2d.IsNull()) {
Logger::Error("Failed to obtain 2d and 3d curve from edge");
continue;
}
Handle(Standard_Type) sty = surf->DynamicType();
if (sty != STANDARD_TYPE(Geom_Plane)) {
Logger::Error("Non-planar p-curves are not supported by this serializer");
continue;
}
gp_Pln pln = Handle(Geom_Plane)::DownCast(surf)->Pln();
curve = GeomAPI::To3d(curve2d, pln);
}
Handle(Standard_Type) ty = curve->DynamicType();
bool conical = (ty == STANDARD_TYPE(Geom_Circle) || ty == STANDARD_TYPE(Geom_Ellipse));
// TODO: ALMOST_THE_SAME utilities in separate header
bool closed = fabs((u1 + PI2) - u2) < 1.e-9;
if (conical && closed) {
if (first) {
if (ty == STANDARD_TYPE(Geom_Circle)) {
Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast(curve);
double r = circle->Radius();
gp_Circ c = circle->Circ();
gp_Pnt center = c.Location();
path.add(" <circle style=\"stroke:black; fill:none;\" r=\"");
radii.push_back(path.add(r));
path.add("\" cx=\"");
xcoords.push_back(path.add(center.X()));
path.add("\" cy=\"");
ycoords.push_back(path.add(center.Y()));
growBoundingBox(center.X() - r, center.Y() - r);
growBoundingBox(center.X() + r, center.Y() + r);
first = false;
continue;
} else if (ty == STANDARD_TYPE(Geom_Ellipse)) {
Handle(Geom_Ellipse) ellipse = Handle(Geom_Ellipse)::DownCast(curve);
gp_Elips e = ellipse->Elips();
gp_Pnt center = e.Location();
// Write the ellipse with major radius along X axis:
path.add(" <ellipse style=\"stroke:black; fill:none;\" rx=\"");
radii.push_back(path.add(e.MajorRadius()));
path.add("\" ry=\"");
radii.push_back(path.add(e.MinorRadius()));
path.add("\" cx=\"");
xcoords.push_back(path.add(center.X()));
path.add("\" cy=\"");
ycoords.push_back(path.add(center.Y()));
path.add("\"");
// Rotate it with "transform":
gp_Ax1 major_axis = e.XAxis();
double z_rotation = major_axis.Direction().AngleWithRef(gp_Dir(1., 0., 0.), gp_Dir(0., 0., 1.));
path.add(" transform=\"rotate(");
path.add(z_rotation);
path.add(" ");
path.add(center.X());
path.add(" ");
path.add(center.Y());
// Bounding box:
// More important to have all geometry in bounding box than to be minimal
growBoundingBox(center.X() - e.MajorRadius(), center.Y() - e.MajorRadius());
growBoundingBox(center.X() + e.MajorRadius(), center.Y() + e.MajorRadius());
first = false;
continue;
}
} else {
std::stringstream ss;
ss << "Skipping full circle/ellipse inside aggregated <path> (id "
<< p.first << ")";
Logger::Warning(ss.str());
}
}
const bool reversed = edge.Orientation() == TopAbs_REVERSED;
gp_Pnt p1, p2;
curve->D0(u1, p1);
curve->D0(u2, p2);
if (reversed) {
std::swap(p1, p2);
}
if (first) {
path.add(" <path style=\"stroke:black; fill:none;\" d=\"");
path.add("M");
addXCoordinate(path.add(p1.X()));
path.add(",");
addYCoordinate(path.add(p1.Y()));
growBoundingBox(p1.X(), p1.Y());
}
growBoundingBox(p2.X(), p2.Y());
if (ty == STANDARD_TYPE(Geom_Circle) || ty == STANDARD_TYPE(Geom_Ellipse)) {
Handle(Geom_Conic) conic = Handle(Geom_Conic)::DownCast(curve);
const bool mirrored = conic->Position().Axis().Direction().Z() < 0;
double r1, r2;
bool larger_arc_segment = (fmod(u2 - u1 + PI2, PI2) > M_PI);
bool positive_direction = (u2 > u1);
if (mirrored != reversed) {
// In case the local coordinate system is mirrored
// the direction is reversed.
positive_direction = !positive_direction;
}
gp_Pnt center;
if (ty == STANDARD_TYPE(Geom_Circle)) {
Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast(curve);
r1 = r2 = circle->Radius();
center = circle->Location();
} else {
Handle(Geom_Ellipse) ellipse = Handle(Geom_Ellipse)::DownCast(curve);
r1 = ellipse->MajorRadius();
r2 = ellipse->MinorRadius();
center = ellipse->Location();
}
// Make sure the arc segment is entirely inside bounding box:
growBoundingBox(center.X() - r1, center.Y() - r1);
growBoundingBox(center.X() + r1, center.Y() + r1);
// Calculate the angle between 2d vecs to have signed result
const gp_Dir& d = conic->Position().XDirection();
const gp_Dir2d d2(d.X(), d.Y());
const double ang = d2.Angle(gp::DX2d());
// Write radii
path.add(" A");
addSizeComponent(path.add(r1));
path.add(",");
addSizeComponent(path.add(r2));
// Write X-axis rotation
{ std::stringstream ss; ss << " " << ang << " ";
path.add(ss.str()); }
// Write large-arc-flag and sweep-flag
path.add(std::string(1, '0'+static_cast<int>(larger_arc_segment)));
path.add(",");
path.add(std::string(1, '0'+static_cast<int>(positive_direction)));
path.add(" ");
// Write arc end point
xcoords.push_back(path.add(p2.X()));
path.add(",");
ycoords.push_back(path.add(p2.Y()));
} else if (ty != STANDARD_TYPE(Geom_Line)) {
BRepAdaptor_Curve crv(edge);
GCPnts_QuasiUniformDeflection tessellater(crv, settings().deflection_tolerance());
// NB: Start at 2: 1-based and skip the first point, assume it coincides with p1.
for (int i = 2; i <= tessellater.NbPoints(); ++i) {
gp_Pnt pi = tessellater.Value(i);
path.add(" L");
xcoords.push_back(path.add(pi.X()));
path.add(",");
ycoords.push_back(path.add(pi.Y()));
growBoundingBox(pi.X(), pi.Y());
}
} else {
// Either a Geom_Line or something unimplemented,
// drawn as a straight line segment.
path.add(" L");
xcoords.push_back(path.add(p2.X()));
path.add(",");
ycoords.push_back(path.add(p2.Y()));
}
first = false;
}
path.add("\"/>\n");
p.second.push_back(path);
}
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;
p.first = id;
return p;
}
void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
{
std::vector<std::pair<std::pair<double, double>, IfcUtil::IfcBaseEntity*>> section_heights_storage;
const std::vector<std::pair<std::pair<double, double>, IfcUtil::IfcBaseEntity*>>* section_heights_used = &section_heights_storage;
if (section_heights) {
section_heights_used = section_heights.get_ptr();
} else {
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() });
} catch (...) {
continue;
}
break;
}
}
if (section_heights_storage.empty()) {
Logger::Warning("No global section height and unable to determine building storey for:", o->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);
make_transform_global.Build();
// (When determinant < 0, copy is implied and the input is not mutated.)
auto compound = 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);
make_transform_mirror.Build();
// (When determinant < 0, copy is implied and the input is not mutated.)
compound = make_transform_mirror.Shape();
TopoDS_Wire annotation;
if (draw_door_arcs_ && o->product()->declaration().is("IfcDoor")) {
boost::optional<std::string> operation_type;
try {
IfcEntityList::ptr rels;
if (o->product()->declaration().schema()->name() == "IFC2X3") {
rels = o->product()->get_inverse("IsDefinedBy");
} else {
// Damn you, IFC
rels = o->product()->get_inverse("IsTypedBy");
}
for (auto& rel : *rels) {
if (rel->declaration().name() == "IfcRelDefinesByType") {
IfcUtil::IfcBaseClass* ty = *((IfcUtil::IfcBaseEntity*)rel)->get("RelatingType");
const std::string& ty_entity_name = ty->declaration().name();
// Damn you, IFC
if (ty_entity_name == "IfcDoorStyle" || ty_entity_name == "IfcDoorType") {
operation_type = *((IfcUtil::IfcBaseEntity*)ty)->get("OperationType");
}
}
}
} catch (std::exception& e) {
Logger::Error(e);
}
if (operation_type && (*operation_type == "SINGLE_SWING_LEFT") || (*operation_type == "SINGLE_SWING_RIGHT")) {
const bool is_left = *operation_type == "SINGLE_SWING_LEFT";
Bnd_Box bb;
BRepBndLib::Add(compound_local, bb);
if (bb.IsVoid()) {
return;
}
double x1, y1, z1, x2, y2, z2;
bb.Get(x1, y1, z1, x2, y2, z2);
double width = x2 - x1;
double y12 = (y1 + y2) / 2.;
gp_Pnt center(is_left ? x1 : x2, y12, 0);
gp_Pnt p1(is_left ? x2 : x1, y12, 0);
gp_Pnt p2(is_left ? x1 : x2, y12 + width, 0);
if (!is_left) {
// circles are counter clockwise, so for swing right
// we need to reverse the points in order to get the
// shorter part of the circle arc.
std::swap(p1, p2);
}
BRepBuilderAPI_MakeEdge me(gp_Circ(gp_Ax2(center, gp::DZ()), width), p1, p2);
if (me.IsDone()) {
BRep_Builder B;
B.MakeWire(annotation);
auto edge = me.Edge();
make_transform_global.Perform(edge, true);
auto edge_global = make_transform_global.Shape();
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(trsf_mirror);
p1.Transform(trsf_mirror);
p2.Transform(trsf_mirror);
if (!is_left) {
// For the purpose of the SVG serializer we do not a topologically
// connected wire. So adding disconnected edges is fine.
B.Add(annotation, BRepBuilderAPI_MakeEdge(center, p1).Edge());
}
B.Add(annotation, edge_global_mirrored);
if (is_left) {
B.Add(annotation, BRepBuilderAPI_MakeEdge(p2, center).Edge());
}
}
}
}
bool emitted = false;
for (auto sit = section_heights_used->begin(); sit != section_heights_used->end(); ++sit) {
const auto& pair = *sit;
// Elev + offset
auto cut_z = pair.first.first + pair.first.second;
// Elev .. Elev(next)
std::pair<double, double> range{ pair.first.first, std::numeric_limits<double>::infinity() };
if (sit == section_heights_used->begin()) {
range.first = -range.second;
}
if (sit + 1 != section_heights_used->end()) {
range.second = (sit + 1)->first.first;
}
auto storey = pair.second;
TopoDS_Iterator it(compound);
TopoDS_Face largest_closed_wire_face;
double largest_closed_wire_area = 0.;
path_object* po = nullptr;
// 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;
try {
BRepBndLib::Add(it.Value(), bb);
} catch (const Standard_Failure&) {}
// Empty geometry
if (bb.IsVoid()) {
continue;
}
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)) {
cut_z = zmin + 1.;
}
if (o->type() == "IfcAnnotation" && ((zmax - zmin) < 1.e-5) && zmin >= range.first && zmin <= range.second) {
if (po == nullptr) {
po = &start_path(storey, nameElement(storey, o));
}
TopExp_Explorer exp(subshape, TopAbs_EDGE, TopAbs_FACE);
for (; exp.More(); exp.Next()) {
const auto& e = TopoDS::Edge(exp.Current());
TopoDS_Vertex v0, v1;
TopExp::Vertices(e, v0, v1);
gp_Pnt p0 = BRep_Tool::Pnt(v0);
gp_Pnt p1 = BRep_Tool::Pnt(v1);
// @todo should we take the average parameter value instead?
gp_XYZ center = (p0.XYZ() + p1.XYZ()) / 2.;
BRep_Builder B;
TopoDS_Wire W;
B.MakeWire(W);
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
path.add(" <text class=\"IfcAnnotation\" text-anchor=\"middle\" x=\"");
xcoords.push_back(path.add(center.X()));
path.add("\" y=\"");
ycoords.push_back(path.add(center.Y()));
path.add("\">");
std::vector<std::string> labels{};
GProp_GProps prop;
BRepGProp::LinearProperties(e, prop);
const double area = prop.Mass();
std::stringstream ss;
ss << std::setprecision(2) << std::fixed << std::showpoint << area;
labels.push_back(ss.str() + "m");
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.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);
}
continue;
}
if (subshape.ShapeType() > TopAbs_FACE) {
// Except for annotations we only emit solids and surfaces to SVG.
emitted = true;
continue;
}
// No intersection with bounding box, fail early
if (zmin > cut_z || zmax < cut_z) continue;
emitted = true;
if (po == nullptr) {
po = &start_path(storey, nameElement(storey, o));
}
// 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);
Handle(TopTools_HSequenceOfShape) edges = new TopTools_HSequenceOfShape();
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);
gp_Pnt prev;
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") {
// 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.
BRepBuilderAPI_MakeFace mf(pln, wire);
if (mf.IsDone()) {
TopoDS_Face f = mf.Face();
GProp_GProps prop;
BRepGProp::SurfaceProperties(f, prop);
const double area = prop.Mass();
if (area > largest_closed_wire_area) {
largest_closed_wire_face = f;
largest_closed_wire_area = area;
}
}
}
write(*po, wire);
}
}
if (!largest_closed_wire_face.IsNull()) {
std::vector<gp_Pnt> points;
TopExp_Explorer exp(largest_closed_wire_face, TopAbs_VERTEX);
for (; exp.More(); exp.Next()) {
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&#178;");
}
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()));
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);
}
}
if (po && !annotation.IsNull()) {
write(*po, annotation);
}
}
if (!emitted) {
Logger::Warning("Element not written to SVG due to section heights", o->product());
}
}
void SvgSerializer::setBoundingRectangle(double width, double height) {
this->width = width;
this->height = height;
this->rescale = true;
}
void SvgSerializer::finalize() {
if (rescale) {
// Scale the resulting image to a bounding rectangle specified by command line arguments
const double dx = xmax - xmin;
const double dy = ymax - ymin;
double sc, cx, cy;
if (scale_) {
sc = (*scale_) * 1000;
cx = (xmax + xmin) / 2. * sc - width / 2.;
cy = (ymax + ymin) / 2. * sc - height / 2.;
} else {
if (dx / width > dy / height) {
sc = width / dx;
} else {
sc = height / dy;
}
cx = xmin * sc;
cy = ymin * sc;
}
{std::vector< boost::shared_ptr<util::string_buffer::float_item> >::const_iterator it;
for (it = xcoords.begin(); it != xcoords.end(); ++it) {
double& v = (*it)->value();
v = v * sc - cx;
}
for (it = ycoords.begin(); it != ycoords.end(); ++it) {
double& v = (*it)->value();
v = v * sc - cy;
}
for (it = radii.begin(); it != radii.end(); ++it) {
(*it)->value() *= sc;
}}
}
std::multimap<IfcUtil::IfcBaseEntity*, path_object>::const_iterator it;
IfcUtil::IfcBaseEntity* previous = 0;
bool first = true;
for (it = paths.begin(); it != paths.end(); ++it) {
if (it->first != previous || first) {
if (!first) {
svg_file << " </g>\n";
}
std::ostringstream oss;
svg_file << " <g " << nameElement(it->first) << ">\n";
}
svg_file << " <g " << it->second.first << ">\n";
std::vector<util::string_buffer>::const_iterator jt;
for (jt = it->second.second.begin(); jt != it->second.second.end(); ++jt) {
svg_file << jt->str();
}
svg_file << " </g>\n";
previous = it->first;
first = false;
}
if (!first) {
svg_file << " </g>\n";
}
svg_file << "</svg>" << std::endl;
}
void SvgSerializer::writeHeader() {
svg_file << "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"";
if (scale_) {
svg_file <<
" width=\"" << width << "mm\""
" height=\"" << height << "mm\"" <<
" viewBox=\"0 0 " << width << " " << height << "\"";
}
svg_file << ">\n"
" <defs>\n"
" <marker id=\"arrowend\" markerWidth=\"10\" markerHeight=\"7\" refX=\"10\" refY=\"3.5\" orient=\"auto\">\n"
" <polygon points=\"0 0, 10 3.5, 0 7\" />\n"
" </marker>\n"
" <marker id=\"arrowstart\" markerWidth=\"10\" markerHeight=\"7\" refX=\"0\" refY=\"3.5\" orient=\"auto\">\n"
" <polygon points=\"10 0, 0 3.5, 10 7\" />\n"
" </marker>\n"
" </defs>\n"
" <style type=\"text/css\" >\n"
" <![CDATA[\n"
" .IfcAnnotation path {\n"
" marker-end: url(#arrowend);\n"
" marker-start: url(#arrowstart);\n"
" }\n";
if (scale_) {
svg_file <<
" text {\n" // (pt) (px) (in) (mm)
" font-size: 4;\n" // approx 12 / 0.75 / 96 * 25.4
" }\n";
}
svg_file <<
" ]]>\n"
" </style>\n";
}
namespace {
std::string nameElement_(const std::vector<std::pair<std::string, std::string> >& 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();
}
}
std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* storey, const IfcGeom::Element<real_t>* elem) {
return nameElement_({
{"id", with_section_heights_from_storey_ ? object_id(storey, elem) : GeometrySerializer::object_id(elem)},
{"class", elem->type()},
{"data-name", elem->name()},
{"data-guid", elem->guid()}
});
}
std::string SvgSerializer::idElement(const IfcUtil::IfcBaseEntity* elem) {
const std::string type = elem->declaration().is("IfcBuildingStorey") ? "storey" : "product";
const std::string name =
(settings().get(SerializerSettings::USE_ELEMENT_GUIDS)
? static_cast<std::string>(*elem->get("GlobalId"))
: ((settings().get(SerializerSettings::USE_ELEMENT_NAMES) && !elem->get("Name")->isNull()))
? static_cast<std::string>(*elem->get("Name"))
: (settings().get(SerializerSettings::USE_ELEMENT_STEPIDS))
? ("id-" + boost::lexical_cast<std::string>(elem->data().id()))
: IfcParse::IfcGlobalId(*elem->get("GlobalId")).formatted());
return type + "-" + name;
}
std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* elem) {
if (elem == 0) { return ""; }
const std::string& entity = elem->declaration().name();
std::string ifc_name;
if (!elem->get("Name")->isNull()) {
ifc_name = (std::string) *elem->get("Name");
}
return nameElement_({
{"id", idElement(elem)},
{"class", entity},
{"data-name", ifc_name},
{"data-guid", *elem->get("GlobalId")}
});
}
void SvgSerializer::setFile(IfcParse::IfcFile* f) {
file = f;
auto storeys = f->instances_by_type("IfcBuildingStorey");
if (!storeys || storeys->size() == 0) {
IfcGeom::Kernel kernel(f);
std::vector<const IfcParse::declaration*> 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"));
for (auto it = to_derive_from.begin(); it != to_derive_from.end(); ++it) {
IfcEntityList::ptr insts = f->instances_by_type(*it);
if (insts) {
for (auto jt = insts->begin(); jt != insts->end(); ++jt) {
IfcUtil::IfcBaseEntity* product = (IfcUtil::IfcBaseEntity*) *jt;
if (!product->get("ObjectPlacement")->isNull()) {
gp_Trsf trsf;
if (kernel.convert_placement(*product->get("ObjectPlacement"), trsf)) {
setSectionHeight(trsf.TranslationPart().Z() + 1.);
Logger::Warning("No building storeys encountered, used for reference:", product);
return;
}
}
}
}
}
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, 0.}, storey });
}
void SvgSerializer::setSectionHeightsFromStoreys(double offset) {
with_section_heights_from_storey_ = true;
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(), 0.}, nullptr });
}
}
+129
View File
@@ -0,0 +1,129 @@
/********************************************************************************
* *
* Copyright 2015 IfcOpenShell and ROOT B.V. *
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef SVGSERIALIZER_H
#define SVGSERIALIZER_H
#include "../serializers/GeometrySerializer.h"
#include "../serializers/util.h"
#include "../ifcparse/utils.h"
#include <sstream>
#include <string>
#include <limits>
struct storey_sorter {
bool operator()(IfcUtil::IfcBaseEntity* a, IfcUtil::IfcBaseEntity* b) const {
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) {
boost::optional<double> a_elev, b_elev;
try {
a_elev = static_cast<double>(*a->get("Elevation"));
b_elev = static_cast<double>(*b->get("Elevation"));
} catch (...) {};
if (a_elev && b_elev) {
if (std::equal_to<double>()(*a_elev, *b_elev)) {
return std::less<unsigned int>()(a->data().id(), b->data().id());
} else {
return std::less<double>()(*a_elev, *b_elev);
}
}
boost::optional<std::string> a_name, b_name;
try {
a_name = static_cast<std::string>(*a->get("Name"));
b_name = static_cast<std::string>(*b->get("Name"));
} catch (...) {};
if (a_name && b_name) {
if (std::equal_to<std::string>()(*a_name, *b_name)) {
return std::less<unsigned int>()(a->data().id(), b->data().id());
} else {
return std::less<std::string>()(*a_name, *b_name);
}
}
}
return std::less<IfcUtil::IfcBaseEntity*>()(a, b);
}
};
class SvgSerializer : public GeometrySerializer {
public:
typedef std::pair<std::string, std::vector<util::string_buffer> > path_object;
protected:
std::ofstream svg_file;
double xmin, ymin, xmax, ymax, width, height;
boost::optional<std::vector<std::pair<std::pair<double, double>, IfcUtil::IfcBaseEntity*>>> section_heights;
boost::optional<double> scale_;
bool rescale, print_space_names_, print_space_areas_, draw_door_arcs_, with_section_heights_from_storey_;
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> > ycoords;
std::vector< boost::shared_ptr<util::string_buffer::float_item> > radii;
IfcParse::IfcFile* file;
IfcUtil::IfcBaseEntity* storey_;
public:
SvgSerializer(const std::string& out_filename, const SerializerSettings& settings)
: GeometrySerializer(settings)
, svg_file(IfcUtil::path::from_utf8(out_filename).c_str())
, xmin(+std::numeric_limits<double>::infinity())
, ymin(+std::numeric_limits<double>::infinity())
, xmax(-std::numeric_limits<double>::infinity())
, ymax(-std::numeric_limits<double>::infinity())
, with_section_heights_from_storey_(false)
, rescale(false)
, print_space_names_(false)
, print_space_areas_(false)
, draw_door_arcs_(false)
, file(0)
, storey_(0)
{}
void addXCoordinate(const boost::shared_ptr<util::string_buffer::float_item>& fi) { xcoords.push_back(fi); }
void addYCoordinate(const boost::shared_ptr<util::string_buffer::float_item>& fi) { ycoords.push_back(fi); }
void addSizeComponent(const boost::shared_ptr<util::string_buffer::float_item>& fi) { radii.push_back(fi); }
void growBoundingBox(double x, double y) { if (x < xmin) xmin = x; if (x > xmax) xmax = x; if (y < ymin) ymin = y; if (y > ymax) ymax = y; }
void writeHeader();
bool ready();
void write(const IfcGeom::TriangulationElement<real_t>* /*o*/) {}
void write(const IfcGeom::BRepElement<real_t>* o);
void write(path_object& p, const TopoDS_Wire& wire);
path_object& start_path(IfcUtil::IfcBaseEntity* storey, const std::string& id);
bool isTesselated() const { return false; }
void finalize();
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
void setFile(IfcParse::IfcFile* f);
void setBoundingRectangle(double width, double height);
void setSectionHeight(double h, IfcUtil::IfcBaseEntity* storey = 0);
void setSectionHeightsFromStoreys(double offset=1.);
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 setScale(double s) { scale_ = s; }
std::string nameElement(const IfcUtil::IfcBaseEntity* storey, const IfcGeom::Element<real_t>* 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<real_t>* o) {
return idElement(storey) + "-" + GeometrySerializer::object_id(o);
}
};
#endif
@@ -0,0 +1,187 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include "WavefrontObjSerializer.h"
#include "../ifcgeom_schema_agnostic/IfcGeomRenderStyles.h"
#include "../ifcparse/utils.h"
#include <boost/lexical_cast.hpp>
#include <iomanip>
WaveFrontOBJSerializer::WaveFrontOBJSerializer(const std::string& obj_filename, const std::string& mtl_filename, const SerializerSettings& settings)
: GeometrySerializer(settings)
, mtl_filename(mtl_filename)
, obj_stream(IfcUtil::path::from_utf8(obj_filename).c_str())
, mtl_stream(IfcUtil::path::from_utf8(mtl_filename).c_str())
, vcount_total(1)
{
obj_stream << std::setprecision(settings.precision);
mtl_stream << std::setprecision(settings.precision);
}
bool WaveFrontOBJSerializer::ready() {
return obj_stream.is_open() && mtl_stream.is_open();
}
void WaveFrontOBJSerializer::writeHeader() {
obj_stream << "# File generated by IfcOpenShell " << IFCOPENSHELL_VERSION << "\n";
#ifdef WIN32
const char dir_separator = '\\';
#else
const char dir_separator = '/';
#endif
std::string mtl_basename = mtl_filename;
std::string::size_type slash = mtl_basename.find_last_of(dir_separator);
if (slash != std::string::npos) {
mtl_basename = mtl_basename.substr(slash+1);
}
obj_stream << "mtllib " << mtl_basename << "\n";
mtl_stream << "# File generated by IfcOpenShell " << IFCOPENSHELL_VERSION << "\n";
}
void WaveFrontOBJSerializer::writeMaterial(const IfcGeom::Material& style)
{
std::string material_name = (settings().get(SerializerSettings::USE_MATERIAL_NAMES)
? style.original_name() : style.name());
IfcUtil::sanitate_material_name(material_name);
mtl_stream << "newmtl " << material_name << "\n";
if (style.hasDiffuse()) {
const double* diffuse = style.diffuse();
mtl_stream << "Kd " << diffuse[0] << " " << diffuse[1] << " " << diffuse[2] << "\n";
}
if (style.hasSpecular()) {
const double* specular = style.specular();
mtl_stream << "Ks " << specular[0] << " " << specular[1] << " " << specular[2] << "\n";
}
if (style.hasSpecularity()) {
mtl_stream << "Ns " << style.specularity() << "\n";
}
if (style.hasTransparency()) {
const double transparency = 1.0 - style.transparency();
if (transparency < 1) {
mtl_stream << "d " << transparency << "\n";
}
}
}
void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement<real_t>* o)
{
obj_stream << "g " << object_id(o) << "\n";
obj_stream << "s 1" << "\n";
const IfcGeom::Representation::Triangulation<real_t>& mesh = o->geometry();
const int vcount = (int)mesh.verts().size() / 3;
for ( std::vector<real_t>::const_iterator it = mesh.verts().begin(); it != mesh.verts().end(); ) {
const real_t x = *(it++);
const real_t y = *(it++);
const real_t z = *(it++);
obj_stream << "v " << x << " " << y << " " << z << "\n";
}
for ( std::vector<real_t>::const_iterator it = mesh.normals().begin(); it != mesh.normals().end(); ) {
const real_t x = *(it++);
const real_t y = *(it++);
const real_t z = *(it++);
obj_stream << "vn " << x << " " << y << " " << z << "\n";
}
for (std::vector<real_t>::const_iterator it = mesh.uvs().begin(); it != mesh.uvs().end();) {
const real_t u = *it++;
const real_t v = *it++;
obj_stream << "vt " << u << " " << v << "\n";
}
int previous_material_id = -2;
std::vector<int>::const_iterator material_it = mesh.material_ids().begin();
const bool has_uvs = !mesh.uvs().empty();
const bool has_normals = !mesh.normals().empty();
for ( std::vector<int>::const_iterator it = mesh.faces().begin(); it != mesh.faces().end(); ) {
const int material_id = *(material_it++);
if (material_id != previous_material_id) {
const IfcGeom::Material& material = mesh.materials()[material_id];
std::string material_name = (settings().get(SerializerSettings::USE_MATERIAL_NAMES)
? material.original_name() : material.name());
IfcUtil::sanitate_material_name(material_name);
obj_stream << "usemtl " << material_name << "\n";
if (materials.find(material_name) == materials.end()) {
writeMaterial(material);
materials.insert(material_name);
}
previous_material_id = material_id;
}
const int v1 = *(it++)+vcount_total;
const int v2 = *(it++)+vcount_total;
const int v3 = *(it++)+vcount_total;
if (has_normals && has_uvs) {
obj_stream << "f " << v1 << "/" << v1 << "/" << v1 << " "
<< v2 << "/" << v2 << "/" << v2 << " "
<< v3 << "/" << v3 << "/" << v3 << "\n";
} else if (has_normals) {
obj_stream << "f " << v1 << "//" << v1 << " "
<< v2 << "//" << v2 << " "
<< v3 << "//" << v3 << "\n";
} else {
obj_stream << "f " << v1 << " " << v2 << " " << v3 << "\n";
}
}
std::set<int> faces_set (mesh.faces().begin(), mesh.faces().end());
const std::vector<int>& edges = mesh.edges();
for ( std::vector<int>::const_iterator it = edges.begin(); it != edges.end(); ) {
const int i1 = *(it++);
const int i2 = *(it++);
if (faces_set.find(i1) != faces_set.end() || faces_set.find(i2) != faces_set.end()) {
continue;
}
const int material_id = *(material_it++);
if (material_id != previous_material_id) {
const IfcGeom::Material& material = mesh.materials()[material_id];
std::string material_name = (settings().get(SerializerSettings::USE_MATERIAL_NAMES)
? material.original_name() : material.name());
IfcUtil::sanitate_material_name(material_name);
obj_stream << "usemtl " << material_name << "\n";
if (materials.find(material_name) == materials.end()) {
writeMaterial(material);
materials.insert(material_name);
}
previous_material_id = material_id;
}
const int v1 = i1 + vcount_total;
const int v2 = i2 + vcount_total;
obj_stream << "l " << v1 << " " << v2 << "\n";
}
vcount_total += vcount;
}
@@ -0,0 +1,51 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef WAVEFRONTOBJSERIALIZER_H
#define WAVEFRONTOBJSERIALIZER_H
#include <set>
#include <string>
#include <fstream>
#include "../serializers/GeometrySerializer.h"
// http://people.sc.fsu.edu/~jburkardt/txt/obj_format.txt
class WaveFrontOBJSerializer : public GeometrySerializer {
private:
const std::string mtl_filename;
std::ofstream obj_stream;
std::ofstream mtl_stream;
unsigned int vcount_total;
std::set<std::string> materials;
public:
WaveFrontOBJSerializer(const std::string& obj_filename, const std::string& mtl_filename, const SerializerSettings& settings);
virtual ~WaveFrontOBJSerializer() {}
bool ready();
void writeHeader();
void writeMaterial(const IfcGeom::Material& style);
void write(const IfcGeom::TriangulationElement<real_t>* o);
void write(const IfcGeom::BRepElement<real_t>* /*o*/) {}
void finalize() {}
bool isTesselated() const { return true; }
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
void setFile(IfcParse::IfcFile*) {}
};
#endif
+41
View File
@@ -0,0 +1,41 @@
#include "XmlSerializer.h"
extern void init_XmlSerializerIfc2x3(XmlSerializerFactory::Factory*);
extern void init_XmlSerializerIfc4(XmlSerializerFactory::Factory*);
extern void init_XmlSerializerIfc4x1(XmlSerializerFactory::Factory*);
extern void init_XmlSerializerIfc4x2(XmlSerializerFactory::Factory*);
extern void init_XmlSerializerIfc4x3_rc1(XmlSerializerFactory::Factory*);
XmlSerializerFactory::Factory::Factory() {
init_XmlSerializerIfc2x3(this);
init_XmlSerializerIfc4(this);
init_XmlSerializerIfc4x1(this);
init_XmlSerializerIfc4x2(this);
init_XmlSerializerIfc4x3_rc1(this);
}
void XmlSerializerFactory::Factory::bind(const std::string& schema_name, fn f) {
const std::string schema_name_lower = boost::to_lower_copy(schema_name);
this->insert(std::make_pair(schema_name_lower, f));
}
XmlSerializer* XmlSerializerFactory::Factory::construct(const std::string& schema_name, IfcParse::IfcFile* file, std::string xml_filename) {
const std::string schema_name_lower = boost::to_lower_copy(schema_name);
typename std::map<std::string, fn>::const_iterator it;
it = this->find(schema_name_lower);
if (it == this->end()) {
throw IfcParse::IfcException("No XML serializer registered for " + schema_name);
}
return it->second(file, xml_filename);
}
XmlSerializer::XmlSerializer(IfcParse::IfcFile* file, const std::string& xml_filename) {
if (file) {
implementation_ = XmlSerializerFactory::implementations().construct(file->schema()->name(), file, xml_filename);
}
}
XmlSerializerFactory::Factory& XmlSerializerFactory::implementations() {
static XmlSerializerFactory::Factory impl;
return impl;
}
+40
View File
@@ -0,0 +1,40 @@
#define SCHEMA_METHOD
#include "../serializers/Serializer.h"
#include "../ifcparse/IfcFile.h"
#include <boost/function.hpp>
#include <map>
class XmlSerializer : public Serializer {
private:
XmlSerializer* implementation_;
protected:
std::string xml_filename;
public:
XmlSerializer(IfcParse::IfcFile* file, const std::string& xml_filename);
virtual ~XmlSerializer() {}
bool ready() { return true; }
void writeHeader() {}
void finalize() { implementation_->finalize(); }
void setFile(IfcParse::IfcFile*) { throw IfcParse::IfcException("Should be supplied on construction"); }
};
struct XmlSerializerFactory {
typedef boost::function2<XmlSerializer*, IfcParse::IfcFile*, std::string> fn;
class Factory : public std::map<std::string, fn> {
public:
Factory();
void bind(const std::string& schema_name, fn);
XmlSerializer* construct(const std::string& schema_name, IfcParse::IfcFile*, std::string);
};
static Factory& implementations();
};
@@ -0,0 +1,579 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/version.hpp>
#include <boost/foreach.hpp>
#include "XmlSerializer.h"
#include <algorithm>
#include "../../ifcparse/IfcSIPrefix.h"
#include "../../ifcgeom/IfcGeom.h"
#include "../../ifcparse/utils.h"
using boost::property_tree::ptree;
#include "XmlSerializer.h"
namespace {
struct MAKE_TYPE_NAME(factory_t) {
XmlSerializer* operator()(IfcParse::IfcFile* file, const std::string& xml_filename) const {
MAKE_TYPE_NAME(XmlSerializer)* s = new MAKE_TYPE_NAME(XmlSerializer)(file, xml_filename);
s->setFile(file);
return s;
}
};
}
void MAKE_INIT_FN(XmlSerializer)(XmlSerializerFactory::Factory* mapping) {
static const std::string schema_name = STRINGIFY(IfcSchema);
MAKE_TYPE_NAME(factory_t) factory;
mapping->bind(schema_name, factory);
}
namespace {
// TODO: Make this a member of XmlSerializer?
std::map<std::string, std::string> MAKE_TYPE_NAME(argument_name_map);
// Format an IFC attribute and maybe returns as string. Only literal scalar
// values are converted. Things like entity instances and lists are omitted.
boost::optional<std::string> format_attribute(const Argument* argument, IfcUtil::ArgumentType argument_type, const std::string& argument_name) {
boost::optional<std::string> value;
// Hard-code lat-lon as it represents an array
// of integers best emitted as a single decimal
if (argument_name == "IfcSite.RefLatitude" ||
argument_name == "IfcSite.RefLongitude")
{
std::vector<int> angle = *argument;
double deg;
if (angle.size() >= 3) {
deg = angle[0] + angle[1] / 60. + angle[2] / 3600.;
int prec = 8;
if (angle.size() == 4) {
deg += angle[3] / (1000000. * 3600.);
prec = 14;
}
std::stringstream stream;
stream << std::setprecision(prec) << deg;
value = stream.str();
}
return value;
}
switch(argument_type) {
case IfcUtil::Argument_BOOL: {
const bool b = *argument;
value = b ? "true" : "false";
break; }
case IfcUtil::Argument_DOUBLE: {
const double d = *argument;
std::stringstream stream;
stream << d;
value = stream.str();
break; }
case IfcUtil::Argument_STRING:
case IfcUtil::Argument_ENUMERATION: {
value = static_cast<std::string>(*argument);
break; }
case IfcUtil::Argument_INT: {
const int v = *argument;
std::stringstream stream;
stream << v;
value = stream.str();
break; }
case IfcUtil::Argument_ENTITY_INSTANCE: {
IfcUtil::IfcBaseClass* e = *argument;
if (!e->declaration().as_entity()) {
IfcUtil::IfcBaseType* f = (IfcUtil::IfcBaseType*) e;
value = format_attribute(f->data().getArgument(0), f->data().getArgument(0)->type(), argument_name);
} else if (e->declaration().is(IfcSchema::IfcSIUnit::Class()) || e->declaration().is(IfcSchema::IfcConversionBasedUnit::Class())) {
// Some string concatenation to have a unit name as a XML attribute.
std::string unit_name;
if (e->declaration().is(IfcSchema::IfcSIUnit::Class())) {
IfcSchema::IfcSIUnit* unit = (IfcSchema::IfcSIUnit*) e;
unit_name = IfcSchema::IfcSIUnitName::ToString(unit->Name());
if (unit->hasPrefix()) {
unit_name = IfcSchema::IfcSIPrefix::ToString(unit->Prefix()) + unit_name;
}
} else {
IfcSchema::IfcConversionBasedUnit* unit = (IfcSchema::IfcConversionBasedUnit*) e;
unit_name = unit->Name();
}
value = unit_name;
} else if (e->declaration().is(IfcSchema::IfcLocalPlacement::Class())) {
IfcSchema::IfcLocalPlacement* placement = e->as<IfcSchema::IfcLocalPlacement>();
gp_Trsf trsf;
IfcGeom::MAKE_TYPE_NAME(Kernel) kernel;
if (kernel.convert(placement, trsf)) {
std::stringstream stream;
for (int i = 1; i < 5; ++i) {
for (int j = 1; j < 4; ++j) {
const double trsf_value = trsf.Value(j, i);
stream << trsf_value << " ";
}
stream << ((i == 4) ? "1" : "0 ");
}
value = stream.str();
}
}
break; }
default:
break;
}
return value;
}
// Appends to a node with possibly existing attributes
ptree* format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& child, ptree& tree, bool as_link = false) {
const unsigned n = instance->declaration().attribute_count();
for (unsigned i = 0; i < n; ++i) {
try {
instance->data().getArgument(i);
} catch (const std::exception&) {
Logger::Error("Expected " + boost::lexical_cast<std::string>(n) + " attributes for:", instance);
break;
}
const Argument* argument = instance->data().getArgument(i);
if (argument->isNull()) continue;
std::string argument_name = instance->declaration().attribute_by_index(i)->name();
std::map<std::string, std::string>::const_iterator argument_name_it;
argument_name_it = MAKE_TYPE_NAME(argument_name_map).find(argument_name);
if (argument_name_it != MAKE_TYPE_NAME(argument_name_map).end()) {
argument_name = argument_name_it->second;
}
const IfcUtil::ArgumentType argument_type = instance->data().getArgument(i)->type();
const std::string qualified_name = instance->declaration().name() + "." + argument_name;
boost::optional<std::string> value;
try {
value = format_attribute(argument, argument_type, qualified_name);
} catch (const std::exception& e) {
Logger::Error(e);
} catch (const Standard_ConstructionError& e) {
Logger::Error(e.GetMessageString(), instance);
}
if (value) {
if (as_link) {
if (argument_name == "id") {
child.put("<xmlattr>.xlink:href", std::string("#") + *value);
}
} else {
std::stringstream stream;
stream << "<xmlattr>." << argument_name;
child.put(stream.str(), *value);
}
}
}
return &tree.add_child(instance->declaration().name(), child);
}
// Formats an entity instances as a ptree node, and insert into the DOM. Recurses
// over the entity attributes and writes them as xml attributes of the node.
ptree* format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& tree, bool as_link = false) {
ptree child;
return format_entity_instance(instance, child, tree, as_link);
}
std::string qualify_unrooted_instance(IfcUtil::IfcBaseClass* inst) {
return inst->declaration().name() + "_" + boost::lexical_cast<std::string>(inst->data().id());
}
// A function to be called recursively. Template specialization is used
// to descend into decomposition, containment and property relationships.
template <typename A>
ptree* descend(A* instance, ptree& tree, IfcUtil::IfcBaseClass* parent=nullptr) {
if (instance->declaration().is(IfcSchema::IfcObjectDefinition::Class())) {
return descend(instance->template as<IfcSchema::IfcObjectDefinition>(), tree, parent);
} else {
return format_entity_instance(instance, tree);
}
}
// Returns related entity instances using IFC's objectified relationship
// model. The second and third argument require a member function pointer.
template <typename T, typename U, typename V, typename F, typename G>
typename V::list::ptr get_related(T* t, F f, G g) {
typename U::list::ptr li = (*t.*f)()->template as<U>();
typename V::list::ptr acc(new typename V::list);
for (typename U::list::it it = li->begin(); it != li->end(); ++it) {
U* u = *it;
acc->push((*u.*g)()->template as<V>());
}
return acc;
}
// Descends into the tree by recursing into IfcRelContainedInSpatialStructure,
// IfcRelDecomposes, IfcRelDefinesByType, IfcRelDefinesByProperties relations.
template <>
ptree* descend(IfcSchema::IfcObjectDefinition* product, ptree& tree, IfcUtil::IfcBaseClass* parent) {
if (product->declaration().is(IfcSchema::IfcElement::Class())) {
auto voids = product->as<IfcSchema::IfcElement>()->FillsVoids();
if (voids && voids->size() == 1 && (*voids->begin())->RelatingOpeningElement() != parent) {
// Fills are placed under their corresponding opening, return early to avoid duplication.
return nullptr;
}
}
ptree& child = *format_entity_instance(product, tree);
if (product->declaration().is(IfcSchema::IfcOpeningElement::Class())) {
IfcSchema::IfcOpeningElement* opening = static_cast<IfcSchema::IfcOpeningElement*>(product);
IfcSchema::IfcElement::list::ptr fills = get_related<IfcSchema::IfcOpeningElement, IfcSchema::IfcRelFillsElement, IfcSchema::IfcElement>(
opening, &IfcSchema::IfcOpeningElement::HasFillings, &IfcSchema::IfcRelFillsElement::RelatedBuildingElement);
for (IfcSchema::IfcElement::list::it it = fills->begin(); it != fills->end(); ++it) {
descend(*it, child, product);
}
}
if (product->declaration().is(IfcSchema::IfcSpatialStructureElement::Class())) {
IfcSchema::IfcSpatialStructureElement* structure = (IfcSchema::IfcSpatialStructureElement*) product;
IfcSchema::IfcObjectDefinition::list::ptr elements = get_related
<IfcSchema::IfcSpatialStructureElement, IfcSchema::IfcRelContainedInSpatialStructure, IfcSchema::IfcObjectDefinition>
(structure, &IfcSchema::IfcSpatialStructureElement::ContainsElements, &IfcSchema::IfcRelContainedInSpatialStructure::RelatedElements);
for (IfcSchema::IfcObjectDefinition::list::it it = elements->begin(); it != elements->end(); ++it) {
descend(*it, child, product);
}
}
if (product->declaration().is(IfcSchema::IfcElement::Class())) {
IfcSchema::IfcElement* element = static_cast<IfcSchema::IfcElement*>(product);
IfcSchema::IfcOpeningElement::list::ptr openings = get_related<IfcSchema::IfcElement, IfcSchema::IfcRelVoidsElement, IfcSchema::IfcOpeningElement>(
element, &IfcSchema::IfcElement::HasOpenings, &IfcSchema::IfcRelVoidsElement::RelatedOpeningElement);
for (IfcSchema::IfcOpeningElement::list::it it = openings->begin(); it != openings->end(); ++it) {
descend(*it, child, product);
}
}
#ifdef SCHEMA_IfcRelDecomposes_HAS_RelatedObjects
IfcSchema::IfcObjectDefinition::list::ptr structures = get_related
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelDecomposes, IfcSchema::IfcObjectDefinition>
(product, &IfcSchema::IfcObjectDefinition::IsDecomposedBy, &IfcSchema::IfcRelDecomposes::RelatedObjects);
#else
IfcSchema::IfcObjectDefinition::list::ptr structures = get_related
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelAggregates, IfcSchema::IfcObjectDefinition>
(product, &IfcSchema::IfcObjectDefinition::IsDecomposedBy, &IfcSchema::IfcRelAggregates::RelatedObjects);
structures->push(get_related
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelNests, IfcSchema::IfcObjectDefinition>
(product, &IfcSchema::IfcObjectDefinition::IsNestedBy, &IfcSchema::IfcRelNests::RelatedObjects));
#endif
for (IfcSchema::IfcObjectDefinition::list::it it = structures->begin(); it != structures->end(); ++it) {
IfcSchema::IfcObjectDefinition* ob = *it;
descend(ob, child, product);
}
if (product->declaration().is(IfcSchema::IfcObject::Class())) {
IfcSchema::IfcObject* object = product->as<IfcSchema::IfcObject>();
IfcSchema::IfcPropertySetDefinition::list::ptr property_sets = get_related
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByProperties, IfcSchema::IfcPropertySetDefinition>
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByProperties::RelatingPropertyDefinition);
for (IfcSchema::IfcPropertySetDefinition::list::it it = property_sets->begin(); it != property_sets->end(); ++it) {
IfcSchema::IfcPropertySetDefinition* pset = *it;
if (pset->declaration().is(IfcSchema::IfcPropertySet::Class())) {
format_entity_instance(pset, child, true);
} else if (pset->declaration().is(IfcSchema::IfcElementQuantity::Class())) {
format_entity_instance(pset, child, true);
}
}
#ifdef SCHEMA_IfcObject_HAS_IsTypedBy
IfcSchema::IfcTypeObject::list::ptr types = get_related
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByType, IfcSchema::IfcTypeObject>
(object, &IfcSchema::IfcObject::IsTypedBy, &IfcSchema::IfcRelDefinesByType::RelatingType);
#else
IfcSchema::IfcTypeObject::list::ptr types = get_related
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByType, IfcSchema::IfcTypeObject>
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByType::RelatingType);
#endif
for (IfcSchema::IfcTypeObject::list::it it = types->begin(); it != types->end(); ++it) {
IfcSchema::IfcTypeObject* type = *it;
format_entity_instance(type, child, true);
}
}
if (product->declaration().is(IfcSchema::IfcProduct::Class())) {
std::map<std::string, IfcUtil::IfcBaseEntity*> layers = IfcGeom::Kernel::get_layers(product);
for (std::map<std::string, IfcUtil::IfcBaseEntity*>::const_iterator it = layers.begin(); it != layers.end(); ++it) {
// IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier) so use name as the ID.
// Note that the IfcPresentationLayerAssignment passed here doesn't really matter as as_link is true
// for the format_entity_instance() call.
ptree node;
node.put("<xmlattr>.xlink:href", "#" + it->first);
format_entity_instance(it->second, node, child, true);
}
IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations();
for (IfcSchema::IfcRelAssociates::list::it it = associations->begin(); it != associations->end(); ++it) {
if ((*it)->as<IfcSchema::IfcRelAssociatesMaterial>()) {
IfcSchema::IfcMaterialSelect* mat = (*it)->as<IfcSchema::IfcRelAssociatesMaterial>()->RelatingMaterial();
ptree node;
node.put("<xmlattr>.xlink:href", "#" + qualify_unrooted_instance(mat));
format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, child, true);
}
}
}
return &child;
}
// Format IfcProperty instances and insert into the DOM. IfcComplexProperties are flattened out.
void format_properties(IfcSchema::IfcProperty::list::ptr properties, ptree& node) {
for (IfcSchema::IfcProperty::list::it it = properties->begin(); it != properties->end(); ++it) {
IfcSchema::IfcProperty* p = *it;
if (p->declaration().is(IfcSchema::IfcComplexProperty::Class())) {
IfcSchema::IfcComplexProperty* complex = (IfcSchema::IfcComplexProperty*) p;
format_properties(complex->HasProperties(), node);
} else {
format_entity_instance(p, node);
}
}
}
// Format IfcElementQuantity instances and insert into the DOM.
void format_quantities(IfcSchema::IfcPhysicalQuantity::list::ptr quantities, ptree& node) {
for (IfcSchema::IfcPhysicalQuantity::list::it it = quantities->begin(); it != quantities->end(); ++it) {
IfcSchema::IfcPhysicalQuantity* p = *it;
ptree* node2 = format_entity_instance(p, node);
if (node2 && p->declaration().is(IfcSchema::IfcPhysicalComplexQuantity::Class())) {
IfcSchema::IfcPhysicalComplexQuantity* complex = (IfcSchema::IfcPhysicalComplexQuantity*)p;
format_quantities(complex->HasQuantities(), *node2);
}
}
}
} // ~unnamed namespace
void MAKE_TYPE_NAME(XmlSerializer)::finalize() {
MAKE_TYPE_NAME(argument_name_map).insert(std::make_pair("GlobalId", "id"));
IfcSchema::IfcProject::list::ptr projects = file->instances_by_type<IfcSchema::IfcProject>();
if (projects->size() != 1) {
Logger::Message(Logger::LOG_ERROR, "Expected a single IfcProject");
return;
}
IfcSchema::IfcProject* project = *projects->begin();
ptree root, header, units, decomposition, properties, quantities, types, layers, materials;
// Write the SPF header as XML nodes.
BOOST_FOREACH(const std::string& s, file->header().file_description().description()) {
header.add_child("file_description.description", ptree(s));
}
BOOST_FOREACH(const std::string& s, file->header().file_name().author()) {
header.add_child("file_name.author", ptree(s));
}
BOOST_FOREACH(const std::string& s, file->header().file_name().organization()) {
header.add_child("file_name.organization", ptree(s));
}
BOOST_FOREACH(const std::string& s, file->header().file_schema().schema_identifiers()) {
header.add_child("file_schema.schema_identifiers", ptree(s));
}
try {
header.put("file_description.implementation_level", file->header().file_description().implementation_level());
}
catch (const IfcParse::IfcException& ex) {
std::stringstream ss;
ss << "Failed to get ifc file header file_description implementation_level, error: '" << ex.what() << "'";
Logger::Message(Logger::LOG_ERROR, ss.str());
}
try {
header.put("file_name.name", file->header().file_name().name());
}
catch (const IfcParse::IfcException& ex) {
std::stringstream ss;
ss << "Failed to get ifc file header file_name name, error: '" << ex.what() << "'";
Logger::Message(Logger::LOG_ERROR, ss.str());
}
try {
header.put("file_name.time_stamp", file->header().file_name().time_stamp());
}
catch (const IfcParse::IfcException& ex) {
std::stringstream ss;
ss << "Failed to get ifc file header file_name time_stamp, error: '" << ex.what() << "'";
Logger::Message(Logger::LOG_ERROR, ss.str());
}
try {
header.put("file_name.preprocessor_version", file->header().file_name().preprocessor_version());
}
catch (const IfcParse::IfcException& ex) {
std::stringstream ss;
ss << "Failed to get ifc file header file_name preprocessor_version, error: '" << ex.what() << "'";
Logger::Message(Logger::LOG_ERROR, ss.str());
}
try {
header.put("file_name.originating_system", file->header().file_name().originating_system());
}
catch (const IfcParse::IfcException& ex) {
std::stringstream ss;
ss << "Failed to get ifc file header file_name originating_system, error: '" << ex.what() << "'";
Logger::Message(Logger::LOG_ERROR, ss.str());
}
try {
header.put("file_name.authorization", file->header().file_name().authorization());
}
catch (const IfcParse::IfcException& ex) {
std::stringstream ss;
ss << "Failed to get ifc file header file_name authorization, error: '" << ex.what() << "'";
Logger::Message(Logger::LOG_ERROR, ss.str());
}
// Descend into the decomposition structure of the IFC file.
descend(project, decomposition);
// Write all property sets and values as XML nodes.
IfcSchema::IfcPropertySet::list::ptr psets = file->instances_by_type<IfcSchema::IfcPropertySet>();
for (IfcSchema::IfcPropertySet::list::it it = psets->begin(); it != psets->end(); ++it) {
IfcSchema::IfcPropertySet* pset = *it;
ptree* node = format_entity_instance(pset, properties);
if (node) {
format_properties(pset->HasProperties(), *node);
}
}
// Write all quantities and values as XML nodes.
IfcSchema::IfcElementQuantity::list::ptr qtosets = file->instances_by_type<IfcSchema::IfcElementQuantity>();
for (IfcSchema::IfcElementQuantity::list::it it = qtosets->begin(); it != qtosets->end(); ++it) {
IfcSchema::IfcElementQuantity* qto = *it;
ptree* node = format_entity_instance(qto, quantities);
if (node) {
format_quantities(qto->Quantities(), *node);
}
}
// Write all type objects as XML nodes.
IfcSchema::IfcTypeObject::list::ptr type_objects = file->instances_by_type<IfcSchema::IfcTypeObject>();
for (IfcSchema::IfcTypeObject::list::it it = type_objects->begin(); it != type_objects->end(); ++it) {
IfcSchema::IfcTypeObject* type_object = *it;
ptree* node = descend(type_object, types);
if (node && type_object->hasHasPropertySets()) {
IfcSchema::IfcPropertySetDefinition::list::ptr property_sets = type_object->HasPropertySets();
for (IfcSchema::IfcPropertySetDefinition::list::it jt = property_sets->begin(); jt != property_sets->end(); ++jt) {
IfcSchema::IfcPropertySetDefinition* pset = *jt;
if (pset->declaration().is(IfcSchema::IfcPropertySet::Class())) {
format_entity_instance(pset, *node, true);
}
}
}
}
// Write all assigned units as XML nodes.
IfcEntityList::ptr unit_assignments = project->UnitsInContext()->Units();
for (IfcEntityList::it it = unit_assignments->begin(); it != unit_assignments->end(); ++it) {
if ((*it)->declaration().is(IfcSchema::IfcNamedUnit::Class())) {
IfcSchema::IfcNamedUnit* named_unit = (*it)->as<IfcSchema::IfcNamedUnit>();
ptree* node = format_entity_instance(named_unit, units);
if (node) {
node->put("<xmlattr>.SI_equivalent", IfcParse::get_SI_equivalent<IfcSchema>(named_unit));
}
} else if ((*it)->declaration().is(IfcSchema::IfcMonetaryUnit::Class())) {
format_entity_instance((*it)->as<IfcSchema::IfcMonetaryUnit>(), units);
}
}
// Layer assignments. IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier)
// so use names as the IDs and only insert those with unique names. In case of possible duplicate names/IDs
// the first IfcPresentationLayerAssignment occurrence takes precedence.
std::set<std::string> layer_names;
IfcSchema::IfcPresentationLayerAssignment::list::ptr layer_assignments = file->instances_by_type<IfcSchema::IfcPresentationLayerAssignment>();
for (IfcSchema::IfcPresentationLayerAssignment::list::it it = layer_assignments->begin(); it != layer_assignments->end(); ++it) {
const std::string& name = (*it)->Name();
if (layer_names.find(name) == layer_names.end()) {
layer_names.insert(name);
ptree node;
node.put("<xmlattr>.id", name);
format_entity_instance(*it, node, layers);
}
}
IfcSchema::IfcRelAssociatesMaterial::list::ptr materal_associations = file->instances_by_type<IfcSchema::IfcRelAssociatesMaterial>();
std::set<IfcSchema::IfcMaterialSelect*> emitted_materials;
for (IfcSchema::IfcRelAssociatesMaterial::list::it it = materal_associations->begin(); it != materal_associations->end(); ++it) {
IfcSchema::IfcMaterialSelect* mat = (**it).RelatingMaterial();
if (emitted_materials.find(mat) == emitted_materials.end()) {
emitted_materials.insert(mat);
ptree node;
node.put("<xmlattr>.id", qualify_unrooted_instance(mat));
if (mat->as<IfcSchema::IfcMaterialLayerSetUsage>() || mat->as<IfcSchema::IfcMaterialLayerSet>()) {
IfcSchema::IfcMaterialLayerSet* layerset = mat->as<IfcSchema::IfcMaterialLayerSet>();
if (!layerset) {
layerset = mat->as<IfcSchema::IfcMaterialLayerSetUsage>()->ForLayerSet();
}
if (layerset->hasLayerSetName()) {
node.put("<xmlattr>.LayerSetName", layerset->LayerSetName());
}
IfcSchema::IfcMaterialLayer::list::ptr ls = layerset->MaterialLayers();
for (IfcSchema::IfcMaterialLayer::list::it jt = ls->begin(); jt != ls->end(); ++jt) {
ptree subnode;
if ((*jt)->hasMaterial()) {
subnode.put("<xmlattr>.Name", (*jt)->Material()->Name());
}
format_entity_instance(*jt, subnode, node);
}
} else if (mat->as<IfcSchema::IfcMaterialList>()) {
IfcSchema::IfcMaterial::list::ptr mats = mat->as<IfcSchema::IfcMaterialList>()->Materials();
for (IfcSchema::IfcMaterial::list::it jt = mats->begin(); jt != mats->end(); ++jt) {
ptree subnode;
format_entity_instance(*jt, subnode, node);
}
}
format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, materials);
}
}
root.add_child("ifc.header", header);
root.add_child("ifc.units", units);
root.add_child("ifc.properties", properties);
root.add_child("ifc.quantities", quantities);
root.add_child("ifc.types", types);
root.add_child("ifc.layers", layers);
root.add_child("ifc.materials", materials);
root.add_child("ifc.decomposition", decomposition);
root.put("ifc.<xmlattr>.xmlns:xlink", "http://www.w3.org/1999/xlink");
#if BOOST_VERSION >= 105600
boost::property_tree::xml_writer_settings<ptree::key_type> settings = boost::property_tree::xml_writer_make_settings<ptree::key_type>('\t', 1);
#else
boost::property_tree::xml_writer_settings<char> settings('\t', 1);
#endif
std::ofstream f(IfcUtil::path::from_utf8(xml_filename).c_str());
boost::property_tree::write_xml(f, root, settings);
}
@@ -0,0 +1,45 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef XMLSERIALIZERIMPL_H
#define XMLSERIALIZERIMPL_H
#include "../../ifcparse/macros.h"
#include "../../serializers/XmlSerializer.h"
#define INCLUDE_PARENT_PARENT_DIR(x) STRINGIFY(../../ifcparse/x.h)
#include INCLUDE_PARENT_PARENT_DIR(IfcSchema)
class MAKE_TYPE_NAME(XmlSerializer) : public XmlSerializer {
private:
IfcParse::IfcFile* file;
public:
MAKE_TYPE_NAME(XmlSerializer)(IfcParse::IfcFile* file, const std::string& xml_filename)
: XmlSerializer(0, "")
{
this->file = file;
this->xml_filename = xml_filename;
}
void finalize();
void setFile(IfcParse::IfcFile*) {}
};
#endif
+43
View File
@@ -0,0 +1,43 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include <set>
#include <iostream>
#include "../serializers/util.h"
using namespace util;
boost::shared_ptr<string_buffer::string_item> string_buffer::add(const std::string& s) {
boost::shared_ptr<string_item> i = boost::shared_ptr<string_item>(new string_item(s));
items.push_back(i);
return i;
}
boost::shared_ptr<string_buffer::float_item> string_buffer::add(const double& d) {
boost::shared_ptr<float_item> i = boost::shared_ptr<float_item>(new float_item(d));
items.push_back(i);
return i;
}
std::string string_buffer::str() const {
std::stringstream ss;
for (std::vector< boost::shared_ptr<item> >::const_iterator it = items.begin(); it != items.end(); ++it) {
ss << (**it).str();
}
return ss.str();
}
+67
View File
@@ -0,0 +1,67 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef IFCCONVERT_UTIL_H
#define IFCCONVERT_UTIL_H
#include <sstream>
#include <vector>
#include <boost/shared_ptr.hpp>
namespace util {
class string_buffer {
public:
class item {
public:
virtual std::string str() const = 0;
virtual ~item() {};
};
class string_item : public item {
std::string s;
public:
string_item(const std::string& s) : s(s) {}
void assign(const std::string& s) { this->s = s; }
const std::string& value() const { return s; }
std::string& value() { return s; }
std::string str() const { return s; }
virtual ~string_item() {};
};
class float_item : public item {
double d;
public:
float_item(const double& d) : d(d) {}
void assign(const double& d) { this->d = d; }
const double& value() const { return d; }
double& value() { return d; }
std::string str() const { std::stringstream ss; ss << d; return ss.str(); }
virtual ~float_item() {};
};
private:
std::vector< boost::shared_ptr<item> > items;
void clear();
void assign(const std::vector< boost::shared_ptr<item> >& other);
public:
boost::shared_ptr<string_item> add(const std::string& s);
boost::shared_ptr<float_item> add(const double& d);
std::string str() const;
};
}
#endif