@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.
- package/LICENSE +21 -0
- package/NOTICE +42 -0
- package/README.md +92 -2
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/lib/api/dto-registry.d.ts +8 -0
- package/lib/api/dto-registry.js +109 -0
- package/lib/api/ifc-service.d.ts +109 -0
- package/lib/api/ifc-service.js +55 -0
- package/lib/api/inputs/base-inputs.d.ts +5 -0
- package/lib/api/inputs/base-inputs.js +5 -0
- package/lib/api/inputs/ifc-inputs.d.ts +2727 -0
- package/lib/api/inputs/ifc-inputs.js +2498 -0
- package/lib/api/inputs/index.d.ts +3 -0
- package/lib/api/inputs/index.js +3 -0
- package/lib/api/resolved-inputs/index.d.ts +70 -0
- package/lib/api/resolved-inputs/index.js +1 -0
- package/lib/api/services/assemblies.d.ts +28 -0
- package/lib/api/services/assemblies.js +49 -0
- package/lib/api/services/beams.d.ts +33 -0
- package/lib/api/services/beams.js +59 -0
- package/lib/api/services/columns.d.ts +26 -0
- package/lib/api/services/columns.js +56 -0
- package/lib/api/services/defaults.constants.d.ts +44 -0
- package/lib/api/services/defaults.constants.js +44 -0
- package/lib/api/services/doors.d.ts +45 -0
- package/lib/api/services/doors.js +94 -0
- package/lib/api/services/geometry.d.ts +51 -0
- package/lib/api/services/geometry.js +87 -0
- package/lib/api/services/index.d.ts +17 -0
- package/lib/api/services/index.js +17 -0
- package/lib/api/services/materials.d.ts +46 -0
- package/lib/api/services/materials.js +86 -0
- package/lib/api/services/member-support.d.ts +7 -0
- package/lib/api/services/member-support.js +29 -0
- package/lib/api/services/members.d.ts +24 -0
- package/lib/api/services/members.js +51 -0
- package/lib/api/services/model.d.ts +244 -0
- package/lib/api/services/model.js +409 -0
- package/lib/api/services/openings.d.ts +57 -0
- package/lib/api/services/openings.js +102 -0
- package/lib/api/services/properties.d.ts +89 -0
- package/lib/api/services/properties.js +142 -0
- package/lib/api/services/quantities.d.ts +38 -0
- package/lib/api/services/quantities.js +51 -0
- package/lib/api/services/roofs.d.ts +24 -0
- package/lib/api/services/roofs.js +52 -0
- package/lib/api/services/service-support.d.ts +8 -0
- package/lib/api/services/service-support.js +37 -0
- package/lib/api/services/service-types.d.ts +3 -0
- package/lib/api/services/service-types.js +1 -0
- package/lib/api/services/site.d.ts +37 -0
- package/lib/api/services/site.js +80 -0
- package/lib/api/services/slabs.d.ts +31 -0
- package/lib/api/services/slabs.js +59 -0
- package/lib/api/services/spaces.d.ts +36 -0
- package/lib/api/services/spaces.js +64 -0
- package/lib/api/services/spatial.d.ts +54 -0
- package/lib/api/services/spatial.js +75 -0
- package/lib/api/services/walls.d.ts +140 -0
- package/lib/api/services/walls.js +268 -0
- package/lib/api/services/windows.d.ts +46 -0
- package/lib/api/services/windows.js +76 -0
- package/lib/build/assemblies.d.ts +4 -0
- package/lib/build/assemblies.js +45 -0
- package/lib/build/build-types.d.ts +345 -0
- package/lib/build/build-types.js +1 -0
- package/lib/build/checks.d.ts +6 -0
- package/lib/build/checks.js +32 -0
- package/lib/build/constants.d.ts +27 -0
- package/lib/build/constants.js +43 -0
- package/lib/build/contexts.d.ts +9 -0
- package/lib/build/contexts.js +52 -0
- package/lib/build/curves.d.ts +3 -0
- package/lib/build/curves.js +23 -0
- package/lib/build/entity-writer.d.ts +23 -0
- package/lib/build/entity-writer.js +97 -0
- package/lib/build/fillings.d.ts +7 -0
- package/lib/build/fillings.js +182 -0
- package/lib/build/georeference.d.ts +5 -0
- package/lib/build/georeference.js +44 -0
- package/lib/build/materials.d.ts +18 -0
- package/lib/build/materials.js +166 -0
- package/lib/build/math.d.ts +10 -0
- package/lib/build/math.js +47 -0
- package/lib/build/measure.d.ts +9 -0
- package/lib/build/measure.js +195 -0
- package/lib/build/moving.d.ts +4 -0
- package/lib/build/moving.js +61 -0
- package/lib/build/openings.d.ts +12 -0
- package/lib/build/openings.js +156 -0
- package/lib/build/outlines.d.ts +4 -0
- package/lib/build/outlines.js +40 -0
- package/lib/build/placement.d.ts +16 -0
- package/lib/build/placement.js +106 -0
- package/lib/build/profiles.d.ts +4 -0
- package/lib/build/profiles.js +35 -0
- package/lib/build/project.d.ts +11 -0
- package/lib/build/project.js +134 -0
- package/lib/build/properties.d.ts +10 -0
- package/lib/build/properties.js +182 -0
- package/lib/build/quantities.d.ts +7 -0
- package/lib/build/quantities.js +262 -0
- package/lib/build/relationships.d.ts +9 -0
- package/lib/build/relationships.js +67 -0
- package/lib/build/removal.d.ts +3 -0
- package/lib/build/removal.js +106 -0
- package/lib/build/representations.d.ts +6 -0
- package/lib/build/representations.js +46 -0
- package/lib/build/roof-clipping.d.ts +5 -0
- package/lib/build/roof-clipping.js +79 -0
- package/lib/build/roofs.d.ts +4 -0
- package/lib/build/roofs.js +131 -0
- package/lib/build/site.d.ts +7 -0
- package/lib/build/site.js +112 -0
- package/lib/build/slab-openings.d.ts +4 -0
- package/lib/build/slab-openings.js +71 -0
- package/lib/build/slabs-columns-beams.d.ts +8 -0
- package/lib/build/slabs-columns-beams.js +73 -0
- package/lib/build/solids.d.ts +5 -0
- package/lib/build/solids.js +33 -0
- package/lib/build/spaces.d.ts +6 -0
- package/lib/build/spaces.js +55 -0
- package/lib/build/spatial.d.ts +17 -0
- package/lib/build/spatial.js +111 -0
- package/lib/build/type-objects.d.ts +4 -0
- package/lib/build/type-objects.js +9 -0
- package/lib/build/wall-edits.d.ts +5 -0
- package/lib/build/wall-edits.js +138 -0
- package/lib/build/wall-geometry.d.ts +15 -0
- package/lib/build/wall-geometry.js +159 -0
- package/lib/build/wall-parameters.d.ts +3 -0
- package/lib/build/wall-parameters.js +47 -0
- package/lib/build/walls.d.ts +12 -0
- package/lib/build/walls.js +238 -0
- package/lib/geometry/advanced.d.ts +27 -0
- package/lib/geometry/advanced.js +292 -0
- package/lib/geometry/colours.d.ts +3 -0
- package/lib/geometry/colours.js +109 -0
- package/lib/geometry/curves.d.ts +32 -0
- package/lib/geometry/curves.js +370 -0
- package/lib/geometry/edges.constants.d.ts +5 -0
- package/lib/geometry/edges.constants.js +5 -0
- package/lib/geometry/edges.d.ts +21 -0
- package/lib/geometry/edges.js +164 -0
- package/lib/geometry/elements.d.ts +3 -0
- package/lib/geometry/elements.js +152 -0
- package/lib/geometry/errors.d.ts +3 -0
- package/lib/geometry/errors.js +6 -0
- package/lib/geometry/faceted.d.ts +31 -0
- package/lib/geometry/faceted.js +214 -0
- package/lib/geometry/geometry-types.d.ts +103 -0
- package/lib/geometry/geometry-types.js +1 -0
- package/lib/geometry/groups.d.ts +21 -0
- package/lib/geometry/groups.js +106 -0
- package/lib/geometry/items.d.ts +34 -0
- package/lib/geometry/items.js +313 -0
- package/lib/geometry/operators.d.ts +3 -0
- package/lib/geometry/operators.js +65 -0
- package/lib/geometry/profiles.d.ts +4 -0
- package/lib/geometry/profiles.js +277 -0
- package/lib/geometry/recipe-builder.d.ts +14 -0
- package/lib/geometry/recipe-builder.js +48 -0
- package/lib/geometry/references.d.ts +4 -0
- package/lib/geometry/references.js +17 -0
- package/lib/geometry/regions.d.ts +4 -0
- package/lib/geometry/regions.js +26 -0
- package/lib/geometry/surfaces.constants.d.ts +17 -0
- package/lib/geometry/surfaces.constants.js +17 -0
- package/lib/geometry/surfaces.d.ts +6 -0
- package/lib/geometry/surfaces.js +306 -0
- package/lib/geometry/swept-solids.constants.d.ts +7 -0
- package/lib/geometry/swept-solids.constants.js +6 -0
- package/lib/geometry/swept-solids.d.ts +25 -0
- package/lib/geometry/swept-solids.js +243 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +6 -0
- package/lib/model/constants.d.ts +30 -0
- package/lib/model/constants.js +30 -0
- package/lib/model/entity-lookup.d.ts +9 -0
- package/lib/model/entity-lookup.js +20 -0
- package/lib/model/guid.d.ts +7 -0
- package/lib/model/guid.js +60 -0
- package/lib/model/id-map.d.ts +10 -0
- package/lib/model/id-map.js +80 -0
- package/lib/model/index.d.ts +4 -0
- package/lib/model/index.js +2 -0
- package/lib/model/io.d.ts +6 -0
- package/lib/model/io.js +136 -0
- package/lib/model/is-model.d.ts +9 -0
- package/lib/model/is-model.js +11 -0
- package/lib/model/model-types.d.ts +111 -0
- package/lib/model/model-types.js +1 -0
- package/lib/model/overlay-indexes.d.ts +17 -0
- package/lib/model/overlay-indexes.js +69 -0
- package/lib/model/references.d.ts +2 -0
- package/lib/model/references.js +51 -0
- package/lib/model/rows-by-id.d.ts +8 -0
- package/lib/model/rows-by-id.js +39 -0
- package/lib/model/snapshot.d.ts +36 -0
- package/lib/model/snapshot.js +144 -0
- package/lib/model/source.d.ts +29 -0
- package/lib/model/source.js +172 -0
- package/lib/model/transaction.d.ts +39 -0
- package/lib/model/transaction.js +250 -0
- package/lib/model/upgrade.d.ts +2 -0
- package/lib/model/upgrade.js +78 -0
- package/lib/schema/constants.d.ts +3 -0
- package/lib/schema/constants.js +3 -0
- package/lib/schema/generated/ifc4.d.ts +2 -0
- package/lib/schema/generated/ifc4.js +1204 -0
- package/lib/schema/generated/ifc4x3.d.ts +2 -0
- package/lib/schema/generated/ifc4x3.js +1325 -0
- package/lib/schema/registry.d.ts +7 -0
- package/lib/schema/registry.js +37 -0
- package/lib/schema/schema-types.d.ts +45 -0
- package/lib/schema/schema-types.js +1 -0
- package/lib/schema/schema.d.ts +23 -0
- package/lib/schema/schema.js +136 -0
- package/lib/step/constants.d.ts +48 -0
- package/lib/step/constants.js +48 -0
- package/lib/step/encoder.d.ts +21 -0
- package/lib/step/encoder.js +321 -0
- package/lib/step/errors.d.ts +39 -0
- package/lib/step/errors.js +45 -0
- package/lib/step/file-index.d.ts +2 -0
- package/lib/step/file-index.js +221 -0
- package/lib/step/header.d.ts +4 -0
- package/lib/step/header.js +52 -0
- package/lib/step/numbers.d.ts +3 -0
- package/lib/step/numbers.js +73 -0
- package/lib/step/reader.d.ts +2 -0
- package/lib/step/reader.js +160 -0
- package/lib/step/scanner.d.ts +13 -0
- package/lib/step/scanner.js +203 -0
- package/lib/step/step-types.d.ts +170 -0
- package/lib/step/step-types.js +1 -0
- package/lib/step/strings.d.ts +2 -0
- package/lib/step/strings.js +150 -0
- package/lib/step/values.d.ts +13 -0
- package/lib/step/values.js +51 -0
- package/package.json +51 -5
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { perimeter2, signedArea2 } from "@bitbybit-dev/base/lib/api/services/helpers/polygons";
|
|
2
|
+
import { length2, subtract2 } from "@bitbybit-dev/base/lib/api/services/helpers/vectors";
|
|
3
|
+
import { isList, isReference } from "../step/values";
|
|
4
|
+
import { polylinePoints } from "./curves";
|
|
5
|
+
import { directionOf } from "./placement";
|
|
6
|
+
import { representationOf } from "./representations";
|
|
7
|
+
import { frameOf } from "./solids";
|
|
8
|
+
import { placedOnPlan } from "./math";
|
|
9
|
+
const UPRIGHT_NORMAL = 1e-9;
|
|
10
|
+
const SAME_BOUND = 1e-9;
|
|
11
|
+
const MOMENT_DIVISOR = 6;
|
|
12
|
+
function numberAt(reader, id, attribute) {
|
|
13
|
+
const value = reader.attribute(id, attribute);
|
|
14
|
+
return typeof value === "number" ? value : 0;
|
|
15
|
+
}
|
|
16
|
+
function curveOutline(reader, curve) {
|
|
17
|
+
return isReference(curve) ? polylinePoints(reader, curve.ref) : undefined;
|
|
18
|
+
}
|
|
19
|
+
export function profileShape(reader, profile) {
|
|
20
|
+
const type = reader.entity(profile).type;
|
|
21
|
+
if (type === "IfcArbitraryClosedProfileDef" || type === "IfcArbitraryProfileDefWithVoids") {
|
|
22
|
+
const outer = curveOutline(reader, reader.attribute(profile, "OuterCurve"));
|
|
23
|
+
if (outer === undefined) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const inner = type === "IfcArbitraryProfileDefWithVoids" ? reader.attribute(profile, "InnerCurves") : null;
|
|
27
|
+
const holes = (isList(inner) ? inner : []).map((curve) => { var _a; return (_a = curveOutline(reader, curve)) !== null && _a !== void 0 ? _a : []; });
|
|
28
|
+
const area = Math.abs(signedArea2(outer)) - holes.reduce((sum, hole) => sum + Math.abs(signedArea2(hole)), 0);
|
|
29
|
+
return { area, perimeter: perimeter2(outer), outline: outer };
|
|
30
|
+
}
|
|
31
|
+
if (type === "IfcRectangleProfileDef") {
|
|
32
|
+
const x = numberAt(reader, profile, "XDim");
|
|
33
|
+
const y = numberAt(reader, profile, "YDim");
|
|
34
|
+
const frame = frameOf(reader, reader.attribute(profile, "Position"));
|
|
35
|
+
const corners = [[-x / 2, -y / 2], [x / 2, -y / 2], [x / 2, y / 2], [-x / 2, y / 2]];
|
|
36
|
+
const outline = corners.map((corner) => placedOnPlan(frame, corner));
|
|
37
|
+
return { area: x * y, perimeter: 2 * (x + y), outline };
|
|
38
|
+
}
|
|
39
|
+
if (type === "IfcCircleProfileDef") {
|
|
40
|
+
const radius = numberAt(reader, profile, "Radius");
|
|
41
|
+
return { area: Math.PI * radius * radius, perimeter: 2 * Math.PI * radius, outline: undefined };
|
|
42
|
+
}
|
|
43
|
+
if (type === "IfcIShapeProfileDef") {
|
|
44
|
+
const width = numberAt(reader, profile, "OverallWidth");
|
|
45
|
+
const depth = numberAt(reader, profile, "OverallDepth");
|
|
46
|
+
const web = numberAt(reader, profile, "WebThickness");
|
|
47
|
+
const flange = numberAt(reader, profile, "FlangeThickness");
|
|
48
|
+
return { area: 2 * width * flange + (depth - 2 * flange) * web, perimeter: 2 * (2 * width - web + depth), outline: undefined };
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function valueAt(f, point) {
|
|
53
|
+
return f.a * point[0] + f.b * point[1] + f.c;
|
|
54
|
+
}
|
|
55
|
+
function minus(f, g) {
|
|
56
|
+
return { a: f.a - g.a, b: f.b - g.b, c: f.c - g.c };
|
|
57
|
+
}
|
|
58
|
+
function clipped(polygon, keep) {
|
|
59
|
+
const result = [];
|
|
60
|
+
polygon.forEach((point, index) => {
|
|
61
|
+
const next = polygon[(index + 1) % polygon.length];
|
|
62
|
+
const here = valueAt(keep, point);
|
|
63
|
+
const there = valueAt(keep, next);
|
|
64
|
+
if (here <= 0) {
|
|
65
|
+
result.push(point);
|
|
66
|
+
}
|
|
67
|
+
if ((here < 0 && there > 0) || (here > 0 && there < 0)) {
|
|
68
|
+
const fraction = here / (here - there);
|
|
69
|
+
result.push([point[0] + (next[0] - point[0]) * fraction, point[1] + (next[1] - point[1]) * fraction]);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
function integral(polygon, f) {
|
|
75
|
+
let twiceArea = 0;
|
|
76
|
+
let x = 0;
|
|
77
|
+
let y = 0;
|
|
78
|
+
polygon.forEach((point, index) => {
|
|
79
|
+
const next = polygon[(index + 1) % polygon.length];
|
|
80
|
+
const cross = point[0] * next[1] - next[0] * point[1];
|
|
81
|
+
twiceArea += cross;
|
|
82
|
+
x += (point[0] + next[0]) * cross;
|
|
83
|
+
y += (point[1] + next[1]) * cross;
|
|
84
|
+
});
|
|
85
|
+
const signed = (f.a * x + f.b * y) / MOMENT_DIVISOR + (f.c * twiceArea) / 2;
|
|
86
|
+
return twiceArea < 0 ? -signed : signed;
|
|
87
|
+
}
|
|
88
|
+
function distinct(functions) {
|
|
89
|
+
const same = (f, g) => Math.abs(f.a - g.a) <= SAME_BOUND && Math.abs(f.b - g.b) <= SAME_BOUND && Math.abs(f.c - g.c) <= SAME_BOUND;
|
|
90
|
+
return functions.filter((f, index) => functions.findIndex((g) => same(f, g)) === index);
|
|
91
|
+
}
|
|
92
|
+
function pairs(bounds) {
|
|
93
|
+
const tops = distinct(bounds.tops);
|
|
94
|
+
const bottoms = distinct(bounds.bottoms);
|
|
95
|
+
return tops.flatMap((top, k) => bottoms.map((bottom, m) => ({
|
|
96
|
+
top,
|
|
97
|
+
bottom,
|
|
98
|
+
keeps: [
|
|
99
|
+
...tops.filter((_, j) => j !== k).map((other) => minus(top, other)),
|
|
100
|
+
...bottoms.filter((_, j) => j !== m).map((other) => minus(other, bottom)),
|
|
101
|
+
minus(bottom, top),
|
|
102
|
+
...bounds.sides,
|
|
103
|
+
],
|
|
104
|
+
})));
|
|
105
|
+
}
|
|
106
|
+
function within(polygon, keeps) {
|
|
107
|
+
return keeps.reduce((region, keep) => (region.length ? clipped(region, keep) : region), [...polygon]);
|
|
108
|
+
}
|
|
109
|
+
export function volumeBetween(footprint, bounds) {
|
|
110
|
+
return pairs(bounds).reduce((sum, { top, bottom, keeps }) => {
|
|
111
|
+
const region = within(footprint, keeps);
|
|
112
|
+
return sum + (region.length > 2 ? integral(region, minus(top, bottom)) : 0);
|
|
113
|
+
}, 0);
|
|
114
|
+
}
|
|
115
|
+
export function areaWithin(footprint, sides) {
|
|
116
|
+
const region = within(footprint, sides);
|
|
117
|
+
return region.length > 2 ? integral(region, { a: 0, b: 0, c: 1 }) : 0;
|
|
118
|
+
}
|
|
119
|
+
export function areaBetween(start, end, bounds) {
|
|
120
|
+
const length = length2(subtract2(end, start));
|
|
121
|
+
const at = (f, s) => valueAt(f, [start[0] + (end[0] - start[0]) * s, start[1] + (end[1] - start[1]) * s]);
|
|
122
|
+
let low = 0;
|
|
123
|
+
let high = 1;
|
|
124
|
+
for (const side of bounds.sides) {
|
|
125
|
+
const from = at(side, 0);
|
|
126
|
+
const to = at(side, 1);
|
|
127
|
+
if (from === to) {
|
|
128
|
+
high = from > 0 ? low : high;
|
|
129
|
+
}
|
|
130
|
+
else if (to > from) {
|
|
131
|
+
high = Math.min(high, from / (from - to));
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
low = Math.max(low, from / (from - to));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!(high > low)) {
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
const all = [...bounds.tops, ...bounds.bottoms];
|
|
141
|
+
const breaks = new Set([low, high]);
|
|
142
|
+
all.forEach((f, i) => all.slice(i + 1).forEach((g) => {
|
|
143
|
+
const from = at(f, 0) - at(g, 0);
|
|
144
|
+
const to = at(f, 1) - at(g, 1);
|
|
145
|
+
const crossing = from === to ? Number.NaN : from / (from - to);
|
|
146
|
+
if (crossing > low && crossing < high) {
|
|
147
|
+
breaks.add(crossing);
|
|
148
|
+
}
|
|
149
|
+
}));
|
|
150
|
+
const lowest = (s) => Math.min(...bounds.tops.map((f) => at(f, s)));
|
|
151
|
+
const highest = (s) => Math.max(...bounds.bottoms.map((f) => at(f, s)));
|
|
152
|
+
const height = (s) => Math.max(0, lowest(s) - highest(s));
|
|
153
|
+
const points = [...breaks].sort((a, b) => a - b);
|
|
154
|
+
return points.slice(1).reduce((sum, to, index) => {
|
|
155
|
+
const from = points[index];
|
|
156
|
+
return sum + length * (to - from) * (height(from) + height(to)) / 2;
|
|
157
|
+
}, 0);
|
|
158
|
+
}
|
|
159
|
+
export function withLevels(bounds, bottom, top) {
|
|
160
|
+
return { tops: [...bounds.tops, { a: 0, b: 0, c: top }], bottoms: [...bounds.bottoms, { a: 0, b: 0, c: bottom }], sides: bounds.sides };
|
|
161
|
+
}
|
|
162
|
+
export function boundsOf(base, top, planes) {
|
|
163
|
+
const level = (height) => ({ a: 0, b: 0, c: height });
|
|
164
|
+
const surface = (plane) => {
|
|
165
|
+
const [x, y, z] = plane.normal;
|
|
166
|
+
return { a: -x / z, b: -y / z, c: plane.origin[2] + (plane.origin[0] * x + plane.origin[1] * y) / z };
|
|
167
|
+
};
|
|
168
|
+
return {
|
|
169
|
+
tops: [level(top), ...planes.filter((plane) => plane.normal[2] > UPRIGHT_NORMAL).map(surface)],
|
|
170
|
+
bottoms: [level(base), ...planes.filter((plane) => plane.normal[2] < -UPRIGHT_NORMAL).map(surface)],
|
|
171
|
+
sides: planes.filter((plane) => Math.abs(plane.normal[2]) <= UPRIGHT_NORMAL).map((plane) => ({
|
|
172
|
+
a: plane.normal[0],
|
|
173
|
+
b: plane.normal[1],
|
|
174
|
+
c: -(plane.origin[0] * plane.normal[0] + plane.origin[1] * plane.normal[1]),
|
|
175
|
+
})),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
export function extrudedBodyOf(reader, product) {
|
|
179
|
+
const body = representationOf(reader, product, "Body");
|
|
180
|
+
const items = body === undefined ? null : reader.attribute(body, "Items");
|
|
181
|
+
const only = isList(items) && items.length === 1 ? items[0] : undefined;
|
|
182
|
+
if (!isReference(only) || reader.entity(only.ref).type !== "IfcExtrudedAreaSolid") {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
const solid = only.ref;
|
|
186
|
+
const profile = reader.attribute(solid, "SweptArea");
|
|
187
|
+
const depth = reader.attribute(solid, "Depth");
|
|
188
|
+
const shape = isReference(profile) ? profileShape(reader, profile.ref) : undefined;
|
|
189
|
+
const direction = directionOf(reader, reader.attribute(solid, "ExtrudedDirection"), [0, 0, 1]);
|
|
190
|
+
const length = Math.hypot(direction[0], direction[1], direction[2]);
|
|
191
|
+
if (!shape || typeof depth !== "number" || !(length > 0)) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
return { solid, frame: frameOf(reader, reader.attribute(solid, "Position")), depth: depth * Math.abs(direction[2]) / length, shape };
|
|
195
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Base } from "@bitbybit-dev/base";
|
|
2
|
+
import type { IfcTransaction } from "../model/transaction";
|
|
3
|
+
import type { EntityWriter } from "./entity-writer";
|
|
4
|
+
export declare function moveElement(tx: IfcTransaction, writer: EntityWriter, element: number, translation: Base.Vector3, turnDegrees: number): void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { WORLD_AXES, relativeAxes } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
|
|
2
|
+
import { isReference } from "../step/values";
|
|
3
|
+
import { DEGREES_TO_RADIANS, FILLING } from "./constants";
|
|
4
|
+
import { absoluteFrame, movePlacement, objectPlacementOf, productsPlacedBy } from "./placement";
|
|
5
|
+
import { firstItem, representationOf, unwrapClippings } from "./representations";
|
|
6
|
+
import { relatingOf } from "./relationships";
|
|
7
|
+
import { describeObject } from "./spatial";
|
|
8
|
+
import { joinsOf } from "./walls";
|
|
9
|
+
const OPENING = "IfcOpeningElement";
|
|
10
|
+
const WALL = "IfcWall";
|
|
11
|
+
const ELEMENT = "IfcElement";
|
|
12
|
+
const SPATIAL_STRUCTURE = "IfcSpatialStructureElement";
|
|
13
|
+
const SPACE = "IfcSpace";
|
|
14
|
+
function refuseUnmovable(reader, element, placement) {
|
|
15
|
+
const type = reader.entity(element).type;
|
|
16
|
+
const what = describeObject(reader, element);
|
|
17
|
+
if (reader.schema.isSubtypeOf(type, OPENING) || relatingOf(reader, FILLING, element) !== undefined) {
|
|
18
|
+
throw new Error(`${what} moves with its wall: use openings.edit`);
|
|
19
|
+
}
|
|
20
|
+
if (reader.schema.isSubtypeOf(type, SPATIAL_STRUCTURE) && !reader.schema.isSubtypeOf(type, SPACE)) {
|
|
21
|
+
throw new Error(`${what} is part of the model's structure; a storey moves with spatial.setElevation`);
|
|
22
|
+
}
|
|
23
|
+
if (reader.schema.isSubtypeOf(type, WALL) && joinsOf(reader, element).length) {
|
|
24
|
+
throw new Error(`${what} is joined to other walls; move it with walls.edit, which keeps its joins`);
|
|
25
|
+
}
|
|
26
|
+
if (reader.schema.isSubtypeOf(type, WALL) && clippingsOf(reader, element).length) {
|
|
27
|
+
throw new Error(`${what} is clipped; move it with walls.edit, which keeps its clippings where they are`);
|
|
28
|
+
}
|
|
29
|
+
const sharer = productsPlacedBy(reader, placement).find((product) => product !== element);
|
|
30
|
+
if (sharer !== undefined) {
|
|
31
|
+
throw new Error(`${what} shares its placement with ${describeObject(reader, sharer)}, so it cannot move on its own`);
|
|
32
|
+
}
|
|
33
|
+
const parent = reader.attribute(placement, "PlacementRelTo");
|
|
34
|
+
const host = isReference(parent) ? productsPlacedBy(reader, parent.ref).find((product) => reader.schema.isSubtypeOf(reader.entity(product).type, ELEMENT)) : undefined;
|
|
35
|
+
if (host !== undefined) {
|
|
36
|
+
throw new Error(`${what} is placed on ${describeObject(reader, host)} and moves with it`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function clippingsOf(reader, wall) {
|
|
40
|
+
const body = representationOf(reader, wall, "Body");
|
|
41
|
+
return body === undefined ? [] : unwrapClippings(reader, firstItem(reader, body))[1];
|
|
42
|
+
}
|
|
43
|
+
function turned(vector, cos, sin) {
|
|
44
|
+
return [vector[0] * cos - vector[1] * sin, vector[0] * sin + vector[1] * cos, vector[2]];
|
|
45
|
+
}
|
|
46
|
+
export function moveElement(tx, writer, element, translation, turnDegrees) {
|
|
47
|
+
const placement = objectPlacementOf(tx, element);
|
|
48
|
+
refuseUnmovable(tx, element, placement);
|
|
49
|
+
const parent = tx.attribute(placement, "PlacementRelTo");
|
|
50
|
+
const world = absoluteFrame(tx, placement);
|
|
51
|
+
const angle = turnDegrees * DEGREES_TO_RADIANS;
|
|
52
|
+
const cos = Math.cos(angle);
|
|
53
|
+
const sin = Math.sin(angle);
|
|
54
|
+
const moved = {
|
|
55
|
+
origin: [world.origin[0] + translation[0], world.origin[1] + translation[1], world.origin[2] + translation[2]],
|
|
56
|
+
x: turned(world.x, cos, sin),
|
|
57
|
+
y: turned(world.y, cos, sin),
|
|
58
|
+
z: turned(world.z, cos, sin),
|
|
59
|
+
};
|
|
60
|
+
movePlacement(tx, writer, placement, relativeAxes(isReference(parent) ? absoluteFrame(tx, parent.ref) : WORLD_AXES, moved));
|
|
61
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IfcTransaction } from "../model/transaction";
|
|
2
|
+
import type { ModelReader, OpeningChange, WallGeometry, WallOpening, WallOpeningPlace, WallOpeningSpec } from "./build-types";
|
|
3
|
+
import type { EntityWriter } from "./entity-writer";
|
|
4
|
+
export declare function openingMargin(thickness: number): number;
|
|
5
|
+
export declare function openingBody(tx: IfcTransaction, writer: EntityWriter, width: number, height: number, depth: number): number;
|
|
6
|
+
export declare function createWallOpening(tx: IfcTransaction, writer: EntityWriter, spec: WallOpeningSpec, tolerance: number): WallOpening;
|
|
7
|
+
export declare function openingsOf(reader: ModelReader, element: number): number[];
|
|
8
|
+
export declare function fillingsOf(reader: ModelReader, opening: number): number[];
|
|
9
|
+
export declare function readWallOpening(reader: ModelReader, wall: WallGeometry, wallPlacement: number, opening: number, tolerance: number): WallOpeningPlace | undefined;
|
|
10
|
+
export declare function carriedWith(reader: ModelReader, opening: number, wallPlacement: number): boolean;
|
|
11
|
+
export declare function carryOpening(tx: IfcTransaction, writer: EntityWriter, place: WallOpeningPlace, next: WallOpeningPlace, before: WallGeometry, after: WallGeometry): void;
|
|
12
|
+
export declare function editWallOpening(tx: IfcTransaction, writer: EntityWriter, element: number, change: OpeningChange, tolerance: number): void;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { composeAxes, pointToLocal, pointToWorld, relativeAxes } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
|
|
2
|
+
import { enumValue, isList, isReference, ref } from "../step/values";
|
|
3
|
+
import { FILLING, OPENING_MARGIN_RATIO, VOIDING } from "./constants";
|
|
4
|
+
import { movePlacement, objectPlacementOf, parentPlacementOf, relativeFrame } from "./placement";
|
|
5
|
+
import { relatingOf } from "./relationships";
|
|
6
|
+
import { describeObject } from "./spatial";
|
|
7
|
+
import { bodyContext } from "./contexts";
|
|
8
|
+
import { extrusionCorners } from "./solids";
|
|
9
|
+
import { axisFrame, readWall } from "./wall-geometry";
|
|
10
|
+
import { replaceRepresentation, representationOf } from "./representations";
|
|
11
|
+
const OPENING = "IfcOpeningElement";
|
|
12
|
+
const WALL = "IfcWall";
|
|
13
|
+
export function openingMargin(thickness) {
|
|
14
|
+
return thickness * OPENING_MARGIN_RATIO;
|
|
15
|
+
}
|
|
16
|
+
export function openingBody(tx, writer, width, height, depth) {
|
|
17
|
+
const profile = writer.create("IfcRectangleProfileDef", {
|
|
18
|
+
ProfileType: enumValue("AREA"),
|
|
19
|
+
Position: ref(writer.placement2([width / 2, height / 2])),
|
|
20
|
+
XDim: width,
|
|
21
|
+
YDim: height,
|
|
22
|
+
});
|
|
23
|
+
const solid = writer.extrusion(profile, { origin: [0, 0, 0], x: [1, 0, 0], y: [0, 0, 1], z: [0, -1, 0] }, depth);
|
|
24
|
+
return writer.shapeRepresentation(bodyContext(tx, writer), "Body", "SweptSolid", [solid]);
|
|
25
|
+
}
|
|
26
|
+
function frameAt(wall, offset, sill, across) {
|
|
27
|
+
const axis = axisFrame(wall);
|
|
28
|
+
return Object.assign(Object.assign({}, axis), { origin: pointToWorld(axis, [offset, across, sill]) });
|
|
29
|
+
}
|
|
30
|
+
function openingFrame(wall, offset, sill) {
|
|
31
|
+
return frameAt(wall, offset, sill, wall.high + openingMargin(wall.high - wall.low));
|
|
32
|
+
}
|
|
33
|
+
function middleFrame(wall, offset, sill) {
|
|
34
|
+
return frameAt(wall, offset, sill, (wall.low + wall.high) / 2);
|
|
35
|
+
}
|
|
36
|
+
function requireFit(wall, offset, width, height, tolerance) {
|
|
37
|
+
if (!(width > 0) || !(height > 0)) {
|
|
38
|
+
throw new Error(`An opening needs a width and a height of more than zero, got ${width} by ${height}`);
|
|
39
|
+
}
|
|
40
|
+
if (offset < -tolerance || offset + width > wall.length + tolerance) {
|
|
41
|
+
throw new Error(`The opening from ${offset} to ${offset + width} does not fit along the wall, which is ${wall.length} long`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function createWallOpening(tx, writer, spec, tolerance) {
|
|
45
|
+
var _a;
|
|
46
|
+
const wall = readWall(tx, spec.wall);
|
|
47
|
+
requireFit(wall, spec.offset, spec.width, spec.height, tolerance);
|
|
48
|
+
const thickness = wall.high - wall.low;
|
|
49
|
+
const margin = openingMargin(thickness);
|
|
50
|
+
const placement = writer.localPlacement(objectPlacementOf(tx, spec.wall), openingFrame(wall, spec.offset, spec.sill));
|
|
51
|
+
const opening = writer.create("IfcOpeningElement", {
|
|
52
|
+
GlobalId: tx.globalId(spec.id),
|
|
53
|
+
Name: (_a = spec.name) !== null && _a !== void 0 ? _a : null,
|
|
54
|
+
ObjectPlacement: ref(placement),
|
|
55
|
+
Representation: ref(writer.productShape([openingBody(tx, writer, spec.width, spec.height, thickness + 2 * margin)])),
|
|
56
|
+
PredefinedType: enumValue("OPENING"),
|
|
57
|
+
});
|
|
58
|
+
writer.create(VOIDING.relationship, { GlobalId: tx.globalId(undefined), [VOIDING.relating]: ref(spec.wall), [VOIDING.related]: ref(opening) });
|
|
59
|
+
return { opening, placement, wallFace: -(thickness + margin), thickness };
|
|
60
|
+
}
|
|
61
|
+
export function openingsOf(reader, element) {
|
|
62
|
+
return reader.referencesTo(element).flatMap((user) => {
|
|
63
|
+
const relating = reader.typeOf(user) === VOIDING.relationship ? reader.attribute(user, VOIDING.relating) : null;
|
|
64
|
+
const opening = isReference(relating) && relating.ref === element ? reader.attribute(user, VOIDING.related) : null;
|
|
65
|
+
return isReference(opening) ? [opening.ref] : [];
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export function fillingsOf(reader, opening) {
|
|
69
|
+
return reader.referencesTo(opening).flatMap((user) => {
|
|
70
|
+
const relating = reader.typeOf(user) === FILLING.relationship ? reader.attribute(user, FILLING.relating) : null;
|
|
71
|
+
const filling = isReference(relating) && relating.ref === opening ? reader.attribute(user, FILLING.related) : null;
|
|
72
|
+
return isReference(filling) ? [filling.ref] : [];
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function bodyCorners(reader, opening) {
|
|
76
|
+
const body = representationOf(reader, opening, "Body");
|
|
77
|
+
const items = body === undefined ? null : reader.attribute(body, "Items");
|
|
78
|
+
const only = isList(items) && items.length === 1 ? items[0] : undefined;
|
|
79
|
+
return isReference(only) ? extrusionCorners(reader, only.ref) : [];
|
|
80
|
+
}
|
|
81
|
+
function spanOf(values, tolerance) {
|
|
82
|
+
const low = Math.min(...values);
|
|
83
|
+
const high = Math.max(...values);
|
|
84
|
+
return values.every((value) => Math.abs(value - low) <= tolerance || Math.abs(value - high) <= tolerance) ? [low, high] : undefined;
|
|
85
|
+
}
|
|
86
|
+
export function readWallOpening(reader, wall, wallPlacement, opening, tolerance) {
|
|
87
|
+
if (parentPlacementOf(reader, opening) !== wallPlacement) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const placement = objectPlacementOf(reader, opening);
|
|
91
|
+
const axis = axisFrame(wall);
|
|
92
|
+
const frame = relativeFrame(reader, placement);
|
|
93
|
+
const local = bodyCorners(reader, opening).map((corner) => pointToLocal(axis, pointToWorld(frame, corner)));
|
|
94
|
+
const along = spanOf(local.map((point) => point[0]), tolerance);
|
|
95
|
+
const across = spanOf(local.map((point) => point[1]), tolerance);
|
|
96
|
+
const up = spanOf(local.map((point) => point[2]), tolerance);
|
|
97
|
+
if (!local.length || !along || !across || !up || across[0] > wall.low + tolerance || across[1] < wall.high - tolerance) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
return { opening, placement, offset: along[0], sill: up[0], width: along[1] - along[0], height: up[1] - up[0] };
|
|
101
|
+
}
|
|
102
|
+
export function carriedWith(reader, opening, wallPlacement) {
|
|
103
|
+
if (parentPlacementOf(reader, opening) !== wallPlacement) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const own = objectPlacementOf(reader, opening);
|
|
107
|
+
return fillingsOf(reader, opening).every((filling) => {
|
|
108
|
+
const parent = parentPlacementOf(reader, filling);
|
|
109
|
+
return parent === own || parent === wallPlacement;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
export function carryOpening(tx, writer, place, next, before, after) {
|
|
113
|
+
const thickness = after.high - after.low;
|
|
114
|
+
const from = relativeFrame(tx, place.placement);
|
|
115
|
+
const to = openingFrame(after, next.offset, next.sill);
|
|
116
|
+
const fromMiddle = middleFrame(before, place.offset, place.sill);
|
|
117
|
+
const toMiddle = middleFrame(after, next.offset, next.sill);
|
|
118
|
+
movePlacement(tx, writer, place.placement, to);
|
|
119
|
+
replaceRepresentation(tx, place.opening, representationOf(tx, place.opening, "Body"), openingBody(tx, writer, next.width, next.height, thickness + 2 * openingMargin(thickness)));
|
|
120
|
+
for (const filling of fillingsOf(tx, place.opening).filter((placed) => objectPlacementOf(tx, placed) !== place.placement)) {
|
|
121
|
+
const placement = objectPlacementOf(tx, filling);
|
|
122
|
+
const onOpening = parentPlacementOf(tx, filling) === place.placement;
|
|
123
|
+
const inWall = composeAxes(toMiddle, relativeAxes(fromMiddle, onOpening ? composeAxes(from, relativeFrame(tx, placement)) : relativeFrame(tx, placement)));
|
|
124
|
+
movePlacement(tx, writer, placement, onOpening ? relativeAxes(to, inWall) : inWall);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function openingOf(reader, element) {
|
|
128
|
+
if (reader.schema.isSubtypeOf(reader.entity(element).type, OPENING)) {
|
|
129
|
+
return element;
|
|
130
|
+
}
|
|
131
|
+
const opening = relatingOf(reader, FILLING, element);
|
|
132
|
+
if (opening === undefined) {
|
|
133
|
+
throw new Error(`${describeObject(reader, element)} is neither an opening nor a door or window in one`);
|
|
134
|
+
}
|
|
135
|
+
return opening;
|
|
136
|
+
}
|
|
137
|
+
export function editWallOpening(tx, writer, element, change, tolerance) {
|
|
138
|
+
var _a, _b, _c, _d;
|
|
139
|
+
const opening = openingOf(tx, element);
|
|
140
|
+
const host = relatingOf(tx, VOIDING, opening);
|
|
141
|
+
if (host === undefined || !tx.schema.isSubtypeOf(tx.entity(host).type, WALL)) {
|
|
142
|
+
throw new Error(`${describeObject(tx, opening)} is not an opening in a wall`);
|
|
143
|
+
}
|
|
144
|
+
const wall = readWall(tx, host);
|
|
145
|
+
const wallPlacement = objectPlacementOf(tx, host);
|
|
146
|
+
const place = readWallOpening(tx, wall, wallPlacement, opening, tolerance);
|
|
147
|
+
if (place === undefined || !carriedWith(tx, opening, wallPlacement)) {
|
|
148
|
+
throw new Error(`${describeObject(tx, opening)} is not an opening this library can move`);
|
|
149
|
+
}
|
|
150
|
+
if (fillingsOf(tx, opening).length && (change.width !== undefined || change.height !== undefined)) {
|
|
151
|
+
throw new Error(`${describeObject(tx, opening)} takes its size from the door or window in it`);
|
|
152
|
+
}
|
|
153
|
+
const next = Object.assign(Object.assign({}, place), { offset: (_a = change.offset) !== null && _a !== void 0 ? _a : place.offset, sill: (_b = change.sill) !== null && _b !== void 0 ? _b : place.sill, width: (_c = change.width) !== null && _c !== void 0 ? _c : place.width, height: (_d = change.height) !== null && _d !== void 0 ? _d : place.height });
|
|
154
|
+
requireFit(wall, next.offset, next.width, next.height, tolerance);
|
|
155
|
+
carryOpening(tx, writer, place, next, wall, wall);
|
|
156
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Base } from "@bitbybit-dev/base";
|
|
2
|
+
export declare function cleanOutline(points: readonly Base.Point2[], tolerance: number, what: string): Base.Point2[];
|
|
3
|
+
export declare function isInsideOrOn(point: Base.Point2, outline: readonly Base.Point2[], tolerance: number): boolean;
|
|
4
|
+
export declare function edgesCross(first: readonly Base.Point2[], second: readonly Base.Point2[], tolerance: number): boolean;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { isInsidePolygon2, signedArea2 } from "@bitbybit-dev/base/lib/api/services/helpers/polygons";
|
|
2
|
+
import { cross2, length2, subtract2 } from "@bitbybit-dev/base/lib/api/services/helpers/vectors";
|
|
3
|
+
import { MIN_OUTLINE_POINTS } from "./constants";
|
|
4
|
+
import { distanceToSegment } from "./math";
|
|
5
|
+
export function cleanOutline(points, tolerance, what) {
|
|
6
|
+
const first = points[0];
|
|
7
|
+
const last = points[points.length - 1];
|
|
8
|
+
const open = first && last && points.length > 1 && length2(subtract2(first, last)) <= tolerance ? points.slice(0, -1) : [...points];
|
|
9
|
+
if (open.length < MIN_OUTLINE_POINTS) {
|
|
10
|
+
throw new Error(`The ${what} needs at least ${MIN_OUTLINE_POINTS} distinct points, got ${open.length}`);
|
|
11
|
+
}
|
|
12
|
+
open.forEach((point, index) => {
|
|
13
|
+
const next = open[(index + 1) % open.length];
|
|
14
|
+
if (length2(subtract2(next, point)) <= tolerance) {
|
|
15
|
+
throw new Error(`The ${what} repeats its point ${index} at ${index + 1}, which leaves a side of no length`);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
if (!(Math.abs(signedArea2(open)) > tolerance * tolerance)) {
|
|
19
|
+
throw new Error(`The ${what} must enclose an area`);
|
|
20
|
+
}
|
|
21
|
+
return open;
|
|
22
|
+
}
|
|
23
|
+
export function isInsideOrOn(point, outline, tolerance) {
|
|
24
|
+
return isInsidePolygon2(point, outline) || outline.some((corner, index) => distanceToSegment(point, corner, outline[(index + 1) % outline.length]) <= tolerance);
|
|
25
|
+
}
|
|
26
|
+
function sideOf(point, start, end) {
|
|
27
|
+
return cross2(subtract2(end, start), subtract2(point, start)) / length2(subtract2(end, start));
|
|
28
|
+
}
|
|
29
|
+
function apart(first, second, tolerance) {
|
|
30
|
+
return (first > tolerance && second < -tolerance) || (first < -tolerance && second > tolerance);
|
|
31
|
+
}
|
|
32
|
+
export function edgesCross(first, second, tolerance) {
|
|
33
|
+
return first.some((a, i) => {
|
|
34
|
+
const b = first[(i + 1) % first.length];
|
|
35
|
+
return second.some((c, j) => {
|
|
36
|
+
const d = second[(j + 1) % second.length];
|
|
37
|
+
return apart(sideOf(c, a, b), sideOf(d, a, b), tolerance) && apart(sideOf(a, c, d), sideOf(b, c, d), tolerance);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Base } from "@bitbybit-dev/base";
|
|
2
|
+
import type { IfcTransaction } from "../model/transaction";
|
|
3
|
+
import type { IfcValue } from "../step/step-types";
|
|
4
|
+
import type { EntityReader, ModelReader } from "./build-types";
|
|
5
|
+
import type { EntityWriter } from "./entity-writer";
|
|
6
|
+
import type { Frame3 } from "./build-types";
|
|
7
|
+
export declare function numbersOf(value: IfcValue, what: string): readonly number[];
|
|
8
|
+
export declare function pointOf(reader: EntityReader, value: IfcValue): Base.Point3;
|
|
9
|
+
export declare function directionOf(reader: EntityReader, value: IfcValue, fallback: Base.Vector3): Base.Vector3;
|
|
10
|
+
export declare function axisPlacementFrame(reader: EntityReader, placement: number): Frame3;
|
|
11
|
+
export declare function relativeFrame(reader: EntityReader, localPlacement: number): Frame3;
|
|
12
|
+
export declare function absoluteFrame(reader: EntityReader, localPlacement: number, known?: Map<number, Frame3>): Frame3;
|
|
13
|
+
export declare function objectPlacementOf(reader: EntityReader, product: number): number;
|
|
14
|
+
export declare function parentPlacementOf(reader: EntityReader, product: number): number | undefined;
|
|
15
|
+
export declare function movePlacement(tx: IfcTransaction, writer: EntityWriter, placement: number, frame: Frame3): void;
|
|
16
|
+
export declare function productsPlacedBy(reader: ModelReader, placement: number): number[];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { WORLD_AXES, composeAxes } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
|
|
2
|
+
import { isList, isReference, ref } from "../step/values";
|
|
3
|
+
import { frameFrom } from "./math";
|
|
4
|
+
const MAX_PLACEMENT_DEPTH = 256;
|
|
5
|
+
const PRODUCT = "IfcProduct";
|
|
6
|
+
const LOCAL_PLACEMENT = "IfcLocalPlacement";
|
|
7
|
+
export function numbersOf(value, what) {
|
|
8
|
+
if (!isList(value) || !value.every((item) => typeof item === "number" && Number.isFinite(item))) {
|
|
9
|
+
throw new Error(`${what} is not a list of finite numbers`);
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
export function pointOf(reader, value) {
|
|
14
|
+
var _a, _b, _c;
|
|
15
|
+
if (!isReference(value)) {
|
|
16
|
+
throw new Error("A point is missing");
|
|
17
|
+
}
|
|
18
|
+
const coordinates = numbersOf(reader.attribute(value.ref, "Coordinates"), `#${value.ref} Coordinates`);
|
|
19
|
+
return [(_a = coordinates[0]) !== null && _a !== void 0 ? _a : 0, (_b = coordinates[1]) !== null && _b !== void 0 ? _b : 0, (_c = coordinates[2]) !== null && _c !== void 0 ? _c : 0];
|
|
20
|
+
}
|
|
21
|
+
export function directionOf(reader, value, fallback) {
|
|
22
|
+
var _a, _b, _c;
|
|
23
|
+
if (!isReference(value)) {
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
const ratios = numbersOf(reader.attribute(value.ref, "DirectionRatios"), `#${value.ref} DirectionRatios`);
|
|
27
|
+
return [(_a = ratios[0]) !== null && _a !== void 0 ? _a : 0, (_b = ratios[1]) !== null && _b !== void 0 ? _b : 0, (_c = ratios[2]) !== null && _c !== void 0 ? _c : 0];
|
|
28
|
+
}
|
|
29
|
+
export function axisPlacementFrame(reader, placement) {
|
|
30
|
+
const entity = reader.entity(placement);
|
|
31
|
+
if (entity.type !== "IfcAxis2Placement2D" && entity.type !== "IfcAxis2Placement3D") {
|
|
32
|
+
throw new Error(`#${placement} is an ${entity.type}, not an axis placement`);
|
|
33
|
+
}
|
|
34
|
+
const origin = pointOf(reader, reader.attribute(placement, "Location"));
|
|
35
|
+
if (entity.type === "IfcAxis2Placement2D") {
|
|
36
|
+
const x = directionOf(reader, reader.attribute(placement, "RefDirection"), [1, 0, 0]);
|
|
37
|
+
return frameFrom(origin, [0, 0, 1], [x[0], x[1], 0]);
|
|
38
|
+
}
|
|
39
|
+
const z = directionOf(reader, reader.attribute(placement, "Axis"), [0, 0, 1]);
|
|
40
|
+
const alongX = Math.abs(z[1]) + Math.abs(z[2]) === 0;
|
|
41
|
+
const x = directionOf(reader, reader.attribute(placement, "RefDirection"), alongX ? [0, 1, 0] : [1, 0, 0]);
|
|
42
|
+
return frameFrom(origin, z, x);
|
|
43
|
+
}
|
|
44
|
+
export function relativeFrame(reader, localPlacement) {
|
|
45
|
+
const relative = reader.attribute(localPlacement, "RelativePlacement");
|
|
46
|
+
if (!isReference(relative)) {
|
|
47
|
+
throw new Error(`#${localPlacement} has no relative placement`);
|
|
48
|
+
}
|
|
49
|
+
return axisPlacementFrame(reader, relative.ref);
|
|
50
|
+
}
|
|
51
|
+
export function absoluteFrame(reader, localPlacement, known) {
|
|
52
|
+
const chain = [];
|
|
53
|
+
const seen = new Set();
|
|
54
|
+
let base = WORLD_AXES;
|
|
55
|
+
for (let current = localPlacement; current !== undefined;) {
|
|
56
|
+
const placed = known === null || known === void 0 ? void 0 : known.get(current);
|
|
57
|
+
if (placed) {
|
|
58
|
+
base = placed;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
if (seen.has(current) || chain.length > MAX_PLACEMENT_DEPTH) {
|
|
62
|
+
throw new Error(`The placement #${localPlacement} is relative to itself through #${current}`);
|
|
63
|
+
}
|
|
64
|
+
seen.add(current);
|
|
65
|
+
if (reader.entity(current).type !== LOCAL_PLACEMENT) {
|
|
66
|
+
throw new Error(`#${current} is a ${reader.entity(current).type}, which this library does not place`);
|
|
67
|
+
}
|
|
68
|
+
chain.unshift(current);
|
|
69
|
+
const parent = reader.attribute(current, "PlacementRelTo");
|
|
70
|
+
current = isReference(parent) ? parent.ref : undefined;
|
|
71
|
+
}
|
|
72
|
+
return chain.reduce((frame, placement) => {
|
|
73
|
+
const placed = composeAxes(frame, relativeFrame(reader, placement));
|
|
74
|
+
known === null || known === void 0 ? void 0 : known.set(placement, placed);
|
|
75
|
+
return placed;
|
|
76
|
+
}, base);
|
|
77
|
+
}
|
|
78
|
+
export function objectPlacementOf(reader, product) {
|
|
79
|
+
const placement = reader.attribute(product, "ObjectPlacement");
|
|
80
|
+
if (!isReference(placement)) {
|
|
81
|
+
throw new Error(`#${product} has no placement`);
|
|
82
|
+
}
|
|
83
|
+
return placement.ref;
|
|
84
|
+
}
|
|
85
|
+
export function parentPlacementOf(reader, product) {
|
|
86
|
+
const placement = reader.attribute(product, "ObjectPlacement");
|
|
87
|
+
const parent = isReference(placement) && reader.entity(placement.ref).type === LOCAL_PLACEMENT ? reader.attribute(placement.ref, "PlacementRelTo") : null;
|
|
88
|
+
return isReference(parent) ? parent.ref : undefined;
|
|
89
|
+
}
|
|
90
|
+
export function movePlacement(tx, writer, placement, frame) {
|
|
91
|
+
const old = tx.attribute(placement, "RelativePlacement");
|
|
92
|
+
tx.update(placement, { RelativePlacement: ref(writer.placement3(frame)) });
|
|
93
|
+
if (isReference(old)) {
|
|
94
|
+
tx.dropIfUnused([old.ref]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export function productsPlacedBy(reader, placement) {
|
|
98
|
+
return reader.referencesTo(placement).filter((user) => {
|
|
99
|
+
var _a;
|
|
100
|
+
if (!reader.schema.isSubtypeOf((_a = reader.typeOf(user)) !== null && _a !== void 0 ? _a : "", PRODUCT)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const placed = reader.attribute(user, "ObjectPlacement");
|
|
104
|
+
return isReference(placed) && placed.ref === placement;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProfileSpec } from "./build-types";
|
|
2
|
+
import type { EntityWriter } from "./entity-writer";
|
|
3
|
+
export declare function checkProfile(profile: ProfileSpec): void;
|
|
4
|
+
export declare function writeProfile(writer: EntityWriter, profile: ProfileSpec, name: string | undefined): number;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as Inputs from "../api/inputs";
|
|
2
|
+
import { enumValue, ref } from "../step/values";
|
|
3
|
+
import { requirePositive } from "./checks";
|
|
4
|
+
export function checkProfile(profile) {
|
|
5
|
+
if (profile.kind === Inputs.IFC.profileKindEnum.rectangle) {
|
|
6
|
+
requirePositive(profile.width, "profile's width");
|
|
7
|
+
requirePositive(profile.depth, "profile's depth");
|
|
8
|
+
}
|
|
9
|
+
else if (profile.kind === Inputs.IFC.profileKindEnum.circle) {
|
|
10
|
+
requirePositive(profile.radius, "profile's radius");
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
requirePositive(profile.width, "profile's width");
|
|
14
|
+
requirePositive(profile.depth, "profile's depth");
|
|
15
|
+
requirePositive(profile.webThickness, "profile's web thickness");
|
|
16
|
+
requirePositive(profile.flangeThickness, "profile's flange thickness");
|
|
17
|
+
if (profile.webThickness >= profile.width) {
|
|
18
|
+
throw new Error("An I section's web must be thinner than its flanges are wide");
|
|
19
|
+
}
|
|
20
|
+
if (2 * profile.flangeThickness >= profile.depth) {
|
|
21
|
+
throw new Error("An I section's two flanges must be thinner together than its depth");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function writeProfile(writer, profile, name) {
|
|
26
|
+
checkProfile(profile);
|
|
27
|
+
const common = { ProfileType: enumValue("AREA"), ProfileName: name !== null && name !== void 0 ? name : null, Position: ref(writer.placement2([0, 0])) };
|
|
28
|
+
if (profile.kind === Inputs.IFC.profileKindEnum.rectangle) {
|
|
29
|
+
return writer.create("IfcRectangleProfileDef", Object.assign(Object.assign({}, common), { XDim: profile.width, YDim: profile.depth }));
|
|
30
|
+
}
|
|
31
|
+
if (profile.kind === Inputs.IFC.profileKindEnum.circle) {
|
|
32
|
+
return writer.create("IfcCircleProfileDef", Object.assign(Object.assign({}, common), { Radius: profile.radius }));
|
|
33
|
+
}
|
|
34
|
+
return writer.create("IfcIShapeProfileDef", Object.assign(Object.assign({}, common), { OverallWidth: profile.width, OverallDepth: profile.depth, WebThickness: profile.webThickness, FlangeThickness: profile.flangeThickness }));
|
|
35
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ModelSnapshot } from "../model/snapshot";
|
|
2
|
+
import * as Inputs from "../api/inputs";
|
|
3
|
+
import type { MeasuredUnit, NewProject } from "./build-types";
|
|
4
|
+
export declare const SCHEMA_FOR_NEW_MODELS = "IFC4";
|
|
5
|
+
export declare const DESIGN_TRANSFER_VIEW = "ViewDefinition [DesignTransferView_V1.0]";
|
|
6
|
+
export declare function precisionFor(unit: Inputs.IFC.lengthUnitEnum): number;
|
|
7
|
+
export declare function createProject(project: NewProject): ModelSnapshot;
|
|
8
|
+
export declare function projectOf(model: ModelSnapshot): number;
|
|
9
|
+
export declare function siPerUnit(model: ModelSnapshot, unitType: MeasuredUnit): number;
|
|
10
|
+
export declare function millimetresPerUnit(model: ModelSnapshot): number;
|
|
11
|
+
export declare function radiansPerUnit(model: ModelSnapshot): number;
|