@abi-software/flatmap-viewer 2.3.1-b.1 → 2.3.2-b.1

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/src/pathways.js CHANGED
@@ -22,6 +22,8 @@ limitations under the License.
22
22
 
23
23
  //==============================================================================
24
24
 
25
+ import { reverseMap } from './utils';
26
+
25
27
  export const PATHWAYS_LAYER = 'pathways';
26
28
 
27
29
  //==============================================================================
@@ -48,28 +50,11 @@ export const PATH_STYLE_RULES =
48
50
 
49
51
  //==============================================================================
50
52
 
51
- function reverseMap(mapping)
52
- //==========================
53
+ export class PathManager
53
54
  {
54
- const reverse = {};
55
- for (const [key, values] of Object.entries(mapping)) {
56
- for (const value of values) {
57
- if (value in reverse) {
58
- reverse[value].add(key);
59
- } else {
60
- reverse[value] = new Set([key]);
61
- }
62
- }
63
- }
64
- return reverse;
65
- }
66
-
67
- //==============================================================================
68
-
69
- export class Pathways
70
- {
71
- constructor(flatmap)
55
+ constructor(flatmap, ui, enabled=true)
72
56
  {
57
+ this.__ui = ui;
73
58
  this.__connectivityModelPaths = {}; // modelId: [pathIds]
74
59
  this.__pathToConnectivityModel = {};
75
60
  if ('models' in flatmap.pathways) {
@@ -82,14 +67,19 @@ export class Pathways
82
67
  }
83
68
  this.__pathModelPaths = {}; // pathModelId: [pathIds]
84
69
  this.__pathToPathModel = {};
70
+
71
+ this.__paths = {};
72
+ const pathLines = {}; // pathId: [lineIds]
73
+ const pathNerves = {}; // pathId: [nerveIds]
85
74
  if ('paths' in flatmap.pathways) {
86
- this._pathLines = {}; // pathId: [lineIds]
87
- this._pathNerves = {}; // pathId: [nerveIds]
88
- this._pathNodes = {}; // pathId: [nodeIds]
89
75
  for (const [pathId, path] of Object.entries(flatmap.pathways.paths)) {
90
- this._pathLines[pathId] = path.lines;
91
- this._pathNerves[pathId] = path.nerves;
92
- this._pathNodes[pathId] = path.nodes;
76
+ pathLines[pathId] = path.lines;
77
+ pathNerves[pathId] = path.nerves;
78
+ this.__paths[pathId] = path;
79
+ for (const lineId of path.lines) {
80
+ this.__ui.enableFeature(lineId, enabled, true);
81
+ }
82
+ this.__paths[pathId].systemCount = 0;
93
83
  if ('models' in path) {
94
84
  const modelId = path['models'];
95
85
  if (!(modelId in this.__pathModelPaths)) {
@@ -99,30 +89,12 @@ export class Pathways
99
89
  this.__pathToPathModel[pathId] = modelId;
100
90
  }
101
91
  }
102
- } else {
103
- // To be deprecated...
104
- this._pathLines = flatmap.pathways['path-lines']; // pathId: [lineIds]
105
- this._pathNerves = flatmap.pathways['path-nerves']; // pathId: [nerveIds]
106
- if ('path-nodes' in flatmap.pathways) {
107
- this._pathNodes = flatmap.pathways['path-nodes']; // pathId: [nodeIds]
108
- } else {
109
- this._pathNodes = {};
110
- for (const path of Object.keys(this._pathLines)) {
111
- this._pathNodes[path] = [];
112
- }
113
- }
114
92
  }
115
- this._linePaths = reverseMap(this._pathLines); // lineId: [pathIds]
116
- this._nervePaths = reverseMap(this._pathNerves); // nerveId: [pathIds]
93
+ this.__pathsByLine = reverseMap(pathLines); // lineId: [pathIds]
94
+ this.__pathsByNerve = reverseMap(pathNerves); // nerveId: [pathIds]
117
95
 
118
96
  const nodePaths = flatmap.pathways['node-paths'];
119
- if (!('start-paths' in nodePaths)) {
120
- this._nodePaths = nodePaths; // nodeId: [pathIds]
121
- } else { // Original format, deprecated
122
- this._nodePaths = nodePaths['start-paths'];
123
- this.extendNodePaths_(nodePaths['through-paths']);
124
- this.extendNodePaths_(nodePaths['end-paths']);
125
- }
97
+ this._nodePaths = nodePaths; // nodeId: [pathIds]
126
98
  const featureIds = new Set();
127
99
  for (const paths of Object.values(this._nodePaths)) {
128
100
  this.addPathsToFeatureSet_(paths, featureIds);
@@ -130,20 +102,28 @@ export class Pathways
130
102
  this._allFeatureIds = featureIds;
131
103
 
132
104
  // Construct a list of path types we know about
133
- const pathTypes = [];
134
- for (const pathType of PATH_TYPES) {
135
- pathTypes.push(pathType.type);
105
+ const pathTypes = {};
106
+ this.__pathtypeEnabled = {};
107
+ for (const pathTypeDefn of PATH_TYPES) {
108
+ pathTypes[pathTypeDefn.type] = pathTypeDefn;
109
+ this.__pathtypeEnabled[pathTypeDefn.type] = !('enabled' in pathTypeDefn) || pathTypeDefn.enabled;
136
110
  }
137
- // Map unknown path types to ``other``
138
- this.__typePaths = {};
139
- this.__typePaths['other'] = [];
111
+
112
+ // Set path types, mapping unknown path types to ``other``
113
+ this.__pathsByType = {};
114
+ this.__pathsByType['other'] = [];
140
115
  for (const [pathType, paths] of Object.entries(flatmap.pathways['type-paths'])) {
141
- if (pathTypes.indexOf(pathType) >= 0) {
142
- this.__typePaths[pathType] = paths;
116
+ if (pathType in pathTypes) {
117
+ this.__pathsByType[pathType] = paths;
118
+ const pathDefn = pathTypes[pathType];
143
119
  } else {
144
- this.__typePaths['other'].push(...paths);
120
+ this.__pathsByType['other'].push(...paths);
121
+ this.__pathtypeEnabled[pathType] = false;
145
122
  }
146
123
  }
124
+ // Assign types to individual paths
125
+ this.__assignPathTypes();
126
+
147
127
  // Nerve centrelines are a special case with their own controls
148
128
  this.__haveCentrelines = false;
149
129
  }
@@ -154,44 +134,44 @@ export class Pathways
154
134
  return this.__haveCentrelines;
155
135
  }
156
136
 
137
+ __assignPathTypes()
138
+ //=================
139
+ {
140
+ for (const [pathType, paths] of Object.entries(this.__pathsByType)) {
141
+ for (const pathId of paths) {
142
+ this.__paths[pathId].pathType = pathType;
143
+ }
144
+ }
145
+ }
146
+
157
147
  pathTypes()
158
148
  //=========
159
149
  {
160
150
  const pathTypes = [];
161
- for (const pathType of PATH_TYPES) {
162
- if (pathType.type in this.__typePaths
163
- && this.__typePaths[pathType.type].length > 0) {
164
- if (pathType.type === 'centreline') {
151
+ for (const pathTypeDefn of PATH_TYPES) {
152
+ if (pathTypeDefn.type in this.__pathsByType
153
+ && this.__pathsByType[pathTypeDefn.type].length > 0) {
154
+ if (pathTypeDefn.type === 'centreline') {
165
155
  this.__haveCentrelines = true;
166
156
  } else {
167
- pathTypes.push(pathType);
157
+ pathTypes.push({
158
+ ...pathTypeDefn,
159
+ enabled: this.__pathtypeEnabled[pathTypeDefn.type]
160
+ });
168
161
  }
169
162
  }
170
163
  }
171
164
  return pathTypes;
172
165
  }
173
166
 
174
- addPathsToFeatureSet_(paths, featureSet)
167
+ addPathsToFeatureSet_(pathIds, featureSet)
175
168
  //======================================
176
169
  {
177
- for (const path of paths) {
178
- if (path in this._pathLines) {
179
- this._pathLines[path].forEach(lineId => featureSet.add(lineId));
180
- this._pathNerves[path].forEach(nerveId => featureSet.add(nerveId));
181
- this._pathNodes[path].forEach(nodeId => featureSet.add(nodeId));
182
- }
183
- }
184
- }
185
-
186
- extendNodePaths_(nodePaths)
187
- //=========================
188
- {
189
- for (const [key, values] of Object.entries(nodePaths)) {
190
- if (key in this._nodePaths) {
191
- this._nodePaths[key].push(...values);
192
- } else {
193
- this._nodePaths[key] = values;
194
- }
170
+ for (const pathId of pathIds) {
171
+ const path = this.__paths[pathId];
172
+ path.lines.forEach(lineId => featureSet.add(lineId));
173
+ path.nerves.forEach(nerveId => featureSet.add(nerveId));
174
+ path.nodes.forEach(nodeId => featureSet.add(nodeId));
195
175
  }
196
176
  }
197
177
 
@@ -206,8 +186,8 @@ export class Pathways
206
186
  {
207
187
  const featureIds = new Set();
208
188
  for (const lineId of lineIds) {
209
- if (lineId in this._linePaths) {
210
- this.addPathsToFeatureSet_(this._linePaths[lineId], featureIds);
189
+ if (lineId in this.__pathsByLine) {
190
+ this.addPathsToFeatureSet_(this.__pathsByLine[lineId], featureIds);
211
191
  }
212
192
  }
213
193
  return featureIds;
@@ -217,8 +197,8 @@ export class Pathways
217
197
  //======================
218
198
  {
219
199
  const featureIds = new Set();
220
- if (nerveId in this._nervePaths) {
221
- this.addPathsToFeatureSet_(this._nervePaths[nerveId], featureIds);
200
+ if (nerveId in this.__pathsByNerve) {
201
+ this.addPathsToFeatureSet_(this.__pathsByNerve[nerveId], featureIds);
222
202
  }
223
203
  return featureIds;
224
204
  }
@@ -237,8 +217,8 @@ export class Pathways
237
217
  //=====================
238
218
  {
239
219
  const properties = Object.assign({}, feature.properties);
240
- if (feature.id in this._linePaths) {
241
- for (const pathId of this._linePaths[feature.id]) {
220
+ if (feature.id in this.__pathsByLine) {
221
+ for (const pathId of this.__pathsByLine[feature.id]) {
242
222
  // There should only be a single path for a line
243
223
  if (pathId in this.__pathToConnectivityModel) {
244
224
  properties['connectivity'] = this.__pathToConnectivityModel[pathId];
@@ -249,7 +229,7 @@ export class Pathways
249
229
  }
250
230
  /*
251
231
  if (!('connectivity' in properties)) {
252
- for (const pathId of this._nervePaths[feature.id]) {
232
+ for (const pathId of this.__pathsByNerve[feature.id]) {
253
233
  if (pathId in this.__pathToConnectivityModel) {
254
234
  properties['connectivity'] = this.__pathToConnectivityModel[pathId];
255
235
  break;
@@ -297,16 +277,53 @@ export class Pathways
297
277
  return featureIds;
298
278
  }
299
279
 
300
- typeFeatureIds(pathType)
301
- //======================
280
+ __typeFeatureIds(pathType)
281
+ //========================
302
282
  {
303
283
  const featureIds = new Set();
304
- if (pathType in this.__typePaths) {
305
- this.addPathsToFeatureSet_(this.__typePaths[pathType], featureIds);
284
+ if (pathType in this.__pathsByType) {
285
+ this.addPathsToFeatureSet_(this.__pathsByType[pathType], featureIds);
306
286
  }
307
287
  return featureIds;
308
288
  }
309
289
 
290
+ enablePathsBySystem(system, enable, force=false)
291
+ //==============================================
292
+ {
293
+ for (const pathId of system.pathIds) {
294
+ const path = this.__paths[pathId];
295
+ if (this.__pathtypeEnabled[path.pathType]
296
+ && (force
297
+ || enable && path.systemCount === 0
298
+ || !enable && path.systemCount == 1)) {
299
+ // and type(pathId) is enabled...
300
+ const featureIds = new Set();
301
+ this.addPathsToFeatureSet_([pathId], featureIds)
302
+ for (const featureId of featureIds) {
303
+ this.__ui.enableFeature(featureId, enable, force);
304
+ }
305
+ }
306
+ path.systemCount += (enable ? 1 : -1);
307
+ if (path.systemCount < 0) {
308
+ path.systemCount = 0;
309
+ }
310
+ // TODO? Show connectors and parent components of these paths??
311
+ }
312
+ }
313
+
314
+ enablePathsByType(pathType, enable, force=false)
315
+ //==============================================
316
+ {
317
+ if (force
318
+ || enable && !this.__pathtypeEnabled[pathType]
319
+ || !enable && this.__pathtypeEnabled[pathType]) {
320
+ for (const featureId of this.__typeFeatureIds(pathType)) {
321
+ this.__ui.enableFeature(featureId, enable, force);
322
+ }
323
+ this.__pathtypeEnabled[pathType] = enable;
324
+ }
325
+ }
326
+
310
327
  nodePathModels(nodeId)
311
328
  //====================
312
329
  {
package/src/styling.js CHANGED
@@ -45,6 +45,17 @@ const NERVE_SELECTED = 'red';
45
45
 
46
46
  //==============================================================================
47
47
 
48
+ const STROKE_INTERPOLATION = [
49
+ 'interpolate',
50
+ ['exponential', 2],
51
+ ['zoom'],
52
+ 2, ["*", ['var', 'width'], ["^", 2, -0.5]],
53
+ 7, ["*", ['var', 'width'], ["^", 2, 2.5]],
54
+ 9, ["*", ['var', 'width'], ["^", 2, 4.0]]
55
+ ];
56
+
57
+ //==============================================================================
58
+
48
59
  class VectorStyleLayer
49
60
  {
50
61
  constructor(id, suffix, sourceLayer)
@@ -272,8 +283,7 @@ export class FeatureLineLayer extends VectorStyleLayer
272
283
  constructor(id, sourceLayer, options={})
273
284
  {
274
285
  const dashed = ('dashed' in options && options.dashed);
275
- const filterType = dashed ? 'line-dash' : 'line';
276
- super(id, `feature-${filterType}`, sourceLayer);
286
+ super(id, `feature-${dashed ? 'line-dash' : 'line'}`, sourceLayer);
277
287
  this.__dashed = dashed;
278
288
  }
279
289
 
@@ -323,14 +333,8 @@ export class FeatureLineLayer extends VectorStyleLayer
323
333
  ['boolean', ['feature-state', 'selected'], false], 1.2,
324
334
  ['boolean', ['feature-state', 'active'], false], 1.2,
325
335
  options.authoring ? 0.7 : 0.5
326
- ], [
327
- 'interpolate',
328
- ['exponential', 2],
329
- ['zoom'],
330
- 2, ["*", ['var', 'width'], ["^", 2, -0.5]],
331
- 7, ["*", ['var', 'width'], ["^", 2, 2.5]],
332
- 9, ["*", ['var', 'width'], ["^", 2, 4.0]]
333
- ]
336
+ ],
337
+ STROKE_INTERPOLATION
334
338
  ]
335
339
  // Need to vary width based on zoom??
336
340
  // Or opacity??
@@ -433,13 +437,7 @@ export class AnnotatedPathLayer extends VectorStyleLayer
433
437
  ['*', 1.2, ['case', ['has', 'stroke-width'], ['get', 'stroke-width'], 1.0]],
434
438
  0.0
435
439
  ],
436
- ['interpolate',
437
- ['exponential', 2],
438
- ['zoom'],
439
- 2, ["*", ['var', 'width'], ["^", 2, -0.5]],
440
- 7, ["*", ['var', 'width'], ["^", 2, 2.5]],
441
- 9, ["*", ['var', 'width'], ["^", 2, 4.0]]
442
- ]
440
+ STROKE_INTERPOLATION
443
441
  ]
444
442
  };
445
443
  return super.changedPaintStyle(paintStyle, changes);
@@ -467,9 +465,10 @@ export class PathLineLayer extends VectorStyleLayer
467
465
  constructor(id, sourceLayer, options={})
468
466
  {
469
467
  const dashed = ('dashed' in options && options.dashed);
470
- const filterType = dashed ? 'line-dash' : 'line';
471
- super(id, `path-${filterType}`, sourceLayer);
468
+ const highlight = ('highlight' in options && options.highlight);
469
+ super(id, `path${highlight ? '-highlight' : ''}-${dashed ? 'line-dash' : 'line'}`, sourceLayer);
472
470
  this.__dashed = dashed;
471
+ this.__highlight = highlight;
473
472
  }
474
473
 
475
474
  makeFilter(options={})
@@ -509,36 +508,45 @@ export class PathLineLayer extends VectorStyleLayer
509
508
  ...PATH_STYLE_RULES,
510
509
  '#888'
511
510
  ],
512
- 'line-opacity': [
511
+ 'line-opacity': this.__highlight ? [
512
+ 'case',
513
+ ['boolean', ['feature-state', 'hidden'], false], 0.0,
514
+ ['boolean', ['get', 'invisible'], false], 0.0,
515
+ ['boolean', ['feature-state', 'selected'], false], 1.0,
516
+ ['boolean', ['feature-state', 'active'], false], 1.0,
517
+ 0.0
518
+ ] : [
513
519
  'case',
514
520
  ['boolean', ['feature-state', 'hidden'], false], 0.01,
515
521
  ['==', ['get', 'type'], 'bezier'], 1.0,
516
522
  ['==', ['get', 'kind'], 'error'], 1.0,
517
523
  ['boolean', ['get', 'invisible'], false], 0.001,
518
- ['boolean', ['feature-state', 'selected'], false], 1.0,
519
- ['boolean', ['feature-state', 'active'], false], 1.0,
524
+ ['boolean', ['feature-state', 'selected'], false], 0.0,
525
+ ['boolean', ['feature-state', 'active'], false], 0.0,
520
526
  dimmed ? 0.1 : 0.8
521
527
  ],
522
528
  'line-width': [
523
529
  'let',
524
- 'width', ["*", [
525
- 'case',
530
+ 'width', [
531
+ "*",
532
+ this.__highlight ? ['case',
533
+ ['boolean', ['get', 'invisible'], false], 0.1,
534
+ ['boolean', ['feature-state', 'selected'], false], 0.6,
535
+ ['boolean', ['feature-state', 'active'], false], 0.9,
536
+ 0.0
537
+ ] : [
538
+ 'case',
526
539
  ['==', ['get', 'type'], 'bezier'], 0.1,
527
540
  ['==', ['get', 'kind'], 'error'], 1,
528
541
  ['==', ['get', 'kind'], 'unknown'], 1,
529
542
  ['boolean', ['get', 'invisible'], false], 0.1,
530
- ['boolean', ['feature-state', 'selected'], false], 2.0,
531
- ['boolean', ['feature-state', 'active'], false], 0.9,
532
- 0.6
543
+ ['boolean', ['feature-state', 'selected'], false], 0.0,
544
+ ['boolean', ['feature-state', 'active'], false], 0.0,
545
+ 0.6
533
546
  ],
534
- ['case', ['has', 'stroke-width'], ['get', 'stroke-width'], 1.0]],
535
- ['interpolate',
536
- ['exponential', 2],
537
- ['zoom'],
538
- 2, ["*", ['var', 'width'], ["^", 2, -0.5]],
539
- 7, ["*", ['var', 'width'], ["^", 2, 2.5]],
540
- 9, ["*", ['var', 'width'], ["^", 2, 4.0]]
541
- ]
547
+ ['case', ['has', 'stroke-width'], ['get', 'stroke-width'], 1.0]
548
+ ],
549
+ STROKE_INTERPOLATION
542
550
  ]
543
551
  };
544
552
  if (this.__dashed) {
@@ -573,6 +581,24 @@ export class PathDashlineLayer extends PathLineLayer
573
581
 
574
582
  //==============================================================================
575
583
 
584
+ export class PathHighlightLayer extends PathLineLayer
585
+ {
586
+ constructor(id, sourceLayer)
587
+ {
588
+ super(id, sourceLayer, {highlight: true});
589
+ }
590
+ }
591
+
592
+ export class PathDashHighlightLayer extends PathLineLayer
593
+ {
594
+ constructor(id, sourceLayer)
595
+ {
596
+ super(id, sourceLayer, {dashed: true, highlight: true});
597
+ }
598
+ }
599
+
600
+ //==============================================================================
601
+
576
602
  class CentrelineLayer extends VectorStyleLayer
577
603
  {
578
604
  constructor(id, type, sourceLayer)
@@ -602,14 +628,7 @@ class CentrelineLayer extends VectorStyleLayer
602
628
  'let',
603
629
  'width',
604
630
  (this.__type == 'edge') ? 1.6 : 1.2,
605
- [
606
- 'interpolate',
607
- ['exponential', 2],
608
- ['zoom'],
609
- 2, ["*", ['var', 'width'], ["^", 2, -0.5]],
610
- 7, ["*", ['var', 'width'], ["^", 2, 2.5]],
611
- 9, ["*", ['var', 'width'], ["^", 2, 4.0]]
612
- ]
631
+ STROKE_INTERPOLATION
613
632
  ]
614
633
  // Need to vary width based on zoom??
615
634
  // Or opacity??
package/src/systems.js CHANGED
@@ -21,46 +21,50 @@ limitations under the License.
21
21
 
22
22
  export class SystemsManager
23
23
  {
24
- constructor(flatmap, ui, enabled=true)
24
+ constructor(flatmap, ui, enabled=false)
25
25
  {
26
26
  this.__ui = ui;
27
27
  this.__systems = new Map();
28
28
  this.__enabledChildren = new Map();
29
29
  for (const [id, ann] of flatmap.annotations) {
30
30
  if (ann['fc-class'] === 'fc-class:System') {
31
- if (this.__systems.has(ann.name)) {
32
- this.__systems.get(ann.name).featureIds.push(ann.featureId)
31
+ const systemId = ann.name.replaceAll(' ', '_');
32
+ if (this.__systems.has(systemId)) {
33
+ this.__systems.get(systemId).featureIds.push(ann.featureId)
33
34
  } else {
34
- this.__systems.set(ann.name, {
35
- id: ann.name.replaceAll(' ', '_'),
35
+ this.__systems.set(systemId, {
36
+ name: ann.name,
36
37
  colour: ann.colour,
37
38
  featureIds: [ ann.featureId ],
38
- enabled: enabled,
39
+ enabled: false,
39
40
  pathIds: ('path-ids' in ann) ? ann['path-ids'] : []
40
41
  });
42
+ this.__ui.enableFeature(ann.featureId, false, true);
41
43
  }
42
44
  for (const childId of ann['children']) {
43
- if (enabled) {
44
- const enabledCount = (this.__enabledChildren.has(childId))
45
- ? this.__enabledChildren.get(childId)
46
- : 0;
47
- this.__enabledChildren.set(childId, enabledCount + 1);
48
- } else {
49
- this.__enabledChildren.set(childId, 0);
50
- }
45
+ this.__enabledChildren.set(childId, 0);
46
+ this.__ui.enableFeatureWithChildren(childId, false, true);
51
47
  }
52
48
  }
53
49
  }
50
+ for (const system of this.__systems.values()) {
51
+ if (enabled) {
52
+ this.__enableSystem(system, true);
53
+ } else {
54
+ // Disable all paths associated with the disabled system
55
+ this.__ui.enablePathsBySystem(system, false, true);
56
+ }
57
+ }
54
58
  }
55
59
 
56
60
  get systems()
57
61
  //===========
58
62
  {
59
63
  const systems = [];
60
- for (const [name, system] of this.__systems.entries()) {
64
+ for (const [systemId, system] of this.__systems.entries()) {
61
65
  systems.push({
62
- name: name,
63
- id: system.id,
66
+ id: systemId,
67
+ name: system.name,
64
68
  colour: system.colour,
65
69
  enabled: system.enabled
66
70
  });
@@ -68,31 +72,44 @@ export class SystemsManager
68
72
  return systems;
69
73
  }
70
74
 
71
- enable(systemName, enable=true)
72
- //=============================
75
+ enable(systemId, enable=true)
76
+ //===========================
73
77
  {
74
- const system = this.__systems.get(systemName);
78
+ const system = this.__systems.get(systemId);
75
79
  if (system !== undefined && enable !== system.enabled) {
76
- for (const featureId of system.featureIds) {
77
- const feature = this.__ui.mapFeature(featureId);
78
- if (feature !== undefined) {
79
- this.__ui.enableFeature(feature, enable);
80
- for (const childFeatureId of feature.children) {
81
- const enabledCount = this.__enabledChildren.get(childFeatureId);
82
- if (enable && enabledCount === 0 || !enable && enabledCount == 1) {
83
- this.__ui.enableFeatureWithChildren(childFeatureId, enable);
84
- }
85
- this.__enabledChildren.set(childFeatureId, enabledCount + (enable ? 1 : -1));
80
+ this.__enableSystem(system, enable);
81
+ }
82
+ }
83
+
84
+ __enableSystem(system, enable=true)
85
+ //=================================
86
+ {
87
+ for (const featureId of system.featureIds) {
88
+ const feature = this.__ui.mapFeature(featureId);
89
+ if (feature !== undefined) {
90
+ this.__ui.enableMapFeature(feature, enable);
91
+ for (const childFeatureId of feature.children) {
92
+ const enabledCount = this.__enabledChildren.get(childFeatureId);
93
+ if (enable && enabledCount === 0 || !enable && enabledCount == 1) {
94
+ this.__ui.enableFeatureWithChildren(childFeatureId, enable);
86
95
  }
96
+ this.__enabledChildren.set(childFeatureId, enabledCount + (enable ? 1 : -1));
87
97
  }
88
98
  }
89
- system.enabled = enable;
90
-
91
- // Enable/disable all paths associated with the system
92
- for (const pathId of system.pathIds) {
93
- this.__ui.enableFeature(this.__ui.mapFeature(pathId), enable);
94
- }
95
99
  }
100
+
101
+ // Enable/disable all paths associated with the system
102
+ this.__ui.enablePathsBySystem(system, enable);
103
+
104
+ // Save system state
105
+ system.enabled = enable;
106
+ }
107
+
108
+ systemEnabled(systemId)
109
+ //=====================
110
+ {
111
+ const system = this.__systems.get(systemId);
112
+ return (system !== undefined && system.enabled);
96
113
  }
97
114
  }
98
115
 
package/src/utils.js CHANGED
@@ -124,3 +124,21 @@ export function setDefaults(options, defaultOptions)
124
124
  }
125
125
 
126
126
  //==============================================================================
127
+
128
+ export function reverseMap(mapping)
129
+ //=================================
130
+ {
131
+ const reverse = {};
132
+ for (const [key, values] of Object.entries(mapping)) {
133
+ for (const value of values) {
134
+ if (value in reverse) {
135
+ reverse[value].add(key);
136
+ } else {
137
+ reverse[value] = new Set([key]);
138
+ }
139
+ }
140
+ }
141
+ return reverse;
142
+ }
143
+
144
+ //==============================================================================