abstract-3d 4.0.0-beta.2 → 4.0.0-beta.7
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/README.md +2 -2
- package/lib/abstract-3d.d.ts +22 -1
- package/lib/abstract-3d.d.ts.map +1 -1
- package/lib/abstract-3d.js +4 -2
- package/lib/abstract-3d.js.map +1 -1
- package/lib/renderers/react/react.js +3 -3
- package/lib/renderers/react/react.js.map +1 -1
- package/lib/renderers/three/appearance/appearance.d.ts +5 -0
- package/lib/renderers/three/appearance/appearance.d.ts.map +1 -1
- package/lib/renderers/three/appearance/appearance.js +15 -5
- package/lib/renderers/three/appearance/appearance.js.map +1 -1
- package/lib/renderers/three/build/build.js +4 -1
- package/lib/renderers/three/build/build.js.map +1 -1
- package/lib/renderers/three/build/materials.d.ts.map +1 -1
- package/lib/renderers/three/build/materials.js +74 -2
- package/lib/renderers/three/build/materials.js.map +1 -1
- package/lib/renderers/three/camera/gizmo.d.ts.map +1 -1
- package/lib/renderers/three/camera/gizmo.js +2 -1
- package/lib/renderers/three/camera/gizmo.js.map +1 -1
- package/lib/renderers/three/environment/studio.d.ts +8 -0
- package/lib/renderers/three/environment/studio.d.ts.map +1 -0
- package/lib/renderers/three/environment/studio.js +14 -0
- package/lib/renderers/three/environment/studio.js.map +1 -0
- package/lib/renderers/three/geometry/geometry.d.ts.map +1 -1
- package/lib/renderers/three/geometry/geometry.js +5 -2
- package/lib/renderers/three/geometry/geometry.js.map +1 -1
- package/lib/renderers/three/quality/quality.d.ts +3 -2
- package/lib/renderers/three/quality/quality.d.ts.map +1 -1
- package/lib/renderers/three/quality/quality.js +4 -3
- package/lib/renderers/three/quality/quality.js.map +1 -1
- package/lib/renderers/three/shadow/key-light.d.ts +6 -0
- package/lib/renderers/three/shadow/key-light.d.ts.map +1 -0
- package/lib/renderers/three/shadow/key-light.js +37 -0
- package/lib/renderers/three/shadow/key-light.js.map +1 -0
- package/lib/renderers/three/shadow/shadow.d.ts +7 -1
- package/lib/renderers/three/shadow/shadow.d.ts.map +1 -1
- package/lib/renderers/three/shadow/shadow.js +63 -24
- package/lib/renderers/three/shadow/shadow.js.map +1 -1
- package/lib/renderers/three/viewer/options.d.ts +7 -6
- package/lib/renderers/three/viewer/options.d.ts.map +1 -1
- package/lib/renderers/three/viewer/options.js +1 -1
- package/lib/renderers/three/viewer/options.js.map +1 -1
- package/lib/renderers/three/viewer/viewer.d.ts +7 -1
- package/lib/renderers/three/viewer/viewer.d.ts.map +1 -1
- package/lib/renderers/three/viewer/viewer.js +69 -65
- package/lib/renderers/three/viewer/viewer.js.map +1 -1
- package/package.json +1 -1
- package/src/abstract-3d.ts +32 -2
- package/src/renderers/ifc/ifc.test.ts +7 -5
- package/src/renderers/react/react.tsx +3 -3
- package/src/renderers/three/appearance/appearance.test.ts +36 -4
- package/src/renderers/three/appearance/appearance.ts +21 -6
- package/src/renderers/three/build/build.test.ts +18 -10
- package/src/renderers/three/build/build.ts +4 -1
- package/src/renderers/three/build/materials.ts +93 -2
- package/src/renderers/three/camera/gizmo.ts +2 -1
- package/src/renderers/three/compile/compile.test.ts +32 -19
- package/src/renderers/three/environment/studio.ts +17 -0
- package/src/renderers/three/geometry/geometry.test.ts +15 -1
- package/src/renderers/three/geometry/geometry.ts +5 -2
- package/src/renderers/three/quality/quality.test.ts +2 -2
- package/src/renderers/three/quality/quality.ts +4 -3
- package/src/renderers/three/shadow/key-light.ts +41 -0
- package/src/renderers/three/shadow/shadow.ts +79 -25
- package/src/renderers/three/viewer/options.ts +8 -7
- package/src/renderers/three/viewer/viewer.ts +75 -74
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BatchedMesh, InstancedMesh, Matrix4, PerspectiveCamera, Raycaster, Vector2, Vector3, Vector4 } from "three";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { assert, describe, expect, it } from "vitest";
|
|
3
3
|
import type { Group, Scene } from "../../../abstract-3d.js";
|
|
4
4
|
import { alignedDimension, box, culledLine, group, sphere, text, vec3, vec3Zero } from "../../../abstract-3d.js";
|
|
5
5
|
import type { PaintState, StructureState } from "../appearance/appearance.js";
|
|
@@ -28,11 +28,18 @@ const rasterize = (): GlyphBitmap => {
|
|
|
28
28
|
const idle: StructureState = { relationsActive: false };
|
|
29
29
|
const scene = (groups: ReadonlyArray<Group>, rest: Partial<Scene> = {}): Scene => ({ size_deprecated: vec3(1, 1, 1), groups, ...rest });
|
|
30
30
|
const part = (id: string, x: number, material = red): Group => group([box(vec3(10, 10, 10), material)], vec3(x, 0, 0), vec3Zero, [], { id });
|
|
31
|
+
// Destructured, as the build's and lint's compiler options disagree on reading an index signature by dot or bracket
|
|
32
|
+
const groupId = (g: Group): string => {
|
|
33
|
+
const { id = "" }: Record<string, string | undefined> = g.data ?? {};
|
|
34
|
+
return id;
|
|
35
|
+
};
|
|
31
36
|
const build = (s: Scene, structure = idle, activeRelations?: ReadonlyArray<string>): BuiltScene =>
|
|
32
|
-
buildScene(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
);
|
|
37
|
+
buildScene(compileScene(s, groupId), { structure, activeRelations, geometries: new Map(), atlas: createGlyphAtlas(rasterize, 256) });
|
|
38
|
+
const firstBatch = (built: BuiltScene): BatchedMesh => {
|
|
39
|
+
const [batch] = batches(built);
|
|
40
|
+
assert(batch);
|
|
41
|
+
return batch;
|
|
42
|
+
};
|
|
36
43
|
const relation = (id: string): NonNullable<Scene["relations"]>[number] => ({ id, pos: vec3Zero, groups: [group([box(vec3(1, 1, 1), red)])] });
|
|
37
44
|
const batches = (built: BuiltScene): ReadonlyArray<BatchedMesh> => built.root.children.filter((o): o is BatchedMesh => o instanceof BatchedMesh);
|
|
38
45
|
|
|
@@ -45,9 +52,10 @@ function pickId(built: BuiltScene, x: number): string | undefined {
|
|
|
45
52
|
const raycaster = new Raycaster();
|
|
46
53
|
raycaster.setFromCamera(new Vector2(0, 0), camera);
|
|
47
54
|
const hits = raycaster.intersectObjects(getPickables(built), false);
|
|
48
|
-
const
|
|
55
|
+
const hit = hits.at(0);
|
|
56
|
+
const item = hit && getPickedItem(built, hit);
|
|
49
57
|
const { compiled }: { readonly compiled: CompiledScene } = built;
|
|
50
|
-
const target = item === undefined ? undefined : compiled.targets[compiled.items
|
|
58
|
+
const target = item === undefined ? undefined : compiled.targets[compiled.items.at(item)?.target ?? -1];
|
|
51
59
|
return target?.kind === "group" || target?.kind === "dimension" ? target.id : undefined;
|
|
52
60
|
}
|
|
53
61
|
|
|
@@ -71,7 +79,7 @@ describe("buildScene", () => {
|
|
|
71
79
|
|
|
72
80
|
it("repaints the hovered and selected targets", () => {
|
|
73
81
|
const built = build(scene([part("a", 0), part("b", 20)]));
|
|
74
|
-
const
|
|
82
|
+
const batch = firstBatch(built);
|
|
75
83
|
const color = (instance: number): ReadonlyArray<number> =>
|
|
76
84
|
batch
|
|
77
85
|
.getColorAt(instance, new Vector4())
|
|
@@ -117,7 +125,7 @@ describe("buildScene", () => {
|
|
|
117
125
|
const built = build(scene([group([box(vec3(1, 1, 1), red)], vec3(5, 0, 0), vec3Zero, [], {}, spin)]));
|
|
118
126
|
expect(built.animatedItems).toHaveLength(1);
|
|
119
127
|
animateScene(built, 0.5);
|
|
120
|
-
const position = new Vector3(1, 0, 0).applyMatrix4(
|
|
128
|
+
const position = new Vector3(1, 0, 0).applyMatrix4(firstBatch(built).getMatrixAt(0, new Matrix4()));
|
|
121
129
|
expect(position.toArray().map((v) => Math.round(v * 1000) / 1000 + 0)).toEqual([5, 1, 0]);
|
|
122
130
|
});
|
|
123
131
|
|
|
@@ -134,7 +142,7 @@ describe("buildScene", () => {
|
|
|
134
142
|
it("disposes its batches and materials", () => {
|
|
135
143
|
const built = build(scene([part("a", 0)]));
|
|
136
144
|
const disposed: Array<string> = [];
|
|
137
|
-
|
|
145
|
+
firstBatch(built).material.addEventListener("dispose", () => disposed.push("material"));
|
|
138
146
|
disposeScene(built);
|
|
139
147
|
expect(disposed).toEqual(["material"]);
|
|
140
148
|
});
|
|
@@ -232,7 +232,10 @@ function createPart(
|
|
|
232
232
|
return { geometry, local: shape.local };
|
|
233
233
|
});
|
|
234
234
|
if (first.role === "solid") {
|
|
235
|
-
|
|
235
|
+
const c = getMaterialClass(first, targetStates[first.target]!);
|
|
236
|
+
const part = createSolidPart(entries, createClassMaterial(c));
|
|
237
|
+
Object.assign(part.object, { castShadow: c.castShadow, receiveShadow: c.lit });
|
|
238
|
+
return part;
|
|
236
239
|
}
|
|
237
240
|
// Images are hidden until their texture loads
|
|
238
241
|
const part = createImagePart(
|
|
@@ -8,12 +8,14 @@ import {
|
|
|
8
8
|
LinearMipmapLinearFilter,
|
|
9
9
|
type Material,
|
|
10
10
|
MeshBasicMaterial,
|
|
11
|
+
MeshPhysicalMaterial,
|
|
12
|
+
type MeshPhysicalMaterialParameters,
|
|
11
13
|
MeshStandardMaterial,
|
|
12
14
|
SRGBColorSpace,
|
|
13
15
|
type Texture,
|
|
14
16
|
TextureLoader,
|
|
15
17
|
} from "three";
|
|
16
|
-
import type { Image } from "../../../abstract-3d.js";
|
|
18
|
+
import type { Image, MaterialType } from "../../../abstract-3d.js";
|
|
17
19
|
import type { MaterialClass, MaterialState } from "../appearance/appearance.js";
|
|
18
20
|
import { ERROR_IMG_KEY } from "../appearance/appearance.js";
|
|
19
21
|
|
|
@@ -22,7 +24,96 @@ import { ERROR_IMG_KEY } from "../appearance/appearance.js";
|
|
|
22
24
|
export function createClassMaterial(c: MaterialClass): Material {
|
|
23
25
|
const side = c.side === "front" ? FrontSide : c.side === "back" ? BackSide : DoubleSide;
|
|
24
26
|
const common = { side, transparent: c.transparent, depthWrite: !c.transparent };
|
|
25
|
-
|
|
27
|
+
if (!c.lit) {
|
|
28
|
+
return new MeshBasicMaterial(common);
|
|
29
|
+
}
|
|
30
|
+
const material = c.type
|
|
31
|
+
? new MeshPhysicalMaterial({ ...common, ...finishes[c.type] })
|
|
32
|
+
: new MeshStandardMaterial({ ...common, metalness: c.metalness, roughness: c.roughness });
|
|
33
|
+
return Object.assign(material, { onBeforeCompile: roundEdges });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The physical finish of each type of surface, in the material's color: metals reflect their color, brighter than physical, as product renders light
|
|
38
|
+
* metals, since the studio around them is dark; the others are dielectrics with a white reflection; painted sheet has a light clearcoat, fabric a
|
|
39
|
+
* soft sheen. Glass is a thin, smooth, mostly see-through surface, not refracting, which would render the scene once more each frame
|
|
40
|
+
*/
|
|
41
|
+
const finishes: Record<MaterialType, MeshPhysicalMaterialParameters> = {
|
|
42
|
+
painted: { metalness: 0, roughness: 0.45, clearcoat: 0.3, clearcoatRoughness: 0.25 },
|
|
43
|
+
galvanized: { metalness: 0.85, roughness: 0.55, envMapIntensity: 1.6 },
|
|
44
|
+
aluminum: { metalness: 0.9, roughness: 0.4, envMapIntensity: 1.6 },
|
|
45
|
+
stainless: { metalness: 1, roughness: 0.25, envMapIntensity: 1.5 },
|
|
46
|
+
copper: { metalness: 1, roughness: 0.3, envMapIntensity: 1.3 },
|
|
47
|
+
plastic: { metalness: 0, roughness: 0.55 },
|
|
48
|
+
rubber: { metalness: 0, roughness: 0.9 },
|
|
49
|
+
fabric: { metalness: 0, roughness: 1, sheen: 1, sheenRoughness: 0.8, sheenColor: 0x888888 },
|
|
50
|
+
glass: { metalness: 0, roughness: 0.05, specularIntensity: 1 },
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Shades the edges of boxes with a radius as rounded by it, so they catch light and reflections: the normal of a rounded box of the same size, from
|
|
55
|
+
* the signed distance function of a rounded box (Inigo Quilez's sdRoundBox), is used at each point of the sharp one. No extra geometry, and the
|
|
56
|
+
* radius stays the same along every edge, as boxes are one unit cube scaled to their size. Only the outline stays sharp
|
|
57
|
+
*/
|
|
58
|
+
function roundEdges(shader: { vertexShader: string; fragmentShader: string }): void {
|
|
59
|
+
shader.vertexShader = shader.vertexShader
|
|
60
|
+
.replace(
|
|
61
|
+
"#include <common>",
|
|
62
|
+
`#include <common>
|
|
63
|
+
attribute float bevel;
|
|
64
|
+
varying float vBevel;
|
|
65
|
+
varying vec3 vBoxPoint;
|
|
66
|
+
varying vec3 vBoxHalf;
|
|
67
|
+
varying mat3 vBoxRotation;`
|
|
68
|
+
)
|
|
69
|
+
.replace(
|
|
70
|
+
"#include <batching_vertex>",
|
|
71
|
+
`#include <batching_vertex>
|
|
72
|
+
#ifdef USE_BATCHING
|
|
73
|
+
vec3 boxSize = vec3( length( batchingMatrix[ 0 ].xyz ), length( batchingMatrix[ 1 ].xyz ), length( batchingMatrix[ 2 ].xyz ) );
|
|
74
|
+
mat3 boxRotation = mat3( batchingMatrix[ 0 ].xyz / boxSize.x, batchingMatrix[ 1 ].xyz / boxSize.y, batchingMatrix[ 2 ].xyz / boxSize.z );
|
|
75
|
+
#else
|
|
76
|
+
vec3 boxSize = vec3( 1.0 );
|
|
77
|
+
mat3 boxRotation = mat3( 1.0 );
|
|
78
|
+
#endif
|
|
79
|
+
// From the box's own axes to view space, where the fragment shader's normal is
|
|
80
|
+
vBoxRotation = normalMatrix * boxRotation;
|
|
81
|
+
vBevel = bevel;
|
|
82
|
+
vBoxHalf = boxSize * 0.5;
|
|
83
|
+
vBoxPoint = position * boxSize;`
|
|
84
|
+
);
|
|
85
|
+
shader.fragmentShader = shader.fragmentShader
|
|
86
|
+
.replace(
|
|
87
|
+
"#include <common>",
|
|
88
|
+
`#include <common>
|
|
89
|
+
varying float vBevel;
|
|
90
|
+
varying vec3 vBoxPoint;
|
|
91
|
+
varying vec3 vBoxHalf;
|
|
92
|
+
varying mat3 vBoxRotation;`
|
|
93
|
+
)
|
|
94
|
+
.replace(
|
|
95
|
+
"#include <normal_fragment_maps>",
|
|
96
|
+
`#include <normal_fragment_maps>
|
|
97
|
+
if ( vBevel > 0.0 ) {
|
|
98
|
+
// At least 2 pixels wide, so a rounded edge far away is a soft line instead of a jagged sub-pixel highlight, and at most half the box
|
|
99
|
+
vec3 pixel = fwidth( vBoxPoint );
|
|
100
|
+
float radius = min( max( vBevel, 2.0 * max( pixel.x, max( pixel.y, pixel.z ) ) ), min( vBoxHalf.x, min( vBoxHalf.y, vBoxHalf.z ) ) );
|
|
101
|
+
vec3 rounded = sign( vBoxPoint ) * max( abs( vBoxPoint ) - ( vBoxHalf - radius ), 0.0 );
|
|
102
|
+
if ( dot( rounded, rounded ) > 0.0 ) {
|
|
103
|
+
normal = normalize( vBoxRotation * normalize( rounded ) ) * faceDirection;
|
|
104
|
+
}
|
|
105
|
+
}`
|
|
106
|
+
)
|
|
107
|
+
.replace(
|
|
108
|
+
"#include <clearcoat_normal_fragment_maps>",
|
|
109
|
+
`#include <clearcoat_normal_fragment_maps>
|
|
110
|
+
#ifdef USE_CLEARCOAT
|
|
111
|
+
// A painted finish's clearcoat follows the rounded edge too
|
|
112
|
+
if ( vBevel > 0.0 ) {
|
|
113
|
+
clearcoatNormal = normal;
|
|
114
|
+
}
|
|
115
|
+
#endif`
|
|
116
|
+
);
|
|
26
117
|
}
|
|
27
118
|
|
|
28
119
|
export function createImageMaterial(backfaceCulling: boolean | undefined, useAlphaTest: boolean): MeshBasicMaterial {
|
|
@@ -59,7 +59,8 @@ export function createGizmo(
|
|
|
59
59
|
size,
|
|
60
60
|
placement: helper.props?.alignment ?? "bottom-right",
|
|
61
61
|
offset: { left: mx - size / 2, right: mx - size / 2, top: my - size / 2, bottom: my - size / 2 },
|
|
62
|
-
|
|
62
|
+
// A family without a generic fallback, e.g. a web font, would otherwise fall back to the browser's serif
|
|
63
|
+
font: { family: `${family}, sans-serif`, weight },
|
|
63
64
|
...(helper.type === "Viewcube" ? getViewcubeOptions(helper.viewcubeProps ?? {}) : getAxesOptions(helper.viewportProps ?? {})),
|
|
64
65
|
});
|
|
65
66
|
gizmo.enabled = !(helper.type === "Viewport" && helper.viewportProps?.disabled);
|
|
@@ -1,43 +1,56 @@
|
|
|
1
1
|
import { Matrix4, Vector3 } from "three";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { assert, describe, expect, it } from "vitest";
|
|
3
3
|
import type { Group, Scene } from "../../../abstract-3d.js";
|
|
4
4
|
import { alignedDimension, box, group, line, sphere, text, vec3, vec3Zero } from "../../../abstract-3d.js";
|
|
5
|
+
import type { CompiledScene, Item } from "./compile.js";
|
|
5
6
|
import { compileScene, getItemMatrix } from "./compile.js";
|
|
6
7
|
|
|
7
8
|
const red = { normal: "rgb(255, 0, 0)" };
|
|
8
9
|
const scene = (groups: ReadonlyArray<Group>, rest: Partial<Scene> = {}): Scene => ({ size_deprecated: vec3(1, 1, 1), groups, ...rest });
|
|
9
|
-
const apply = (m: Matrix4, x: number, y: number, z: number): ReadonlyArray<number> =>
|
|
10
|
-
|
|
10
|
+
const apply = (m: Matrix4 | undefined, x: number, y: number, z: number): ReadonlyArray<number> => {
|
|
11
|
+
assert(m);
|
|
12
|
+
return new Vector3(x, y, z)
|
|
11
13
|
.applyMatrix4(m)
|
|
12
14
|
.toArray()
|
|
13
15
|
.map((v) => Math.round(v * 1000) / 1000 + 0);
|
|
16
|
+
};
|
|
17
|
+
const getItem = (compiled: CompiledScene, index: number): Item => {
|
|
18
|
+
const item = compiled.items.at(index);
|
|
19
|
+
assert(item);
|
|
20
|
+
return item;
|
|
21
|
+
};
|
|
22
|
+
// Destructured, as the build's and lint's compiler options disagree on reading an index signature by dot or bracket
|
|
23
|
+
const groupId = (g: Group): string => {
|
|
24
|
+
const { id = "" }: Record<string, string | undefined> = g.data ?? {};
|
|
25
|
+
return id;
|
|
26
|
+
};
|
|
14
27
|
|
|
15
28
|
describe("compileScene", () => {
|
|
16
29
|
it("flattens nested groups into items with world matrices", () => {
|
|
17
30
|
const inner = group([sphere(1, red)], vec3(0, 10, 0));
|
|
18
31
|
const compiled = compileScene(scene([group([box(vec3(1, 1, 1), red)], vec3(100, 0, 0), vec3Zero, [inner])]));
|
|
19
32
|
expect(compiled.items.map((i) => i.role)).toEqual(["solid", "solid"]);
|
|
20
|
-
expect(apply(compiled.items
|
|
21
|
-
expect(apply(compiled.items
|
|
33
|
+
expect(apply(compiled.items.at(0)?.matrix, 0, 0, 0)).toEqual([100, 0, 0]);
|
|
34
|
+
expect(apply(compiled.items.at(1)?.matrix, 0, 0, 0)).toEqual([100, 10, 0]);
|
|
22
35
|
});
|
|
23
36
|
|
|
24
37
|
it("applies group rotation before the group's position", () => {
|
|
25
38
|
const compiled = compileScene(scene([group([box(vec3(1, 1, 1), red)], vec3(5, 0, 0), vec3(0, 0, Math.PI / 2))]));
|
|
26
|
-
expect(apply(compiled.items
|
|
39
|
+
expect(apply(compiled.items.at(0)?.matrix, 1, 0, 0)).toEqual([5, 1, 0]);
|
|
27
40
|
});
|
|
28
41
|
|
|
29
42
|
it("rotates the scene by its deprecated rotation, then moves it by minus its deprecated center", () => {
|
|
30
43
|
const g = group([box(vec3(1, 1, 1), red)], vec3(10, 0, 0));
|
|
31
44
|
const centered = compileScene(scene([g], { center_deprecated: vec3(10, 0, 0) }));
|
|
32
|
-
expect(apply(centered.items
|
|
45
|
+
expect(apply(centered.items.at(0)?.matrix, 0, 0, 0)).toEqual([0, 0, 0]);
|
|
33
46
|
const rotated = compileScene(scene([g], { center_deprecated: vec3(-10, 0, 0), rotation_deprecated: vec3(0, Math.PI, 0) }));
|
|
34
|
-
expect(apply(rotated.items
|
|
47
|
+
expect(apply(rotated.items.at(0)?.matrix, 1, 0, 0)).toEqual([-1, 0, 0]);
|
|
35
48
|
});
|
|
36
49
|
|
|
37
50
|
it("gives every item of a top-level group that group's target, and the data of the group it's in", () => {
|
|
38
51
|
const inner = group([sphere(1, red)], vec3Zero, vec3Zero, [], { part: "motor" });
|
|
39
52
|
const fan = group([box(vec3(1, 1, 1), red)], vec3Zero, vec3Zero, [inner], { id: "fan" });
|
|
40
|
-
const compiled = compileScene(scene([fan, group([box(vec3(1, 1, 1), red)], vec3Zero, vec3Zero, [], { id: "coil" })]),
|
|
53
|
+
const compiled = compileScene(scene([fan, group([box(vec3(1, 1, 1), red)], vec3Zero, vec3Zero, [], { id: "coil" })]), groupId);
|
|
41
54
|
expect(compiled.targets).toEqual([
|
|
42
55
|
{ kind: "group", id: "fan", root: 0, rootData: { id: "fan" } },
|
|
43
56
|
{ kind: "group", id: "coil", root: 1, rootData: { id: "coil" } },
|
|
@@ -70,9 +83,9 @@ describe("compileScene", () => {
|
|
|
70
83
|
);
|
|
71
84
|
const compiled = compileScene(scene([group([], vec3(10, 0, 0), vec3Zero, [fan])]));
|
|
72
85
|
expect(compiled.animations).toHaveLength(1);
|
|
73
|
-
expect(compiled.animations
|
|
74
|
-
expect(compiled.items
|
|
75
|
-
const world = (seconds: number): ReadonlyArray<number> => apply(getItemMatrix(compiled, compiled
|
|
86
|
+
expect(compiled.animations.at(0)?.speed).toEqual(vec3(0, 0, Math.PI));
|
|
87
|
+
expect(compiled.items.at(0)?.animation).toBe(0);
|
|
88
|
+
const world = (seconds: number): ReadonlyArray<number> => apply(getItemMatrix(compiled, getItem(compiled, 0), seconds, new Matrix4()), 1, 0, 0);
|
|
76
89
|
expect(world(0)).toEqual([11, 5, 0]);
|
|
77
90
|
expect(world(0.5)).toEqual([10, 6, 0]);
|
|
78
91
|
});
|
|
@@ -82,8 +95,8 @@ describe("compileScene", () => {
|
|
|
82
95
|
const inner = group([box(vec3(1, 1, 1), red)], vec3(1, 0, 0), vec3Zero, [], {}, spin);
|
|
83
96
|
const compiled = compileScene(scene([group([], vec3Zero, vec3Zero, [inner], {}, spin)]));
|
|
84
97
|
expect(compiled.animations.map((a) => a.parent)).toEqual([undefined, 0]);
|
|
85
|
-
expect(apply(getItemMatrix(compiled, compiled
|
|
86
|
-
expect(apply(getItemMatrix(compiled, compiled
|
|
98
|
+
expect(apply(getItemMatrix(compiled, getItem(compiled, 0), 0.5, new Matrix4()), 0, 0, 0)).toEqual([0, 1, 0]);
|
|
99
|
+
expect(apply(getItemMatrix(compiled, getItem(compiled, 0), 0.5, new Matrix4()), 1, 0, 0)).toEqual([-1, 1, 0]);
|
|
87
100
|
});
|
|
88
101
|
|
|
89
102
|
it("converts aligned dimensions to dimension items with a target per dimension", () => {
|
|
@@ -92,22 +105,22 @@ describe("compileScene", () => {
|
|
|
92
105
|
const items = compiled.items.filter((i) => i.style === "dimension");
|
|
93
106
|
expect(items.length).toBeGreaterThan(0);
|
|
94
107
|
expect(new Set(items.map((i) => i.role))).toEqual(new Set(["line", "text", "solid"]));
|
|
95
|
-
expect(compiled.targets[items
|
|
108
|
+
expect(compiled.targets[items.at(0)?.target ?? -1]).toEqual({ kind: "dimension", id: "fan" });
|
|
96
109
|
});
|
|
97
110
|
|
|
98
111
|
it("adds relation shapes, with an outline around their boxes", () => {
|
|
99
112
|
const relation = { id: "r1", pos: vec3Zero, groups: [group([box(vec3(10, 10, 10), red), sphere(1, red)], vec3(5, 0, 0))] };
|
|
100
113
|
const compiled = compileScene(scene([], { relations: [relation] }));
|
|
101
114
|
expect(compiled.items.map((i) => i.style)).toEqual(["relation", "outline", "relation"]);
|
|
102
|
-
expect(compiled.targets[compiled
|
|
103
|
-
const outline = compiled
|
|
115
|
+
expect(compiled.targets[getItem(compiled, 0).target]).toEqual({ kind: "relation", relation });
|
|
116
|
+
const outline = getItem(compiled, 1).mesh.geometry;
|
|
104
117
|
expect(outline.type === "Box" && outline.size).toEqual(vec3(10.125, 10.125, 10.125));
|
|
105
|
-
expect(apply(compiled.items
|
|
118
|
+
expect(apply(compiled.items.at(0)?.matrix, 0, 0, 0)).toEqual([5, 0, 0]);
|
|
106
119
|
});
|
|
107
120
|
|
|
108
121
|
it("keeps the world matrix of each top-level group for its tooltips", () => {
|
|
109
122
|
const compiled = compileScene(scene([group([], vec3(3, 0, 0))], { center_deprecated: vec3(1, 0, 0) }));
|
|
110
|
-
expect(apply(compiled.roots
|
|
123
|
+
expect(apply(compiled.roots.at(0), 0, 0, 0)).toEqual([2, 0, 0]);
|
|
111
124
|
expect(apply(compiled.matrix, 0, 0, 0)).toEqual([-1, 0, 0]);
|
|
112
125
|
});
|
|
113
126
|
});
|