@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,14 @@
|
|
|
1
|
+
import type { Base } from "@bitbybit-dev/base";
|
|
2
|
+
export declare class RecipeBuilder {
|
|
3
|
+
private readonly nodes;
|
|
4
|
+
private readonly roots;
|
|
5
|
+
private f64;
|
|
6
|
+
private i32;
|
|
7
|
+
private f64Length;
|
|
8
|
+
private i32Length;
|
|
9
|
+
numbers(values: readonly number[]): Base.RecipeRange;
|
|
10
|
+
indices(values: readonly number[]): Base.RecipeRange;
|
|
11
|
+
node(node: Base.RecipeNode): number;
|
|
12
|
+
root(root: Base.RecipeRoot): void;
|
|
13
|
+
build(millimetresPerUnit: number, tolerance: number, groups?: readonly Base.RecipeGroup[]): Base.Recipe;
|
|
14
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const INITIAL_NUMBERS = 1024;
|
|
2
|
+
function grown(buffer, needed, make) {
|
|
3
|
+
if (needed <= buffer.length) {
|
|
4
|
+
return buffer;
|
|
5
|
+
}
|
|
6
|
+
let size = buffer.length;
|
|
7
|
+
while (size < needed) {
|
|
8
|
+
size *= 2;
|
|
9
|
+
}
|
|
10
|
+
const next = make(size);
|
|
11
|
+
next.set(buffer);
|
|
12
|
+
return next;
|
|
13
|
+
}
|
|
14
|
+
export class RecipeBuilder {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.nodes = [];
|
|
17
|
+
this.roots = [];
|
|
18
|
+
this.f64 = new Float64Array(INITIAL_NUMBERS);
|
|
19
|
+
this.i32 = new Int32Array(INITIAL_NUMBERS);
|
|
20
|
+
this.f64Length = 0;
|
|
21
|
+
this.i32Length = 0;
|
|
22
|
+
}
|
|
23
|
+
numbers(values) {
|
|
24
|
+
this.f64 = grown(this.f64, this.f64Length + values.length, (size) => new Float64Array(size));
|
|
25
|
+
this.f64.set(values, this.f64Length);
|
|
26
|
+
const range = [this.f64Length, values.length];
|
|
27
|
+
this.f64Length += values.length;
|
|
28
|
+
return range;
|
|
29
|
+
}
|
|
30
|
+
indices(values) {
|
|
31
|
+
this.i32 = grown(this.i32, this.i32Length + values.length, (size) => new Int32Array(size));
|
|
32
|
+
this.i32.set(values, this.i32Length);
|
|
33
|
+
const range = [this.i32Length, values.length];
|
|
34
|
+
this.i32Length += values.length;
|
|
35
|
+
return range;
|
|
36
|
+
}
|
|
37
|
+
node(node) {
|
|
38
|
+
this.nodes.push(node);
|
|
39
|
+
return this.nodes.length - 1;
|
|
40
|
+
}
|
|
41
|
+
root(root) {
|
|
42
|
+
this.roots.push(root);
|
|
43
|
+
}
|
|
44
|
+
build(millimetresPerUnit, tolerance, groups = []) {
|
|
45
|
+
return Object.assign({ format: "bitbybit.recipe", version: 1, millimetresPerUnit,
|
|
46
|
+
tolerance, buffers: { f64: this.f64.slice(0, this.f64Length), i32: this.i32.slice(0, this.i32Length) }, nodes: this.nodes, roots: this.roots }, (groups.length ? { groups: [...groups] } : {}));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ModelSnapshot } from "../model/snapshot";
|
|
2
|
+
import type { IfcValue } from "../step/step-types";
|
|
3
|
+
export declare function referencesOf(value: IfcValue, what: string): number[];
|
|
4
|
+
export declare function referencesIn(model: ModelSnapshot, id: number, attribute: string): readonly number[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { isList, isReference } from "../step/values";
|
|
2
|
+
import { UnsupportedGeometryError } from "./errors";
|
|
3
|
+
export function referencesOf(value, what) {
|
|
4
|
+
if (!isList(value)) {
|
|
5
|
+
throw new UnsupportedGeometryError(`${what} is not a list`);
|
|
6
|
+
}
|
|
7
|
+
return value.map((item) => {
|
|
8
|
+
if (!isReference(item)) {
|
|
9
|
+
throw new UnsupportedGeometryError(`${what} holds something other than a reference`);
|
|
10
|
+
}
|
|
11
|
+
return item.ref;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export function referencesIn(model, id, attribute) {
|
|
15
|
+
var _a;
|
|
16
|
+
return (_a = model.referenceList(id)) !== null && _a !== void 0 ? _a : referencesOf(model.valueOf(model.peek(id), attribute), `#${id} ${attribute}`);
|
|
17
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CircleRegion, PolygonRegion, ProfileRegion } from "./geometry-types";
|
|
2
|
+
import type { RecipeBuilder } from "./recipe-builder";
|
|
3
|
+
export declare function profileNode(builder: RecipeBuilder, region: PolygonRegion | CircleRegion, radius?: number): number;
|
|
4
|
+
export declare function regionSolid(builder: RecipeBuilder, region: ProfileRegion, make: (profile: number, part: PolygonRegion | CircleRegion) => number): number;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function flat(points) {
|
|
2
|
+
return points.flatMap(([x, y]) => [x, y]);
|
|
3
|
+
}
|
|
4
|
+
export function profileNode(builder, region, radius = region.kind === "circle" ? region.radius : 0) {
|
|
5
|
+
if (region.kind === "circle") {
|
|
6
|
+
return builder.node({ op: "circle", center: region.center, radius });
|
|
7
|
+
}
|
|
8
|
+
return builder.node({ op: "polygon", points: builder.numbers(flat(region.outer)), holes: region.holes.map((hole) => builder.numbers(flat(hole))) });
|
|
9
|
+
}
|
|
10
|
+
export function regionSolid(builder, region, make) {
|
|
11
|
+
switch (region.kind) {
|
|
12
|
+
case "composite": {
|
|
13
|
+
const parts = region.parts.map((part) => regionSolid(builder, part, make));
|
|
14
|
+
return parts.length === 1 ? parts[0] : builder.node({ op: "compound", of: parts });
|
|
15
|
+
}
|
|
16
|
+
case "circle": {
|
|
17
|
+
const solid = make(profileNode(builder, region), region);
|
|
18
|
+
if (!(region.innerRadius > 0)) {
|
|
19
|
+
return solid;
|
|
20
|
+
}
|
|
21
|
+
return builder.node({ op: "difference", of: solid, tools: [make(profileNode(builder, region, region.innerRadius), region)] });
|
|
22
|
+
}
|
|
23
|
+
default:
|
|
24
|
+
return make(profileNode(builder, region), region);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const FULL_TURN: number;
|
|
2
|
+
export declare const CLOSED_CURVE_RATIO = 1e-9;
|
|
3
|
+
export declare const NEWTON_STEPS = 30;
|
|
4
|
+
export declare const NEWTON_TOLERANCE = 1e-12;
|
|
5
|
+
export declare const SEED_STEPS = 16;
|
|
6
|
+
export declare const NEWTON_DAMPING = 0.25;
|
|
7
|
+
export declare const FIT_RATIO = 1e-7;
|
|
8
|
+
export declare const SPLINE_STEP = 0.000001;
|
|
9
|
+
export declare const NORMAL_STEP = 0.000001;
|
|
10
|
+
export declare const MIN_LOOP_POINTS = 3;
|
|
11
|
+
export declare const TRIANGLE_CORNERS = 3;
|
|
12
|
+
export declare const PROFILE_DENSITY = 8;
|
|
13
|
+
export declare const FIT_PROBES = 8;
|
|
14
|
+
export declare const FIT_TOLERANCE = 0.000001;
|
|
15
|
+
export declare const CUT_SAMPLES = 64;
|
|
16
|
+
export declare const CUT_TURN: number;
|
|
17
|
+
export declare const POLE_RATIO = 1e-9;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const FULL_TURN = 2 * Math.PI;
|
|
2
|
+
export const CLOSED_CURVE_RATIO = 1e-9;
|
|
3
|
+
export const NEWTON_STEPS = 30;
|
|
4
|
+
export const NEWTON_TOLERANCE = 1e-12;
|
|
5
|
+
export const SEED_STEPS = 16;
|
|
6
|
+
export const NEWTON_DAMPING = 0.25;
|
|
7
|
+
export const FIT_RATIO = 1e-7;
|
|
8
|
+
export const SPLINE_STEP = 1e-6;
|
|
9
|
+
export const NORMAL_STEP = 1e-6;
|
|
10
|
+
export const MIN_LOOP_POINTS = 3;
|
|
11
|
+
export const TRIANGLE_CORNERS = 3;
|
|
12
|
+
export const PROFILE_DENSITY = 8;
|
|
13
|
+
export const FIT_PROBES = 8;
|
|
14
|
+
export const FIT_TOLERANCE = 1e-6;
|
|
15
|
+
export const CUT_SAMPLES = 64;
|
|
16
|
+
export const CUT_TURN = (2 * Math.PI) / 32;
|
|
17
|
+
export const POLE_RATIO = 1e-9;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type FrameAxes } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
|
|
2
|
+
import type { ModelSnapshot } from "../model/snapshot";
|
|
3
|
+
import { CurveSampler } from "./curves";
|
|
4
|
+
import type { ParametricSurface } from "./geometry-types";
|
|
5
|
+
export declare function plane(frame: FrameAxes): ParametricSurface;
|
|
6
|
+
export declare function parametricSurface(model: ModelSnapshot, curves: CurveSampler, surface: number, divided?: boolean): ParametricSurface | undefined;
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { WORLD_AXES, pointToLocal, pointToWorld, vectorToWorld } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
|
|
2
|
+
import { expandedKnots, knotDomain, nurbsSurfacePoint } from "@bitbybit-dev/base/lib/api/services/helpers/nurbs";
|
|
3
|
+
import { add3, cross3, dot3, length3, scale3, subtract3 } from "@bitbybit-dev/base/lib/api/services/helpers/vectors";
|
|
4
|
+
import { axisPlacementFrame, directionOf, pointOf } from "../build/placement";
|
|
5
|
+
import { isList, isReference } from "../step/values";
|
|
6
|
+
import { CurveSampler } from "./curves";
|
|
7
|
+
import { UnsupportedGeometryError } from "./errors";
|
|
8
|
+
import { CLOSED_CURVE_RATIO, FIT_RATIO, POLE_RATIO, PROFILE_DENSITY, FULL_TURN, NEWTON_DAMPING, NEWTON_STEPS, NEWTON_TOLERANCE, SEED_STEPS, SPLINE_STEP } from "./surfaces.constants";
|
|
9
|
+
const Z_AXIS = [0, 0, 1];
|
|
10
|
+
const NO_PERIODS = [undefined, undefined];
|
|
11
|
+
const noPole = () => undefined;
|
|
12
|
+
function frameOf(model, value) {
|
|
13
|
+
return isReference(value) ? axisPlacementFrame(model, value.ref) : WORLD_AXES;
|
|
14
|
+
}
|
|
15
|
+
function positive(model, surface, name) {
|
|
16
|
+
const value = model.attribute(surface, name);
|
|
17
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !(value > 0)) {
|
|
18
|
+
throw new UnsupportedGeometryError(`#${surface} has no ${name} above zero`);
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
function polyline(points) {
|
|
23
|
+
const lengths = [0];
|
|
24
|
+
for (let at = 1; at < points.length; at++) {
|
|
25
|
+
lengths.push(lengths[at - 1] + length3(subtract3(points[at], points[at - 1])));
|
|
26
|
+
}
|
|
27
|
+
const total = lengths[lengths.length - 1];
|
|
28
|
+
return { points, lengths, closed: total > 0 && length3(subtract3(points[0], points[points.length - 1])) <= CLOSED_CURVE_RATIO * total };
|
|
29
|
+
}
|
|
30
|
+
function pointAlong(line, at) {
|
|
31
|
+
const { points, lengths } = line;
|
|
32
|
+
const total = lengths[lengths.length - 1];
|
|
33
|
+
const s = line.closed ? ((at % total) + total) % total : Math.min(Math.max(at, 0), total);
|
|
34
|
+
let segment = 1;
|
|
35
|
+
while (segment < lengths.length - 1 && lengths[segment] < s) {
|
|
36
|
+
segment++;
|
|
37
|
+
}
|
|
38
|
+
const span = lengths[segment] - lengths[segment - 1];
|
|
39
|
+
const t = span > 0 ? (s - lengths[segment - 1]) / span : 0;
|
|
40
|
+
return add3(points[segment - 1], scale3(subtract3(points[segment], points[segment - 1]), t));
|
|
41
|
+
}
|
|
42
|
+
function lengthNearest(line, point, flatten) {
|
|
43
|
+
let best = 0;
|
|
44
|
+
let bestDistance = Infinity;
|
|
45
|
+
const target = flatten(point);
|
|
46
|
+
for (let at = 1; at < line.points.length; at++) {
|
|
47
|
+
const a = flatten(line.points[at - 1]);
|
|
48
|
+
const along = subtract3(flatten(line.points[at]), a);
|
|
49
|
+
const squared = dot3(along, along);
|
|
50
|
+
const t = squared > 0 ? Math.min(1, Math.max(0, dot3(subtract3(target, a), along) / squared)) : 0;
|
|
51
|
+
const d = length3(subtract3(target, add3(a, scale3(along, t))));
|
|
52
|
+
if (d < bestDistance) {
|
|
53
|
+
bestDistance = d;
|
|
54
|
+
best = line.lengths[at - 1] + t * (line.lengths[at] - line.lengths[at - 1]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return best;
|
|
58
|
+
}
|
|
59
|
+
function periodOf(line) {
|
|
60
|
+
return line.closed ? line.lengths[line.lengths.length - 1] : undefined;
|
|
61
|
+
}
|
|
62
|
+
export function plane(frame) {
|
|
63
|
+
return {
|
|
64
|
+
periods: NO_PERIODS,
|
|
65
|
+
point: (u, v) => pointToWorld(frame, [u, v, 0]),
|
|
66
|
+
parameters: (point) => {
|
|
67
|
+
const [u, v] = pointToLocal(frame, point);
|
|
68
|
+
return [u, v];
|
|
69
|
+
},
|
|
70
|
+
pole: noPole,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function cylinder(frame, radius) {
|
|
74
|
+
return {
|
|
75
|
+
periods: [FULL_TURN, undefined],
|
|
76
|
+
point: (u, v) => pointToWorld(frame, [radius * Math.cos(u), radius * Math.sin(u), v]),
|
|
77
|
+
parameters: (point) => {
|
|
78
|
+
const [x, y, z] = pointToLocal(frame, point);
|
|
79
|
+
return [Math.atan2(y, x), z];
|
|
80
|
+
},
|
|
81
|
+
pole: noPole,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function sphere(frame, radius) {
|
|
85
|
+
return {
|
|
86
|
+
periods: [FULL_TURN, undefined],
|
|
87
|
+
point: (u, v) => pointToWorld(frame, [radius * Math.cos(v) * Math.cos(u), radius * Math.cos(v) * Math.sin(u), radius * Math.sin(v)]),
|
|
88
|
+
parameters: (point) => {
|
|
89
|
+
const [x, y, z] = pointToLocal(frame, point);
|
|
90
|
+
const across = Math.hypot(x, y);
|
|
91
|
+
return [across <= POLE_RATIO * radius ? Number.NaN : Math.atan2(y, x), Math.atan2(z, across)];
|
|
92
|
+
},
|
|
93
|
+
pole: (axis, side) => (axis === 1 ? (side * Math.PI) / 2 : undefined),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function torus(frame, major, minor) {
|
|
97
|
+
return {
|
|
98
|
+
periods: [FULL_TURN, FULL_TURN],
|
|
99
|
+
point: (u, v) => {
|
|
100
|
+
const r = major + minor * Math.cos(v);
|
|
101
|
+
return pointToWorld(frame, [r * Math.cos(u), r * Math.sin(u), minor * Math.sin(v)]);
|
|
102
|
+
},
|
|
103
|
+
parameters: (point) => {
|
|
104
|
+
const [x, y, z] = pointToLocal(frame, point);
|
|
105
|
+
return [Math.atan2(y, x), Math.atan2(z, Math.hypot(x, y) - major)];
|
|
106
|
+
},
|
|
107
|
+
pole: noPole,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function revolution(profile, origin, axis) {
|
|
111
|
+
const offsetOf = (point) => subtract3(point, origin);
|
|
112
|
+
const radialOf = (point) => {
|
|
113
|
+
const offset = offsetOf(point);
|
|
114
|
+
return subtract3(offset, scale3(axis, dot3(offset, axis)));
|
|
115
|
+
};
|
|
116
|
+
const reference = profile.points.map(radialOf).find((radial) => length3(radial) > 0);
|
|
117
|
+
if (reference === undefined) {
|
|
118
|
+
throw new UnsupportedGeometryError("A surface of revolution has its whole profile on its axis");
|
|
119
|
+
}
|
|
120
|
+
const x = scale3(reference, 1 / length3(reference));
|
|
121
|
+
const y = cross3(axis, x);
|
|
122
|
+
const halfPlane = (point) => [length3(radialOf(point)), dot3(offsetOf(point), axis), 0];
|
|
123
|
+
const ends = [profile.points[0], profile.points[profile.points.length - 1]].map((point) => length3(radialOf(point)) <= CLOSED_CURVE_RATIO * profile.lengths[profile.lengths.length - 1]);
|
|
124
|
+
return {
|
|
125
|
+
periods: [periodOf(profile), FULL_TURN],
|
|
126
|
+
point: (u, v) => {
|
|
127
|
+
const [radius, height] = halfPlane(pointAlong(profile, u));
|
|
128
|
+
return add3(origin, add3(scale3(axis, height), add3(scale3(x, radius * Math.cos(v)), scale3(y, radius * Math.sin(v)))));
|
|
129
|
+
},
|
|
130
|
+
parameters: (point) => {
|
|
131
|
+
const radial = radialOf(point);
|
|
132
|
+
const onAxis = length3(radial) <= POLE_RATIO * profile.lengths[profile.lengths.length - 1];
|
|
133
|
+
return [lengthNearest(profile, point, halfPlane), onAxis ? Number.NaN : Math.atan2(dot3(radial, y), dot3(radial, x))];
|
|
134
|
+
},
|
|
135
|
+
pole: (along, side) => {
|
|
136
|
+
if (along !== 0) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
const end = side > 0 ? 1 : 0;
|
|
140
|
+
return ends[end] ? (side > 0 ? profile.lengths[profile.lengths.length - 1] : 0) : undefined;
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function extrusion(curve, direction) {
|
|
145
|
+
const flatten = (point) => subtract3(point, scale3(direction, dot3(point, direction)));
|
|
146
|
+
return {
|
|
147
|
+
periods: [periodOf(curve), undefined],
|
|
148
|
+
point: (u, v) => add3(pointAlong(curve, u), scale3(direction, v)),
|
|
149
|
+
parameters: (point) => {
|
|
150
|
+
const u = lengthNearest(curve, point, flatten);
|
|
151
|
+
return [u, dot3(subtract3(point, pointAlong(curve, u)), direction)];
|
|
152
|
+
},
|
|
153
|
+
pole: noPole,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function splineSurface(model, surface, divided) {
|
|
157
|
+
const degrees = [model.attribute(surface, "UDegree"), model.attribute(surface, "VDegree")];
|
|
158
|
+
const controls = model.attribute(surface, "ControlPointsList");
|
|
159
|
+
const numbers = (name) => {
|
|
160
|
+
const value = model.attribute(surface, name);
|
|
161
|
+
if (!isList(value) || !value.every((item) => typeof item === "number" && Number.isFinite(item))) {
|
|
162
|
+
throw new UnsupportedGeometryError(`#${surface} has no ${name}`);
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
};
|
|
166
|
+
if (typeof degrees[0] !== "number" || typeof degrees[1] !== "number" || !isList(controls)) {
|
|
167
|
+
throw new UnsupportedGeometryError(`#${surface} is not a B-spline surface it can read`);
|
|
168
|
+
}
|
|
169
|
+
const [uDegree, vDegree] = [degrees[0], degrees[1]];
|
|
170
|
+
const net = controls.map((row) => (isList(row) ? row : []).map((point) => pointOf(model, point)));
|
|
171
|
+
const uKnots = expandedKnots(numbers("UKnots"), numbers("UMultiplicities"));
|
|
172
|
+
const vKnots = expandedKnots(numbers("VKnots"), numbers("VMultiplicities"));
|
|
173
|
+
if (uKnots.length !== net.length + uDegree + 1 || net.some((row) => vKnots.length !== row.length + vDegree + 1)) {
|
|
174
|
+
throw new UnsupportedGeometryError(`#${surface} has knots that do not match its control points`);
|
|
175
|
+
}
|
|
176
|
+
const weightsValue = model.entity(surface).type === "IfcRationalBSplineSurfaceWithKnots" ? model.attribute(surface, "WeightsData") : null;
|
|
177
|
+
const weights = isList(weightsValue) ? weightsValue.map((row) => (isList(row) ? row.map((item) => (typeof item === "number" ? item : 1)) : [])) : undefined;
|
|
178
|
+
if (divided && weights) {
|
|
179
|
+
net.forEach((row, i) => row.forEach((point, j) => {
|
|
180
|
+
var _a, _b;
|
|
181
|
+
row[j] = scale3(point, 1 / ((_b = (_a = weights[i]) === null || _a === void 0 ? void 0 : _a[j]) !== null && _b !== void 0 ? _b : 1));
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
const [u0, u1] = knotDomain(uDegree, uKnots);
|
|
185
|
+
const [v0, v1] = knotDomain(vDegree, vKnots);
|
|
186
|
+
const closedU = model.attribute(surface, "UClosed") === true;
|
|
187
|
+
const closedV = model.attribute(surface, "VClosed") === true;
|
|
188
|
+
const into = (value, low, high, closed) => (closed ? low + ((((value - low) % (high - low)) + (high - low)) % (high - low)) : Math.min(high, Math.max(low, value)));
|
|
189
|
+
const clampU = (u) => into(u, u0, u1, closedU);
|
|
190
|
+
const clampV = (v) => into(v, v0, v1, closedV);
|
|
191
|
+
const point = (u, v) => nurbsSurfacePoint([uDegree, vDegree], net, weights, [uKnots, vKnots], clampU(u), clampV(v));
|
|
192
|
+
const corners = net.flat();
|
|
193
|
+
const size = Math.hypot(...[0, 1, 2].map((axis) => Math.max(...corners.map((corner) => corner[axis])) - Math.min(...corners.map((corner) => corner[axis]))));
|
|
194
|
+
const accepted = size * FIT_RATIO;
|
|
195
|
+
const seed = (target) => {
|
|
196
|
+
let best = [u0, v0];
|
|
197
|
+
let bestDistance = Infinity;
|
|
198
|
+
for (let i = 0; i <= SEED_STEPS; i++) {
|
|
199
|
+
for (let j = 0; j <= SEED_STEPS; j++) {
|
|
200
|
+
const uv = [u0 + ((u1 - u0) * i) / SEED_STEPS, v0 + ((v1 - v0) * j) / SEED_STEPS];
|
|
201
|
+
const d = length3(subtract3(point(uv[0], uv[1]), target));
|
|
202
|
+
if (d < bestDistance) {
|
|
203
|
+
bestDistance = d;
|
|
204
|
+
best = uv;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return best;
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
periods: [closedU ? u1 - u0 : undefined, closedV ? v1 - v0 : undefined],
|
|
212
|
+
point,
|
|
213
|
+
parameters: (target, near) => {
|
|
214
|
+
const solved = (from) => {
|
|
215
|
+
let [u, v] = [closedU ? from[0] : clampU(from[0]), closedV ? from[1] : clampV(from[1])];
|
|
216
|
+
const [hu, hv] = [(u1 - u0) * SPLINE_STEP, (v1 - v0) * SPLINE_STEP];
|
|
217
|
+
for (let step = 0; step < NEWTON_STEPS; step++) {
|
|
218
|
+
const here = point(u, v);
|
|
219
|
+
const [uLow, uHigh] = closedU ? [u - hu, u + hu] : [Math.max(u0, u - hu), Math.min(u1, u + hu)];
|
|
220
|
+
const [vLow, vHigh] = closedV ? [v - hv, v + hv] : [Math.max(v0, v - hv), Math.min(v1, v + hv)];
|
|
221
|
+
const su = scale3(subtract3(point(uHigh, v), point(uLow, v)), 1 / (uHigh - uLow));
|
|
222
|
+
const sv = scale3(subtract3(point(u, vHigh), point(u, vLow)), 1 / (vHigh - vLow));
|
|
223
|
+
const miss = subtract3(target, here);
|
|
224
|
+
const [a, b, c] = [dot3(su, su), dot3(su, sv), dot3(sv, sv)];
|
|
225
|
+
const determinant = a * c - b * b;
|
|
226
|
+
if (!(Math.abs(determinant) > 0)) {
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
const limitU = (u1 - u0) * NEWTON_DAMPING;
|
|
230
|
+
const limitV = (v1 - v0) * NEWTON_DAMPING;
|
|
231
|
+
const du = Math.max(-limitU, Math.min(limitU, (c * dot3(su, miss) - b * dot3(sv, miss)) / determinant));
|
|
232
|
+
const dv = Math.max(-limitV, Math.min(limitV, (a * dot3(sv, miss) - b * dot3(su, miss)) / determinant));
|
|
233
|
+
[u, v] = [closedU ? u + du : clampU(u + du), closedV ? v + dv : clampV(v + dv)];
|
|
234
|
+
if (Math.abs(du) <= NEWTON_TOLERANCE * (u1 - u0) && Math.abs(dv) <= NEWTON_TOLERANCE * (v1 - v0)) {
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return { uv: [u, v], miss: length3(subtract3(point(u, v), target)) };
|
|
239
|
+
};
|
|
240
|
+
let best = near ? solved(near) : undefined;
|
|
241
|
+
if (!best || best.miss > accepted) {
|
|
242
|
+
const seeded = solved(seed(target));
|
|
243
|
+
best = !best || seeded.miss < best.miss ? seeded : best;
|
|
244
|
+
}
|
|
245
|
+
return best.uv;
|
|
246
|
+
},
|
|
247
|
+
pole: (axis, side) => {
|
|
248
|
+
const row = axis === 0 ? net[side > 0 ? net.length - 1 : 0] : net.map((column) => column[side > 0 ? column.length - 1 : 0]);
|
|
249
|
+
const collapsed = row.every((point) => length3(subtract3(point, row[0])) <= CLOSED_CURVE_RATIO * Math.max(1, length3(row[0])));
|
|
250
|
+
if (!collapsed) {
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
if (axis === 0) {
|
|
254
|
+
return side > 0 ? u1 : u0;
|
|
255
|
+
}
|
|
256
|
+
return side > 0 ? v1 : v0;
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function profileCurve(model, surface) {
|
|
261
|
+
const profile = model.attribute(surface, "SweptCurve");
|
|
262
|
+
const curve = isReference(profile) ? model.attribute(profile.ref, "Curve") : null;
|
|
263
|
+
if (!isReference(curve)) {
|
|
264
|
+
throw new UnsupportedGeometryError(`#${surface} sweeps no curve`);
|
|
265
|
+
}
|
|
266
|
+
const frame = frameOf(model, model.attribute(surface, "Position"));
|
|
267
|
+
return polyline(new CurveSampler(model, PROFILE_DENSITY).path(curve.ref).map((point) => pointToWorld(frame, point)));
|
|
268
|
+
}
|
|
269
|
+
export function parametricSurface(model, curves, surface, divided = false) {
|
|
270
|
+
const type = model.entity(surface).type;
|
|
271
|
+
switch (type) {
|
|
272
|
+
case "IfcPlane":
|
|
273
|
+
return plane(frameOf(model, model.attribute(surface, "Position")));
|
|
274
|
+
case "IfcCylindricalSurface":
|
|
275
|
+
return cylinder(frameOf(model, model.attribute(surface, "Position")), positive(model, surface, "Radius"));
|
|
276
|
+
case "IfcSphericalSurface":
|
|
277
|
+
return sphere(frameOf(model, model.attribute(surface, "Position")), positive(model, surface, "Radius"));
|
|
278
|
+
case "IfcToroidalSurface":
|
|
279
|
+
return torus(frameOf(model, model.attribute(surface, "Position")), positive(model, surface, "MajorRadius"), positive(model, surface, "MinorRadius"));
|
|
280
|
+
case "IfcSurfaceOfRevolution": {
|
|
281
|
+
const placement = model.attribute(surface, "AxisPosition");
|
|
282
|
+
if (!isReference(placement)) {
|
|
283
|
+
throw new UnsupportedGeometryError(`#${surface} has no axis`);
|
|
284
|
+
}
|
|
285
|
+
const frame = frameOf(model, model.attribute(surface, "Position"));
|
|
286
|
+
const axis = vectorToWorld(frame, directionOf(model, model.attribute(placement.ref, "Axis"), Z_AXIS));
|
|
287
|
+
return revolution(profileCurve(model, surface), pointToWorld(frame, pointOf(model, model.attribute(placement.ref, "Location"))), scale3(axis, 1 / length3(axis)));
|
|
288
|
+
}
|
|
289
|
+
case "IfcSurfaceOfLinearExtrusion": {
|
|
290
|
+
const frame = frameOf(model, model.attribute(surface, "Position"));
|
|
291
|
+
const direction = vectorToWorld(frame, directionOf(model, model.attribute(surface, "ExtrudedDirection"), Z_AXIS));
|
|
292
|
+
return extrusion(profileCurve(model, surface), scale3(direction, 1 / length3(direction)));
|
|
293
|
+
}
|
|
294
|
+
case "IfcBSplineSurfaceWithKnots":
|
|
295
|
+
case "IfcRationalBSplineSurfaceWithKnots":
|
|
296
|
+
return splineSurface(model, surface, divided);
|
|
297
|
+
case "IfcRectangularTrimmedSurface":
|
|
298
|
+
case "IfcCurveBoundedPlane":
|
|
299
|
+
case "IfcCurveBoundedSurface": {
|
|
300
|
+
const basis = model.attribute(surface, "BasisSurface");
|
|
301
|
+
return isReference(basis) ? parametricSurface(model, curves, basis.ref, divided) : undefined;
|
|
302
|
+
}
|
|
303
|
+
default:
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Base } from "@bitbybit-dev/base";
|
|
2
|
+
export declare const FULL_TURN: number;
|
|
3
|
+
export declare const TURN_SLACK = 1e-9;
|
|
4
|
+
export declare const PARAMETER_RATIO = 0.000001;
|
|
5
|
+
export declare const IDENTITY_MATRIX: Base.TransformMatrix;
|
|
6
|
+
export declare const Y_TO_Z: Base.TransformMatrix;
|
|
7
|
+
export declare const PYRAMID_TRIANGLES: readonly number[];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const FULL_TURN = 2 * Math.PI;
|
|
2
|
+
export const TURN_SLACK = 1e-9;
|
|
3
|
+
export const PARAMETER_RATIO = 1e-6;
|
|
4
|
+
export const IDENTITY_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
5
|
+
export const Y_TO_Z = [1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1];
|
|
6
|
+
export const PYRAMID_TRIANGLES = [0, 2, 1, 0, 3, 2, 0, 1, 4, 1, 2, 4, 2, 3, 4, 3, 0, 4];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ModelSnapshot } from "../model/snapshot";
|
|
2
|
+
import { type CurveSampler } from "./curves";
|
|
3
|
+
import type { RecipeBuilder } from "./recipe-builder";
|
|
4
|
+
export declare class SweptSolids {
|
|
5
|
+
private readonly model;
|
|
6
|
+
private readonly builder;
|
|
7
|
+
private readonly curves;
|
|
8
|
+
constructor(model: ModelSnapshot, builder: RecipeBuilder, curves: CurveSampler);
|
|
9
|
+
revolved(item: number): number;
|
|
10
|
+
tapered(item: number): number;
|
|
11
|
+
sweptDisk(item: number, polygonal: boolean): number;
|
|
12
|
+
areaSweep(item: number, fixed: boolean): number;
|
|
13
|
+
primitive(item: number, type: string): number;
|
|
14
|
+
private pyramid;
|
|
15
|
+
private taperBetween;
|
|
16
|
+
private fixedAxes;
|
|
17
|
+
private surfaceAxes;
|
|
18
|
+
private surfaceNormal;
|
|
19
|
+
private endsOf;
|
|
20
|
+
private sweptPath;
|
|
21
|
+
private flat;
|
|
22
|
+
private region;
|
|
23
|
+
private number;
|
|
24
|
+
private placed;
|
|
25
|
+
}
|