Skip to content

Commit c6d5adf

Browse files
committed
Merge branch 'master' into performance_improvements
# Conflicts: # src/ifcexpressparser/implementation.py # src/ifcgeom/IfcGeomFunctions.cpp # src/ifcparse/Ifc4.cpp
2 parents 25a5135 + 1e262c8 commit c6d5adf

32 files changed

+1271
-371
lines changed

cmake/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ IF(MSVC)
5959
SET(Boost_USE_STATIC_RUNTIME ON)
6060
SET(Boost_USE_MULTITHREADED ON)
6161
ENDIF()
62-
FIND_PACKAGE(Boost REQUIRED COMPONENTS program_options)
62+
FIND_PACKAGE(Boost REQUIRED COMPONENTS system program_options regex thread date_time)
6363
MESSAGE(STATUS "Boost include files found in ${Boost_INCLUDE_DIRS}")
6464
MESSAGE(STATUS "Boost libraries found in ${Boost_LIBRARY_DIRS}")
6565

@@ -378,7 +378,7 @@ set(IFCPARSE_FILES ${IFCPARSE_CPP_FILES} ${IFCPARSE_H_FILES})
378378
ADD_LIBRARY(IfcParse STATIC ${IFCPARSE_FILES})
379379

380380
IF(UNICODE_SUPPORT)
381-
TARGET_LINK_LIBRARIES(IfcParse ${ICU_LIBRARIES})
381+
TARGET_LINK_LIBRARIES(IfcParse ${ICU_LIBRARIES} ${Boost_LIBRARIES})
382382
ENDIF()
383383

384384
# IfcGeom

nix/build-all.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ set -e
3737
PROJECT_NAME=IfcOpenShell
3838
OCE_VERSION=0.16
3939
PYTHON_VERSIONS=(2.7.10 3.2.6 3.3.6 3.4.4 3.5.1)
40-
BOOST_VERSION=1.55.0
40+
BOOST_VERSION=1.59.0
4141
PCRE_VERSION=8.38
4242
LIBXML_VERSION=2.9.3
4343
CMAKE_VERSION=3.4.1
@@ -277,14 +277,14 @@ build_dependency pcre-$PCRE_VERSION autoconf "--disable-shared" ftp://ftp.csx.ca
277277
build_dependency swig autoconf "--with-pcre-prefix=$DEPS_DIR/install/pcre-$PCRE_VERSION" https://github.com/swig/swig.git swig git_clone rel-3.0.8
278278

279279
build_dependency oce-$OCE_VERSION cmake "-DOCE_DISABLE_TKSERVICE_FONT=ON -DOCE_TESTING=OFF -DOCE_BUILD_SHARED_LIB=OFF -DOCE_DISABLE_X11=ON -DOCE_VISUALISATION=OFF -DOCE_OCAF=OFF -DOCE_INSTALL_PREFIX=$DEPS_DIR/install/oce-$OCE_VERSION/" https://github.com/tpaviot/oce/archive/ OCE-$OCE_VERSION.tar.gz download
280-
build_dependency libxml2-$LIBXML_VERSION autoconf "--without-python --disable-shared --without-zlib --without-iconv" ftp://xmlsoft.org/libxml2/ libxml2-$LIBXML_VERSION.tar.gz download
280+
build_dependency libxml2-$LIBXML_VERSION autoconf "--without-python --disable-shared --without-zlib --without-iconv --without-lzma" ftp://xmlsoft.org/libxml2/ libxml2-$LIBXML_VERSION.tar.gz download
281281
build_dependency OpenCOLLADA cmake "
282282
-DLIBXML2_INCLUDE_DIR=$DEPS_DIR/install/libxml2-$LIBXML_VERSION/include/libxml2
283283
-DLIBXML2_LIBRARIES=$DEPS_DIR/install/libxml2-$LIBXML_VERSION/lib/libxml2.a
284284
-DPCRE_INCLUDE_DIR=$DEPS_DIR/install/pcre-$PCRE_VERSION/include
285285
-DPCRE_PCREPOSIX_LIBRARY=$DEPS_DIR/install/pcre-$PCRE_VERSION/lib/libpcreposix.a
286286
-DPCRE_PCRE_LIBRARY=$DEPS_DIR/install/pcre-$PCRE_VERSION/lib/libpcre.a
287-
-DCMAKE_INSTALL_PREFIX=$DEPS_DIR/install/OpenCOLLADA/" https://github.com/KhronosGroup/OpenCOLLADA.git OpenCOLLADA git_clone
287+
-DCMAKE_INSTALL_PREFIX=$DEPS_DIR/install/OpenCOLLADA/" https://github.com/KhronosGroup/OpenCOLLADA.git OpenCOLLADA git_clone $OPENCOLLADA_COMMIT
288288

