@kupcad/ui 0.1.4 → 0.1.6
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/package.json +3 -3
- package/src/viewer/appearance.js +19 -4
- package/src/viewer/model.js +16 -9
- package/src/viewer/testing.js +5 -1
- package/src/viewer/types.d.ts +6 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kupcad/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "KupCAD's UI: a three.js viewer of a model the core made, for any host (@kupcad/ui/viewer), and the Svelte panels around it (the viewer's pane, parameters, console, a language server's preview), styled by --kc-* tokens.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@kupcad/protocol": "0.1.
|
|
39
|
+
"@kupcad/protocol": "0.1.6",
|
|
40
40
|
"three": "^0.186.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@kupcad/wasm": "0.1.
|
|
43
|
+
"@kupcad/wasm": "0.1.6",
|
|
44
44
|
"@playwright/test": "^1.64.1",
|
|
45
45
|
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
|
46
46
|
"@testing-library/svelte": "^5.4.2",
|
package/src/viewer/appearance.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { Color } from 'three';
|
|
6
6
|
|
|
7
7
|
/** @import { LineBasicMaterial, MeshBasicMaterial, MeshStandardMaterial } from 'three' */
|
|
8
|
-
/** @import { RenderMode, SceneModel, Theme } from './types.js' */
|
|
8
|
+
/** @import { RenderMode, SceneModel, Shape, Theme } from './types.js' */
|
|
9
9
|
|
|
10
10
|
/** How see-through a solid is in x-ray. */
|
|
11
11
|
export const XRAY_OPACITY = 0.25;
|
|
@@ -16,8 +16,9 @@ export const SELECTION_TINT = 0.45;
|
|
|
16
16
|
* Draws the model's shapes as `mode` says: as they are, see-through, or not at all but for their
|
|
17
17
|
* edges (the mesh stays, for picking, and its edges with it); their edges with them when
|
|
18
18
|
* `edges`, and always in a wireframe, which is nothing else. A highlight is seen through what
|
|
19
|
-
* hides it only as they are: in x-ray everything is, and a wireframe has no faces
|
|
20
|
-
* `theme`'s colour for where they are
|
|
19
|
+
* hides it only as they are: in x-ray everything is, and a wireframe has no faces, its edges
|
|
20
|
+
* drawn in the highlight's colour instead. The edges take `theme`'s colour for where they are
|
|
21
|
+
* drawn (edgeColorOf).
|
|
21
22
|
* @param {SceneModel | null} model
|
|
22
23
|
* @param {RenderMode} mode
|
|
23
24
|
* @param {boolean} edges
|
|
@@ -26,6 +27,9 @@ export const SELECTION_TINT = 0.45;
|
|
|
26
27
|
export function applyRenderMode(model, mode, edges, theme) {
|
|
27
28
|
if (!model) return;
|
|
28
29
|
model.edgeMaterial.color.set(edgeColorOf(model.kind, mode, theme));
|
|
30
|
+
model.highlightEdges?.color.set(
|
|
31
|
+
mode === 'wireframe' ? theme.highlight : edgeColorOf(model.kind, mode, theme),
|
|
32
|
+
);
|
|
29
33
|
for (const shape of model.shapes) {
|
|
30
34
|
if (shape.edges) shape.edges.visible = edges || mode === 'wireframe';
|
|
31
35
|
if (shape.seenThrough) shape.seenThrough.visible = mode === 'solid';
|
|
@@ -68,10 +72,21 @@ export function applySelection(model, part, selection, selectionEdges) {
|
|
|
68
72
|
const chosen = part !== null && shape.part === part;
|
|
69
73
|
material.color.copy(shape.color);
|
|
70
74
|
if (chosen) material.color.lerp(tint, SELECTION_TINT);
|
|
71
|
-
if (shape.edges) shape.edges.material = chosen ? selectionEdges : model
|
|
75
|
+
if (shape.edges) shape.edges.material = chosen ? selectionEdges : edgesOf(model, shape);
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
78
|
|
|
79
|
+
/**
|
|
80
|
+
* The material a shape's edges are drawn with unless it is selected: a highlight's own, else
|
|
81
|
+
* the model's.
|
|
82
|
+
* @param {SceneModel} model
|
|
83
|
+
* @param {Shape} shape
|
|
84
|
+
* @returns {LineBasicMaterial}
|
|
85
|
+
*/
|
|
86
|
+
function edgesOf(model, shape) {
|
|
87
|
+
return shape.seenThrough && model.highlightEdges ? model.highlightEdges : model.edgeMaterial;
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
/**
|
|
76
91
|
* Gives the model the colours of `theme` it takes besides its edges' (applyRenderMode's): a
|
|
77
92
|
* ghost's and a highlight's (the selection, applied after, tints from them).
|
package/src/viewer/model.js
CHANGED
|
@@ -96,30 +96,32 @@ async function solids(glb, parts, loader, theme) {
|
|
|
96
96
|
return index === undefined ? undefined : nodes[index]?.name;
|
|
97
97
|
};
|
|
98
98
|
const edgeMaterial = new LineBasicMaterial({ color: theme.edges });
|
|
99
|
+
const highlightEdges = new LineBasicMaterial({ color: theme.edges });
|
|
99
100
|
/** @type {Shape[]} */
|
|
100
101
|
const shapes = [];
|
|
101
102
|
scene.traverse((object) => {
|
|
102
103
|
if (object instanceof Mesh) {
|
|
103
|
-
shapes.push(solidShape(object, partOf, nodeName, edgeMaterial, theme));
|
|
104
|
+
shapes.push(solidShape(object, partOf, nodeName, { edgeMaterial, highlightEdges }, theme));
|
|
104
105
|
}
|
|
105
106
|
});
|
|
106
107
|
const root = new Group();
|
|
107
108
|
root.add(scene);
|
|
108
|
-
return { kind: '3d', root, shapes, edgeMaterial };
|
|
109
|
+
return { kind: '3d', root, shapes, edgeMaterial, highlightEdges };
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
/**
|
|
112
113
|
* A solid's mesh as a shape: its own material (so tinting one part tints no other), its part,
|
|
113
114
|
* and its edges; a ghost seen through, in the theme's colour; a highlighted one (the core tags
|
|
114
|
-
* its material) in the highlight's, seen through what hides it.
|
|
115
|
+
* its material) in the highlight's, seen through what hides it, its edges its own.
|
|
115
116
|
* @param {Mesh} mesh
|
|
116
117
|
* @param {Map<string, string>} partOf
|
|
117
118
|
* @param {(object: Object3D) => string | undefined} nodeName
|
|
118
|
-
* @param {LineBasicMaterial}
|
|
119
|
+
* @param {{ edgeMaterial: LineBasicMaterial, highlightEdges: LineBasicMaterial }} edges the
|
|
120
|
+
* model's edges' materials
|
|
119
121
|
* @param {Theme} theme
|
|
120
122
|
* @returns {Shape}
|
|
121
123
|
*/
|
|
122
|
-
function solidShape(mesh, partOf, nodeName, edgeMaterial, theme) {
|
|
124
|
+
function solidShape(mesh, partOf, nodeName, { edgeMaterial, highlightEdges }, theme) {
|
|
123
125
|
const material = /** @type {import('three').MeshStandardMaterial} */ (
|
|
124
126
|
/** @type {Material} */ (mesh.material).clone()
|
|
125
127
|
);
|
|
@@ -144,7 +146,10 @@ function solidShape(mesh, partOf, nodeName, edgeMaterial, theme) {
|
|
|
144
146
|
const highlighted = role === 'highlight';
|
|
145
147
|
if (highlighted) material.color.set(theme.highlight);
|
|
146
148
|
Object.assign(material, FACES_BEHIND_EDGES);
|
|
147
|
-
const edges = new LineSegments(
|
|
149
|
+
const edges = new LineSegments(
|
|
150
|
+
new EdgesGeometry(mesh.geometry, EDGE_ANGLE_DEG),
|
|
151
|
+
highlighted ? highlightEdges : edgeMaterial,
|
|
152
|
+
);
|
|
148
153
|
mesh.add(edges);
|
|
149
154
|
return {
|
|
150
155
|
mesh,
|
|
@@ -242,7 +247,7 @@ function drawing(svg, theme) {
|
|
|
242
247
|
opacity,
|
|
243
248
|
});
|
|
244
249
|
}
|
|
245
|
-
return { kind: '2d', root, shapes, edgeMaterial };
|
|
250
|
+
return { kind: '2d', root, shapes, edgeMaterial, highlightEdges: null };
|
|
246
251
|
}
|
|
247
252
|
|
|
248
253
|
/**
|
|
@@ -267,14 +272,16 @@ function outlineOf(path, closed) {
|
|
|
267
272
|
}
|
|
268
273
|
|
|
269
274
|
/**
|
|
270
|
-
* The materials the model's scene owns: each shape's, a highlight's copy's, and its edges'
|
|
271
|
-
* selected shape's edges are drawn with the viewer's material,
|
|
275
|
+
* The materials the model's scene owns: each shape's, a highlight's copy's, and its edges' (a
|
|
276
|
+
* highlight's of their own). A selected shape's edges are drawn with the viewer's material,
|
|
277
|
+
* which isn't the model's to free.
|
|
272
278
|
* @param {SceneModel} model
|
|
273
279
|
* @returns {Set<Material>}
|
|
274
280
|
*/
|
|
275
281
|
export function ownMaterials(model) {
|
|
276
282
|
return new Set([
|
|
277
283
|
model.edgeMaterial,
|
|
284
|
+
...(model.highlightEdges ? [model.highlightEdges] : []),
|
|
278
285
|
...model.shapes.flatMap((shape) =>
|
|
279
286
|
[shape.mesh, shape.seenThrough].flatMap((mesh) =>
|
|
280
287
|
mesh ? [/** @type {Material} */ (mesh.material)] : [],
|
package/src/viewer/testing.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// the camera's controls capture the pointer while it drags, and a screenshot reads the canvas as
|
|
4
4
|
// an image, which jsdom draws none of: here an empty PNG. A canvas has no context, as jsdom's has
|
|
5
5
|
// none, but quietly (jsdom says "not implemented" when asked). A dialog (the shortcuts') opens and
|
|
6
|
-
// closes, which jsdom's element can't.
|
|
6
|
+
// closes, which jsdom's element can't. A range of text measures, as an editor beside the viewer
|
|
7
|
+
// measures its lines (jsdom lays out nothing: no box). `stubRenderer` draws in place of WebGL.
|
|
7
8
|
|
|
8
9
|
/** @import { Camera, Object3D } from 'three' */
|
|
9
10
|
/** @import { RendererLike } from './types.js' */
|
|
@@ -14,6 +15,9 @@ globalThis.ResizeObserver ??= class {
|
|
|
14
15
|
disconnect() {}
|
|
15
16
|
};
|
|
16
17
|
|
|
18
|
+
Range.prototype.getClientRects ??= () => /** @type {DOMRectList} */ (/** @type {unknown} */ ([]));
|
|
19
|
+
Range.prototype.getBoundingClientRect ??= () => DOMRect.fromRect();
|
|
20
|
+
|
|
17
21
|
Element.prototype.setPointerCapture ??= () => {};
|
|
18
22
|
Element.prototype.releasePointerCapture ??= () => {};
|
|
19
23
|
Element.prototype.hasPointerCapture ??= () => false;
|
package/src/viewer/types.d.ts
CHANGED
|
@@ -131,6 +131,11 @@ export interface SceneModel {
|
|
|
131
131
|
kind: '3d' | '2d';
|
|
132
132
|
root: Group;
|
|
133
133
|
shapes: Shape[];
|
|
134
|
-
/** The edges' material, shared by every shape but the selected ones. */
|
|
134
|
+
/** The edges' material, shared by every shape but the selected and the highlighted ones. */
|
|
135
135
|
edgeMaterial: LineBasicMaterial;
|
|
136
|
+
/**
|
|
137
|
+
* A highlighted solid's edges' material: the highlight's colour in a wireframe, which has no
|
|
138
|
+
* faces to show it by, the edges' elsewhere; null for a drawing, which has no highlights.
|
|
139
|
+
*/
|
|
140
|
+
highlightEdges: LineBasicMaterial | null;
|
|
136
141
|
}
|