@buerli.io/core 0.0.34 → 0.0.35-beta.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.
Files changed (74) hide show
  1. package/build/cjs/geometry/ThreeGeometryCreator.js +284 -0
  2. package/build/cjs/geometry/index.js +13 -0
  3. package/build/cjs/index.js +20 -0
  4. package/build/cjs/store/index.js +46 -0
  5. package/build/{store → cjs/store}/states/buerli.js +9 -5
  6. package/build/{store → cjs/store}/states/canvas.js +7 -2
  7. package/build/cjs/store/states/drawing.js +44 -0
  8. package/build/{store → cjs/store}/states/geometry.js +29 -5
  9. package/build/{store → cjs/store}/states/graphic.js +7 -2
  10. package/build/cjs/store/states/message.js +25 -0
  11. package/build/{store → cjs/store}/states/plugin.js +12 -13
  12. package/build/{store → cjs/store}/states/selection.js +47 -18
  13. package/build/cjs/store/states/structure.js +203 -0
  14. package/build/cjs/store/store.js +14 -0
  15. package/build/cjs/store/subscribtion/selection.js +47 -0
  16. package/build/cjs/store/subscribtion/subscribeToDrawing.js +9 -0
  17. package/build/cjs/types/ErrorTypes.js +2 -0
  18. package/build/cjs/types/IGraphicPackage.js +24 -0
  19. package/build/cjs/types/IStructurePatch.js +2 -0
  20. package/build/cjs/types/IStructureTree.js +27 -0
  21. package/build/cjs/types/index.js +16 -0
  22. package/build/cjs/utils/Math.js +33 -0
  23. package/build/cjs/utils/Measure.js +576 -0
  24. package/build/cjs/utils/Timing.js +34 -0
  25. package/build/{geometry → esm/geometry}/ThreeGeometryCreator.js +47 -17
  26. package/build/{geometry → esm/geometry}/index.js +0 -0
  27. package/build/{index.js → esm/index.js} +1 -0
  28. package/build/{store → esm/store}/index.js +4 -1
  29. package/build/esm/store/states/buerli.js +116 -0
  30. package/build/esm/store/states/canvas.js +13 -0
  31. package/build/{store → esm/store}/states/drawing.js +0 -0
  32. package/build/esm/store/states/geometry.js +98 -0
  33. package/build/esm/store/states/graphic.js +82 -0
  34. package/build/{store → esm/store}/states/message.js +2 -2
  35. package/build/esm/store/states/plugin.js +152 -0
  36. package/build/esm/store/states/selection.js +269 -0
  37. package/build/{store → esm/store}/states/structure.js +10 -4
  38. package/build/{store → esm/store}/store.js +0 -0
  39. package/build/esm/store/subscribtion/selection.js +41 -0
  40. package/build/esm/store/subscribtion/subscribeToDrawing.js +5 -0
  41. package/build/{types → esm/types}/ErrorTypes.js +0 -0
  42. package/build/{types → esm/types}/IGraphicPackage.js +0 -0
  43. package/build/{types → esm/types}/IStructurePatch.js +0 -0
  44. package/build/{types → esm/types}/IStructureTree.js +0 -0
  45. package/build/{types → esm/types}/index.js +0 -0
  46. package/build/{utils → esm/utils}/Math.js +0 -0
  47. package/build/{utils → esm/utils}/Measure.js +1 -1
  48. package/build/{utils → esm/utils}/Timing.js +5 -1
  49. package/build/{geometry → types/geometry}/ThreeGeometryCreator.d.ts +0 -0
  50. package/build/{geometry → types/geometry}/index.d.ts +0 -0
  51. package/build/types/index.d.ts +36 -4
  52. package/build/{store → types/store}/index.d.ts +3 -0
  53. package/build/{store → types/store}/states/buerli.d.ts +0 -0
  54. package/build/{store → types/store}/states/canvas.d.ts +0 -0
  55. package/build/{store → types/store}/states/drawing.d.ts +0 -0
  56. package/build/{store → types/store}/states/geometry.d.ts +0 -0
  57. package/build/{store → types/store}/states/graphic.d.ts +0 -0
  58. package/build/{store → types/store}/states/message.d.ts +0 -0
  59. package/build/{store → types/store}/states/plugin.d.ts +1 -1
  60. package/build/{store → types/store}/states/selection.d.ts +1 -0
  61. package/build/{store → types/store}/states/structure.d.ts +0 -0
  62. package/build/{store → types/store}/store.d.ts +0 -0
  63. package/build/types/store/subscribtion/selection.d.ts +5 -0
  64. package/build/types/store/subscribtion/subscribeToDrawing.d.ts +2 -0
  65. package/build/types/{ErrorTypes.d.ts → types/ErrorTypes.d.ts} +0 -0
  66. package/build/types/{IGraphicPackage.d.ts → types/IGraphicPackage.d.ts} +0 -0
  67. package/build/types/{IStructurePatch.d.ts → types/IStructurePatch.d.ts} +0 -0
  68. package/build/types/{IStructureTree.d.ts → types/IStructureTree.d.ts} +0 -1
  69. package/build/types/types/index.d.ts +4 -0
  70. package/build/{utils → types/utils}/Math.d.ts +0 -0
  71. package/build/{utils → types/utils}/Measure.d.ts +0 -0
  72. package/build/{utils → types/utils}/Timing.d.ts +0 -0
  73. package/package.json +9 -6
  74. package/build/index.d.ts +0 -35