289289
# Python should not be built with -fvisibility=hidden, from experience that introduces segfaults
290290

@@ -306,7 +306,7 @@ done
306306
export CXXFLAGS=$OLD_CXX_FLAGS
307307
export CFLAGS=$OLD_C_FLAGS
308308

309-
build_dependency boost-$BOOST_VERSION bjam "--stagedir=$DEPS_DIR/install/boost-$BOOST_VERSION --with-program_options link=static $BOOST_ADDRESS_MODEL cxxflags=\"$CXXFLAGS\" linkflags=\"$LDFLAGS\" stage" http://downloads.sourceforge.net/project/boost/boost/$BOOST_VERSION/ boost_$BOOST_VERSION_UNDERSCORE.tar.bz2 download
309+
build_dependency boost-$BOOST_VERSION bjam "--stagedir=$DEPS_DIR/install/boost-$BOOST_VERSION --with-system --with-program_options --with-regex --with-thread --with-date_time link=static $BOOST_ADDRESS_MODEL cxxflags=\"$CXXFLAGS\" linkflags=\"$LDFLAGS\" stage" http://downloads.sourceforge.net/project/boost/boost/$BOOST_VERSION/ boost_$BOOST_VERSION_UNDERSCORE.tar.bz2 download
310310

311311
build_dependency icu-$ICU_VERSION icu "--enable-static --disable-shared" http://download.icu-project.org/files/icu4c/$ICU_VERSION/ icu4c-${ICU_VERSION_UNDERSCORE}-src.tgz download
312312

