Repository navigation
Expand file tree
/
Copy pathexport_ifc.py
More file actions
114 lines (90 loc) · 4.24 KB
/
Copy pathexport_ifc.py
File metadata and controls
114 lines (90 loc) · 4.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env python3
"""
Export an IFC4 model from a web-UI layout JSON.
IFC is the open multi-trade interchange format (IfcProject -> IfcSite ->
IfcBuilding -> IfcBuildingStorey -> IfcWall). This reads the same JSON the
web UI exports and emits one IfcWall per module, grouped by level/storey, so
the model opens in any IFC viewer or hands off to a trade's software.
Today it exports the structural walls (all that the model carries). As trade
layers (electrical, plumbing, ...) gain real entities, they map onto their own
IFC classes (IfcFlowSegment, IfcOutlet, ...) under the same storeys.
Usage:
python3 export_ifc.py layout.json [out.ifc]
Requires: ifcopenshell (pip install ifcopenshell)
"""
import json
import sys
import numpy as np
import ifcopenshell
from ifcopenshell.api import run
IN_TO_MM = 25.4
HORIZONTAL = ("north", "south")
def wall_height_mm(module_id):
"""Wall height from the module id (matches the 3D builder's logic)."""
if "4x9" in module_id:
return 9 * 12 * IN_TO_MM
if "4x10" in module_id:
return 10 * 12 * IN_TO_MM
if "8.5" in module_id:
return 8.5 * 12 * IN_TO_MM
return 8 * 12 * IN_TO_MM
def main(in_path, out_path):
with open(in_path) as f:
data = json.load(f)
items = data.get("entities") or data.get("modules") or []
levels = {lv["id"]: lv for lv in data.get("levels", [])}
model = run("project.create_file", version="IFC4")
project = run("root.create_entity", model, ifc_class="IfcProject", name="Iconic CAD House")
# Length unit = millimetre, so JSON mm values pass through unchanged.
mm = run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI")
run("unit.assign_unit", model, units=[mm])
model_ctx = run("context.add_context", model, context_type="Model")
body = run("context.add_context", model, context_type="Model",
context_identifier="Body", target_view="MODEL_VIEW", parent=model_ctx)
site = run("root.create_entity", model, ifc_class="IfcSite", name="Site")
building = run("root.create_entity", model, ifc_class="IfcBuilding", name="Building")
run("aggregate.assign_object", model, products=[site], relating_object=project)
run("aggregate.assign_object", model, products=[building], relating_object=site)
# One storey per level (default a single Level 1).
storeys = {}
def storey_for(level_id):
if level_id not in storeys:
lv = levels.get(level_id, {})
st = run("root.create_entity", model, ifc_class="IfcBuildingStorey",
name=lv.get("name", level_id or "Level 1"))
run("aggregate.assign_object", model, products=[st], relating_object=building)
storeys[level_id] = (st, lv.get("z_mm", 0))
return storeys[level_id]
n = 0
for it in items:
module_id = it.get("module", "wall")
direction = it.get("direction", "north")
x = float(it.get("x_mm", 0))
y = float(it.get("y_mm", 0))
w = float(it.get("width_mm", 1219.2))
depth = float(it.get("depth_mm", 150.8125))
height = wall_height_mm(module_id)
storey, z = storey_for(it.get("level", "L1"))
wall = run("root.create_entity", model, ifc_class="IfcWall",
name=f"{module_id} [{it.get('id', n)}]")
run("spatial.assign_container", model, products=[wall], relating_structure=storey)
rep = run("geometry.add_wall_representation", model, context=body,
length=w, height=height, thickness=depth)
run("geometry.assign_representation", model, product=wall, representation=rep)
# Wall geometry runs along local +X; rotate 90° for vertical (E/W) runs.
m = np.eye(4)
if direction not in HORIZONTAL:
m[0, 0], m[0, 1] = 0.0, -1.0
m[1, 0], m[1, 1] = 1.0, 0.0
m[0, 3], m[1, 3], m[2, 3] = x, y, z
run("geometry.edit_object_placement", model, product=wall, matrix=m, is_si=False)
n += 1
model.write(out_path)
print(f"Wrote {out_path}: {n} walls, {len(storeys)} storey(s)")
if __name__ == "__main__":
if len(sys.argv) < 2:
print(__doc__)
sys.exit(1)
src = sys.argv[1]
dst = sys.argv[2] if len(sys.argv) > 2 else src.rsplit(".", 1)[0] + ".ifc"
main(src, dst)