Skip to content

Commit 9bf9f82

Browse files
committed
Format obj and dae output predictably based on rfc notation of GlobalId. As discussed in https://sourceforge.net/p/ifcopenshell/discussion/1782717/thread/cbb0c8ca/
1 parent b37a684 commit 9bf9f82

File tree

15 files changed

+175
-114
lines changed

15 files changed

+175
-114
lines changed

cmake/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ ADD_LIBRARY(IfcParse STATIC
129129
../src/ifcparse/Ifc4-latebound.cpp
130130
../src/ifcparse/Ifc4.cpp
131131
../src/ifcparse/IfcCharacterDecoder.cpp
132-
../src/ifcparse/IfcGuidHelper.cpp
132+
../src/ifcparse/IfcGlobalId.cpp
133133
../src/ifcparse/IfcHierarchyHelper.cpp
134134
../src/ifcparse/IfcLateBoundEntity.cpp
135135
../src/ifcparse/IfcParse.cpp
@@ -220,6 +220,7 @@ SET(include_files_parse
220220
../src/ifcparse/IfcEntityDescriptor.h
221221
../src/ifcparse/IfcException.h
222222
../src/ifcparse/IfcFile.h
223+
../src/ifcparse/IfcGlobalId.h
223224
../src/ifcparse/IfcHierarchyHelper.h
224225
../src/ifcparse/IfcLateBoundEntity.h
225226
../src/ifcparse/IfcParse.h

src/examples/IfcOpenHouse.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
// Some convenience typedefs and definitions.
4545
typedef std::string S;
46-
typedef IfcWrite::IfcGuidHelper guid;
46+
typedef IfcParse::IfcGlobalId guid;
4747
typedef std::pair<double, double> XY;
4848
boost::none_t const null = (static_cast<boost::none_t>(0));
4949

src/ifcconvert/ColladaSerializer.cpp

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ std::string collada_id(const std::string& s) {
2828
id.reserve(s.size());
2929
for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
3030
const std::string::value_type c = *it;
31-
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_')) {
31+
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || ( c == '-')) {
3232
id.push_back(c);
3333
}
3434
}
@@ -247,7 +247,7 @@ void ColladaSerializer::ColladaExporter::startDocument(const std::string& unit_n
247247
asset.add();
248248
}
249249

250-
void ColladaSerializer::ColladaExporter::write(const std::string& guid, const std::string& name, const std::string& type, const std::string& context, int obj_id, const std::vector<double>& matrix, const std::vector<double>& vertices, const std::vector<double>& normals, const std::vector<int>& faces, const std::vector<int>& edges, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& _materials) {
250+
void ColladaSerializer::ColladaExporter::write(const std::string& unique_id, const std::string& type, const std::vector<double>& matrix, const std::vector<double>& vertices, const std::vector<double>& normals, const std::vector<int>& faces, const std::vector<int>& edges, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& _materials) {
251251
std::vector<std::string> material_references;
252252
for (std::vector<IfcGeom::Material>::const_iterator it = _materials.begin(); it != _materials.end(); ++it) {
253253
const IfcGeom::Material& material = *it;
@@ -256,32 +256,21 @@ void ColladaSerializer::ColladaExporter::write(const std::string& guid, const st
256256
}
257257
material_references.push_back(collada_id(material.name()));
258258
}
259-
deferreds.push_back(DeferredObject(guid, name, type, context, obj_id, matrix, vertices, normals, faces, edges, material_ids, _materials, material_references));
260-
}
261-
262-
const std::string ColladaSerializer::ColladaExporter::DeferredObject::Name() const {
263-
std::stringstream ss;
264-
if (!this->name.empty()) {
265-
ss << this->obj_id << "_" << this->name;
266-
} else {
267-
ss << this->guid;
268-
}
269-
ss << "_" << this->context;
270-
return collada_id(ss.str());
259+
deferreds.push_back(DeferredObject(unique_id, type, matrix, vertices, normals, faces, edges, material_ids, _materials, material_references));
271260
}
272261

273262
void ColladaSerializer::ColladaExporter::endDocument() {
274263
// In fact due the XML based nature of Collada and its dependency on library nodes,
275264
// only at this point all objects are written to the stream.
276265
materials.write();
277266
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
278-
const std::string object_name = it->Name();
267+
const std::string object_name = it->unique_id + "-representation";
279268
geometries.write(object_name, it->type, it->vertices, it->normals, it->faces, it->edges, it->material_ids, it->materials);
280269
}
281270
geometries.close();
282271
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
283-
const std::string object_name = it->Name();
284-
scene.add(object_name + "-instance", object_name, object_name, it->material_references, it->matrix);
272+
const std::string object_name = it->unique_id;
273+
scene.add(object_name, object_name, object_name + "-representation", it->material_references, it->matrix);
285274
}
286275
scene.write();
287276
stream.endDocument();
@@ -297,7 +286,7 @@ void ColladaSerializer::writeHeader() {
297286

298287
void ColladaSerializer::write(const IfcGeom::TriangulationElement<double>* o) {
299288
const IfcGeom::Representation::Triangulation<double>& mesh = o->geometry();
300-
exporter.write(o->guid(), o->name(), o->type(), o->context(), o->id(), o->transformation().matrix().data(), mesh.verts(), mesh.normals(), mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials());
289+
exporter.write(o->unique_id(), o->type(), o->transformation().matrix().data(), mesh.verts(), mesh.normals(), mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials());
301290
}
302291

303292
void ColladaSerializer::finalize() {

src/ifcconvert/ColladaSerializer.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@ class ColladaSerializer : public GeometrySerializer
9494
};
9595
class DeferredObject {
9696
public:
97-
std::string guid, name, type, context;
98-
int obj_id;
97+
std::string unique_id, type;
9998
std::vector<double> matrix;
10099
std::vector<double> vertices;
101100
std::vector<double> normals;
@@ -104,14 +103,11 @@ class ColladaSerializer : public GeometrySerializer
104103
std::vector<int> material_ids;
105104
std::vector<IfcGeom::Material> materials;
106105
std::vector<std::string> material_references;
107-
DeferredObject(const std::string& guid, const std::string& name, const std::string& type, const std::string& context, int obj_id, const std::vector<double>& matrix, const std::vector<double>& vertices,
106+
DeferredObject(const std::string& unique_id, const std::string& type, const std::vector<double>& matrix, const std::vector<double>& vertices,
108107
const std::vector<double>& normals, const std::vector<int>& faces, const std::vector<int>& edges, const std::vector<int>& material_ids,
109108
const std::vector<IfcGeom::Material>& materials, const std::vector<std::string>& material_references)
110-
: guid(guid)
111-
, name(name)
109+
: unique_id(unique_id)
112110
, type(type)
113-
, context(context)
114-
, obj_id(obj_id)
115111
, matrix(matrix)
116112
, vertices(vertices)
117113
, normals(normals)
@@ -121,7 +117,6 @@ class ColladaSerializer : public GeometrySerializer
121117
, materials(materials)
122118
, material_references(material_references)
123119
{}
124-
const std::string Name() const;
125120
};
126121
COLLADABU::NativeString filename;
127122
COLLADASW::StreamWriter stream;
@@ -139,7 +134,7 @@ class ColladaSerializer : public GeometrySerializer
139134
std::vector<DeferredObject> deferreds;
140135
virtual ~ColladaExporter() {}
141136
void startDocument(const std::string& unit_name, float unit_magnitude);
142-
void write(const std::string& guid, const std::string& name, const std::string& type, const std::string& context, int obj_id, const std::vector<double>& matrix, const std::vector<double>& vertices, const std::vector<double>& normals, const std::vector<int>& faces, const std::vector<int>& edges, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials);
137+
void write(const std::string& unique_id, const std::string& type, const std::vector<double>& matrix, const std::vector<double>& vertices, const std::vector<double>& normals, const std::vector<int>& faces, const std::vector<int>& edges, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials);
143138
void endDocument();
144139
};
145140
ColladaExporter exporter;

src/ifcconvert/WavefrontObjSerializer.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,7 @@ void WaveFrontOBJSerializer::writeMaterial(const IfcGeom::Material& style) {
6767
}
6868
void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement<double>* o) {
6969

70-
std::string tmp = o->name().empty() ? o->guid() : o->name();
71-
72-
std::replace( tmp.begin(), tmp.end(), ' ', '_');
73-
std::string name = tmp;
74-
name += std::string("_") + o->context();
75-
76-
obj_stream << "g " << name << "\n";
70+
obj_stream << "g " << o->unique_id() << "\n";
7771
obj_stream << "s 1" << "\n";
7872

7973
const IfcGeom::Representation::Triangulation<double>& mesh = o->geometry();

src/ifcgeom/IfcGeomElement.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@
2020
#ifndef IFCGEOMELEMENT_H
2121
#define IFCGEOMELEMENT_H
2222

23+
#include <string>
24+
#include <algorithm>
25+
26+
#include "../ifcparse/IfcGlobalId.h"
27+
2328
#include "../ifcgeom/IfcGeomRepresentation.h"
2429
#include "../ifcgeom/IfcGeomIteratorSettings.h"
2530

26-
2731
namespace IfcGeom {
2832

2933
template <typename P>
@@ -73,6 +77,7 @@ namespace IfcGeom {
7377
std::string _type;
7478
std::string _guid;
7579
std::string _context;
80+
std::string _unique_id;
7681
Transformation<P> _transformation;
7782
public:
7883
int id() const { return _id; }
@@ -81,10 +86,21 @@ namespace IfcGeom {
8186
const std::string& type() const { return _type; }
8287
const std::string& guid() const { return _guid; }
8388
const std::string& context() const { return _context; }
89+
const std::string& unique_id() const { return _unique_id; }
8490
const Transformation<P>& transformation() const { return _transformation; }
8591
Element(const ElementSettings& settings, int id, int parent_id, const std::string& name, const std::string& type, const std::string& guid, const std::string& context, const gp_Trsf& trsf)
8692
: _id(id), _parent_id(parent_id), _name(name), _type(type), _guid(guid), _context(context), _transformation(settings, trsf)
87-
{}
93+
{
94+
std::ostringstream oss;
95+
oss << "product-" << IfcParse::IfcGlobalId(guid).formatted();
96+
if (!_context.empty()) {
97+
std::string ctx = _context;
98+
std::transform(ctx.begin(), ctx.end(), ctx.begin(), ::tolower);
99+
std::replace(ctx.begin(), ctx.end(), ' ', '-');
100+
oss << "-" << ctx;
101+
}
102+
_unique_id = oss.str();
103+
}
88104
virtual ~Element() {}
89105
};
90106

src/ifcgeom/IfcGeomRenderStyles.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,20 @@ namespace IfcGeom {
5757
boost::optional<double> specularity;
5858
public:
5959
SurfaceStyle() {
60-
this->name = "IfcSurfaceStyleShading";
60+
this->name = "surface-style";
6161
}
6262
SurfaceStyle(int id) : id(id) {
6363
std::stringstream sstr;
64-
sstr << "IfcSurfaceStyleShading_" << id;
64+
sstr << "surface-style-" << id;
6565
this->name = sstr.str();
6666
}
6767
SurfaceStyle(const std::string& name) : name(name) {}
6868
SurfaceStyle(int id, const std::string& name) : id(id) {
6969
std::stringstream sstr;
70-
sstr << id << "_" << name;
70+
std::string sanitized = name;
71+
std::transform(sanitized.begin(), sanitized.end(), sanitized.begin(), ::tolower);
72+
std::replace(sanitized.begin(), sanitized.end(), ' ', '-');
73+
sstr << "surface-style-" << id << "-" << sanitized;
7174
this->name = sstr.str();
7275
}
7376

src/ifcopenshell-python/ifcopenshell/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ def __getitem__(self, key):
108108
return entity_instance(self.wrapped_data.by_id(key))
109109
elif isinstance(key, str):
110110
return entity_instance(self.wrapped_data.by_guid(key))
111+
def by_id(self, id): return self[id]
112+
def by_guid(self, guid): return self[guid]
111113
def add(self, inst):
112114
inst.wrapped_data.this.disown()
113115
return entity_instance(self.wrapped_data.add(inst.wrapped_data))
Lines changed: 76 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -17,91 +17,112 @@
1717
* *
1818
********************************************************************************/
1919

20-
/********************************************************************************
21-
* *
22-
* Please consider this a placeholder for an actual GlobalId generation *
23-
* algorithm. A real implementation could for example be based on Boost::uuid *
24-
* *
25-
********************************************************************************/
26-
27-
#include <time.h>
28-
#include <stdlib.h>
20+
#include <vector>
2921
#include <algorithm>
3022

3123
#include <boost/uuid/uuid.hpp>
3224
#include <boost/uuid/uuid_generators.hpp>
3325
#include <boost/uuid/uuid_io.hpp>
3426

35-
#include "../ifcparse/IfcWrite.h"
27+
#include "../ifcparse/IfcGlobalId.h"
3628
#include "../ifcparse/IfcException.h"
3729

3830
static const char* chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
3931

4032
// Converts an unsigned integer into a base64 string of length l
4133
std::string base64(unsigned v, int l) {
42-
std::string r;
43-
r.reserve(l);
44-
while ( v ) {
45-
r.push_back(chars[v%64]);
46-
v /= 64;
47-
}
48-
while ( r.size() != l ) r.push_back('0');
49-
std::reverse(r.begin(),r.end());
50-
return r;
34+
std::string r;
35+
r.reserve(l);
36+
while ( v ) {
37+
r.push_back(chars[v%64]);
38+
v /= 64;
39+
}
40+
while ( r.size() != l ) r.push_back('0');
41+
std::reverse(r.begin(),r.end());
42+
return r;
5143
}
5244

5345
// Converts a base64 string into an unsigned integer
5446
unsigned from_base64(const std::string& s) {
55-
std::string::size_type zeros = s.find_first_not_of('0');
56-
unsigned r = 0;
57-
if ( zeros != std::string::npos )
58-
for ( std::string::const_iterator i = s.begin()+zeros; i != s.end(); ++ i ) {
59-
r *= 64;
60-
const char* c = strchr(chars,*i);
61-
if ( !c ) throw IfcParse::IfcException("Failed to decode GlobalId");
62-
r += (c-chars);
63-
}
64-
return r;
47+
std::string::size_type zeros = s.find_first_not_of('0');
48+
unsigned r = 0;
49+
if ( zeros != std::string::npos )
50+
for ( std::string::const_iterator i = s.begin()+zeros; i != s.end(); ++ i ) {
51+
r *= 64;
52+
const char* c = strchr(chars,*i);
53+
if ( !c ) throw IfcParse::IfcException("Failed to decode GlobalId");
54+
r += (c-chars);
55+
}
56+
return r;
6557
}
6658

6759
// Compresses the UUID byte array into a base64 representation
6860
std::string compress(unsigned char* v) {
69-
std::string r;
70-
r.reserve(22);
71-
r += base64(v[0],2);
72-
for ( unsigned i = 1; i < 16; i += 3 ) {
73-
r += base64((v[i]<<16) + (v[i+1]<<8) + v[i+2],4);
74-
}
75-
return r;
61+
std::string r;
62+
r.reserve(22);
63+
r += base64(v[0],2);
64+
for ( unsigned i = 1; i < 16; i += 3 ) {
65+
r += base64((v[i]<<16) + (v[i+1]<<8) + v[i+2],4);
66+
}
67+
return r;
7668
}
7769

7870
// Expands the base64 representation into a UUID byte array
7971
void expand(const std::string& s, std::vector<unsigned char>& v) {
80-
v.push_back(from_base64(s.substr(0,2)));
81-
for( unsigned i = 0; i < 5; ++i ) {
82-
unsigned d = from_base64(s.substr(2+4*i,4));
83-
for ( unsigned j = 0; j < 3; ++ j ) {
84-
v.push_back((d>>(8*(2-j))) % 256);
85-
}
86-
}
72+
v.push_back(from_base64(s.substr(0,2)));
73+
for( unsigned i = 0; i < 5; ++i ) {
74+
unsigned d = from_base64(s.substr(2+4*i,4));
75+
for ( unsigned j = 0; j < 3; ++ j ) {
76+
v.push_back((d>>(8*(2-j))) % 256);
77+
}
78+
}
8779
}
8880

8981
// A random number generator for the UUID
9082
static boost::uuids::basic_random_generator<boost::mt19937> gen;
9183

92-
IfcWrite::IfcGuidHelper::IfcGuidHelper() {
93-
boost::uuids::uuid u = gen();
94-
std::vector<unsigned char> v(u.size());
95-
std::copy(u.begin(), u.end(), v.begin());
96-
data = compress(&v[0]);
84+
IfcParse::IfcGlobalId::IfcGlobalId() {
85+
uuid_data = gen();
86+
std::vector<unsigned char> v(uuid_data.size());
87+
std::copy(uuid_data.begin(), uuid_data.end(), v.begin());
88+
string_data = compress(&v[0]);
89+
formatted_string = boost::uuids::to_string(uuid_data);
90+
91+
#ifndef NDEBUG
92+
std::vector<unsigned char> test_vector;
93+
expand(string_data, test_vector);
94+
boost::uuids::uuid test_uuid;
95+
std::copy(test_vector.begin(), test_vector.end(), test_uuid.begin());
96+
if (uuid_data != test_uuid) {
97+
throw IfcParse::IfcException("Internal error generating GlobalId");
98+
}
99+
#endif
100+
}
101+
102+
IfcParse::IfcGlobalId::IfcGlobalId(const std::string& s)
103+
: string_data(s)
104+
{
105+
std::vector<unsigned char> v;
106+
expand(string_data, v);
107+
std::copy(v.begin(), v.end(), uuid_data.begin());
108+
formatted_string = boost::uuids::to_string(uuid_data);
109+
110+
#ifndef NDEBUG
111+
const std::string test_string = compress(&uuid_data.data[0]);
112+
if (string_data != test_string) {
113+
throw IfcParse::IfcException("Internal error generating GlobalId");
114+
}
115+
#endif
116+
}
97117

98-
std::vector<unsigned char> v2;
99-
expand(data,v2);
100-
boost::uuids::uuid u2;
101-
std::copy(v2.begin(), v2.end(), u2.begin());
118+
IfcParse::IfcGlobalId::operator const std::string&() const {
119+
return string_data;
102120
}
103-
IfcWrite::IfcGuidHelper::operator std::string() const {
104-
return data;
121+
122+
IfcParse::IfcGlobalId::operator const boost::uuids::uuid&() const {
123+
return uuid_data;
105124
}
106125

107-
bool IfcWrite::IfcGuidHelper::seeded = false;
126+
const std::string& IfcParse::IfcGlobalId::formatted() const {
127+
return formatted_string;
128+
}

0 commit comments

Comments
 (0)