src/ifcconvert/IfcConvert.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -378,12 +378,13 @@ int main(int argc, char** argv) {
378378

379379
GeometrySerializer* serializer;
380380
if (output_extension == ".obj") {
381-
const std::string mtl_temp_filename = change_extension(output_filename, "mtl") + TEMP_FILE_EXTENSION;
381+
// Do not use temp file for MTL as it's such a small file.
382+
const std::string mtl_filename = change_extension(output_filename, "mtl");
382383
if (!use_world_coords) {
383384
Logger::Message(Logger::LOG_NOTICE, "Using world coords when writing WaveFront OBJ files");
384385
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true);
385386
}
386-
serializer = new WaveFrontOBJSerializer(output_temp_filename, mtl_temp_filename, settings);
387+
serializer = new WaveFrontOBJSerializer(output_temp_filename, mtl_filename, settings);
387388
#ifdef WITH_OPENCOLLADA
388389
} else if (output_extension == ".dae") {
389390
serializer = new ColladaSerializer(output_temp_filename, settings);
@@ -506,12 +507,11 @@ int main(int argc, char** argv) {
506507
serializer->finalize();
507508
delete serializer;
508509

509-
rename_file(output_temp_filename, output_filename);
510-
511-
if (output_extension == ".obj") {
512-
std::string mtl_filename = change_extension(output_filename, "mtl");
513-
std::string mtl_tmp_filename = mtl_filename + TEMP_FILE_EXTENSION;
514-
rename_file(mtl_tmp_filename, mtl_filename);
510+
// Renaming might fail (e.g. maybe the existing file was open in a viewer application)
511+
// Do not remove the temp file as user can salvage the conversion result from it.
512+
bool successful = rename_file(output_temp_filename, output_filename);
513+
if (!successful) {
514+
Logger::Message(Logger::LOG_ERROR, "Unable to write output file '" + output_filename + "");
515515
}
516516

517517
write_log();
@@ -535,7 +535,7 @@ int main(int argc, char** argv) {
535535
}
536536
Logger::Status(msg.str());
537537

538-
return 0;
538+
return successful ? 0 : 1;
539539
}
540540

541541
void write_log() {

src/ifcexpressparser/bootstrap.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,14 @@
1919

2020
import sys
2121
import string
22+
import operator
23+
import itertools
24+
2225
from pyparsing import *
2326

27+
try: from functools import reduce
28+
except: pass
29+
2430
class Expression:
2531
def __init__(self, contents):
2632
self.contents = contents[0]
@@ -56,12 +62,12 @@ def __repr__(self):
5662
class Terminal:
5763
def __init__(self, contents):
5864
self.contents = contents[0]
59-
def __repr__(self):
6065
s = self.contents
61-
is_keyword = len(s) >= 4 and s[0::len(s)-1] == '""' and \
66+
self.is_keyword = len(s) >= 4 and s[0::len(s)-1] == '""' and \
6267
all(c in alphanums+"_" for c in s[1:-1])
63-
ty = "CaselessKeyword" if is_keyword else "CaselessLiteral"
64-
return "%s(%s)" % (ty, s)
68+
def __repr__(self):
69+
ty = "CaselessKeyword" if self.is_keyword else "CaselessLiteral"
70+
return "%s(%s)" % (ty, self.contents)
6571

6672

6773
LPAREN = Suppress("(")
@@ -94,16 +100,16 @@ def __repr__(self):
94100

95101
express = grammar.parseFile(sys.argv[1])
96102

97-
def find_keywords(expr, li = None):
103+
def find_bytype(expr, ty, li = None):
98104
if li is None: li = []
99105
if isinstance(expr, Term):
100106
expr = expr.contents
101-
if isinstance(expr, Keyword):
102-
li.append(repr(expr))
103-
return li
107+
if isinstance(expr, ty):
108+
li.append(expr)
109+
return set(li)
104110
elif isinstance(expr, Expression):
105111
for term in expr:
106-
find_keywords(term, li)
112+
find_bytype(term, ty, li)
107113
return set(li)
108114

109115
actions = {
@@ -122,25 +128,31 @@ def find_keywords(expr, li = None):
122128
'inverse_attr' : "lambda t: InverseAttribute(t)",
123129
'bound_spec' : "lambda t: BoundSpecification(t)",
124130
'explicit_attr' : "lambda t: ExplicitAttribute(t)",
131+
'width_spec' : "lambda t: WidthSpec(t)",
132+
'string_type' : "lambda t: StringType(t)",
125133
}
126134

127135
to_emit = set(id for id, expr in express)
128136
emitted = set()
129137
to_combine = set(["simple_id"])
130138
to_ignore = set(["where_clause", "supertype_constraint", "unique_clause"])
131139
statements = []
140+
141+
terminals = reduce(lambda x,y: x | y, (find_bytype(e, Terminal) for id, e in express))
142+
keywords = list(filter(operator.attrgetter('is_keyword'), terminals))
143+
negated_keywords = map(lambda s: "~%s" % s, keywords)
132144

133145
while True:
134146
emitted_in_loop = set()
135147
for id, expr in express:
136-
kws = find_keywords(expr)
148+
kws = map(repr, find_bytype(expr, Keyword))
137149
found = [k in emitted for k in kws]
138150
if id in to_emit and all(found):
139151
emitted_in_loop.add(id)
140152
emitted.add(id)
141153
stmt = "(%s)" % expr
142154
if id in to_combine:
143-
stmt = "originalTextFor(Combine%s)" % stmt
155+
stmt = " + ".join(itertools.chain(negated_keywords, ("originalTextFor(Combine%s)" % stmt,)))
144156
if id in actions:
145157
stmt = "%s.setParseAction(%s)" % (stmt, actions[id])
146158
statements.append("%s = %s" % (id, stmt))

src/ifcexpressparser/documentation.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@
3131
import os
3232
import csv
3333

34+
from schema import OrderedCaseInsensitiveDict
35+
3436
try: from html.entities import entitydefs
3537
except: from htmlentitydefs import entitydefs
3638

3739
make_absolute = lambda fn: os.path.join(os.path.dirname(os.path.realpath(__file__)), fn)
3840

39-
name_to_oid = {}
41+
name_to_oid = OrderedCaseInsensitiveDict()
4042
oid_to_desc = {}
4143
oid_to_name = {}
4244
oid_to_pid = {}
@@ -59,7 +61,7 @@
5961
for oid, name, desc in csv.reader(f, delimiter=';', quotechar='"'):
6062
pid = oid_to_pid[oid]
6163
pname = oid_to_name[pid]
62-
name_to_oid[(pname, name)] = oid
64+
name_to_oid[".".join((pname, name))] = oid
6365
oid_to_desc[oid] = desc
6466

6567
def description(item):

src/ifcexpressparser/express.bnf

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# Taken from http://sourceforge.net/p/exp-engine/expresso/ci/master/tree/docs/iso-10303-11--2004.bnf
2-
31
ABS = "abs" .
42
ABSTRACT = "abstract" .
53
ACOS = "acos" .
@@ -202,7 +200,7 @@ constructed_types = enumeration_type | select_type .
202200
declaration = entity_decl | function_decl | procedure_decl | subtype_constraint_decl | type_decl .
203201
derived_attr = attribute_decl ":" parameter_type ":=" expression ";" .
204202
derive_clause = DERIVE derived_attr { derived_attr } .
205-
domain_rule = rule_label_id ":" expression .
203+
domain_rule = [ rule_label_id ":" ] expression .
206204
element = expression [ ":" repetition ] .
207205
entity_body = { explicit_attr } [ derive_clause ] [ inverse_clause ] [ unique_clause ] [ where_clause ] .
208206
entity_constructor = entity_ref "(" [ expression { "," expression } ] ")" .
@@ -334,7 +332,7 @@ type_label_id = simple_id .
334332
unary_op = "+" | "-" | NOT .
335333
underlying_type = constructed_types | concrete_types .
336334
unique_clause = UNIQUE unique_rule ";" { unique_rule ";" } .
337-
unique_rule = rule_label_id ":" referenced_attribute { "," referenced_attribute } .
335+
unique_rule = [ rule_label_id ":" ] referenced_attribute { "," referenced_attribute } .
338336
until_control = UNTIL logical_expression .
339337
use_clause = USE FROM schema_ref [ "(" named_type_or_rename { "," named_type_or_rename } ")" ] ";" .
340338
variable_id = simple_id .

src/ifcexpressparser/header.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,18 @@ def __init__(self, mapping):
4141
emitted_simpletypes = set()
4242
while len(emitted_simpletypes) < len(mapping.schema.simpletypes):
4343
for name, type in mapping.schema.simpletypes.items():
44-
if name in emitted_simpletypes: continue
44+
if name.lower() in emitted_simpletypes: continue
4545
type_str = mapping.make_type_string(mapping.flatten_type_string(type))
4646
attr_type = mapping.make_argument_type(type)
4747
superclass = mapping.simple_type_parent(name)
4848
if superclass is None:
4949
superclass = "IfcUtil::IfcBaseType"
50-
elif superclass not in emitted_simpletypes:
50+
elif superclass.lower() not in emitted_simpletypes:
5151
continue
52-
emitted_simpletypes.add(name)
52+
else:
53+
# Case normalize
54+
superclass = [k for k in mapping.schema.simpletypes.keys() if k.lower() == superclass.lower()][0]
55+
emitted_simpletypes.add(name.lower())
5356
write(templates.simpletype, name=name, type=type_str, attr_type=attr_type, superclass=superclass)
5457

5558
class_definitions = []
@@ -60,14 +63,14 @@ def __init__(self, mapping):
6063
emitted_entities = set()
6164
while len(emitted_entities) < len(mapping.schema.entities):
6265
for name, type in mapping.schema.entities.items():
63-
if name in emitted_entities: continue
64-
if len(type.supertypes) == 0 or set(type.supertypes) < emitted_entities:
66+
if name.lower() in emitted_entities: continue
67+
if len(type.supertypes) == 0 or set(map(str.lower, type.supertypes)) <= emitted_entities:
6568
attr_lines = []
6669
def write_method(attr):
6770
if attr.optional:
6871
attr_lines.append(templates.optional_attribute_description % (attr.name, name))
6972
attr_lines.append("bool has%s() const;"%(attr.name))
70-
attr_lines.extend(["/// %s"%d for d in documentation.description((name, attr.name))])
73+
attr_lines.extend(["/// %s"%d for d in documentation.description(".".join((name, attr.name)))])
7174
type_str = mapping.get_parameter_type(attr, allow_optional=False, allow_entities=False)
7275
if mapping.make_argument_type(attr) != "IfcUtil::Argument_UNKNOWN":
7376
attr_lines.append("%s %s() const;"%(type_str, attr.name))
@@ -88,7 +91,11 @@ def write_inverse(attr):
8891
inverse = "\n".join(["%s%s"%(' '*4, a) for a in inv_lines])
8992
if len(inverse): inverse += '\n'
9093

91-
supertypes = type.supertypes if len(type.supertypes) else ['IfcUtil::IfcBaseEntity']
94+
def case_norm(n):
95+
n = n.lower()
96+
return [k for k in mapping.schema.entities.keys() if k.lower() == n][0]
97+
98+
supertypes = map(case_norm, type.supertypes) if len(type.supertypes) else ['IfcUtil::IfcBaseEntity']
9299
superclass = ": %s "%(", ".join(["public %s"%c for c in supertypes]))
93100

94101
argument_count = mapping.argument_count(type)

src/ifcexpressparser/implementation.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import codegen
2121
import templates
2222

23+
from schema import OrderedCaseInsensitiveDict
24+
2325
class Implementation(codegen.Base):
2426
def __init__(self, mapping):
2527
enumeration_functions = []
@@ -132,7 +134,7 @@ def find_template(arg):
132134

133135
def get_attribute_index(entity, attr_name):
134136
related_entity = mapping.schema.entities[entity]
135-
return [a['name'] for a in mapping.get_assignable_arguments(related_entity, include_derived=True)].index(attr_name)
137+
return [a['name'].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True)].index(attr_name.lower())
136138

137139
inverse = [templates.const_function % {
138140
'class_name' : name,
@@ -155,7 +157,7 @@ def get_attribute_index(entity, attr_name):
155157
superclass = superclass
156158
)
157159

158-
selectable_simple_types = sorted(set(sum([b.values for a,b in mapping.schema.selects.items()], [])) & set(mapping.schema.types.keys()))
160+
selectable_simple_types = sorted(set(sum([b.values for a,b in mapping.schema.selects.items()], [])) & set(map(str, mapping.schema.types.keys())))
159161
schema_entity_statements += [templates.schema_entity_stmt%locals() for name, type in mapping.schema.simpletypes.items()]
160162
schema_entity_statements += [templates.schema_entity_stmt%locals() for name, type in mapping.schema.entities.items()]
161163

@@ -168,10 +170,10 @@ def get_attribute_index(entity, attr_name):
168170
'padding' : ' ' * (max_len - len(name))
169171
} for name in enumerable_types]
170172

171-
enumeration_index_by_str = dict((j,i) for i,j in enumerate(enumerable_types))
173+
enumeration_index_by_str = OrderedCaseInsensitiveDict((j,i) for i,j in enumerate(enumerable_types))
172174
def get_parent_id(s):
173175
e = mapping.schema.entities.get(s)
174-
if e and e.supertypes:
176+
if e and e.supertypes:
175177
return enumeration_index_by_str[e.supertypes[0]]
176178
else: return -1
177179

src/ifcexpressparser/latebound_implementation.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,10 @@ def __init__(self, mapping):
4141
})
4242

