Skip to content

Commit 8851641

Browse files
committed
Add more idiomatic wrapper wrapper
1 parent 0a7b1dd commit 8851641

File tree

3 files changed

+47
-90
lines changed

3 files changed

+47
-90
lines changed

README.md

Lines changed: 39 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -8,70 +8,56 @@ http://IfcOpenShell.org
88
About this repository
99
=====================
1010

11-
This is an initiative to expose the functionality of IfcOpenShell for parsing and writing IFC files to a Python interface. The creation of a topological representation for building elements using Open Cascade is also wrapped by returning a string serialization of the Open Cascade TopoDS_Shape. Using SWIG a wrapper is obtained for a C++ Class which evaluates its Express attribute names and indices at runtime. The functions created by the SWIG wrapper closely resemble the C++ structure. The idea is to create an additional wrapper around these function in Python that is more idiomatic to Python.
11+
This is an initiative to expose the functionality of IfcOpenShell for parsing and writing IFC files to a Python interface. The creation of a topological representation for building elements using Open Cascade is also wrapped by returning a string serialization of the Open Cascade TopoDS_Shape. Using SWIG, a wrapper is obtained for a C++ Class which evaluates its Express attribute names at runtime. The functions created by the SWIG wrapper closely resemble the C++ structure. An additional Python wrapper around these functions is created that is more idiomatic to Python.
1212

1313
The following interactive session illustrates its use:
1414

