@bitbybit-dev/ifc 0.0.0-stage → 1.5.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +42 -0
  3. package/README.md +92 -2
  4. package/index.d.ts +1 -0
  5. package/index.js +1 -0
  6. package/lib/api/dto-registry.d.ts +8 -0
  7. package/lib/api/dto-registry.js +109 -0
  8. package/lib/api/ifc-service.d.ts +109 -0
  9. package/lib/api/ifc-service.js +55 -0
  10. package/lib/api/inputs/base-inputs.d.ts +5 -0
  11. package/lib/api/inputs/base-inputs.js +5 -0
  12. package/lib/api/inputs/ifc-inputs.d.ts +2727 -0
  13. package/lib/api/inputs/ifc-inputs.js +2498 -0
  14. package/lib/api/inputs/index.d.ts +3 -0
  15. package/lib/api/inputs/index.js +3 -0
  16. package/lib/api/resolved-inputs/index.d.ts +70 -0
  17. package/lib/api/resolved-inputs/index.js +1 -0
  18. package/lib/api/services/assemblies.d.ts +28 -0
  19. package/lib/api/services/assemblies.js +49 -0
  20. package/lib/api/services/beams.d.ts +33 -0
  21. package/lib/api/services/beams.js +59 -0
  22. package/lib/api/services/columns.d.ts +26 -0
  23. package/lib/api/services/columns.js +56 -0
  24. package/lib/api/services/defaults.constants.d.ts +44 -0
  25. package/lib/api/services/defaults.constants.js +44 -0
  26. package/lib/api/services/doors.d.ts +45 -0
  27. package/lib/api/services/doors.js +94 -0
  28. package/lib/api/services/geometry.d.ts +51 -0
  29. package/lib/api/services/geometry.js +87 -0
  30. package/lib/api/services/index.d.ts +17 -0
  31. package/lib/api/services/index.js +17 -0
  32. package/lib/api/services/materials.d.ts +46 -0
  33. package/lib/api/services/materials.js +86 -0
  34. package/lib/api/services/member-support.d.ts +7 -0
  35. package/lib/api/services/member-support.js +29 -0
  36. package/lib/api/services/members.d.ts +24 -0
  37. package/lib/api/services/members.js +51 -0
  38. package/lib/api/services/model.d.ts +244 -0
  39. package/lib/api/services/model.js +409 -0
  40. package/lib/api/services/openings.d.ts +57 -0
  41. package/lib/api/services/openings.js +102 -0
  42. package/lib/api/services/properties.d.ts +89 -0
  43. package/lib/api/services/properties.js +142 -0
  44. package/lib/api/services/quantities.d.ts +38 -0
  45. package/lib/api/services/quantities.js +51 -0
  46. package/lib/api/services/roofs.d.ts +24 -0
  47. package/lib/api/services/roofs.js +52 -0
  48. package/lib/api/services/service-support.d.ts +8 -0
  49. package/lib/api/services/service-support.js +37 -0
  50. package/lib/api/services/service-types.d.ts +3 -0
  51. package/lib/api/services/service-types.js +1 -0
  52. package/lib/api/services/site.d.ts +37 -0
  53. package/lib/api/services/site.js +80 -0
  54. package/lib/api/services/slabs.d.ts +31 -0
  55. package/lib/api/services/slabs.js +59 -0
  56. package/lib/api/services/spaces.d.ts +36 -0
  57. package/lib/api/services/spaces.js +64 -0
  58. package/lib/api/services/spatial.d.ts +54 -0
  59. package/lib/api/services/spatial.js +75 -0
  60. package/lib/api/services/walls.d.ts +140 -0
  61. package/lib/api/services/walls.js +268 -0
  62. package/lib/api/services/windows.d.ts +46 -0
  63. package/lib/api/services/windows.js +76 -0
  64. package/lib/build/assemblies.d.ts +4 -0
  65. package/lib/build/assemblies.js +45 -0
  66. package/lib/build/build-types.d.ts +345 -0
  67. package/lib/build/build-types.js +1 -0
  68. package/lib/build/checks.d.ts +6 -0
  69. package/lib/build/checks.js +32 -0
  70. package/lib/build/constants.d.ts +27 -0
  71. package/lib/build/constants.js +43 -0
  72. package/lib/build/contexts.d.ts +9 -0
  73. package/lib/build/contexts.js +52 -0
  74. package/lib/build/curves.d.ts +3 -0
  75. package/lib/build/curves.js +23 -0
  76. package/lib/build/entity-writer.d.ts +23 -0
  77. package/lib/build/entity-writer.js +97 -0
  78. package/lib/build/fillings.d.ts +7 -0
  79. package/lib/build/fillings.js +182 -0
  80. package/lib/build/georeference.d.ts +5 -0
  81. package/lib/build/georeference.js +44 -0
  82. package/lib/build/materials.d.ts +18 -0
  83. package/lib/build/materials.js +166 -0
  84. package/lib/build/math.d.ts +10 -0
  85. package/lib/build/math.js +47 -0
  86. package/lib/build/measure.d.ts +9 -0
  87. package/lib/build/measure.js +195 -0
  88. package/lib/build/moving.d.ts +4 -0
  89. package/lib/build/moving.js +61 -0
  90. package/lib/build/openings.d.ts +12 -0
  91. package/lib/build/openings.js +156 -0
  92. package/lib/build/outlines.d.ts +4 -0
  93. package/lib/build/outlines.js +40 -0
  94. package/lib/build/placement.d.ts +16 -0
  95. package/lib/build/placement.js +106 -0
  96. package/lib/build/profiles.d.ts +4 -0
  97. package/lib/build/profiles.js +35 -0
  98. package/lib/build/project.d.ts +11 -0
  99. package/lib/build/project.js +134 -0
  100. package/lib/build/properties.d.ts +10 -0
  101. package/lib/build/properties.js +182 -0
  102. package/lib/build/quantities.d.ts +7 -0
  103. package/lib/build/quantities.js +262 -0
  104. package/lib/build/relationships.d.ts +9 -0
  105. package/lib/build/relationships.js +67 -0
  106. package/lib/build/removal.d.ts +3 -0
  107. package/lib/build/removal.js +106 -0
  108. package/lib/build/representations.d.ts +6 -0
  109. package/lib/build/representations.js +46 -0
  110. package/lib/build/roof-clipping.d.ts +5 -0
  111. package/lib/build/roof-clipping.js +79 -0
  112. package/lib/build/roofs.d.ts +4 -0
  113. package/lib/build/roofs.js +131 -0
  114. package/lib/build/site.d.ts +7 -0
  115. package/lib/build/site.js +112 -0
  116. package/lib/build/slab-openings.d.ts +4 -0
  117. package/lib/build/slab-openings.js +71 -0
  118. package/lib/build/slabs-columns-beams.d.ts +8 -0
  119. package/lib/build/slabs-columns-beams.js +73 -0
  120. package/lib/build/solids.d.ts +5 -0
  121. package/lib/build/solids.js +33 -0
  122. package/lib/build/spaces.d.ts +6 -0
  123. package/lib/build/spaces.js +55 -0
  124. package/lib/build/spatial.d.ts +17 -0
  125. package/lib/build/spatial.js +111 -0
  126. package/lib/build/type-objects.d.ts +4 -0
  127. package/lib/build/type-objects.js +9 -0
  128. package/lib/build/wall-edits.d.ts +5 -0
  129. package/lib/build/wall-edits.js +138 -0
  130. package/lib/build/wall-geometry.d.ts +15 -0
  131. package/lib/build/wall-geometry.js +159 -0
  132. package/lib/build/wall-parameters.d.ts +3 -0
  133. package/lib/build/wall-parameters.js +47 -0
  134. package/lib/build/walls.d.ts +12 -0
  135. package/lib/build/walls.js +238 -0
  136. package/lib/geometry/advanced.d.ts +27 -0
  137. package/lib/geometry/advanced.js +292 -0
  138. package/lib/geometry/colours.d.ts +3 -0
  139. package/lib/geometry/colours.js +109 -0
  140. package/lib/geometry/curves.d.ts +32 -0
  141. package/lib/geometry/curves.js +370 -0
  142. package/lib/geometry/edges.constants.d.ts +5 -0
  143. package/lib/geometry/edges.constants.js +5 -0
  144. package/lib/geometry/edges.d.ts +21 -0
  145. package/lib/geometry/edges.js +164 -0
  146. package/lib/geometry/elements.d.ts +3 -0
  147. package/lib/geometry/elements.js +152 -0
  148. package/lib/geometry/errors.d.ts +3 -0
  149. package/lib/geometry/errors.js +6 -0
  150. package/lib/geometry/faceted.d.ts +31 -0
  151. package/lib/geometry/faceted.js +214 -0
  152. package/lib/geometry/geometry-types.d.ts +103 -0
  153. package/lib/geometry/geometry-types.js +1 -0
  154. package/lib/geometry/groups.d.ts +21 -0
  155. package/lib/geometry/groups.js +106 -0
  156. package/lib/geometry/items.d.ts +34 -0
  157. package/lib/geometry/items.js +313 -0
  158. package/lib/geometry/operators.d.ts +3 -0
  159. package/lib/geometry/operators.js +65 -0
  160. package/lib/geometry/profiles.d.ts +4 -0
  161. package/lib/geometry/profiles.js +277 -0
  162. package/lib/geometry/recipe-builder.d.ts +14 -0
  163. package/lib/geometry/recipe-builder.js +48 -0
  164. package/lib/geometry/references.d.ts +4 -0
  165. package/lib/geometry/references.js +17 -0
  166. package/lib/geometry/regions.d.ts +4 -0
  167. package/lib/geometry/regions.js +26 -0
  168. package/lib/geometry/surfaces.constants.d.ts +17 -0
  169. package/lib/geometry/surfaces.constants.js +17 -0
  170. package/lib/geometry/surfaces.d.ts +6 -0
  171. package/lib/geometry/surfaces.js +306 -0
  172. package/lib/geometry/swept-solids.constants.d.ts +7 -0
  173. package/lib/geometry/swept-solids.constants.js +6 -0
  174. package/lib/geometry/swept-solids.d.ts +25 -0
  175. package/lib/geometry/swept-solids.js +243 -0
  176. package/lib/index.d.ts +6 -0
  177. package/lib/index.js +6 -0
  178. package/lib/model/constants.d.ts +30 -0
  179. package/lib/model/constants.js +30 -0
  180. package/lib/model/entity-lookup.d.ts +9 -0
  181. package/lib/model/entity-lookup.js +20 -0
  182. package/lib/model/guid.d.ts +7 -0
  183. package/lib/model/guid.js +60 -0
  184. package/lib/model/id-map.d.ts +10 -0
  185. package/lib/model/id-map.js +80 -0
  186. package/lib/model/index.d.ts +4 -0
  187. package/lib/model/index.js +2 -0
  188. package/lib/model/io.d.ts +6 -0
  189. package/lib/model/io.js +136 -0
  190. package/lib/model/is-model.d.ts +9 -0
  191. package/lib/model/is-model.js +11 -0
  192. package/lib/model/model-types.d.ts +111 -0
  193. package/lib/model/model-types.js +1 -0
  194. package/lib/model/overlay-indexes.d.ts +17 -0
  195. package/lib/model/overlay-indexes.js +69 -0
  196. package/lib/model/references.d.ts +2 -0
  197. package/lib/model/references.js +51 -0
  198. package/lib/model/rows-by-id.d.ts +8 -0
  199. package/lib/model/rows-by-id.js +39 -0
  200. package/lib/model/snapshot.d.ts +36 -0
  201. package/lib/model/snapshot.js +144 -0
  202. package/lib/model/source.d.ts +29 -0
  203. package/lib/model/source.js +172 -0
  204. package/lib/model/transaction.d.ts +39 -0
  205. package/lib/model/transaction.js +250 -0
  206. package/lib/model/upgrade.d.ts +2 -0
  207. package/lib/model/upgrade.js +78 -0
  208. package/lib/schema/constants.d.ts +3 -0
  209. package/lib/schema/constants.js +3 -0
  210. package/lib/schema/generated/ifc4.d.ts +2 -0
  211. package/lib/schema/generated/ifc4.js +1204 -0
  212. package/lib/schema/generated/ifc4x3.d.ts +2 -0
  213. package/lib/schema/generated/ifc4x3.js +1325 -0
  214. package/lib/schema/registry.d.ts +7 -0
  215. package/lib/schema/registry.js +37 -0
  216. package/lib/schema/schema-types.d.ts +45 -0
  217. package/lib/schema/schema-types.js +1 -0
  218. package/lib/schema/schema.d.ts +23 -0
  219. package/lib/schema/schema.js +136 -0
  220. package/lib/step/constants.d.ts +48 -0
  221. package/lib/step/constants.js +48 -0
  222. package/lib/step/encoder.d.ts +21 -0
  223. package/lib/step/encoder.js +321 -0
  224. package/lib/step/errors.d.ts +39 -0
  225. package/lib/step/errors.js +45 -0
  226. package/lib/step/file-index.d.ts +2 -0
  227. package/lib/step/file-index.js +221 -0
  228. package/lib/step/header.d.ts +4 -0
  229. package/lib/step/header.js +52 -0
  230. package/lib/step/numbers.d.ts +3 -0
  231. package/lib/step/numbers.js +73 -0
  232. package/lib/step/reader.d.ts +2 -0
  233. package/lib/step/reader.js +160 -0
  234. package/lib/step/scanner.d.ts +13 -0
  235. package/lib/step/scanner.js +203 -0
  236. package/lib/step/step-types.d.ts +170 -0
  237. package/lib/step/step-types.js +1 -0
  238. package/lib/step/strings.d.ts +2 -0
  239. package/lib/step/strings.js +150 -0
  240. package/lib/step/values.d.ts +13 -0
  241. package/lib/step/values.js +51 -0
  242. package/package.json +51 -5