4343
emitted_entities = set()
44-
entities_to_emit = mapping.schema.entities.keys()
4544
while len(emitted_entities) < len(mapping.schema.entities):
4645
for name, type in mapping.schema.entities.items():
47-
if name in emitted_entities: continue
48-
if len(type.supertypes) == 0 or set(type.supertypes) < emitted_entities:
46+
if name.lower() in emitted_entities: continue
47+
if len(type.supertypes) == 0 or set(map(str.lower, type.supertypes)) <= emitted_entities:
4948
constructor_arguments = mapping.get_assignable_arguments(type, include_derived = True)
5049
entity_descriptor_attributes = []
5150
for arg in constructor_arguments:
@@ -61,8 +60,9 @@ def __init__(self, mapping):
6160
})
6261

6362
emitted_entities.add(name)
63+
6464
parent_statement = '0' if len(type.supertypes) != 1 else templates.entity_descriptor_parent % {
65-
'type' : type.supertypes[0]
65+
'type' : [k for k in mapping.schema.entities.keys() if k.lower() == type.supertypes[0].lower()][0]
6666
}
6767
entity_descriptors.append(templates.entity_descriptor % {
6868
'type' : name,
@@ -92,13 +92,13 @@ def __init__(self, mapping):
9292
if type.inverse:
9393
for attr in type.inverse.elements:
9494
related_entity = mapping.schema.entities[attr.entity]
95-
related_attrs = [a['name'] for a in mapping.get_assignable_arguments(related_entity, include_derived=True)]
95+
related_attrs = [a['name'].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True)]
9696

9797
inverse_implementations.append(templates.inverse_implementation % {
9898
'type' : name,
9999
'name' : attr.name,
100100
'related_type' : attr.entity,
101-
'index' : related_attrs.index(attr.attribute)
101+
'index' : related_attrs.index(attr.attribute.lower())
102102
})
103103

104104
self.str = templates.lb_implementation % {

src/ifcexpressparser/mapping.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def simple_type_parent(self, type):
5757
return None if str(parent) in self.express_to_cpp_typemapping else parent
5858

5959
def make_type_string(self, type):
60-
if isinstance(type, (str, nodes.BinaryType)):
60+
if isinstance(type, (str, nodes.BinaryType, nodes.StringType)):
6161
return self.express_to_cpp_typemapping.get(str(type), type)
6262
else:
6363
is_list = self.schema.is_entity(type.type)
@@ -89,6 +89,8 @@ def _make_argument_type(type):
8989
return "ENTITY_INSTANCE"
9090
elif isinstance(type, nodes.BinaryType):
9191
return "BINARY"
92+
elif isinstance(type, nodes.StringType):
93+
return "STRING"
9294
elif isinstance(type, nodes.EnumerationType):
9395
return "ENUMERATION"
9496
elif isinstance(type, nodes.AggregationType):
@@ -128,10 +130,11 @@ def get_parameter_type(self, attr, allow_optional, allow_entities, allow_pointer
128130
type_str = templates.untyped_list
129131
elif self.schema.is_simpletype(ty) or str(ty) in self.express_to_cpp_typemapping.values():
130132
tmpl = templates.nested_array_type if is_nested_list else templates.array_type
133+
bounds = (attr_type.bounds.lower, attr_type.bounds.upper) if attr_type.bounds else (-1, -1)
131134
type_str = tmpl % {
132135
'instance_type' : ty,
133-
'lower' : attr_type.bounds.lower,
134-
'upper' : attr_type.bounds.upper
136+
'lower' : bounds[0],
137+
'upper' : bounds[1]
135138
}
136139
else:
137140
tmpl = templates.list_list_type if is_nested_list else templates.list_type

0 commit comments

Comments
 (0)