15-
>>> import IfcImport
16-
>>> f = IfcImport.open(filename)
17-
>>> f.by_type("ifcwall")
18-
[#170=IfcWallStandardCase('0lJANQLbCEHPkU1Xq9w_bk',#13,'Wand-001',$,$,#167,#240,'2F4CA5DA-5653-0E45-9B-9E-061D09EBE96E'),
19-
#288=IfcWallStandardCase('1sFBHOg98nGQTuD1XjjAKK',#13,'Wand-002',$,$,#285,#358,'763CB458-A892-3141-A7-78-34186DB4A514')]
15+
>>> import ifc
16+
>>> f = ifc.open("Duplex_A_20110907_optimized.ifc")
17+
>>> f.by_type("ifcwall")[:2]
18+
[#91=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FL9r',#1,'Basic Wall:Interior - Partition (92mm Stud):144586',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5198,#18806,'144586'), #92=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FLIE',#1,'Basic Wall:Interior - Partition (92mm Stud):143921',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5206,#18805,'143921')]
2019
>>> wall = _[0]
21-
>>> brep_data = IfcImport.create_shape(wall)
22-
>>> wall.get_argument_count()
20+
>>> brep_data = ifc.create_shape(wall, ifc.SEW_SHELLS)
21+
>>> len(wall) # number of EXPRESS attributes
2322
8
24-
>>> wall.get_argument('GlobalId')
25-
'0lJANQLbCEHPkU1Xq9w_bk'
26-
>>> wall.get_argument_index('Name')
27-
2
28-
>>> wall.set_argument(2,"John")
29-
>>> wall.get_argument('Name')
30-
'John'
31-
>>> wall.get_argument_index("foo")
23+
>>> wall.GlobalId
24+
'2O2Fr$t4X7Zf8NOew3FL9r'
25+
>>> wall.Name = "My wall"
26+
>>> wall.NonExistingAttr
3227
Traceback (most recent call last):
3328
File "<stdin>", line 1, in <module>
34-
File "IfcImport.py", line 112, in get_argument_index
35-
def get_argument_index(self, *args): return _IfcImport.Entity_get_argument_index(self, *args)
36-
RuntimeError: Argument foo not found on IfcRoot
37-
>>> wall.set_argument(5,"Hi")
29+
File ".\ifc.py", line 14, in __getattr__
30+
except: raise AttributeError("entity instance of type '%s' has no attribute'%s'"%(self.wrapped_data.is_a(), name)) from None
31+
AttributeError: entity instance of type 'IfcWallStandardCase' has no attribute 'NonExistingAttr'
32+
>>> wall.GlobalId = 3
3833
Traceback (most recent call last):
3934
File "<stdin>", line 1, in <module>
40-
File "IfcImport.py", line 114, in set_argument
41-
def set_argument(self, *args): return _IfcImport.Entity_set_argument(self, *args)
42-
RuntimeError: STRING is not a valid type for 'ObjectPlacement'
43-
>>> e = IfcImport.Entity("IfcJohn")
44-
Traceback (most recent call last):
45-
File "<stdin>", line 1, in <module>
46-
File "IfcImport.py", line 105, in __init__
47-
this = _IfcImport.new_Entity(*args)
48-
RuntimeError: Unable to find find keyword in schema
49-
>>> pnt = IfcImport.Entity("ifccartesianpoint")
50-
[60478 refs]
51-
>>> pnt.set_argument(0,IfcImport.Doubles([0,0,0]))
52-
[60486 refs]
53-
>>> pnt
54-
=IfcCartesianPoint((0,0,0))
55-
>>> f.add(pnt)
35+
File ".\ifc.py", line 26, in __setattr__
36+
self[self.wrapped_data.get_argument_index(key)] = value
37+
File ".\ifc.py", line 30, in __setitem__
38+
self.wrapped_data.set_argument(idx, entity_instance.map_value(value))
39+
File ".\ifc_wrapper.py", line 118, in <lambda>
40+
set_argument = lambda self,x,y: self._set_argument(x) if y is None else self
41+
._set_argument(x,y)
42+
File ".\ifc_wrapper.py", line 114, in _set_argument
43+
def _set_argument(self, *args): return _ifc_wrapper.entity_instance__set_argument(self, *args)
44+
RuntimeError: INT is not a valid type for 'GlobalId'
45+
>>> f.createIfcCartesianPoint(Coordinates=(1.0,1.5,2.0))
46+
#27530=IfcCartesianPoint((1.,1.5,2.))
47+
>>> import uuid
48+
>>> ifc.guid.compress(uuid.uuid1().hex)
49+
'3x4C8Q_6qHuv$P$FYkANRX'
50+
>>> new_guid = _
51+
>>> owner_hist = f.by_type("IfcOwnerHistory")[0]
52+
>>> new_wall = f.createIfcWallStandardCase(new_guid, owner_hist, None, None, Tag='my_tag')
53+
>>> new_wall.ObjectType = ''
54+
>>> new_wall.ObjectPlacement = new_wall.Representation = None
55+
>>> f[92]
56+
#92=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FLIE',#1,'Basic Wall:Interior - Partition (92mm Stud):143921',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5206,#18805,'143921')
57+
>>> f['2O2Fr$t4X7Zf8NOew3FLIE']
58+
#92=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FLIE',#1,'Basic Wall:Interior - Partition (92mm Stud):143921',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5206,#18805,'143921')
5659
>>> f.write("out.ifc")
5760

58-
**TODO**: By creating an additional wrapper in Python around these methods some syntactic sugar can be added so one is able to say the following instead:
59-
60-
d = IfcDocument()
61-
my_wall = d.createIfcWallStandardCase("1$gyu8123...",Representation=my_repr)
62-
63-
With a document specifying a 'catch-all' function that creates the ifc entity based on the method being called, perhaps like so:
64-
65-
class IfcDocument:
66-
def create_entity(self,type,*args,**kwargs):
67-
e = new IfcEntity(type)
68-
# set all attributes in args and kwargs
69-
self.add(e)
70-
return e
71-
def __getattr__(self,attr):
72-
if attr[0:5] == 'create': return functools.partial(self.create_entity,attr[5:])
73-
...
74-
7561

7662
Compiling on Windows
7763
====================
@@ -120,33 +106,3 @@ If all worked out correctly you can now use IfcOpenShell. For example:
120106
$ unzip Munkerud_hus6_BE.zip
121107
$ ./IfcObj Munkerud_hus6_BE.ifc
122108
$ less Munkerud_hus6_BE.obj
123-
124-
125-
Ubuntu Notes
126-
============
127-
128-
The following sequence of commands has been tested successfully on Ubuntu 14.04.
129-
130-
OCE installation. This step worked OK with cmake 2.8.12.2, gcc 4.8.2, g++ 4.8.2. Also, it might take up to an hour to complete.
131-
132-
apt-get install git cmake gcc g++ libftgl-dev libtbb2 libtbb-dev libboost-all-dev
133-
cd /
134-
mkdir git && cd git
135-
git clone https://github.com/tpaviot/oce.git
136-
mkdir oceBuild && cd oceBuild
137-
cmake ../oce
138-
make -j4
139-
sudo make install
140-
141-
IfcOpenShell installation. This step worked OK with cmake 2.8.12.2, gcc 4.8.2, g++ 4.8.2.
142-
143-
apt-get install swig
144-
cd /git/
145-
git clone https://github.com/aothms/IfcOpenShell.git
146-
cd IfcOpenShell/cmake
147-
mkdir build && cd build
148-
cmake ../
149-
make -j4
150-
151-
Once done, IfcConvert is to be found in the build folder.
152-

src/ifcwrap/IfcPython.i

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@
3333
%ignore operator<<;
3434

3535
%rename("by_type") EntitiesByType;
36-
%rename("get_argument_count") getArgumentCount;
36+
%rename("__len__") getArgumentCount;
3737
%rename("get_argument_type") getArgumentType;
3838
%rename("get_argument_name") getArgumentName;
3939
%rename("get_argument_index") getArgumentIndex;
4040
%rename("_set_argument") setArgument;
4141
%rename("__repr__") toString;
42-
%rename("Entity") IfcUntypedEntity;
42+
%rename("entity_instance") IfcUntypedEntity;
43+
%rename("file") IfcFile;
4344

4445
%typemap(typecheck,precedence=SWIG_TYPECHECK_INTEGER) IfcEntities {
4546
$1 = (PySequence_Check($input) && !PyUnicode_Check($input) && !PyString_Check($input)) ? 1 : 0;
@@ -150,7 +151,7 @@
150151
}
151152
}
152153

153-
%module IfcImport %{
154+
%module ifc_wrapper %{
154155
#include "../ifcparse/IfcException.h"
155156
#include "Interface.h"
156157
using namespace IfcParse;
@@ -181,7 +182,7 @@
181182
}
182183

183184
namespace std {
184-
%template(Ints) vector<int>;
185-
%template(Doubles) vector<double>;
186-
%template(Strings) vector<string>;
185+
%template(ints) vector<int>;
186+
%template(doubles) vector<double>;
187+
%template(strings) vector<string>;
187188
};

src/ifcwrap/Interface.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ namespace IfcParse {
7676
std::string _author_email;
7777
std::string _author_organisation;
7878
public:
79-
IfcParse::IfcSpfStream* file;
79+
IfcParse::IfcSpfStream* stream;
8080
IfcParse::Tokens* tokens;
8181
bool Init(const std::string& fn);
8282
IfcEntities EntitiesByType(const std::string& t);

0 commit comments

Comments
 (0)