@peter.prib/renderengine 0.2.2 → 0.2.4

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.
@@ -0,0 +1,70 @@
1
+ import { ViewsCompared } from '../diagramModel/ViewsCompared.js';
2
+
3
+ /** The lists of a view's JSON whose items have ids made anew each time a model is read. */
4
+ const INSTANCE_LISTS = ['elementInstances', 'relationshipInstances', 'viewConnections'];
5
+
6
+ /**
7
+ * ArchimateViewsCompared — an ArchimateModel's views (ArchiMate diagrams,
8
+ * sketches, canvases, templates). A view's content is what it draws and
9
+ * says (toJSON), its Archi type, folder and viewpoint, with its boxes and
10
+ * lines known by their place in it rather than by their ids, which reading
11
+ * a file makes anew, and without Archi's ids for them, which a copy drops.
12
+ * A view needs the elements and relationships it draws.
13
+ */
14
+ export class ArchimateViewsCompared extends ViewsCompared {
15
+ content(view) {
16
+ const { elements, relationships, ...json } = view.toJSON();
17
+ return ArchimateViewsCompared.stableIds({
18
+ ...json,
19
+ archiViewType: view.archiViewType,
20
+ folder: (view.archiFolder ?? []).map(step => step.name),
21
+ viewpoint: view.viewpoint ?? null,
22
+ template: !!view.template,
23
+ });
24
+ }
25
+
26
+ requires(view) {
27
+ const json = view.toJSON();
28
+ return [
29
+ ...json.elements.map(el => ['element', el.id]),
30
+ ...json.relationships.map(rel => ['relationship', rel.id]),
31
+ ];
32
+ }
33
+
34
+ /**
35
+ * A view's JSON with each box's and line's id (wherever it appears, as a
36
+ * value or a key) replaced by its list and place ('elementInstances#3'),
37
+ * a parent box named by place too (`archi.parent`), and Archi's ids
38
+ * (`archi.id`) left out.
39
+ */
40
+ static stableIds(json) {
41
+ const ids = new Map();
42
+ // A box inside another names it (archi.parent) by its Archi id, or in a copy as diag-<its id>.
43
+ const parents = new Map();
44
+ for (const list of INSTANCE_LISTS) {
45
+ (json[list] ?? []).forEach((item, i) => {
46
+ if (item?.id == null) return;
47
+ const label = `${list}#${i}`;
48
+ ids.set(item.id, label);
49
+ parents.set(`diag-${item.id}`, label);
50
+ if (item.archi?.id != null) parents.set(item.archi.id, label);
51
+ });
52
+ }
53
+ const walk = (value) => {
54
+ if (Array.isArray(value)) return value.map(walk);
55
+ if (typeof value === 'string') return ids.get(value) ?? value;
56
+ if (!value || typeof value !== 'object') return value;
57
+ const out = {};
58
+ for (const [k, v] of Object.entries(value)) {
59
+ const walked = walk(v);
60
+ if (k === 'archi' && walked && typeof walked === 'object') {
61
+ delete walked.id;
62
+ if (parents.has(walked.parent)) walked.parent = parents.get(walked.parent);
63
+ }
64
+ out[ids.get(k) ?? k] = walked;
65
+ }
66
+ return out;
67
+ };
68
+ return walk(json);
69
+ }
70
+ }
@@ -0,0 +1,76 @@
1
+ import { ComparedKind } from '../diagramModel/ComparedKind.js';
2
+ import { structuredCopy } from './ElementInstance.js';
3
+
4
+ /**
5
+ * ConceptsCompared — an ArchimateModel's elements, or its relationships, as
6
+ * two models are compared on them: one's content is all it holds (name, type,
7
+ * documentation, properties, ends); bringing one adds a copy, or puts the
8
+ * copy's fields in place of those of the one with its id. ElementsCompared
9
+ * and RelationshipsCompared say which model collection it is.
10
+ */
11
+ export class ConceptsCompared extends ComparedKind {
12
+ constructor() {
13
+ super();
14
+ if (new.target === ConceptsCompared) throw new TypeError('ConceptsCompared is abstract — subclass it');
15
+ }
16
+
17
+ content(item) {
18
+ return structuredCopy(item);
19
+ }
20
+
21
+ /** Its name, else its type and id. */
22
+ nameOf(item) {
23
+ return item.name || `${item.type ?? item.kind ?? ''} ${item.id}`.trim();
24
+ }
25
+
26
+ bring(model, item) {
27
+ const copy = structuredCopy(item);
28
+ const old = this.get(model, item.id);
29
+ if (!old) {
30
+ this._add(model, copy);
31
+ return () => this._remove(model, item.id);
32
+ }
33
+ const before = structuredCopy(old);
34
+ const become = (fields) => {
35
+ for (const k of Object.keys(old)) delete old[k];
36
+ Object.assign(old, structuredCopy(fields));
37
+ };
38
+ become(copy);
39
+ return () => become(before);
40
+ }
41
+
42
+ /** Add one to the model. @abstract @protected */
43
+ _add(model, item) {
44
+ throw new Error(`${this.constructor.name} must implement _add()`);
45
+ }
46
+
47
+ /** Take one out of the model. @abstract @protected */
48
+ _remove(model, id) {
49
+ throw new Error(`${this.constructor.name} must implement _remove()`);
50
+ }
51
+ }
52
+
53
+ /** An ArchimateModel's elements. */
54
+ export class ElementsCompared extends ConceptsCompared {
55
+ get key() { return 'element'; }
56
+ get label() { return 'Elements'; }
57
+ items(model) { return model.elements(); }
58
+ get(model, id) { return model.getElement(id) ?? null; }
59
+ _add(model, item) { model.addElement(item); }
60
+ _remove(model, id) { model.removeElement(id); }
61
+ }
62
+
63
+ /** An ArchimateModel's relationships: each needs its ends (an element, or another relationship). */
64
+ export class RelationshipsCompared extends ConceptsCompared {
65
+ get key() { return 'relationship'; }
66
+ get label() { return 'Relationships'; }
67
+ items(model) { return model.relationships(); }
68
+ get(model, id) { return model.getRelationship(id) ?? null; }
69
+ _add(model, item) { model.addRelationship(item); }
70
+ _remove(model, id) { model.removeRelationship(id); }
71
+
72
+ requires(rel, from) {
73
+ return [rel.source, rel.target].filter(Boolean)
74
+ .map(id => [from.getRelationship(id) ? 'relationship' : 'element', id]);
75
+ }
76
+ }