@@ -65,14 +65,17 @@ export class ThreeGeometryCreator {
65
65
  });
66
66
  }
67
67
  const points = Object.values(pointMap);
68
- return Object.assign({ graphicId,
68
+ return {
69
+ graphicId,
69
70
  map,
70
71
  meshes,
71
72
  lines,
72
73
  edges,
73
74
  arcs,
74
75
  points,
75
- box }, this.getMetadata(null, container, color));
76
+ box,
77
+ ...this.getMetadata(null, container, color),
78
+ };
76
79
  }
77
80
  createSurface(mesh, container, color = [0, 0, 0]) {
78
81
  let meshGeom = this.createMesh(mesh, container, color);
@@ -100,38 +103,41 @@ export class ThreeGeometryCreator {
100
103
  geometry.setIndex(mesh.indices);
101
104
  geometry.setAttribute('position', new THREE.Float32BufferAttribute(mesh.vertices, 3));
102
105
  mesh.normals && geometry.setAttribute('normal', new THREE.Float32BufferAttribute(mesh.normals, 3));
103
- return Object.assign({
106
+ return {
104
107
  // TODO: The server should not send invalid meshes.
105
- isValid: mesh.vertices && mesh.vertices.length > 0, geometry,
108
+ isValid: mesh.vertices && mesh.vertices.length > 0,
109
+ geometry,
106
110
  graphicId,
107
111
  type,
108
- loops }, this.getMetadata(mesh, container, color));
112
+ loops,
113
+ ...this.getMetadata(mesh, container, color),
114
+ };
109
115
  }
110
116
  createPlane(mesh, meshGeom) {
111
117
  const type = GraphicType.PLANE;
112
118
  const { normal: n, pointOnPlane: p } = mesh.properties.surface;
113
119
  const pointOnPlane = new THREE.Vector3(p[0], p[1], p[2]);
114
120
  const normal = new THREE.Vector3(n[0], n[1], n[2]).normalize();
115
- return Object.assign(Object.assign({}, meshGeom), { type, pointOnPlane, normal });
121
+ return { ...meshGeom, type, pointOnPlane, normal };
116
122
  }
117
123
  createCylinder(mesh, meshGeom) {
118
124
  const type = GraphicType.CYLINDER;
119
125
  const { origin: o, axis: a, height, radius } = mesh.properties.surface;
120
126
  const origin = new THREE.Vector3(o[0], o[1], o[2]);
121
127
  const axis = new THREE.Vector3(a[0], a[1], a[2]).normalize();
122
- return Object.assign(Object.assign({}, meshGeom), { type, origin, axis, height, radius });
128
+ return { ...meshGeom, type, origin, axis, height, radius };
123
129
  }
124
130
  createCone(mesh, meshGeom) {
125
131
  const type = GraphicType.CONE;
126
132
  const { origin: o, axis: a, height, radiusBottom, radiusTop } = mesh.properties.surface;
127
133
  const origin = new THREE.Vector3(o[0], o[1], o[2]);
128
134
  const axis = new THREE.Vector3(a[0], a[1], a[2]).normalize();
129
- return Object.assign(Object.assign({}, meshGeom), { type, origin, axis, height, radiusBottom, radiusTop });
135
+ return { ...meshGeom, type, origin, axis, height, radiusBottom, radiusTop };
130
136
  }
131
137
  createSphere(mesh, meshGeom) {
132
138
  const type = GraphicType.SPHERE;
133
139
  const { radius } = mesh.properties.surface;
134
- return Object.assign(Object.assign({}, meshGeom), { type, radius });
140
+ return { ...meshGeom, type, radius };
135
141
  }
136
142
  createLine(line, container, points, color = [0, 0, 0]) {
137
143
  const graphicId = line.id;
@@ -143,9 +149,14 @@ export class ThreeGeometryCreator {
143
149
  this.insertPoint(container, `${startId}|point`, startId, start.clone(), line.id, 'start', points);
144
150
  this.insertPoint(container, `${endId}|point`, endId, end.clone(), line.id, 'end', points);
145
151
  const geometry = new THREE.BufferGeometry().setFromPoints([start.clone(), end.clone()]);
146
- return Object.assign(Object.assign({ geometry,
152
+ return {
153
+ geometry,
147
154
  graphicId,
148
- type }, this.getMetadata(line, container, color)), { start: start.clone(), end: end.clone() });
155
+ type,
156
+ ...this.getMetadata(line, container, color),
157
+ start: start.clone(),
158
+ end: end.clone(),
159
+ };
149
160
  }
150
161
  createEdge(edge, container, points, color = [0, 0, 0]) {
151
162
  const threePts = [];
@@ -171,9 +182,14 @@ export class ThreeGeometryCreator {
171
182
  this.insertPoint(container, `${startId}|point`, startId, start, edge.id, 'start', points);
172
183
  this.insertPoint(container, `${endId}|point`, endId, end, edge.id, 'end', points);
173
184
  const geometry = new THREE.BufferGeometry().setFromPoints(threePts);
174
- return Object.assign(Object.assign({ geometry,
185
+ return {
186
+ geometry,
175
187
  graphicId,
176
- type }, this.getMetadata(edge, container, color)), { start: start.clone(), end: end.clone() });
188
+ type,
189
+ ...this.getMetadata(edge, container, color),
190
+ start: start.clone(),
191
+ end: end.clone(),
192
+ };
177
193
  }
178
194
  createArc(arc, container, points, color = [0, 0, 0]) {
179
195
  const graphicId = arc.id;
@@ -217,13 +233,27 @@ export class ThreeGeometryCreator {
217
233
  this.insertPoint(container, `${startId}|point`, startId, start, arc.id, 'start', points);
218
234
  this.insertPoint(container, `${endId}|point`, endId, end, arc.id, 'end', points);
219
235
  const geometry = new THREE.BufferGeometry().setFromPoints(threePts);
220
- return Object.assign(Object.assign({ geometry,
236
+ return {
237
+ geometry,
221
238
  graphicId,
222
- type }, this.getMetadata(arc, container, color)), { normal: normal.clone(), center: center.clone(), radius: arc.radius, angle: arc.angle });
239
+ type,
240
+ ...this.getMetadata(arc, container, color),
241
+ normal: normal.clone(),
242
+ center: center.clone(),
243
+ radius: arc.radius,
244
+ angle: arc.angle,
245
+ };
223
246
  }
224
247
  createPoint(container, key, graphicId, position, ownerId, type) {
225
- return Object.assign(Object.assign({ key,
226
- graphicId, type: GraphicType.POINT }, this.getMetadata(null, container)), { position: position.clone(), geometry: position.clone(), owners: [{ ownerId, type }] });
248
+ return {
249
+ key,
250
+ graphicId,
251
+ type: GraphicType.POINT,
252
+ ...this.getMetadata(null, container),
253
+ position: position.clone(),
254
+ geometry: position.clone(),
255
+ owners: [{ ownerId, type }],
256
+ };
227
257
  }
228
258
  insertPoint(container, key, graphicId, position, ownerId, type, points) {
229
259
  const existingPoint = points[key];
File without changes
@@ -4,4 +4,5 @@ export * from './types';
4
4
  export * from './utils/Math';
5
5
  export * from './utils/Measure';
6
6
  export * from './utils/Timing';
7
+ export * from './store/subscribtion/subscribeToDrawing';
7
8
  //#endregion Re-export type definitions
@@ -3,9 +3,12 @@ function getDrawing(id) {
3
3
  const { drawing } = api.getState();
4
4
  return drawing.refs[id];
5
5
  }
6
+ /**
7
+ * @returns PluginState or undefined if either drawing or plugin don't exist
8
+ */
6
9
  function getPlugin(drawingId, pluginId) {
7
10
  const { drawing } = api.getState();
8
- return drawing.refs[drawingId].plugin.refs[pluginId];
11
+ return drawing.refs[drawingId]?.plugin.refs[pluginId];
9
12
  }
10
13
  function init(sel) {
11
14
  api.getState().api.init(typeof sel === 'function' ? sel(api.getState()) || {} : sel);
@@ -0,0 +1,116 @@
1
+ import { createDrawingState } from './drawing';
2
+ import { getInitialMessageState } from './message';
3
+ const initError = 'Please call the init action before you use the buerli store.';
4
+ export const createState = (set, get, api) => ({
5
+ initDone: false,
6
+ options: {
7
+ url: '',
8
+ elements: {},
9
+ plugins: {},
10
+ globalPlugins: [],
11
+ theme: {
12
+ primary: '#e36b7c',
13
+ secondary: '#fcc7cb',
14
+ dark: '#54545d',
15
+ highlightedGeom: '#e36b7c',
16
+ hoveredGeom: '#40a9ff',
17
+ },
18
+ config: {
19
+ geometry: {},
20
+ },
21
+ },
22
+ drawing: {
23
+ ids: [],
24
+ refs: {},
25
+ active: null,
26
+ },
27
+ api: {
28
+ ...generalApi(set, get),
29
+ ...drawingManagementApi(set, get, api),
30
+ },
31
+ ...getInitialMessageState(set, get, api),
32
+ });
33
+ const generalApi = (set, get) => ({
34
+ // TODO: rename init to setOptions and remove initDone if everything is ok
35
+ /**
36
+ * This api method initializes state. Considering user can accidentally ommit some important like elements,
37
+ * there is a warning disaplying for these cases.
38
+ *
39
+ * @param options - options which will be set into state.options
40
+ */
41
+ init(options) {
42
+ set(state => ({ options: { ...state.options, ...options }, initDone: true }));
43
+ },
44
+ });
45
+ const drawingManagementApi = (set, get, api) => ({
46
+ async createDrawing(name) {
47
+ const { initDone, drawing } = get();
48
+ if (!initDone)
49
+ throw new Error(initError);
50
+ const drawingState = createDrawingState(sel => set(state => {
51
+ const newState = typeof sel === 'function' ? sel(state.drawing.refs[drawingState.id]) : sel;
52
+ return {
53
+ drawing: {
54
+ ...state.drawing,
55
+ refs: {
56
+ ...state.drawing.refs,
57
+ [drawingState.id]: {
58
+ ...state.drawing.refs[drawingState.id],
59
+ ...newState,
60
+ },
61
+ },
62
+ },
63
+ };
64
+ }), () => get().drawing.refs[drawingState.id], api, drawing.ids.length, name);
65
+ set(state => ({
66
+ drawing: {
67
+ ...state.drawing,
68
+ refs: { ...state.drawing.refs, [drawingState.id]: drawingState },
69
+ ids: [...state.drawing.ids, drawingState.id],
70
+ active: drawingState.id,
71
+ },
72
+ }));
73
+ // Initialize global plugins
74
+ get().options.globalPlugins.forEach(def => get().drawing.refs[drawingState.id].api.plugin.createPlugin(def));
75
+ return drawingState;
76
+ },
77
+ removeDrawing(id) {
78
+ const { initDone } = get();
79
+ if (!initDone)
80
+ throw new Error(initError);
81
+ // Note:
82
+ // Since it can lead to unexpected behaviour because of updating everything at once
83
+ // split the state modification into two parts.
84
+ // 1. Reset active drawing
85
+ // 2. Remove drawing from state
86
+ // Give the listeners the possibility to handle the active drawing before it is removed.
87
+ // There was exactly such a situation in buerli-examples, where a cad instance of buerli-headless
88
+ // was destroyed (removes drawing as well) while a components is going to be unmounted, but an
89
+ // other component already binds to the active drawing before it is removed. Yes, it can be handled
90
+ // by the application but it makes sense to pay attention to this behaviour at this place already.
91
+ // 1.
92
+ // Calculate and set the active drawing before removing the drawing from the store.
93
+ set(state => {
94
+ const index = state.drawing.ids.indexOf(id);
95
+ // Pick close by drawing unless another drawing has been active all along
96
+ const active = index >= 0 && state.drawing.active === id
97
+ ? state.drawing.ids[index + 1] || state.drawing.ids[index - 1] || null
98
+ : state.drawing.active;
99
+ return { drawing: { ...state.drawing, active } };
100
+ });
101
+ // 2.
102
+ // Remove drawing from store.
103
+ set(state => {
104
+ const refs = { ...state.drawing.refs };
105
+ const ids = state.drawing.ids.filter(d => d !== id);
106
+ delete refs[id];
107
+ return { drawing: { ...state.drawing, refs, ids } };
108
+ });
109
+ },
110
+ setActiveDrawing(id) {
111
+ const { initDone, drawing } = get();
112
+ if (!initDone)
113
+ throw new Error(initError);
114
+ set({ drawing: { ...drawing, active: id } });
115
+ },
116
+ });
@@ -0,0 +1,13 @@
1
+ ////////////////////////////////////////////////////
2
+ // DATA
3
+ ////////////////////////////////////////////////////
4
+ export const canvasData = () => ({
5
+ controls: { isInteracting: false, enabled: true },
6
+ });
7
+ ////////////////////////////////////////////////////
8
+ // API
9
+ ////////////////////////////////////////////////////
10
+ export const canvasApi = (set, get) => ({
11
+ setInteraction: (isInteracting) => set(state => ({ canvas: { ...state.canvas, controls: { ...state.canvas.controls, isInteracting } } })),
12
+ setControlsEnabled: (enabled) => set(state => ({ canvas: { ...state.canvas, controls: { ...state.canvas.controls, enabled } } })),
13
+ });
File without changes
@@ -0,0 +1,98 @@
1
+ import * as THREE from 'three';
2
+ import { ThreeGeometryCreator } from '../../geometry/ThreeGeometryCreator';
3
+ ////////////////////////////////////////////////////
4
+ // DATA
5
+ ////////////////////////////////////////////////////
6
+ export const geometryData = () => ({
7
+ cache: {},
8
+ stamp: -1,
9
+ bounds: {
10
+ center: new THREE.Vector3(0, 0, 0),
11
+ radius: 200,
12
+ min: new THREE.Vector3(-100, -100, -100),
13
+ max: new THREE.Vector3(100, 100, 100),
14
+ },
15
+ config: { product: {} },
16
+ });
17
+ ////////////////////////////////////////////////////
18
+ // API
19
+ ////////////////////////////////////////////////////
20
+ export const geometryApi = (set, get) => ({
21
+ /**
22
+ * Updates the @see geometry.bounds depending on all products of the model.
23
+ * The update will only happen, if at least one solid of one product is available.
24
+ */
25
+ updateBounds: () => {
26
+ const { api, structure } = get();
27
+ if (structure.visibleContainers.length > 0) {
28
+ const bb = api.structure.calculateProductBounds(structure.root);
29
+ set(state => ({ geometry: { ...state.geometry, stamp: Date.now(), bounds: bb } }));
30
+ }
31
+ },
32
+ createGeometryIfNotCached: (containerId, color) => {
33
+ const { graphic, geometry } = get();
34
+ let geom = geometry.cache[containerId];
35
+ if (!geom) {
36
+ const container = graphic.containers[containerId];
37
+ if (container) {
38
+ geom = new ThreeGeometryCreator().createGeometry(container, color);
39
+ set(state => {
40
+ const geometryCache = { ...state.geometry.cache, [containerId]: geom };
41
+ return {
42
+ geometry: {
43
+ ...state.geometry,
44
+ cache: geometryCache,
45
+ stamp: Date.now(),
46
+ },
47
+ };
48
+ });
49
+ }
50
+ }
51
+ return geom;
52
+ },
53
+ setColor: (containerId, r, g, b) => {
54
+ const thCol = new THREE.Color(r, g, b);
55
+ const cachedGeom = { ...get().geometry.cache[containerId] };
56
+ cachedGeom.color = thCol;
57
+ cachedGeom.meshes.forEach(mesh => (mesh.color = thCol));
58
+ set(state => ({
59
+ geometry: {
60
+ ...state.geometry,
61
+ cache: { ...state.geometry.cache, [containerId]: cachedGeom },
62
+ stamp: Date.now(),
63
+ },
64
+ }));
65
+ },
66
+ setOpacity: (containerId, opacity) => {
67
+ const cachedGeom = { ...get().geometry.cache[containerId], opacity };
68
+ cachedGeom.meshes.forEach(mesh => (mesh.opacity = opacity));
69
+ set(state => ({
70
+ geometry: {
71
+ ...state.geometry,
72
+ cache: { ...state.geometry.cache, [containerId]: cachedGeom },
73
+ stamp: Date.now(),
74
+ },
75
+ }));
76
+ },
77
+ setConfig: (objId, config) => {
78
+ set(state => {
79
+ const productConfigs = { ...state.geometry.config.product };
80
+ if (config) {
81
+ const newConfig = { ...state.geometry.config.product[objId] };
82
+ if ('disabled' in config)
83
+ newConfig.disabled = config.disabled;
84
+ if ('edges' in config)
85
+ newConfig.edges = { ...newConfig.edges, ...config.edges };
86
+ if ('meshes' in config)
87
+ newConfig.meshes = { ...newConfig.meshes, ...config.meshes };
88
+ if ('points' in config)
89
+ newConfig.points = { ...newConfig.points, ...config.points };
90
+ productConfigs[objId] = newConfig;
91
+ }
92
+ else {
93
+ delete productConfigs[objId];
94
+ }
95
+ return { geometry: { ...state.geometry, config: { ...state.geometry.config, product: productConfigs } } };
96
+ });
97
+ },
98
+ });
@@ -0,0 +1,82 @@
1
+ ////////////////////////////////////////////////////
2
+ // DATA
3
+ ////////////////////////////////////////////////////
4
+ export const graphicData = () => ({
5
+ containers: {},
6
+ });
7
+ ////////////////////////////////////////////////////
8
+ // API
9
+ ////////////////////////////////////////////////////
10
+ export const graphicApi = (set, get) => ({
11
+ addPackages: (packages) => {
12
+ // Timing.dump('load', `add graphic packages ${graphic.length} ${graphic[0].containers.length}`)
13
+ let cacheChanged = false;
14
+ const newChache = { ...get().geometry.cache };
15
+ const addedContainers = {};
16
+ packages.forEach(gr => {
17
+ gr.containers.forEach(cont => {
18
+ addedContainers[cont.id] = cont;
19
+ // If the cached geometry for the added container already exist,
20
+ // it has to be removed from the cache to ensure geometry updates.
21
+ if (newChache[cont.id]) {
22
+ delete newChache[cont.id];
23
+ cacheChanged = true;
24
+ }
25
+ });
26
+ });
27
+ set(state => ({
28
+ graphic: { ...state.graphic, containers: { ...state.graphic.containers, ...addedContainers } },
29
+ geometry: cacheChanged ? { ...state.geometry, cache: newChache, stamp: Date.now() } : state.geometry,
30
+ }));
31
+ const { graphic, api } = get();
32
+ /**
33
+ * Update the bounds with the newly added graphic.
34
+ */
35
+ api.geometry.updateBounds();
36
+ /**
37
+ * Create the geometry for the graphic packages.
38
+ * Only the geometry which does not already exist will be created (@see createGeometryIfNotCached).
39
+ */
40
+ const containerIds = Object.getOwnPropertyNames(graphic.containers);
41
+ const createGeometry = async () => {
42
+ for (const id of containerIds) {
43
+ api.geometry.createGeometryIfNotCached(id);
44
+ }
45
+ };
46
+ createGeometry().catch();
47
+ // console.log('graphicContainers after add:', Object.getOwnPropertyNames(get().graphic.containers))
48
+ },
49
+ cleanupGraphicRecords: () => {
50
+ const { graphic, geometry, api } = get();
51
+ // collect the used solid ids from the structure tree
52
+ const solidIds = api.structure.collectUsedSolidIds();
53
+ // console.log('solidIds before cleanup:', solidIds)
54
+ // cleanup the graphic containers in the state
55
+ const usedContainers = {};
56
+ const existingContainers = graphic.containers;
57
+ const existingContainerIds = Object.getOwnPropertyNames(existingContainers);
58
+ if (existingContainerIds.length > 0) {
59
+ existingContainerIds.forEach(contIdStr => {
60
+ const contId = Number(contIdStr);
61
+ if (solidIds.indexOf(contId) >= 0) {
62
+ usedContainers[contId] = existingContainers[contId];
63
+ }
64
+ });
65
+ set({ graphic: { ...graphic, containers: usedContainers } });
66
+ // console.log('graphicContainers after cleanup:', Object.getOwnPropertyNames(get().graphic.ontainers))
67
+ }
68
+ // cleanup the cached geometry depending on the uses solids
69
+ const usedGeometry = {};
70
+ const existingGeometry = geometry.cache;
71
+ const existingGeometryIds = Object.getOwnPropertyNames(existingGeometry);
72
+ if (existingGeometryIds.length > 0) {
73
+ existingGeometryIds.forEach(contIdStr => {
74
+ const contId = Number(contIdStr);
75
+ if (solidIds.indexOf(contId) >= 0) {
76
+ usedGeometry[contId] = existingGeometry[contId];
77
+ }
78
+ });
79
+ set({ geometry: { ...geometry, cache: usedGeometry, stamp: Date.now() } });
80
+ }
81
+ },
82
+ });
@@ -11,11 +11,11 @@ const messageApi = (set, get, api) => ({
11
11
  show(message) {
12
12
  const newId = uuidv4();
13
13
  const msg = get().message;
14
- set({ message: Object.assign(Object.assign({}, msg), { data: Object.assign({ id: newId }, message), timestamp: Date.now() }) });
14
+ set({ message: { ...msg, data: { id: newId, ...message }, timestamp: Date.now() } });
15
15
  return newId;
16
16
  },
17
17
  update(id, message) {
18
18
  const msg = get().message;
19
- set({ message: Object.assign(Object.assign({}, msg), { data: Object.assign({ id }, message), timestamp: Date.now() }) });
19
+ set({ message: { ...msg, data: { id, ...message }, timestamp: Date.now() } });
20
20
  },
21
21
  });
@@ -0,0 +1,152 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+ export const pluginData = () => ({
3
+ feature: [],
4
+ global: [],
5
+ refs: {},
6
+ active: {
7
+ feature: null,
8
+ global: [],
9
+ },
10
+ visible: [],
11
+ });
12
+ export const pluginApi = (set, get) => ({
13
+ setActiveFeature: (id) => {
14
+ set(state => {
15
+ let plugins = state.plugin.refs;
16
+ const oldPlugin = state.plugin.active.feature !== null && state.plugin.refs[state.plugin.active.feature];
17
+ if (oldPlugin && state.plugin.active.feature !== id && !oldPlugin.persistent && id !== null) {
18
+ // Reset plugin-state when the plugin is re-activated, not persistent and has not been active all along
19
+ const initState = oldPlugin.initialState || {};
20
+ plugins = {
21
+ ...plugins,
22
+ [oldPlugin.id]: {
23
+ ...oldPlugin,
24
+ state: typeof initState === 'function' ? initState(get().id, id) : initState,
25
+ },
26
+ };
27
+ }
28
+ return {
29
+ plugin: { ...state.plugin, active: { ...state.plugin.active, feature: id }, refs: plugins },
30
+ selection: { ...state.selection, active: null },
31
+ };
32
+ });
33
+ },
34
+ setVisiblePlugin: (id, visible) => {
35
+ set(currentState => {
36
+ const filtered = currentState.plugin.visible.filter(p => p !== id);
37
+ if (visible)
38
+ filtered.push(id);
39
+ return {
40
+ plugin: {
41
+ ...currentState.plugin,
42
+ visible: filtered,
43
+ },
44
+ };
45
+ });
46
+ },
47
+ setActiveGlobal: (id, active) => {
48
+ set(currentState => {
49
+ if (currentState.plugin.global.indexOf(id) === -1) {
50
+ console.warn('Given plugin id does not exist.');
51
+ return currentState;
52
+ }
53
+ const filtered = currentState.plugin.active.global.filter(p => p !== id);
54
+ if (active)
55
+ filtered.push(id);
56
+ return {
57
+ plugin: {
58
+ ...currentState.plugin,
59
+ active: {
60
+ ...currentState.plugin.active,
61
+ global: filtered,
62
+ },
63
+ },
64
+ };
65
+ });
66
+ },
67
+ // TODO remove defaultId and useEmbeddedPlugin ?
68
+ createPlugin: (def, defaultId, drawingId, objectId) => {
69
+ const id = defaultId !== void 0 ? defaultId : uuidv4();
70
+ function init(definition) {
71
+ const { description, Root = null, View = null } = definition;
72
+ const initialState = description.initialState || {};
73
+ const localState = typeof initialState === 'function' ? initialState(drawingId, objectId) : initialState;
74
+ set(currentState => {
75
+ const newPlugin = {
76
+ id,
77
+ ...description,
78
+ drawingId,
79
+ objectId,
80
+ state: localState,
81
+ Root,
82
+ View,
83
+ set: (sel) => {
84
+ // set function can be called when drawing doesn't exist anymore, in this case nothing should be done.
85
+ if (get() === undefined)
86
+ return;
87
+ set(state => {
88
+ const newState = typeof sel === 'function' ? sel(state.plugin.refs[id].state) : sel;
89
+ return {
90
+ plugin: {
91
+ ...state.plugin,
92
+ refs: {
93
+ ...state.plugin.refs,
94
+ [id]: {
95
+ ...state.plugin.refs[id],
96
+ state: { ...state.plugin.refs[id].state, ...newState },
97
+ },
98
+ },
99
+ },
100
+ };
101
+ });
102
+ },
103
+ };
104
+ return {
105
+ plugin: {
106
+ ...currentState.plugin,
107
+ feature: description.global ? currentState.plugin.feature : [...currentState.plugin.feature, id],
108
+ global: description.global ? [...currentState.plugin.global, id] : currentState.plugin.global,
109
+ refs: { ...currentState.plugin.refs, [id]: { ...currentState.plugin.refs[id], ...newPlugin } },
110
+ },
111
+ };
112
+ });
113
+ }
114
+ // Initialize plugin definition, which may be a promise or an object
115
+ if (isPromisePlugin(def)) {
116
+ // The definition is async, so we create a plugin slot right away and complete it later
117
+ init({
118
+ description: { author: '', name: '', persistent: false, global: false, version: '' },
119
+ Root: null,
120
+ View: null,
121
+ });
122
+ def.then(init).catch();
123
+ }
124
+ else
125
+ init(def);
126
+ // Return unsubscribe
127
+ return (deactivate = true) => {
128
+ // Use 'get' to check if the drawing still exists
129
+ get &&
130
+ get() &&
131
+ set(state => {
132
+ const newPlugins = { ...state.plugin.refs };
133
+ delete newPlugins[id];
134
+ return {
135
+ plugin: {
136
+ ...state.plugin,
137
+ active: {
138
+ ...state.plugin.active,
139
+ feature: deactivate ? null : state.plugin.active.feature,
140
+ },
141
+ feature: state.plugin.feature.filter(pluginId => pluginId !== id),
142
+ global: state.plugin.global.filter(pluginId => pluginId !== id),
143
+ refs: newPlugins,
144
+ },
145
+ };
146
+ });
147
+ };
148
+ },
149
+ });
150
+ export function isPromisePlugin(def) {
151
+ return def.then !== undefined;
152
+ }