@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,159 @@
|
|
|
1
|
+
import { pointToLocal, vectorToLocal } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
|
|
2
|
+
import { signedArea2 } from "@bitbybit-dev/base/lib/api/services/helpers/polygons";
|
|
3
|
+
import { add2, leftOf2, length2, scale2, subtract2 } from "@bitbybit-dev/base/lib/api/services/helpers/vectors";
|
|
4
|
+
import * as Inputs from "../api/inputs";
|
|
5
|
+
import { isEnumeration, isReference } from "../step/values";
|
|
6
|
+
import { requiredReference } from "./checks";
|
|
7
|
+
import { PARALLEL_TOLERANCE, VERTICAL_COSINE } from "./constants";
|
|
8
|
+
import { layerSetThickness, materialOf } from "./materials";
|
|
9
|
+
import { distanceToLine, intersectLines, normalize2, parameterOnLine, placedOnPlan } from "./math";
|
|
10
|
+
import { absoluteFrame, axisPlacementFrame, directionOf, objectPlacementOf } from "./placement";
|
|
11
|
+
import { describeObject } from "./spatial";
|
|
12
|
+
import { firstItem, representationOf, unwrapClippings } from "./representations";
|
|
13
|
+
import { polylinePoints } from "./curves";
|
|
14
|
+
function axisPoints(reader, wall) {
|
|
15
|
+
const axis = representationOf(reader, wall, "Axis");
|
|
16
|
+
if (axis === undefined) {
|
|
17
|
+
throw new Error(`${describeObject(reader, wall)} has no Axis representation, so it is not a wall this library can edit`);
|
|
18
|
+
}
|
|
19
|
+
const curve = firstItem(reader, axis);
|
|
20
|
+
const points = polylinePoints(reader, curve);
|
|
21
|
+
if (points === undefined) {
|
|
22
|
+
throw new Error(`${describeObject(reader, wall)} has an ${reader.entity(curve).type} axis, which this library does not edit`);
|
|
23
|
+
}
|
|
24
|
+
if (points.length < 2) {
|
|
25
|
+
throw new Error(`${describeObject(reader, wall)} has an axis of fewer than two points`);
|
|
26
|
+
}
|
|
27
|
+
const start = points[0];
|
|
28
|
+
const end = points[points.length - 1];
|
|
29
|
+
const run = end[0] - start[0];
|
|
30
|
+
if (!(run > 0) || Math.abs(end[1] - start[1]) > run * PARALLEL_TOLERANCE) {
|
|
31
|
+
throw new Error(`${describeObject(reader, wall)} has an axis that does not run along its placement's X axis, so the layers IFC offsets along that placement's Y axis do not lie across it`);
|
|
32
|
+
}
|
|
33
|
+
return [start, end];
|
|
34
|
+
}
|
|
35
|
+
function layerBounds(reader, wall) {
|
|
36
|
+
const usage = materialOf(reader, wall);
|
|
37
|
+
if (usage === undefined || reader.entity(usage).type !== "IfcMaterialLayerSetUsage") {
|
|
38
|
+
throw new Error(`${describeObject(reader, wall)} has no material layer set usage, so its thickness is unknown`);
|
|
39
|
+
}
|
|
40
|
+
const layerSet = reader.attribute(usage, "ForLayerSet");
|
|
41
|
+
const offset = reader.attribute(usage, "OffsetFromReferenceLine");
|
|
42
|
+
const sense = reader.attribute(usage, "DirectionSense");
|
|
43
|
+
const thickness = isReference(layerSet) ? layerSetThickness(reader, layerSet.ref) : 0;
|
|
44
|
+
const start = typeof offset === "number" ? offset : 0;
|
|
45
|
+
return isEnumeration(sense) && sense.enum === "NEGATIVE" ? [start - thickness, start, usage] : [start, start + thickness, usage];
|
|
46
|
+
}
|
|
47
|
+
function verticalExtrusion(reader, wall, body) {
|
|
48
|
+
var _a;
|
|
49
|
+
const [solid] = unwrapClippings(reader, firstItem(reader, body));
|
|
50
|
+
const refusal = `${describeObject(reader, wall)} has a body this library does not edit as a wall: `;
|
|
51
|
+
if (reader.entity(solid).type !== "IfcExtrudedAreaSolid") {
|
|
52
|
+
throw new Error(`${refusal}an ${reader.entity(solid).type}, where a plan outline extruded upwards is expected`);
|
|
53
|
+
}
|
|
54
|
+
const depth = reader.attribute(solid, "Depth");
|
|
55
|
+
const position = reader.attribute(solid, "Position");
|
|
56
|
+
const frame = isReference(position) ? axisPlacementFrame(reader, position.ref) : undefined;
|
|
57
|
+
const direction = directionOf(reader, reader.attribute(solid, "ExtrudedDirection"), [0, 0, 1]);
|
|
58
|
+
const upwards = Math.hypot(direction[0], direction[1], direction[2]);
|
|
59
|
+
if (typeof depth !== "number" || !(depth > 0) || (frame !== undefined && frame.z[2] < VERTICAL_COSINE) || !(direction[2] / upwards >= VERTICAL_COSINE)) {
|
|
60
|
+
throw new Error(`${refusal}its extrusion does not rise straight up from a plan outline`);
|
|
61
|
+
}
|
|
62
|
+
return [depth, (_a = frame === null || frame === void 0 ? void 0 : frame.origin[2]) !== null && _a !== void 0 ? _a : 0];
|
|
63
|
+
}
|
|
64
|
+
export function readWall(reader, wall) {
|
|
65
|
+
const frame = absoluteFrame(reader, objectPlacementOf(reader, wall));
|
|
66
|
+
if (frame.z[2] < VERTICAL_COSINE) {
|
|
67
|
+
throw new Error(`${describeObject(reader, wall)} is not upright, so it is not a wall this library can edit`);
|
|
68
|
+
}
|
|
69
|
+
const [localStart, localEnd] = axisPoints(reader, wall);
|
|
70
|
+
const start = placedOnPlan(frame, localStart);
|
|
71
|
+
const end = placedOnPlan(frame, localEnd);
|
|
72
|
+
const [low, high, usage] = layerBounds(reader, wall);
|
|
73
|
+
const body = representationOf(reader, wall, "Body");
|
|
74
|
+
const [height, base] = body === undefined ? [0, 0] : verticalExtrusion(reader, wall, body);
|
|
75
|
+
return { wall, frame, start, end, direction: normalize2(subtract2(end, start)), length: length2(subtract2(end, start)), low, high, height, base, body, usage };
|
|
76
|
+
}
|
|
77
|
+
export function axisFrame(geometry) {
|
|
78
|
+
const start = pointToLocal(geometry.frame, [geometry.start[0], geometry.start[1], 0]);
|
|
79
|
+
const end = pointToLocal(geometry.frame, [geometry.end[0], geometry.end[1], 0]);
|
|
80
|
+
const direction = normalize2([end[0] - start[0], end[1] - start[1]]);
|
|
81
|
+
return { origin: [start[0], start[1], geometry.base], x: [direction[0], direction[1], 0], y: [-direction[1], direction[0], 0], z: [0, 0, 1] };
|
|
82
|
+
}
|
|
83
|
+
export function alignmentOf(geometry, tolerance) {
|
|
84
|
+
if (Math.abs(geometry.low) <= tolerance) {
|
|
85
|
+
return Inputs.IFC.wallAlignmentEnum.left;
|
|
86
|
+
}
|
|
87
|
+
if (Math.abs(geometry.high) <= tolerance) {
|
|
88
|
+
return Inputs.IFC.wallAlignmentEnum.right;
|
|
89
|
+
}
|
|
90
|
+
return Math.abs(geometry.low + geometry.high) <= tolerance ? Inputs.IFC.wallAlignmentEnum.center : undefined;
|
|
91
|
+
}
|
|
92
|
+
export function layerSetOf(reader, geometry) {
|
|
93
|
+
return requiredReference(reader, geometry.usage, "ForLayerSet");
|
|
94
|
+
}
|
|
95
|
+
export function sideLine(geometry, across) {
|
|
96
|
+
return { point: add2(geometry.start, scale2(leftOf2(geometry.direction), across)), direction: geometry.direction };
|
|
97
|
+
}
|
|
98
|
+
export function axisLine(geometry) {
|
|
99
|
+
return { point: geometry.start, direction: geometry.direction };
|
|
100
|
+
}
|
|
101
|
+
export function endPoint(geometry, end) {
|
|
102
|
+
return end === "ATSTART" ? geometry.start : geometry.end;
|
|
103
|
+
}
|
|
104
|
+
export function mitreCut(wall, wallEnd, other, otherEnd, tolerance, parallelTolerance) {
|
|
105
|
+
const sameFlow = wallEnd !== otherEnd;
|
|
106
|
+
const pairs = sameFlow ? [[wall.high, other.high], [wall.low, other.low]] : [[wall.high, other.low], [wall.low, other.high]];
|
|
107
|
+
const corners = pairs.map(([mine, theirs]) => intersectLines(sideLine(wall, mine), sideLine(other, theirs), parallelTolerance));
|
|
108
|
+
const [first, second] = corners;
|
|
109
|
+
if (!first || !second) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
const along = subtract2(second, first);
|
|
113
|
+
if (length2(along) <= tolerance) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
return { point: first, direction: normalize2(along) };
|
|
117
|
+
}
|
|
118
|
+
export function buttCut(stem, stemEnd, through) {
|
|
119
|
+
const far = stemEnd === "ATSTART" ? stem.end : stem.start;
|
|
120
|
+
return sideLine(through, distanceToLine(far, axisLine(through)) >= 0 ? through.high : through.low);
|
|
121
|
+
}
|
|
122
|
+
export function footprintOf(geometry, cuts, tolerance, parallelTolerance) {
|
|
123
|
+
const toLocalLine = (line) => {
|
|
124
|
+
const point = pointToLocal(geometry.frame, [line.point[0], line.point[1], 0]);
|
|
125
|
+
const direction = vectorToLocal(geometry.frame, [line.direction[0], line.direction[1], 0]);
|
|
126
|
+
return { point: [point[0], point[1]], direction: normalize2([direction[0], direction[1]]) };
|
|
127
|
+
};
|
|
128
|
+
const startLocal = pointToLocal(geometry.frame, [geometry.start[0], geometry.start[1], 0]);
|
|
129
|
+
const endLocal = pointToLocal(geometry.frame, [geometry.end[0], geometry.end[1], 0]);
|
|
130
|
+
const axisDirection = normalize2([endLocal[0] - startLocal[0], endLocal[1] - startLocal[1]]);
|
|
131
|
+
const across = leftOf2(axisDirection);
|
|
132
|
+
const squareAt = (point) => ({ point: [point[0], point[1]], direction: across });
|
|
133
|
+
const startCut = cuts.start ? toLocalLine(cuts.start) : squareAt(startLocal);
|
|
134
|
+
const endCut = cuts.end ? toLocalLine(cuts.end) : squareAt(endLocal);
|
|
135
|
+
const side = (offset) => ({ point: add2([startLocal[0], startLocal[1]], scale2(across, offset)), direction: axisDirection });
|
|
136
|
+
const corner = (line, cut, fallback) => { var _a; return (_a = intersectLines(line, cut, parallelTolerance)) !== null && _a !== void 0 ? _a : intersectLines(line, fallback, parallelTolerance); };
|
|
137
|
+
const low = side(geometry.low);
|
|
138
|
+
const high = side(geometry.high);
|
|
139
|
+
const outline = [
|
|
140
|
+
corner(low, startCut, squareAt(startLocal)),
|
|
141
|
+
corner(low, endCut, squareAt(endLocal)),
|
|
142
|
+
corner(high, endCut, squareAt(endLocal)),
|
|
143
|
+
corner(high, startCut, squareAt(startLocal)),
|
|
144
|
+
];
|
|
145
|
+
const lowSpan = parameterOnLine(outline[1], low) - parameterOnLine(outline[0], low);
|
|
146
|
+
const highSpan = parameterOnLine(outline[2], high) - parameterOnLine(outline[3], high);
|
|
147
|
+
if (!(lowSpan > tolerance) || !(highSpan > tolerance) || !(signedArea2(outline) > 0)) {
|
|
148
|
+
throw new Error(`The joins of wall #${geometry.wall} leave it with no length on one face; it is too short for the walls it meets`);
|
|
149
|
+
}
|
|
150
|
+
return outline;
|
|
151
|
+
}
|
|
152
|
+
export function readableWall(reader, wall) {
|
|
153
|
+
try {
|
|
154
|
+
return readWall(reader, wall);
|
|
155
|
+
}
|
|
156
|
+
catch (_a) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { WORLD_AXES, pointToLocal, pointToWorld } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
|
|
2
|
+
import * as Inputs from "../api/inputs";
|
|
3
|
+
import { textValue } from "../step/values";
|
|
4
|
+
import { TYPE_DEFINITION } from "./constants";
|
|
5
|
+
import { openingsOf } from "./openings";
|
|
6
|
+
import { relatingOf } from "./relationships";
|
|
7
|
+
import { containerOf, storeyFrame } from "./spatial";
|
|
8
|
+
import { alignmentOf, layerSetOf, readWall } from "./wall-geometry";
|
|
9
|
+
import { joinsOf } from "./walls";
|
|
10
|
+
import { firstItem, unwrapClippings } from "./representations";
|
|
11
|
+
const END_OF = {
|
|
12
|
+
ATSTART: Inputs.IFC.wallEndEnum.start,
|
|
13
|
+
ATEND: Inputs.IFC.wallEndEnum.end,
|
|
14
|
+
ATPATH: Inputs.IFC.wallEndEnum.along,
|
|
15
|
+
};
|
|
16
|
+
function globalIdOf(reader, id) {
|
|
17
|
+
var _a;
|
|
18
|
+
return (_a = textValue(reader.attribute(id, "GlobalId"))) !== null && _a !== void 0 ? _a : "";
|
|
19
|
+
}
|
|
20
|
+
export function wallParametersOf(reader, wall, tolerance) {
|
|
21
|
+
var _a, _b, _c;
|
|
22
|
+
const geometry = readWall(reader, wall);
|
|
23
|
+
const storey = containerOf(reader, wall);
|
|
24
|
+
const plan = storey === undefined ? WORLD_AXES : storeyFrame(reader, storey);
|
|
25
|
+
const inPlan = (point) => {
|
|
26
|
+
const local = pointToLocal(plan, [point[0], point[1], plan.origin[2]]);
|
|
27
|
+
return [local[0], local[1]];
|
|
28
|
+
};
|
|
29
|
+
const type = relatingOf(reader, TYPE_DEFINITION, wall);
|
|
30
|
+
return {
|
|
31
|
+
globalId: globalIdOf(reader, wall),
|
|
32
|
+
name: (_a = textValue(reader.attribute(wall, "Name"))) !== null && _a !== void 0 ? _a : "",
|
|
33
|
+
storey: storey === undefined ? "" : globalIdOf(reader, storey),
|
|
34
|
+
start: inPlan(geometry.start),
|
|
35
|
+
end: inPlan(geometry.end),
|
|
36
|
+
height: geometry.height,
|
|
37
|
+
baseOffset: pointToLocal(plan, pointToWorld(geometry.frame, [0, 0, geometry.base]))[2],
|
|
38
|
+
thickness: geometry.high - geometry.low,
|
|
39
|
+
offset: geometry.low,
|
|
40
|
+
alignment: (_b = alignmentOf(geometry, tolerance)) !== null && _b !== void 0 ? _b : "",
|
|
41
|
+
layerSet: (_c = textValue(reader.attribute(layerSetOf(reader, geometry), "LayerSetName"))) !== null && _c !== void 0 ? _c : "",
|
|
42
|
+
wallType: type === undefined ? "" : globalIdOf(reader, type),
|
|
43
|
+
joins: joinsOf(reader, wall).map((join) => ({ other: globalIdOf(reader, join.other), at: END_OF[join.mine], otherAt: END_OF[join.theirs] })),
|
|
44
|
+
openings: openingsOf(reader, wall).map((opening) => globalIdOf(reader, opening)),
|
|
45
|
+
clippings: geometry.body === undefined ? 0 : unwrapClippings(reader, firstItem(reader, geometry.body))[1].length,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Base } from "@bitbybit-dev/base";
|
|
2
|
+
import * as Inputs from "../api/inputs";
|
|
3
|
+
import type { IfcTransaction } from "../model/transaction";
|
|
4
|
+
import type { ModelReader, WallBody, WallEnd, WallGeometry, WallJoin, WallSpec } from "./build-types";
|
|
5
|
+
import type { EntityWriter } from "./entity-writer";
|
|
6
|
+
export declare function offsetFor(alignment: Inputs.IFC.wallAlignmentEnum, thickness: number): number;
|
|
7
|
+
export declare function createWall(tx: IfcTransaction, writer: EntityWriter, spec: WallSpec, tolerance: number): number;
|
|
8
|
+
export declare function joinsOf(reader: ModelReader, wall: number): WallJoin[];
|
|
9
|
+
export declare function regenerateWall(tx: IfcTransaction, writer: EntityWriter, wall: number, tolerance: number, parallelTolerance: number, body?: WallBody): void;
|
|
10
|
+
export declare function stillMeet(a: WallGeometry, aEnd: WallEnd, b: WallGeometry, bEnd: WallEnd, tolerance: number, parallelTolerance: number): boolean;
|
|
11
|
+
export declare function connectWalls(tx: IfcTransaction, writer: EntityWriter, first: number, second: number, tolerance: number, parallelTolerance: number): void;
|
|
12
|
+
export declare function clipWall(tx: IfcTransaction, writer: EntityWriter, wall: number, origin: Base.Point3, normal: Base.Vector3): void;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { WORLD_AXES, pointToLocal, vectorToLocal } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
|
|
2
|
+
import { cross2, length2, subtract2 } from "@bitbybit-dev/base/lib/api/services/helpers/vectors";
|
|
3
|
+
import * as Inputs from "../api/inputs";
|
|
4
|
+
import { enumValue, isEnumeration, isReference, ref } from "../step/values";
|
|
5
|
+
import { WALL_CONNECTION, WALL_ENDS } from "./constants";
|
|
6
|
+
import { axisContext, bodyContext } from "./contexts";
|
|
7
|
+
import { associateNewMaterial, layerSetThickness, layerSetUsage } from "./materials";
|
|
8
|
+
import { distanceToLine, normalize2, parameterOnLine } from "./math";
|
|
9
|
+
import { objectPlacementOf } from "./placement";
|
|
10
|
+
import { containIn, describeObject } from "./spatial";
|
|
11
|
+
import { defineByType } from "./type-objects";
|
|
12
|
+
import { axisLine, buttCut, endPoint, footprintOf, mitreCut, readWall } from "./wall-geometry";
|
|
13
|
+
import { firstItem, replaceRepresentation, unwrapClippings } from "./representations";
|
|
14
|
+
const END_NAMES = { ATSTART: "start", ATEND: "end", ATPATH: "length" };
|
|
15
|
+
export function offsetFor(alignment, thickness) {
|
|
16
|
+
if (alignment === Inputs.IFC.wallAlignmentEnum.left) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
return alignment === Inputs.IFC.wallAlignmentEnum.right ? -thickness : -thickness / 2;
|
|
20
|
+
}
|
|
21
|
+
function isWallEnd(value) {
|
|
22
|
+
return WALL_ENDS.includes(value);
|
|
23
|
+
}
|
|
24
|
+
function clippedBody(writer, solid, halfSpaces) {
|
|
25
|
+
let item = solid;
|
|
26
|
+
for (const halfSpace of halfSpaces) {
|
|
27
|
+
item = writer.clipping(item, halfSpace);
|
|
28
|
+
}
|
|
29
|
+
return writer.shapeRepresentation(bodyContext(writer.tx, writer), "Body", halfSpaces.length ? "Clipping" : "SweptSolid", [item]);
|
|
30
|
+
}
|
|
31
|
+
function extrudedBody(writer, footprint, base, height, halfSpaces) {
|
|
32
|
+
const solid = writer.extrusion(writer.profile({ outer: footprint, holes: [] }), Object.assign(Object.assign({}, WORLD_AXES), { origin: [0, 0, base] }), height);
|
|
33
|
+
return clippedBody(writer, solid, halfSpaces);
|
|
34
|
+
}
|
|
35
|
+
export function createWall(tx, writer, spec, tolerance) {
|
|
36
|
+
var _a;
|
|
37
|
+
const along = subtract2(spec.end, spec.start);
|
|
38
|
+
const length = length2(along);
|
|
39
|
+
if (!(length > tolerance)) {
|
|
40
|
+
throw new Error("A wall's start and end must be apart");
|
|
41
|
+
}
|
|
42
|
+
if (!(spec.height > 0)) {
|
|
43
|
+
throw new Error(`A wall's height must be more than zero, got ${spec.height}`);
|
|
44
|
+
}
|
|
45
|
+
const thickness = layerSetThickness(tx, spec.layerSet);
|
|
46
|
+
if (!(thickness > 0)) {
|
|
47
|
+
throw new Error("A wall's layers must add up to more than zero thickness");
|
|
48
|
+
}
|
|
49
|
+
const direction = normalize2(along);
|
|
50
|
+
const offset = offsetFor(spec.alignment, thickness);
|
|
51
|
+
const placement = writer.localPlacement(objectPlacementOf(tx, spec.storey), {
|
|
52
|
+
origin: [spec.start[0], spec.start[1], spec.baseOffset],
|
|
53
|
+
x: [direction[0], direction[1], 0],
|
|
54
|
+
y: [-direction[1], direction[0], 0],
|
|
55
|
+
z: [0, 0, 1],
|
|
56
|
+
});
|
|
57
|
+
const axis = writer.shapeRepresentation(axisContext(tx, writer), "Axis", "Curve2D", [writer.polyCurve2([[0, 0], [length, 0]], false)]);
|
|
58
|
+
const footprint = [[0, offset], [length, offset], [length, offset + thickness], [0, offset + thickness]];
|
|
59
|
+
const wall = writer.create("IfcWall", {
|
|
60
|
+
GlobalId: tx.globalId(spec.id),
|
|
61
|
+
Name: (_a = spec.name) !== null && _a !== void 0 ? _a : null,
|
|
62
|
+
ObjectPlacement: ref(placement),
|
|
63
|
+
Representation: ref(writer.productShape([axis, extrudedBody(writer, footprint, 0, spec.height, [])])),
|
|
64
|
+
PredefinedType: spec.predefinedType === undefined ? null : enumValue(spec.predefinedType),
|
|
65
|
+
});
|
|
66
|
+
containIn(tx, writer, spec.storey, wall);
|
|
67
|
+
associateNewMaterial(tx, writer, [wall], layerSetUsage(writer, spec.layerSet, "AXIS2", "POSITIVE", offset));
|
|
68
|
+
if (spec.wallType !== undefined) {
|
|
69
|
+
defineByType(tx, writer, wall, spec.wallType);
|
|
70
|
+
}
|
|
71
|
+
return wall;
|
|
72
|
+
}
|
|
73
|
+
export function joinsOf(reader, wall) {
|
|
74
|
+
const joins = [];
|
|
75
|
+
for (const user of reader.referencesTo(wall)) {
|
|
76
|
+
if (reader.typeOf(user) !== WALL_CONNECTION) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const relating = reader.attribute(user, "RelatingElement");
|
|
80
|
+
const related = reader.attribute(user, "RelatedElement");
|
|
81
|
+
const relatingType = reader.attribute(user, "RelatingConnectionType");
|
|
82
|
+
const relatedType = reader.attribute(user, "RelatedConnectionType");
|
|
83
|
+
if (!isReference(relating) || !isReference(related) || !isEnumeration(relatingType) || !isEnumeration(relatedType) || !isWallEnd(relatingType.enum) || !isWallEnd(relatedType.enum)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (relating.ref === wall) {
|
|
87
|
+
joins.push({ mine: relatingType.enum, other: related.ref, theirs: relatedType.enum, relationship: user });
|
|
88
|
+
}
|
|
89
|
+
else if (related.ref === wall) {
|
|
90
|
+
joins.push({ mine: relatedType.enum, other: relating.ref, theirs: relatingType.enum, relationship: user });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return joins;
|
|
94
|
+
}
|
|
95
|
+
function cutsOf(reader, geometry, tolerance, parallelTolerance) {
|
|
96
|
+
let start;
|
|
97
|
+
let end;
|
|
98
|
+
for (const { mine, other, theirs } of joinsOf(reader, geometry.wall)) {
|
|
99
|
+
if (mine === "ATPATH") {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const otherGeometry = readWall(reader, other);
|
|
103
|
+
const cut = theirs === "ATPATH" ? buttCut(geometry, mine, otherGeometry) : mitreCut(geometry, mine, otherGeometry, theirs, tolerance, parallelTolerance);
|
|
104
|
+
if (mine === "ATSTART") {
|
|
105
|
+
start = cut;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
end = cut;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { start, end };
|
|
112
|
+
}
|
|
113
|
+
export function regenerateWall(tx, writer, wall, tolerance, parallelTolerance, body) {
|
|
114
|
+
var _a;
|
|
115
|
+
const read = readWall(tx, wall);
|
|
116
|
+
const geometry = body === undefined ? read : Object.assign(Object.assign({}, read), { height: body.height, base: body.base });
|
|
117
|
+
const footprint = footprintOf(geometry, cutsOf(tx, geometry, tolerance, parallelTolerance), tolerance, parallelTolerance);
|
|
118
|
+
const halfSpaces = (_a = body === null || body === void 0 ? void 0 : body.halfSpaces) !== null && _a !== void 0 ? _a : (read.body === undefined ? [] : unwrapClippings(tx, firstItem(tx, read.body))[1]);
|
|
119
|
+
replaceRepresentation(tx, wall, read.body, extrudedBody(writer, footprint, geometry.base, geometry.height, halfSpaces));
|
|
120
|
+
}
|
|
121
|
+
function overlapHeights(a, b, tolerance) {
|
|
122
|
+
const bottom = (wall) => wall.frame.origin[2] + wall.base;
|
|
123
|
+
return bottom(a) < bottom(b) + b.height - tolerance && bottom(b) < bottom(a) + a.height - tolerance;
|
|
124
|
+
}
|
|
125
|
+
function cornerGap(a, aEnd, b, bEnd) {
|
|
126
|
+
return length2(subtract2(endPoint(a, aEnd), endPoint(b, bEnd)));
|
|
127
|
+
}
|
|
128
|
+
function teeGap(stem, stemEnd, through, reach, tolerance, parallelTolerance) {
|
|
129
|
+
if (!(Math.abs(cross2(stem.direction, through.direction)) > parallelTolerance)) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
const point = endPoint(stem, stemEnd);
|
|
133
|
+
const across = distanceToLine(point, axisLine(through));
|
|
134
|
+
const along = parameterOnLine(point, axisLine(through));
|
|
135
|
+
if (across < through.low - reach || across > through.high + reach || along <= tolerance || along >= through.length - tolerance) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
return Math.max(0, through.low - across, across - through.high);
|
|
139
|
+
}
|
|
140
|
+
function reachOf(a, b) {
|
|
141
|
+
return Math.max(a.high - a.low, b.high - b.low);
|
|
142
|
+
}
|
|
143
|
+
export function stillMeet(a, aEnd, b, bEnd, tolerance, parallelTolerance) {
|
|
144
|
+
const reach = reachOf(a, b);
|
|
145
|
+
if (!overlapHeights(a, b, tolerance)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (aEnd === "ATPATH") {
|
|
149
|
+
return bEnd !== "ATPATH" && teeGap(b, bEnd, a, reach, tolerance, parallelTolerance) !== undefined;
|
|
150
|
+
}
|
|
151
|
+
if (bEnd === "ATPATH") {
|
|
152
|
+
return teeGap(a, aEnd, b, reach, tolerance, parallelTolerance) !== undefined;
|
|
153
|
+
}
|
|
154
|
+
return cornerGap(a, aEnd, b, bEnd) <= reach;
|
|
155
|
+
}
|
|
156
|
+
function findMeeting(stem, other, reach, tolerance, parallelTolerance) {
|
|
157
|
+
let corner;
|
|
158
|
+
let tee;
|
|
159
|
+
for (const stemEnd of ["ATSTART", "ATEND"]) {
|
|
160
|
+
for (const otherEnd of ["ATSTART", "ATEND"]) {
|
|
161
|
+
const gap = cornerGap(stem, stemEnd, other, otherEnd);
|
|
162
|
+
if (gap <= reach && (!corner || gap < corner.gap)) {
|
|
163
|
+
corner = { stemEnd, otherEnd, gap };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const gap = teeGap(stem, stemEnd, other, reach, tolerance, parallelTolerance);
|
|
167
|
+
if (gap !== undefined && (!tee || gap < tee.gap)) {
|
|
168
|
+
tee = { stemEnd, otherEnd: "ATPATH", gap };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return corner !== null && corner !== void 0 ? corner : tee;
|
|
172
|
+
}
|
|
173
|
+
function refuseJoinedEnd(tx, wall, end, partner) {
|
|
174
|
+
if (end === "ATPATH") {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const taken = joinsOf(tx, wall).find((join) => join.mine === end && join.other !== partner);
|
|
178
|
+
if (taken) {
|
|
179
|
+
throw new Error(`The ${END_NAMES[end]} of ${describeObject(tx, wall)} is already joined to ${describeObject(tx, taken.other)}; a wall end joins one other wall`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export function connectWalls(tx, writer, first, second, tolerance, parallelTolerance) {
|
|
183
|
+
if (first === second) {
|
|
184
|
+
throw new Error("A wall cannot be joined to itself");
|
|
185
|
+
}
|
|
186
|
+
const a = readWall(tx, first);
|
|
187
|
+
const b = readWall(tx, second);
|
|
188
|
+
for (const geometry of [a, b]) {
|
|
189
|
+
if (geometry.body === undefined) {
|
|
190
|
+
throw new Error(`${describeObject(tx, geometry.wall)} has no Body representation, so its height is unknown and it cannot be joined`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (!overlapHeights(a, b, tolerance)) {
|
|
194
|
+
throw new Error("The walls do not meet: they stand at heights that do not overlap");
|
|
195
|
+
}
|
|
196
|
+
const reach = reachOf(a, b);
|
|
197
|
+
const forward = findMeeting(a, b, reach, tolerance, parallelTolerance);
|
|
198
|
+
const backward = findMeeting(b, a, reach, tolerance, parallelTolerance);
|
|
199
|
+
const useBackward = !forward || (forward.otherEnd === "ATPATH" && backward !== undefined && backward.otherEnd !== "ATPATH");
|
|
200
|
+
const meeting = useBackward ? backward : forward;
|
|
201
|
+
if (!meeting) {
|
|
202
|
+
throw new Error("The walls do not meet: neither ends at the other's end or along its length");
|
|
203
|
+
}
|
|
204
|
+
const relating = useBackward ? second : first;
|
|
205
|
+
const related = useBackward ? first : second;
|
|
206
|
+
refuseJoinedEnd(tx, relating, meeting.stemEnd, related);
|
|
207
|
+
refuseJoinedEnd(tx, related, meeting.otherEnd, relating);
|
|
208
|
+
for (const join of joinsOf(tx, first)) {
|
|
209
|
+
if (join.other === second) {
|
|
210
|
+
tx.delete(join.relationship);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
writer.create(WALL_CONNECTION, {
|
|
214
|
+
GlobalId: tx.globalId(undefined),
|
|
215
|
+
RelatingElement: ref(relating),
|
|
216
|
+
RelatedElement: ref(related),
|
|
217
|
+
RelatingPriorities: [],
|
|
218
|
+
RelatedPriorities: [],
|
|
219
|
+
RelatedConnectionType: enumValue(meeting.otherEnd),
|
|
220
|
+
RelatingConnectionType: enumValue(meeting.stemEnd),
|
|
221
|
+
});
|
|
222
|
+
regenerateWall(tx, writer, relating, tolerance, parallelTolerance);
|
|
223
|
+
if (meeting.otherEnd !== "ATPATH") {
|
|
224
|
+
regenerateWall(tx, writer, related, tolerance, parallelTolerance);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
export function clipWall(tx, writer, wall, origin, normal) {
|
|
228
|
+
const geometry = readWall(tx, wall);
|
|
229
|
+
if (geometry.body === undefined) {
|
|
230
|
+
throw new Error(`${describeObject(tx, wall)} has no body to clip`);
|
|
231
|
+
}
|
|
232
|
+
if (!(Math.hypot(normal[0], normal[1], normal[2]) > 0)) {
|
|
233
|
+
throw new Error("A clipping plane needs a normal of some length");
|
|
234
|
+
}
|
|
235
|
+
const halfSpace = writer.halfSpace(pointToLocal(geometry.frame, origin), vectorToLocal(geometry.frame, normal), [1, 0, 0]);
|
|
236
|
+
const [solid, halfSpaces] = unwrapClippings(tx, firstItem(tx, geometry.body));
|
|
237
|
+
replaceRepresentation(tx, wall, geometry.body, clippedBody(writer, solid, [...halfSpaces, halfSpace]));
|
|
238
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RefinedSurfaceMesh } from "@bitbybit-dev/base/lib/api/services/helpers/helper-types";
|
|
2
|
+
import type { ModelSnapshot } from "../model/snapshot";
|
|
3
|
+
import type { CurveSampler } from "./curves";
|
|
4
|
+
import type { EdgeSampler } from "./edges";
|
|
5
|
+
export declare class AdvancedFaces {
|
|
6
|
+
private readonly model;
|
|
7
|
+
private readonly curves;
|
|
8
|
+
private readonly edges;
|
|
9
|
+
private readonly surfaces;
|
|
10
|
+
constructor(model: ModelSnapshot, curves: CurveSampler, edges: EdgeSampler);
|
|
11
|
+
face(face: number, bounds: readonly number[]): RefinedSurfaceMesh;
|
|
12
|
+
private surface;
|
|
13
|
+
private fitting;
|
|
14
|
+
private bound;
|
|
15
|
+
private parameters;
|
|
16
|
+
private windingOf;
|
|
17
|
+
private leastWound;
|
|
18
|
+
private assembled;
|
|
19
|
+
private shiftedInto;
|
|
20
|
+
private stitched;
|
|
21
|
+
private closedAtPole;
|
|
22
|
+
private across;
|
|
23
|
+
private oriented;
|
|
24
|
+
private surfaceNormal;
|
|
25
|
+
private value;
|
|
26
|
+
}
|
|
27
|
+
export declare function faceBounds(model: ModelSnapshot, face: number): readonly number[];
|