@@ -0,0 +1,313 @@
1
+ import { WORLD_AXES, axesToMatrix, vectorToLocal } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
2
+ import { add3, length3, scale3 } from "@bitbybit-dev/base/lib/api/services/helpers/vectors";
3
+ import { wound2 } from "@bitbybit-dev/base/lib/api/services/helpers/polygons";
4
+ import { PARALLEL_TOLERANCE } from "../build/constants";
5
+ import { axisPlacementFrame, directionOf } from "../build/placement";
6
+ import { isEnumeration, isList, isReference } from "../step/values";
7
+ import { CurveSampler } from "./curves";
8
+ import { UnsupportedGeometryError } from "./errors";
9
+ import { INWARD, MeshCollector, OUTWARD, UNORIENTED, concatenated } from "./faceted";
10
+ import { itemColour } from "./colours";
11
+ import { operatorMatrix } from "./operators";
12
+ import { profileRegion } from "./profiles";
13
+ import { regionSolid } from "./regions";
14
+ import { SweptSolids } from "./swept-solids";
15
+ const IDENTITY = axesToMatrix(WORLD_AXES);
16
+ const Z_AXIS = [0, 0, 1];
17
+ const PLAIN_HALF_SPACES = new Set(["IfcHalfSpaceSolid", "IfcBoxedHalfSpace"]);
18
+ const CLOSED_SHELL = "IfcClosedShell";
19
+ const MAPPED_ITEM = "IfcMappedItem";
20
+ const SURFACE_MODEL_FACES = { IfcFaceBasedSurfaceModel: "FbsmFaces", IfcShellBasedSurfaceModel: "SbsmBoundary" };
21
+ const MESH_ITEMS = new Set([
22
+ "IfcTriangulatedFaceSet", "IfcPolygonalFaceSet", "IfcFacetedBrep", "IfcFacetedBrepWithVoids", "IfcAdvancedBrep", "IfcAdvancedBrepWithVoids", "IfcFaceBasedSurfaceModel", "IfcShellBasedSurfaceModel",
23
+ ]);
24
+ const BREPS_WITH_VOIDS = new Set(["IfcFacetedBrepWithVoids", "IfcAdvancedBrepWithVoids"]);
25
+ function isIdentity(matrix) {
26
+ return matrix.every((value, index) => value === IDENTITY[index]);
27
+ }
28
+ function flat(points) {
29
+ return points.flatMap(([x, y]) => [x, y]);
30
+ }
31
+ export class ItemConverter {
32
+ constructor(model, builder, reach) {
33
+ this.items = new Map();
34
+ this.maps = new Map();
35
+ this.representations = new Map();
36
+ this.itemColours = new Map();
37
+ this.model = model;
38
+ this.builder = builder;
39
+ this.curves = new CurveSampler(model);
40
+ this.reach = reach;
41
+ this.solids = new SweptSolids(model, builder, this.curves);
42
+ }
43
+ representation(representation) {
44
+ return this.joined(this.parts(representation).map((part) => part.node));
45
+ }
46
+ parts(representation) {
47
+ const known = this.representations.get(representation);
48
+ if (known) {
49
+ return known;
50
+ }
51
+ const items = this.model.attribute(representation, "Items");
52
+ const ids = (isList(items) ? items : []).filter(isReference).map((item) => item.ref);
53
+ if (!ids.length) {
54
+ throw new UnsupportedGeometryError(`#${representation} holds no items`);
55
+ }
56
+ const groups = new Map();
57
+ const groupOf = (rgba) => {
58
+ const key = rgba ? rgba.join(",") : "";
59
+ let group = groups.get(key);
60
+ if (!group) {
61
+ group = { rgba, entries: [] };
62
+ groups.set(key, group);
63
+ }
64
+ return group;
65
+ };
66
+ for (const item of ids) {
67
+ const own = itemColour(this.model, this.itemColours, item);
68
+ if (this.model.typeOf(item) === MAPPED_ITEM) {
69
+ this.mappedParts(item).forEach((part) => { var _a; return groupOf((_a = part.rgba) !== null && _a !== void 0 ? _a : own).entries.push({ node: part.node }); });
70
+ }
71
+ else {
72
+ groupOf(own).entries.push({ item });
73
+ }
74
+ }
75
+ const parts = [...groups.values()].map((group) => ({ rgba: group.rgba, node: this.groupNode(representation, group.entries) }));
76
+ this.representations.set(representation, parts);
77
+ return parts;
78
+ }
79
+ item(item) {
80
+ const known = this.items.get(item);
81
+ if (known !== undefined) {
82
+ return known;
83
+ }
84
+ const node = this.convert(item);
85
+ this.items.set(item, node);
86
+ return node;
87
+ }
88
+ convert(item) {
89
+ const type = this.model.entity(item).type;
90
+ switch (type) {
91
+ case "IfcExtrudedAreaSolid":
92
+ return this.extrusion(item);
93
+ case "IfcBooleanClippingResult":
94
+ case "IfcBooleanResult":
95
+ return this.boolean(item);
96
+ case "IfcMappedItem":
97
+ return this.mapped(item);
98
+ case "IfcRevolvedAreaSolid":
99
+ return this.solids.revolved(item);
100
+ case "IfcExtrudedAreaSolidTapered":
101
+ return this.solids.tapered(item);
102
+ case "IfcSweptDiskSolid":
103
+ case "IfcSweptDiskSolidPolygonal":
104
+ return this.solids.sweptDisk(item, type === "IfcSweptDiskSolidPolygonal");
105
+ case "IfcSurfaceCurveSweptAreaSolid":
106
+ case "IfcFixedReferenceSweptAreaSolid":
107
+ return this.solids.areaSweep(item, type === "IfcFixedReferenceSweptAreaSolid");
108
+ case "IfcBlock":
109
+ case "IfcRightCircularCylinder":
110
+ case "IfcRightCircularCone":
111
+ case "IfcSphere":
112
+ case "IfcRectangularPyramid":
113
+ return this.solids.primitive(item, type);
114
+ case "IfcCsgSolid": {
115
+ const root = this.model.attribute(item, "TreeRootExpression");
116
+ if (!isReference(root)) {
117
+ throw new UnsupportedGeometryError(`#${item} has no tree root expression`);
118
+ }
119
+ return this.item(root.ref);
120
+ }
121
+ case "IfcTriangulatedFaceSet":
122
+ case "IfcPolygonalFaceSet":
123
+ case "IfcFacetedBrep":
124
+ case "IfcFacetedBrepWithVoids":
125
+ case "IfcAdvancedBrep":
126
+ case "IfcAdvancedBrepWithVoids":
127
+ case "IfcFaceBasedSurfaceModel":
128
+ case "IfcShellBasedSurfaceModel":
129
+ return this.meshNode(item, this.meshOf(item));
130
+ default:
131
+ throw new UnsupportedGeometryError(`An ${type} is not supported yet`);
132
+ }
133
+ }
134
+ placedBy(node, placement) {
135
+ if (placement === undefined) {
136
+ return node;
137
+ }
138
+ const matrix = axesToMatrix(axisPlacementFrame(this.model, placement));
139
+ return isIdentity(matrix) ? node : this.builder.node({ op: "transform", of: node, matrix });
140
+ }
141
+ extrusion(item) {
142
+ const area = this.model.attribute(item, "SweptArea");
143
+ if (!isReference(area)) {
144
+ throw new UnsupportedGeometryError(`#${item} has no swept area`);
145
+ }
146
+ const region = profileRegion(this.model, area.ref, this.curves);
147
+ const direction = directionOf(this.model, this.model.attribute(item, "ExtrudedDirection"), Z_AXIS);
148
+ const depth = this.model.attribute(item, "Depth");
149
+ if (typeof depth !== "number" || !Number.isFinite(depth) || depth === 0) {
150
+ throw new UnsupportedGeometryError(`#${item} has no depth`);
151
+ }
152
+ const solid = this.extruded(region, scale3(direction, Math.sign(depth) / length3(direction)), Math.abs(depth));
153
+ const position = this.model.attribute(item, "Position");
154
+ return this.placedBy(solid, isReference(position) ? position.ref : undefined);
155
+ }
156
+ extruded(region, direction, depth) {
157
+ return regionSolid(this.builder, region, (profile, part) => {
158
+ if (part.kind !== "polygon" || part.plane === undefined) {
159
+ return this.builder.node({ op: "extrude", profile, direction, depth });
160
+ }
161
+ const across = vectorToLocal(part.plane, direction);
162
+ if (!(Math.abs(across[2]) > PARALLEL_TOLERANCE)) {
163
+ throw new UnsupportedGeometryError("An extrusion runs along the plane of its outline");
164
+ }
165
+ const solid = this.builder.node({ op: "extrude", profile, direction: across, depth });
166
+ return this.builder.node({ op: "transform", of: solid, matrix: axesToMatrix(part.plane) });
167
+ });
168
+ }
169
+ boolean(item) {
170
+ var _a;
171
+ const operator = this.model.attribute(item, "Operator");
172
+ const first = this.model.attribute(item, "FirstOperand");
173
+ const second = this.model.attribute(item, "SecondOperand");
174
+ if (!isReference(first) || !isReference(second)) {
175
+ throw new UnsupportedGeometryError(`#${item} lacks an operand`);
176
+ }
177
+ const solid = this.item(first.ref);
178
+ const plain = PLAIN_HALF_SPACES.has((_a = this.model.typeOf(second.ref)) !== null && _a !== void 0 ? _a : "");
179
+ switch (isEnumeration(operator) ? operator.enum : undefined) {
180
+ case "DIFFERENCE":
181
+ return this.builder.node({ op: "difference", of: solid, tools: [this.tool(second.ref)] });
182
+ case "UNION":
183
+ if (plain) {
184
+ throw new UnsupportedGeometryError(`#${item} unites a solid with a half-space, which has no bounds`);
185
+ }
186
+ return this.builder.node({ op: "union", of: [solid, this.tool(second.ref)] });
187
+ case "INTERSECTION": {
188
+ if (plain) {
189
+ const space = this.halfSpace(second.ref);
190
+ return this.builder.node({ op: "difference", of: solid, tools: [this.builder.node(Object.assign(Object.assign({}, space), { normal: scale3(space.normal, -1) }))] });
191
+ }
192
+ return this.builder.node({ op: "intersection", of: [solid, this.tool(second.ref)] });
193
+ }
194
+ default:
195
+ throw new UnsupportedGeometryError(`#${item} has no boolean operator`);
196
+ }
197
+ }
198
+ tool(operand) {
199
+ switch (this.model.entity(operand).type) {
200
+ case "IfcHalfSpaceSolid":
201
+ case "IfcBoxedHalfSpace":
202
+ return this.builder.node(this.halfSpace(operand));
203
+ case "IfcPolygonalBoundedHalfSpace":
204
+ return this.boundedHalfSpace(operand);
205
+ default:
206
+ return this.item(operand);
207
+ }
208
+ }
209
+ boundedHalfSpace(solid) {
210
+ const position = this.model.attribute(solid, "Position");
211
+ const boundary = this.model.attribute(solid, "PolygonalBoundary");
212
+ if (!isReference(position) || !isReference(boundary)) {
213
+ throw new UnsupportedGeometryError(`#${solid} lacks its position or its boundary`);
214
+ }
215
+ const outline = wound2(this.curves.outline(boundary.ref), true);
216
+ const profile = this.builder.node({ op: "polygon", points: this.builder.numbers(flat(outline)), holes: [] });
217
+ const prism = this.builder.node({ op: "extrude", profile, direction: Z_AXIS, depth: 2 * this.reach });
218
+ const frame = axisPlacementFrame(this.model, position.ref);
219
+ const placed = this.builder.node({ op: "transform", of: prism, matrix: axesToMatrix(Object.assign(Object.assign({}, frame), { origin: add3(frame.origin, scale3(frame.z, -this.reach)) })) });
220
+ const space = this.halfSpace(solid);
221
+ const outside = this.builder.node(Object.assign(Object.assign({}, space), { normal: scale3(space.normal, -1) }));
222
+ return this.builder.node({ op: "difference", of: placed, tools: [outside] });
223
+ }
224
+ halfSpace(solid) {
225
+ const surface = this.model.attribute(solid, "BaseSurface");
226
+ if (!isReference(surface) || this.model.entity(surface.ref).type !== "IfcPlane") {
227
+ throw new UnsupportedGeometryError("Only half-spaces bounded by a plane are supported yet");
228
+ }
229
+ const position = this.model.attribute(surface.ref, "Position");
230
+ if (!isReference(position)) {
231
+ throw new UnsupportedGeometryError(`#${surface.ref} has no position`);
232
+ }
233
+ const frame = axisPlacementFrame(this.model, position.ref);
234
+ const agreement = this.model.attribute(solid, "AgreementFlag");
235
+ const normal = agreement === true ? scale3(frame.z, -1) : frame.z;
236
+ return { op: "halfSpace", origin: frame.origin, normal };
237
+ }
238
+ joined(nodes) {
239
+ return nodes.length === 1 ? nodes[0] : this.builder.node({ op: "compound", of: [...nodes] });
240
+ }
241
+ groupNode(representation, entries) {
242
+ const meshes = entries.flatMap((entry) => { var _a; return ("item" in entry && MESH_ITEMS.has((_a = this.model.typeOf(entry.item)) !== null && _a !== void 0 ? _a : "") ? [entry.item] : []); });
243
+ if (entries.length > 1 && meshes.length === entries.length) {
244
+ return this.meshNode(representation, concatenated(meshes.map((item) => this.meshOf(item))));
245
+ }
246
+ return this.joined(entries.map((entry) => ("item" in entry ? this.item(entry.item) : entry.node)));
247
+ }
248
+ mapped(item) {
249
+ return this.joined(this.mappedParts(item).map((part) => part.node));
250
+ }
251
+ mappedParts(item) {
252
+ const source = this.model.attribute(item, "MappingSource");
253
+ const target = this.model.attribute(item, "MappingTarget");
254
+ if (!isReference(source) || !isReference(target)) {
255
+ throw new UnsupportedGeometryError(`#${item} lacks its source or target`);
256
+ }
257
+ let shared = this.maps.get(source.ref);
258
+ if (shared === undefined) {
259
+ const representation = this.model.attribute(source.ref, "MappedRepresentation");
260
+ if (!isReference(representation)) {
261
+ throw new UnsupportedGeometryError(`#${source.ref} maps no representation`);
262
+ }
263
+ const origin = this.model.attribute(source.ref, "MappingOrigin");
264
+ const placement = isReference(origin) ? origin.ref : undefined;
265
+ shared = this.parts(representation.ref).map((part) => ({ rgba: part.rgba, node: this.placedBy(part.node, placement) }));
266
+ this.maps.set(source.ref, shared);
267
+ }
268
+ const matrix = operatorMatrix(this.model, target.ref);
269
+ return isIdentity(matrix) ? shared : shared.map((part) => ({ rgba: part.rgba, node: this.builder.node({ op: "transform", of: part.node, matrix }) }));
270
+ }
271
+ meshNode(item, mesh) {
272
+ if (!mesh.indices.length) {
273
+ throw new UnsupportedGeometryError(`#${item} holds no faces with an area`);
274
+ }
275
+ return this.builder.node({ op: "triangles", positions: this.builder.numbers(mesh.positions), indices: this.builder.indices(mesh.indices) });
276
+ }
277
+ meshOf(item) {
278
+ const collector = new MeshCollector(this.model);
279
+ const type = this.model.entity(item).type;
280
+ switch (type) {
281
+ case "IfcTriangulatedFaceSet":
282
+ collector.triangulatedFaces(item);
283
+ break;
284
+ case "IfcPolygonalFaceSet":
285
+ collector.polygonalFaces(item);
286
+ break;
287
+ case "IfcFaceBasedSurfaceModel":
288
+ case "IfcShellBasedSurfaceModel":
289
+ this.surfaceModel(collector, item, type);
290
+ break;
291
+ default:
292
+ this.facetedBrep(collector, item, type);
293
+ }
294
+ return collector.mesh;
295
+ }
296
+ facetedBrep(collector, item, type) {
297
+ const outer = this.model.attribute(item, "Outer");
298
+ if (!isReference(outer)) {
299
+ throw new UnsupportedGeometryError(`#${item} has no outer shell`);
300
+ }
301
+ collector.shell(outer.ref, OUTWARD);
302
+ const voids = BREPS_WITH_VOIDS.has(type) ? this.model.attribute(item, "Voids") : null;
303
+ for (const shell of (isList(voids) ? voids : []).filter(isReference)) {
304
+ collector.shell(shell.ref, INWARD);
305
+ }
306
+ }
307
+ surfaceModel(collector, item, type) {
308
+ const sets = this.model.attribute(item, SURFACE_MODEL_FACES[type]);
309
+ for (const set of (isList(sets) ? sets : []).filter(isReference)) {
310
+ collector.shell(set.ref, this.model.typeOf(set.ref) === CLOSED_SHELL ? OUTWARD : UNORIENTED);
311
+ }
312
+ }
313
+ }
@@ -0,0 +1,3 @@
1
+ import type { Base } from "@bitbybit-dev/base";
2
+ import type { ModelSnapshot } from "../model/snapshot";
3
+ export declare function operatorMatrix(model: ModelSnapshot, operator: number): Base.TransformMatrix;
@@ -0,0 +1,65 @@
1
+ import { cross3, dot3, length3, scale3, subtract3 } from "@bitbybit-dev/base/lib/api/services/helpers/vectors";
2
+ import { PARALLEL_TOLERANCE } from "../build/constants";
3
+ import { directionOf, pointOf } from "../build/placement";
4
+ import { isReference } from "../step/values";
5
+ import { UnsupportedGeometryError } from "./errors";
6
+ const X_AXIS = [1, 0, 0];
7
+ const Y_AXIS = [0, 1, 0];
8
+ const Z_AXIS = [0, 0, 1];
9
+ const SPATIAL_OPERATOR = "IfcCartesianTransformationOperator3D";
10
+ function optionalAttribute(model, id, name) {
11
+ return model.schema.entity(model.entity(id).type).positions.has(name) ? model.attribute(id, name) : null;
12
+ }
13
+ function operatorAxis(model, operator, name) {
14
+ const value = optionalAttribute(model, operator, name);
15
+ if (!isReference(value)) {
16
+ return undefined;
17
+ }
18
+ const direction = directionOf(model, value, Z_AXIS);
19
+ const length = length3(direction);
20
+ if (!(length > 0)) {
21
+ throw new UnsupportedGeometryError(`#${operator} has an ${name} of zero length`);
22
+ }
23
+ return scale3(direction, 1 / length);
24
+ }
25
+ function operatorScale(model, operator, name, fallback) {
26
+ const value = optionalAttribute(model, operator, name);
27
+ if (value === null) {
28
+ return fallback;
29
+ }
30
+ if (typeof value !== "number" || !Number.isFinite(value) || !(value > 0)) {
31
+ throw new UnsupportedGeometryError(`#${operator} has a ${name} that is not a number above zero`);
32
+ }
33
+ return value;
34
+ }
35
+ function spaceAxes(operator, axis1, axis2, axis3) {
36
+ const z = axis3 !== null && axis3 !== void 0 ? axis3 : Z_AXIS;
37
+ const hint = axis1 !== null && axis1 !== void 0 ? axis1 : (length3(cross3(z, X_AXIS)) > PARALLEL_TOLERANCE ? X_AXIS : Y_AXIS);
38
+ const across = subtract3(hint, scale3(z, dot3(hint, z)));
39
+ const length = length3(across);
40
+ if (!(length > PARALLEL_TOLERANCE)) {
41
+ throw new UnsupportedGeometryError(`#${operator} has its Axis1 along its Axis3`);
42
+ }
43
+ const x = scale3(across, 1 / length);
44
+ const y = cross3(z, x);
45
+ return [x, dot3(axis2 !== null && axis2 !== void 0 ? axis2 : Y_AXIS, y) < -PARALLEL_TOLERANCE ? cross3(x, z) : y, z];
46
+ }
47
+ function planeAxes(axis1, axis2) {
48
+ if (axis1 !== undefined) {
49
+ const y = cross3(Z_AXIS, axis1);
50
+ return [axis1, axis2 !== undefined && dot3(axis2, y) < -PARALLEL_TOLERANCE ? cross3(axis1, Z_AXIS) : y, Z_AXIS];
51
+ }
52
+ return axis2 === undefined ? [X_AXIS, Y_AXIS, Z_AXIS] : [cross3(axis2, Z_AXIS), axis2, Z_AXIS];
53
+ }
54
+ export function operatorMatrix(model, operator) {
55
+ const origin = pointOf(model, model.attribute(operator, "LocalOrigin"));
56
+ const axis1 = operatorAxis(model, operator, "Axis1");
57
+ const axis2 = operatorAxis(model, operator, "Axis2");
58
+ const [x, y, z] = model.schema.isSubtypeOf(model.entity(operator).type, SPATIAL_OPERATOR)
59
+ ? spaceAxes(operator, axis1, axis2, operatorAxis(model, operator, "Axis3"))
60
+ : planeAxes(axis1, axis2);
61
+ const scale = operatorScale(model, operator, "Scale", 1);
62
+ const scaleY = operatorScale(model, operator, "Scale2", scale);
63
+ const scaleZ = operatorScale(model, operator, "Scale3", scale);
64
+ return [...scale3(x, scale), 0, ...scale3(y, scaleY), 0, ...scale3(z, scaleZ), 0, ...origin, 1];
65
+ }
@@ -0,0 +1,4 @@
1
+ import type { ModelSnapshot } from "../model/snapshot";
2
+ import { type CurveSampler } from "./curves";
3
+ import type { ProfileRegion } from "./geometry-types";
4
+ export declare function profileRegion(model: ModelSnapshot, profile: number, curves: CurveSampler): ProfileRegion;
@@ -0,0 +1,277 @@
1
+ import { roundedCorners2, wound2 } from "@bitbybit-dev/base/lib/api/services/helpers/polygons";
2
+ import { axisPlacementFrame } from "../build/placement";
3
+ import { radiansPerUnit } from "../build/project";
4
+ import { isList, isReference } from "../step/values";
5
+ import { arcSegments } from "./curves";
6
+ import { UnsupportedGeometryError } from "./errors";
7
+ import { placedOnPlan } from "../build/math";
8
+ import { operatorMatrix } from "./operators";
9
+ const FULL_TURN = 2 * Math.PI;
10
+ const SIMILARITY_TOLERANCE = 1e-9;
11
+ const MIRROR_ABOUT_Y = [-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
12
+ const SCHEMA_WITHOUT_EDGE_RADII = "IFC2X3";
13
+ function finiteAttribute(model, id, name) {
14
+ const value = model.attribute(id, name);
15
+ if (typeof value !== "number" || !Number.isFinite(value)) {
16
+ throw new UnsupportedGeometryError(`#${id} has no finite ${name}`);
17
+ }
18
+ return value;
19
+ }
20
+ function optionalAttribute(model, id, name) {
21
+ const value = model.attribute(id, name);
22
+ return value === null ? 0 : finiteAttribute(model, id, name);
23
+ }
24
+ function slopeRise(model, id, name) {
25
+ const slope = optionalAttribute(model, id, name);
26
+ return slope === 0 ? 0 : Math.tan(slope * radiansPerUnit(model));
27
+ }
28
+ function refuseSlope(model, id, name) {
29
+ if (optionalAttribute(model, id, name) !== 0) {
30
+ throw new UnsupportedGeometryError(`#${id} has a ${name}, which is not supported yet`);
31
+ }
32
+ }
33
+ function placed(model, profile, points) {
34
+ const position = model.attribute(profile, "Position");
35
+ if (!isReference(position)) {
36
+ return points;
37
+ }
38
+ const frame = axisPlacementFrame(model, position.ref);
39
+ return points.map((point) => placedOnPlan(frame, point));
40
+ }
41
+ function rounded(corners) {
42
+ return roundedCorners2(corners.map((corner) => corner.point), corners.map((corner) => corner.radius), arcSegments);
43
+ }
44
+ const corner = (x, y, radius = 0) => ({ point: [x, y], radius });
45
+ function iShape(model, profile) {
46
+ const x = finiteAttribute(model, profile, "OverallWidth") / 2;
47
+ const y = finiteAttribute(model, profile, "OverallDepth") / 2;
48
+ const web = finiteAttribute(model, profile, "WebThickness") / 2;
49
+ const flange = finiteAttribute(model, profile, "FlangeThickness");
50
+ const fillet = optionalAttribute(model, profile, "FilletRadius");
51
+ const edge = optionalAttribute(model, profile, "FlangeEdgeRadius");
52
+ const rise = (x - web) * slopeRise(model, profile, "FlangeSlope");
53
+ return [
54
+ corner(-x, -y), corner(x, -y), corner(x, -y + flange, edge), corner(web, -y + flange + rise, fillet),
55
+ corner(web, y - flange - rise, fillet), corner(x, y - flange, edge), corner(x, y), corner(-x, y),
56
+ corner(-x, y - flange, edge), corner(-web, y - flange - rise, fillet), corner(-web, -y + flange + rise, fillet), corner(-x, -y + flange, edge),
57
+ ];
58
+ }
59
+ function lShape(model, profile) {
60
+ refuseSlope(model, profile, "LegSlope");
61
+ const depth = finiteAttribute(model, profile, "Depth");
62
+ const width = model.attribute(profile, "Width") === null ? depth : finiteAttribute(model, profile, "Width");
63
+ const [x, y] = [width / 2, depth / 2];
64
+ const thickness = finiteAttribute(model, profile, "Thickness");
65
+ const fillet = optionalAttribute(model, profile, "FilletRadius");
66
+ const edge = optionalAttribute(model, profile, "EdgeRadius");
67
+ return [corner(-x, -y), corner(x, -y), corner(x, -y + thickness, edge), corner(-x + thickness, -y + thickness, fillet), corner(-x + thickness, y, edge), corner(-x, y)];
68
+ }
69
+ function uShape(model, profile) {
70
+ const y = finiteAttribute(model, profile, "Depth") / 2;
71
+ const x = finiteAttribute(model, profile, "FlangeWidth") / 2;
72
+ const web = finiteAttribute(model, profile, "WebThickness");
73
+ const flange = finiteAttribute(model, profile, "FlangeThickness");
74
+ const fillet = optionalAttribute(model, profile, "FilletRadius");
75
+ const edge = optionalAttribute(model, profile, "EdgeRadius");
76
+ const slope = slopeRise(model, profile, "FlangeSlope");
77
+ const [atWeb, atTip] = [(x - web) * slope, x * slope];
78
+ return [
79
+ corner(-x, -y), corner(x, -y), corner(x, -y + flange - atTip, edge), corner(-x + web, -y + flange + atWeb, fillet),
80
+ corner(-x + web, y - flange - atWeb, fillet), corner(x, y - flange + atTip, edge), corner(x, y), corner(-x, y),
81
+ ];
82
+ }
83
+ function tShape(model, profile) {
84
+ refuseSlope(model, profile, "WebSlope");
85
+ refuseSlope(model, profile, "FlangeSlope");
86
+ const y = finiteAttribute(model, profile, "Depth") / 2;
87
+ const x = finiteAttribute(model, profile, "FlangeWidth") / 2;
88
+ const web = finiteAttribute(model, profile, "WebThickness") / 2;
89
+ const flange = finiteAttribute(model, profile, "FlangeThickness");
90
+ const fillet = optionalAttribute(model, profile, "FilletRadius");
91
+ const flangeEdge = optionalAttribute(model, profile, "FlangeEdgeRadius");
92
+ const webEdge = optionalAttribute(model, profile, "WebEdgeRadius");
93
+ return [
94
+ corner(web, -y, webEdge), corner(web, y - flange, fillet), corner(x, y - flange, flangeEdge), corner(x, y),
95
+ corner(-x, y), corner(-x, y - flange, flangeEdge), corner(-web, y - flange, fillet), corner(-web, -y, webEdge),
96
+ ];
97
+ }
98
+ function rectangle(model, profile, inset, radius) {
99
+ const x = finiteAttribute(model, profile, "XDim") / 2 - inset;
100
+ const y = finiteAttribute(model, profile, "YDim") / 2 - inset;
101
+ return [corner(-x, -y, radius), corner(x, -y, radius), corner(x, y, radius), corner(-x, y, radius)];
102
+ }
103
+ function cShape(model, profile) {
104
+ const y = finiteAttribute(model, profile, "Depth") / 2;
105
+ const x = finiteAttribute(model, profile, "Width") / 2;
106
+ const wall = finiteAttribute(model, profile, "WallThickness");
107
+ const girth = finiteAttribute(model, profile, "Girth");
108
+ const inner = optionalAttribute(model, profile, "InternalFilletRadius");
109
+ const outer = inner > 0 ? inner + wall : 0;
110
+ return [
111
+ corner(-x, -y, outer), corner(x, -y, outer), corner(x, -y + girth), corner(x - wall, -y + girth), corner(x - wall, -y + wall, inner), corner(-x + wall, -y + wall, inner),
112
+ corner(-x + wall, y - wall, inner), corner(x - wall, y - wall, inner), corner(x - wall, y - girth), corner(x, y - girth), corner(x, y, outer), corner(-x, y, outer),
113
+ ];
114
+ }
115
+ function zShape(model, profile) {
116
+ const y = finiteAttribute(model, profile, "Depth") / 2;
117
+ const x = finiteAttribute(model, profile, "FlangeWidth");
118
+ const web = finiteAttribute(model, profile, "WebThickness") / 2;
119
+ const flange = finiteAttribute(model, profile, "FlangeThickness");
120
+ const fillet = optionalAttribute(model, profile, "FilletRadius");
121
+ const edge = optionalAttribute(model, profile, "EdgeRadius");
122
+ return [
123
+ corner(-web, -y), corner(x, -y), corner(x, -y + flange, edge), corner(web, -y + flange, fillet),
124
+ corner(web, y), corner(-x, y), corner(-x, y - flange, edge), corner(-web, y - flange, fillet),
125
+ ];
126
+ }
127
+ function asymmetricIShape(model, profile) {
128
+ const bottom = finiteAttribute(model, profile, "BottomFlangeWidth") / 2;
129
+ const top = finiteAttribute(model, profile, "TopFlangeWidth") / 2;
130
+ const y = finiteAttribute(model, profile, "OverallDepth") / 2;
131
+ const web = finiteAttribute(model, profile, "WebThickness") / 2;
132
+ const bottomFlange = finiteAttribute(model, profile, "BottomFlangeThickness");
133
+ const topFlange = model.attribute(profile, "TopFlangeThickness") === null ? bottomFlange : finiteAttribute(model, profile, "TopFlangeThickness");
134
+ const bottomFillet = optionalAttribute(model, profile, "BottomFlangeFilletRadius");
135
+ const topFillet = optionalAttribute(model, profile, "TopFlangeFilletRadius");
136
+ const hasEdges = model.schemaName !== SCHEMA_WITHOUT_EDGE_RADII;
137
+ const bottomEdge = hasEdges ? optionalAttribute(model, profile, "BottomFlangeEdgeRadius") : 0;
138
+ const topEdge = hasEdges ? optionalAttribute(model, profile, "TopFlangeEdgeRadius") : 0;
139
+ const bottomRise = hasEdges ? (bottom - web) * slopeRise(model, profile, "BottomFlangeSlope") : 0;
140
+ const topRise = hasEdges ? (top - web) * slopeRise(model, profile, "TopFlangeSlope") : 0;
141
+ return [
142
+ corner(-bottom, -y), corner(bottom, -y), corner(bottom, -y + bottomFlange, bottomEdge), corner(web, -y + bottomFlange + bottomRise, bottomFillet),
143
+ corner(web, y - topFlange - topRise, topFillet), corner(top, y - topFlange, topEdge), corner(top, y), corner(-top, y),
144
+ corner(-top, y - topFlange, topEdge), corner(-web, y - topFlange - topRise, topFillet), corner(-web, -y + bottomFlange + bottomRise, bottomFillet), corner(-bottom, -y + bottomFlange, bottomEdge),
145
+ ];
146
+ }
147
+ function trapezium(model, profile) {
148
+ const x = finiteAttribute(model, profile, "BottomXDim") / 2;
149
+ const top = finiteAttribute(model, profile, "TopXDim");
150
+ const offset = finiteAttribute(model, profile, "TopXOffset");
151
+ const y = finiteAttribute(model, profile, "YDim") / 2;
152
+ return [corner(-x, -y), corner(x, -y), corner(offset + top - x, y), corner(offset - x, y)];
153
+ }
154
+ function ellipse(model, profile) {
155
+ const a = finiteAttribute(model, profile, "SemiAxis1");
156
+ const b = finiteAttribute(model, profile, "SemiAxis2");
157
+ const segments = arcSegments(FULL_TURN);
158
+ return Array.from({ length: segments }, (_, at) => corner(a * Math.cos((FULL_TURN * at) / segments), b * Math.sin((FULL_TURN * at) / segments)));
159
+ }
160
+ function onPlan(matrix, [x, y]) {
161
+ return [matrix[0] * x + matrix[4] * y + matrix[12], matrix[1] * x + matrix[5] * y + matrix[13]];
162
+ }
163
+ function isSimilarity(matrix) {
164
+ const scale = Math.hypot(matrix[0], matrix[1]);
165
+ return Math.abs(Math.hypot(matrix[4], matrix[5]) - scale) <= SIMILARITY_TOLERANCE * scale && Math.abs(matrix[0] * matrix[4] + matrix[1] * matrix[5]) <= SIMILARITY_TOLERANCE * scale * scale;
166
+ }
167
+ function circlePoints(center, radius) {
168
+ const segments = arcSegments(FULL_TURN);
169
+ return Array.from({ length: segments }, (_, at) => [center[0] + radius * Math.cos((FULL_TURN * at) / segments), center[1] + radius * Math.sin((FULL_TURN * at) / segments)]);
170
+ }
171
+ function transformedRegion(region, matrix) {
172
+ switch (region.kind) {
173
+ case "composite":
174
+ return { kind: "composite", parts: region.parts.map((part) => transformedRegion(part, matrix)) };
175
+ case "circle":
176
+ if (isSimilarity(matrix)) {
177
+ const scale = Math.hypot(matrix[0], matrix[1]);
178
+ return { kind: "circle", center: onPlan(matrix, region.center), radius: region.radius * scale, innerRadius: region.innerRadius * scale };
179
+ }
180
+ return transformedRegion({ kind: "polygon", outer: circlePoints(region.center, region.radius), holes: region.innerRadius > 0 ? [circlePoints(region.center, region.innerRadius)] : [] }, matrix);
181
+ default:
182
+ return Object.assign(Object.assign({}, region), { outer: wound2(region.outer.map((point) => onPlan(matrix, point)), true), holes: region.holes.map((hole) => wound2(hole.map((point) => onPlan(matrix, point)), false)) });
183
+ }
184
+ }
185
+ function derived(model, profile, curves) {
186
+ const parent = model.attribute(profile, "ParentProfile");
187
+ if (!isReference(parent)) {
188
+ throw new UnsupportedGeometryError(`#${profile} has no parent profile`);
189
+ }
190
+ if (model.schema.isSubtypeOf(model.entity(profile).type, "IfcMirroredProfileDef")) {
191
+ return transformedRegion(profileRegion(model, parent.ref, curves), MIRROR_ABOUT_Y);
192
+ }
193
+ const operator = model.attribute(profile, "Operator");
194
+ if (!isReference(operator)) {
195
+ throw new UnsupportedGeometryError(`#${profile} has no transformation operator`);
196
+ }
197
+ return transformedRegion(profileRegion(model, parent.ref, curves), operatorMatrix(model, operator.ref));
198
+ }
199
+ function composite(model, profile, curves) {
200
+ const profiles = model.attribute(profile, "Profiles");
201
+ const parts = (isList(profiles) ? profiles : []).filter(isReference).map((part) => profileRegion(model, part.ref, curves));
202
+ if (!parts.length) {
203
+ throw new UnsupportedGeometryError(`#${profile} combines no profiles`);
204
+ }
205
+ return { kind: "composite", parts };
206
+ }
207
+ function polygon(model, profile, outer, holes = []) {
208
+ return {
209
+ kind: "polygon",
210
+ outer: wound2(placed(model, profile, rounded(outer)), true),
211
+ holes: holes.map((hole) => wound2(placed(model, profile, rounded(hole)), false)),
212
+ };
213
+ }
214
+ function circle(model, profile, innerRadius) {
215
+ const [center] = placed(model, profile, [[0, 0]]);
216
+ return { kind: "circle", center: center, radius: finiteAttribute(model, profile, "Radius"), innerRadius };
217
+ }
218
+ export function profileRegion(model, profile, curves) {
219
+ const type = model.entity(profile).type;
220
+ switch (type) {
221
+ case "IfcArbitraryClosedProfileDef":
222
+ case "IfcArbitraryProfileDefWithVoids": {
223
+ const outer = model.attribute(profile, "OuterCurve");
224
+ if (!isReference(outer)) {
225
+ throw new UnsupportedGeometryError(`#${profile} has no outer curve`);
226
+ }
227
+ const inner = type === "IfcArbitraryProfileDefWithVoids" ? model.attribute(profile, "InnerCurves") : null;
228
+ const plane = curves.planeOf(outer.ref);
229
+ const holes = (isList(inner) ? inner : []).filter(isReference).map((curve) => wound2(curves.outline(curve.ref, plane), false));
230
+ const region = { kind: "polygon", outer: wound2(curves.outline(outer.ref, plane), true), holes };
231
+ return plane === undefined ? region : Object.assign(Object.assign({}, region), { plane });
232
+ }
233
+ case "IfcRectangleProfileDef":
234
+ return polygon(model, profile, rectangle(model, profile, 0, 0));
235
+ case "IfcRoundedRectangleProfileDef":
236
+ return polygon(model, profile, rectangle(model, profile, 0, finiteAttribute(model, profile, "RoundingRadius")));
237
+ case "IfcRectangleHollowProfileDef": {
238
+ const wall = finiteAttribute(model, profile, "WallThickness");
239
+ const outer = rectangle(model, profile, 0, optionalAttribute(model, profile, "OuterFilletRadius"));
240
+ return polygon(model, profile, outer, [rectangle(model, profile, wall, optionalAttribute(model, profile, "InnerFilletRadius"))]);
241
+ }
242
+ case "IfcIShapeProfileDef":
243
+ return polygon(model, profile, iShape(model, profile));
244
+ case "IfcLShapeProfileDef":
245
+ return polygon(model, profile, lShape(model, profile));
246
+ case "IfcUShapeProfileDef":
247
+ return polygon(model, profile, uShape(model, profile));
248
+ case "IfcTShapeProfileDef":
249
+ return polygon(model, profile, tShape(model, profile));
250
+ case "IfcCShapeProfileDef":
251
+ return polygon(model, profile, cShape(model, profile));
252
+ case "IfcZShapeProfileDef":
253
+ return polygon(model, profile, zShape(model, profile));
254
+ case "IfcAsymmetricIShapeProfileDef":
255
+ return polygon(model, profile, asymmetricIShape(model, profile));
256
+ case "IfcTrapeziumProfileDef":
257
+ return polygon(model, profile, trapezium(model, profile));
258
+ case "IfcEllipseProfileDef":
259
+ return polygon(model, profile, ellipse(model, profile));
260
+ case "IfcDerivedProfileDef":
261
+ case "IfcMirroredProfileDef":
262
+ return derived(model, profile, curves);
263
+ case "IfcCompositeProfileDef":
264
+ return composite(model, profile, curves);
265
+ case "IfcCircleProfileDef":
266
+ return circle(model, profile, 0);
267
+ case "IfcCircleHollowProfileDef": {
268
+ const inner = finiteAttribute(model, profile, "Radius") - finiteAttribute(model, profile, "WallThickness");
269
+ if (!(inner > 0)) {
270
+ throw new UnsupportedGeometryError(`#${profile} has a wall as thick as its radius`);
271
+ }
272
+ return circle(model, profile, inner);
273
+ }
274
+ default:
275
+ throw new UnsupportedGeometryError(`An ${type} profile is not supported yet`);
276
+ }
277
+ }