@kupcad/ui 0.1.4 → 0.1.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kupcad/ui",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
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.4",
39
+ "@kupcad/protocol": "0.1.5",
40
40
  "three": "^0.186.1"
41
41
  },
42
42
  "devDependencies": {
43
- "@kupcad/wasm": "0.1.4",
43
+ "@kupcad/wasm": "0.1.5",
44
44
  "@playwright/test": "^1.64.1",
45
45
  "@sveltejs/vite-plugin-svelte": "^7.3.1",
46
46
  "@testing-library/svelte": "^5.4.2",
@@ -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. The edges take
20
- * `theme`'s colour for where they are drawn (edgeColorOf).
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.edgeMaterial;
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).
@@ -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} edgeMaterial
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(new EdgesGeometry(mesh.geometry, EDGE_ANGLE_DEG), edgeMaterial);
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'. A
271
- * selected shape's edges are drawn with the viewer's material, which isn't the model's to free.
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)] : [],
@@ -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
  }