@rsconcept/domain 1.0.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.
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/dist/cctext/index.d.ts +1 -0
- package/dist/cctext/index.js +42 -0
- package/dist/cctext/index.js.map +1 -0
- package/dist/cctext/language-api.d.ts +43 -0
- package/dist/cctext/language-api.js +252 -0
- package/dist/cctext/language-api.js.map +1 -0
- package/dist/cctext/language.d.ts +58 -0
- package/dist/cctext/language.js +44 -0
- package/dist/cctext/language.js.map +1 -0
- package/dist/graph/graph.d.ts +62 -0
- package/dist/graph/graph.js +385 -0
- package/dist/graph/graph.js.map +1 -0
- package/dist/graph/index.d.ts +1 -0
- package/dist/graph/index.js +384 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +5851 -0
- package/dist/index.js.map +1 -0
- package/dist/library/folder-tree.d.ts +32 -0
- package/dist/library/folder-tree.js +136 -0
- package/dist/library/folder-tree.js.map +1 -0
- package/dist/library/index.d.ts +17 -0
- package/dist/library/index.js +2800 -0
- package/dist/library/index.js.map +1 -0
- package/dist/library/library-api.d.ts +6 -0
- package/dist/library/library-api.js +13 -0
- package/dist/library/library-api.js.map +1 -0
- package/dist/library/library.d.ts +56 -0
- package/dist/library/library.js +23 -0
- package/dist/library/library.js.map +1 -0
- package/dist/library/oss-api.d.ts +47 -0
- package/dist/library/oss-api.js +1105 -0
- package/dist/library/oss-api.js.map +1 -0
- package/dist/library/oss-layout-api.d.ts +36 -0
- package/dist/library/oss-layout-api.js +330 -0
- package/dist/library/oss-layout-api.js.map +1 -0
- package/dist/library/oss-layout.d.ts +25 -0
- package/dist/library/oss-layout.js +1 -0
- package/dist/library/oss-layout.js.map +1 -0
- package/dist/library/oss.d.ts +136 -0
- package/dist/library/oss.js +30 -0
- package/dist/library/oss.js.map +1 -0
- package/dist/library/rsengine.d.ts +116 -0
- package/dist/library/rsengine.js +2604 -0
- package/dist/library/rsengine.js.map +1 -0
- package/dist/library/rsform-api.d.ts +74 -0
- package/dist/library/rsform-api.js +879 -0
- package/dist/library/rsform-api.js.map +1 -0
- package/dist/library/rsform.d.ts +206 -0
- package/dist/library/rsform.js +32 -0
- package/dist/library/rsform.js.map +1 -0
- package/dist/library/rsmodel-api.d.ts +43 -0
- package/dist/library/rsmodel-api.js +836 -0
- package/dist/library/rsmodel-api.js.map +1 -0
- package/dist/library/rsmodel.d.ts +52 -0
- package/dist/library/rsmodel.js +25 -0
- package/dist/library/rsmodel.js.map +1 -0
- package/dist/library/structure-planner.d.ts +33 -0
- package/dist/library/structure-planner.js +481 -0
- package/dist/library/structure-planner.js.map +1 -0
- package/dist/parsing/ast.d.ts +49 -0
- package/dist/parsing/ast.js +93 -0
- package/dist/parsing/ast.js.map +1 -0
- package/dist/parsing/index.d.ts +3 -0
- package/dist/parsing/index.js +141 -0
- package/dist/parsing/index.js.map +1 -0
- package/dist/parsing/lezer-tree.d.ts +13 -0
- package/dist/parsing/lezer-tree.js +50 -0
- package/dist/parsing/lezer-tree.js.map +1 -0
- package/dist/rslang/api.d.ts +53 -0
- package/dist/rslang/api.js +846 -0
- package/dist/rslang/api.js.map +1 -0
- package/dist/rslang/ast-annotations.d.ts +18 -0
- package/dist/rslang/ast-annotations.js +56 -0
- package/dist/rslang/ast-annotations.js.map +1 -0
- package/dist/rslang/error.d.ts +85 -0
- package/dist/rslang/error.js +159 -0
- package/dist/rslang/error.js.map +1 -0
- package/dist/rslang/eval/calculator.d.ts +43 -0
- package/dist/rslang/eval/calculator.js +1639 -0
- package/dist/rslang/eval/calculator.js.map +1 -0
- package/dist/rslang/eval/evaluation-cache.d.ts +36 -0
- package/dist/rslang/eval/evaluation-cache.js +310 -0
- package/dist/rslang/eval/evaluation-cache.js.map +1 -0
- package/dist/rslang/eval/evaluator.d.ts +70 -0
- package/dist/rslang/eval/evaluator.js +1514 -0
- package/dist/rslang/eval/evaluator.js.map +1 -0
- package/dist/rslang/eval/value-api.d.ts +48 -0
- package/dist/rslang/eval/value-api.js +490 -0
- package/dist/rslang/eval/value-api.js.map +1 -0
- package/dist/rslang/eval/value.d.ts +36 -0
- package/dist/rslang/eval/value.js +118 -0
- package/dist/rslang/eval/value.js.map +1 -0
- package/dist/rslang/index.d.ts +17 -0
- package/dist/rslang/index.js +4314 -0
- package/dist/rslang/index.js.map +1 -0
- package/dist/rslang/labels.d.ts +16 -0
- package/dist/rslang/labels.js +315 -0
- package/dist/rslang/labels.js.map +1 -0
- package/dist/rslang/parser/expression-generator.d.ts +10 -0
- package/dist/rslang/parser/expression-generator.js +451 -0
- package/dist/rslang/parser/expression-generator.js.map +1 -0
- package/dist/rslang/parser/normalize.d.ts +11 -0
- package/dist/rslang/parser/normalize.js +507 -0
- package/dist/rslang/parser/normalize.js.map +1 -0
- package/dist/rslang/parser/parser.d.ts +5 -0
- package/dist/rslang/parser/parser.js +24 -0
- package/dist/rslang/parser/parser.js.map +1 -0
- package/dist/rslang/parser/parser.terms.d.ts +42 -0
- package/dist/rslang/parser/parser.terms.js +84 -0
- package/dist/rslang/parser/parser.terms.js.map +1 -0
- package/dist/rslang/parser/syntax-errors.d.ts +11 -0
- package/dist/rslang/parser/syntax-errors.js +403 -0
- package/dist/rslang/parser/syntax-errors.js.map +1 -0
- package/dist/rslang/parser/token.d.ts +79 -0
- package/dist/rslang/parser/token.js +95 -0
- package/dist/rslang/parser/token.js.map +1 -0
- package/dist/rslang/semantic/analyzer.d.ts +39 -0
- package/dist/rslang/semantic/analyzer.js +2604 -0
- package/dist/rslang/semantic/analyzer.js.map +1 -0
- package/dist/rslang/semantic/arguments-extractor.d.ts +42 -0
- package/dist/rslang/semantic/arguments-extractor.js +366 -0
- package/dist/rslang/semantic/arguments-extractor.js.map +1 -0
- package/dist/rslang/semantic/type-auditor.d.ts +73 -0
- package/dist/rslang/semantic/type-auditor.js +1570 -0
- package/dist/rslang/semantic/type-auditor.js.map +1 -0
- package/dist/rslang/semantic/typification-api.d.ts +27 -0
- package/dist/rslang/semantic/typification-api.js +320 -0
- package/dist/rslang/semantic/typification-api.js.map +1 -0
- package/dist/rslang/semantic/typification-parser.d.ts +12 -0
- package/dist/rslang/semantic/typification-parser.js +226 -0
- package/dist/rslang/semantic/typification-parser.js.map +1 -0
- package/dist/rslang/semantic/typification.d.ts +119 -0
- package/dist/rslang/semantic/typification.js +74 -0
- package/dist/rslang/semantic/typification.js.map +1 -0
- package/dist/rslang/semantic/value-auditor.d.ts +43 -0
- package/dist/rslang/semantic/value-auditor.js +523 -0
- package/dist/rslang/semantic/value-auditor.js.map +1 -0
- package/dist/rslang/semantic/value-class.d.ts +10 -0
- package/dist/rslang/semantic/value-class.js +9 -0
- package/dist/rslang/semantic/value-class.js.map +1 -0
- package/dist/rslang/typification-graph.d.ts +33 -0
- package/dist/rslang/typification-graph.js +311 -0
- package/dist/rslang/typification-graph.js.map +1 -0
- package/dist/shared/branded.d.ts +7 -0
- package/dist/shared/branded.js +1 -0
- package/dist/shared/branded.js.map +1 -0
- package/dist/shared/hash.d.ts +6 -0
- package/dist/shared/hash.js +18 -0
- package/dist/shared/hash.js.map +1 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +18 -0
- package/dist/shared/index.js.map +1 -0
- package/package.json +184 -0
- package/src/cctext/index.ts +9 -0
- package/src/cctext/language-api.test.ts +149 -0
- package/src/cctext/language-api.ts +285 -0
- package/src/cctext/language.ts +80 -0
- package/src/graph/graph.test.ts +392 -0
- package/src/graph/graph.ts +433 -0
- package/src/graph/index.ts +1 -0
- package/src/index.ts +96 -0
- package/src/library/folder-tree.test.ts +47 -0
- package/src/library/folder-tree.ts +156 -0
- package/src/library/index.ts +46 -0
- package/src/library/library-api.test.ts +32 -0
- package/src/library/library-api.ts +11 -0
- package/src/library/library.ts +61 -0
- package/src/library/oss-api.ts +449 -0
- package/src/library/oss-layout-api.ts +377 -0
- package/src/library/oss-layout.ts +27 -0
- package/src/library/oss.ts +150 -0
- package/src/library/rsengine.ts +593 -0
- package/src/library/rsform-api.ts +533 -0
- package/src/library/rsform.ts +228 -0
- package/src/library/rsmodel-api.ts +340 -0
- package/src/library/rsmodel.ts +50 -0
- package/src/library/structure-planner.ts +143 -0
- package/src/parsing/ast.ts +136 -0
- package/src/parsing/index.ts +15 -0
- package/src/parsing/lezer-tree.ts +69 -0
- package/src/rslang/api.test.ts +116 -0
- package/src/rslang/api.ts +183 -0
- package/src/rslang/ast-annotations.ts +70 -0
- package/src/rslang/error.ts +129 -0
- package/src/rslang/eval/calculator.test.ts +124 -0
- package/src/rslang/eval/calculator.ts +121 -0
- package/src/rslang/eval/evaluation-cache.ts +257 -0
- package/src/rslang/eval/evaluator.test.ts +352 -0
- package/src/rslang/eval/evaluator.ts +935 -0
- package/src/rslang/eval/value-api.test.ts +105 -0
- package/src/rslang/eval/value-api.ts +444 -0
- package/src/rslang/eval/value.ts +102 -0
- package/src/rslang/index.ts +23 -0
- package/src/rslang/labels.ts +191 -0
- package/src/rslang/parser/expression-generator.test.ts +100 -0
- package/src/rslang/parser/expression-generator.ts +466 -0
- package/src/rslang/parser/normalize.test.ts +99 -0
- package/src/rslang/parser/normalize.ts +462 -0
- package/src/rslang/parser/parser.terms.ts +42 -0
- package/src/rslang/parser/parser.test.ts +153 -0
- package/src/rslang/parser/parser.ts +20 -0
- package/src/rslang/parser/rslang.grammar +251 -0
- package/src/rslang/parser/syntax-errors.ts +209 -0
- package/src/rslang/parser/token.ts +106 -0
- package/src/rslang/semantic/analyzer.test.ts +59 -0
- package/src/rslang/semantic/analyzer.ts +179 -0
- package/src/rslang/semantic/arguments-extractor.ts +327 -0
- package/src/rslang/semantic/type-auditor.test.ts +326 -0
- package/src/rslang/semantic/type-auditor.ts +1049 -0
- package/src/rslang/semantic/typification-api.test.ts +46 -0
- package/src/rslang/semantic/typification-api.ts +321 -0
- package/src/rslang/semantic/typification-parser.test.ts +50 -0
- package/src/rslang/semantic/typification-parser.ts +220 -0
- package/src/rslang/semantic/typification.ts +180 -0
- package/src/rslang/semantic/value-auditor.test.ts +206 -0
- package/src/rslang/semantic/value-auditor.ts +332 -0
- package/src/rslang/semantic/value-class.ts +11 -0
- package/src/rslang/typification-graph.ts +155 -0
- package/src/shared/branded.ts +6 -0
- package/src/shared/hash.ts +17 -0
- package/src/shared/index.ts +2 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { NodeType, type OperationSchema, OperationType } from './oss';
|
|
2
|
+
import { type NodePosition, type OssLayout, type Position2D, type Rectangle2D } from './oss-layout';
|
|
3
|
+
|
|
4
|
+
export const GRID_SIZE = 10; // pixels - size of OSS grid
|
|
5
|
+
const MIN_DISTANCE = 2 * GRID_SIZE; // pixels - minimum distance between nodes
|
|
6
|
+
|
|
7
|
+
export const OPERATION_NODE_WIDTH = 150;
|
|
8
|
+
export const OPERATION_NODE_HEIGHT = 40;
|
|
9
|
+
|
|
10
|
+
/** Layout manipulations for {@link OperationSchema}. */
|
|
11
|
+
export class LayoutManager {
|
|
12
|
+
public oss: OperationSchema;
|
|
13
|
+
public layout: OssLayout;
|
|
14
|
+
|
|
15
|
+
constructor(oss: OperationSchema, layout?: OssLayout) {
|
|
16
|
+
this.oss = oss;
|
|
17
|
+
if (layout) {
|
|
18
|
+
this.layout = layout;
|
|
19
|
+
} else {
|
|
20
|
+
this.layout = this.oss.layout;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Calculate insert position for a new {@link Operation} */
|
|
25
|
+
newOperationPosition(position: Rectangle2D, parent: number | null, args: number[] = []): Rectangle2D {
|
|
26
|
+
const result = { ...position };
|
|
27
|
+
const parentNode = this.layout.find(pos => pos.nodeID === `b${parent}`) ?? null;
|
|
28
|
+
const operations = this.layout.filter(pos => pos.nodeID.startsWith('o'));
|
|
29
|
+
const hasArguments = args.length !== 0;
|
|
30
|
+
if (hasArguments) {
|
|
31
|
+
const pos = calculatePositionFromArgs(operations.filter(node => args.includes(Number(node.nodeID.slice(1)))));
|
|
32
|
+
result.x = pos.x;
|
|
33
|
+
result.y = pos.y;
|
|
34
|
+
} else if (parentNode) {
|
|
35
|
+
result.x = parentNode.x + MIN_DISTANCE;
|
|
36
|
+
result.y = parentNode.y + MIN_DISTANCE;
|
|
37
|
+
} else {
|
|
38
|
+
const pos = this.calculatePositionForFreeOperation(result);
|
|
39
|
+
result.x = pos.x;
|
|
40
|
+
result.y = pos.y;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const siblingBlocks = this.oss.blocks.filter(block => block.parent === parent).map(block => block.nodeID);
|
|
44
|
+
preventOverlap(
|
|
45
|
+
result,
|
|
46
|
+
this.layout.filter(node => siblingBlocks.includes(node.nodeID)),
|
|
47
|
+
{
|
|
48
|
+
moveX: !hasArguments,
|
|
49
|
+
moveY: hasArguments
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
preventOverlap(result, operations);
|
|
54
|
+
this.extendParentBounds(parentNode, result);
|
|
55
|
+
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Calculate insert position for a new {@link Block} */
|
|
60
|
+
newBlockPosition(position: Rectangle2D, parent: number | null, blocks: number[], operations: number[]): Rectangle2D {
|
|
61
|
+
const blockNodes = blocks.map(id => this.layout.find(block => block.nodeID === `b${id}`)).filter(node => !!node);
|
|
62
|
+
const operationNodes = operations
|
|
63
|
+
.map(id => this.layout.find(operation => operation.nodeID === `o${id}`))
|
|
64
|
+
.filter(node => !!node);
|
|
65
|
+
const parentNode = this.layout.find(pos => pos.nodeID === `b${parent}`) ?? null;
|
|
66
|
+
const parentID = parentNode ? parent : null;
|
|
67
|
+
|
|
68
|
+
let result: Rectangle2D = { ...position };
|
|
69
|
+
|
|
70
|
+
if (blockNodes.length !== 0 || operationNodes.length !== 0) {
|
|
71
|
+
result = calculatePositionFromChildren(position, operationNodes, blockNodes);
|
|
72
|
+
} else if (parentNode) {
|
|
73
|
+
result = {
|
|
74
|
+
x: parentNode.x + MIN_DISTANCE,
|
|
75
|
+
y: parentNode.y + MIN_DISTANCE,
|
|
76
|
+
width: position.width,
|
|
77
|
+
height: position.height
|
|
78
|
+
};
|
|
79
|
+
} else {
|
|
80
|
+
result = this.calculatePositionForFreeBlock(result);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (blockNodes.length === 0 && operationNodes.length === 0) {
|
|
84
|
+
const siblings = this.oss.blocks.filter(block => block.parent === parentID).map(block => block.nodeID);
|
|
85
|
+
preventOverlap(
|
|
86
|
+
result,
|
|
87
|
+
this.layout.filter(node => siblings.includes(node.nodeID))
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.extendParentBounds(parentNode, result);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Calculate insert position for a new clone of {@link Operation} */
|
|
96
|
+
newClonePosition(targetID: string): Rectangle2D | null {
|
|
97
|
+
const targetNode = this.layout.find(pos => pos.nodeID === targetID);
|
|
98
|
+
if (!targetNode) {
|
|
99
|
+
return null;
|
|
100
|
+
} else {
|
|
101
|
+
return {
|
|
102
|
+
x: targetNode.x + targetNode.width / 2 + GRID_SIZE,
|
|
103
|
+
y: targetNode.y + targetNode.height / 2 + GRID_SIZE,
|
|
104
|
+
width: OPERATION_NODE_WIDTH,
|
|
105
|
+
height: OPERATION_NODE_HEIGHT
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Update layout when parent changes */
|
|
111
|
+
onChangeParent(targetID: string, newParent: string | null) {
|
|
112
|
+
const targetNode = this.layout.find(pos => pos.nodeID === targetID);
|
|
113
|
+
if (!targetNode) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const parentNode = this.layout.find(pos => pos.nodeID === newParent) ?? null;
|
|
118
|
+
const offset = this.calculateOffsetForParentChange(targetNode, parentNode);
|
|
119
|
+
if (offset.x === 0 && offset.y === 0) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
targetNode.x += offset.x;
|
|
124
|
+
targetNode.y += offset.y;
|
|
125
|
+
|
|
126
|
+
const children = this.oss.hierarchy.expandAllOutputs([targetID]);
|
|
127
|
+
const childrenPositions = this.layout.filter(pos => children.includes(pos.nodeID));
|
|
128
|
+
for (const child of childrenPositions) {
|
|
129
|
+
child.x += offset.x;
|
|
130
|
+
child.y += offset.y;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.extendParentBounds(parentNode, targetNode);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Calculate closest node to the left */
|
|
137
|
+
selectLeft(targetID: string): string | null {
|
|
138
|
+
const targetNode = this.layout.find(pos => pos.nodeID === targetID);
|
|
139
|
+
if (!targetNode) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const operationNodes = this.layout.filter(pos => pos.nodeID !== targetID && pos.nodeID.startsWith('o'));
|
|
143
|
+
const leftNodes = operationNodes.filter(pos => pos.x <= targetNode.x);
|
|
144
|
+
if (leftNodes.length === 0) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const similarYNodes = leftNodes.filter(pos => Math.abs(pos.y - targetNode.y) <= MIN_DISTANCE);
|
|
148
|
+
let closestNode: typeof targetNode | null = null;
|
|
149
|
+
if (similarYNodes.length > 0) {
|
|
150
|
+
closestNode = similarYNodes.reduce((prev, curr) => (curr.x > prev.x ? curr : prev));
|
|
151
|
+
} else {
|
|
152
|
+
closestNode = findClosestNodeByDistance(leftNodes, targetNode);
|
|
153
|
+
}
|
|
154
|
+
return closestNode?.nodeID ?? null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Calculate closest node to the right */
|
|
158
|
+
selectRight(targetID: string): string | null {
|
|
159
|
+
const targetNode = this.layout.find(pos => pos.nodeID === targetID);
|
|
160
|
+
if (!targetNode) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const operationNodes = this.layout.filter(pos => pos.nodeID !== targetID && pos.nodeID.startsWith('o'));
|
|
164
|
+
const rightNodes = operationNodes.filter(pos => pos.x >= targetNode.x);
|
|
165
|
+
if (rightNodes.length === 0) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const similarYNodes = rightNodes.filter(pos => Math.abs(pos.y - targetNode.y) <= MIN_DISTANCE);
|
|
169
|
+
let closestNode: typeof targetNode | null = null;
|
|
170
|
+
if (similarYNodes.length > 0) {
|
|
171
|
+
closestNode = similarYNodes.reduce((prev, curr) => (curr.x < prev.x ? curr : prev));
|
|
172
|
+
} else {
|
|
173
|
+
closestNode = findClosestNodeByDistance(rightNodes, targetNode);
|
|
174
|
+
}
|
|
175
|
+
return closestNode?.nodeID ?? null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Calculate closest node upwards */
|
|
179
|
+
selectUp(targetID: string): string | null {
|
|
180
|
+
const targetNode = this.layout.find(pos => pos.nodeID === targetID);
|
|
181
|
+
if (!targetNode) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const operationNodes = this.layout.filter(pos => pos.nodeID !== targetID && pos.nodeID.startsWith('o'));
|
|
186
|
+
const upperNodes = operationNodes.filter(pos => pos.y <= targetNode.y - MIN_DISTANCE);
|
|
187
|
+
const targetOperation = this.oss.itemByNodeID.get(targetID);
|
|
188
|
+
if (upperNodes.length === 0 || !targetOperation || targetOperation.nodeType === NodeType.BLOCK) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const predecessors = this.oss.graph.expandAllInputs([targetOperation.id]);
|
|
193
|
+
const predecessorNodes = upperNodes.filter(pos => predecessors.includes(Number(pos.nodeID.slice(1))));
|
|
194
|
+
|
|
195
|
+
let closestNode: typeof targetNode | null = null;
|
|
196
|
+
if (predecessorNodes.length > 0) {
|
|
197
|
+
closestNode = findClosestNodeByDistance(predecessorNodes, targetNode);
|
|
198
|
+
} else {
|
|
199
|
+
closestNode = findClosestNodeByDistance(upperNodes, targetNode);
|
|
200
|
+
}
|
|
201
|
+
return closestNode?.nodeID ?? null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Calculate closest node downwards */
|
|
205
|
+
selectDown(targetID: string): string | null {
|
|
206
|
+
const targetNode = this.layout.find(pos => pos.nodeID === targetID);
|
|
207
|
+
if (!targetNode) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const operationNodes = this.layout.filter(pos => pos.nodeID !== targetID && pos.nodeID.startsWith('o'));
|
|
212
|
+
const lowerNodes = operationNodes.filter(pos => pos.y >= targetNode.y - MIN_DISTANCE);
|
|
213
|
+
const targetOperation = this.oss.itemByNodeID.get(targetID);
|
|
214
|
+
if (lowerNodes.length === 0 || !targetOperation || targetOperation.nodeType === NodeType.BLOCK) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const descendants = this.oss.graph.expandAllOutputs([targetOperation.id]);
|
|
219
|
+
const descendantsNodes = lowerNodes.filter(pos => descendants.includes(Number(pos.nodeID.slice(1))));
|
|
220
|
+
|
|
221
|
+
let closestNode: typeof targetNode | null = null;
|
|
222
|
+
if (descendantsNodes.length > 0) {
|
|
223
|
+
closestNode = findClosestNodeByDistance(descendantsNodes, targetNode);
|
|
224
|
+
} else {
|
|
225
|
+
closestNode = findClosestNodeByDistance(lowerNodes, targetNode);
|
|
226
|
+
}
|
|
227
|
+
return closestNode?.nodeID ?? null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private extendParentBounds(parent: NodePosition | null, child: Rectangle2D) {
|
|
231
|
+
if (!parent) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const borderX = child.x + child.width + MIN_DISTANCE;
|
|
235
|
+
const borderY = child.y + child.height + MIN_DISTANCE;
|
|
236
|
+
parent.width = Math.max(parent.width, borderX - parent.x);
|
|
237
|
+
parent.height = Math.max(parent.height, borderY - parent.y);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private calculatePositionForFreeOperation(initial: Position2D): Position2D {
|
|
241
|
+
if (this.oss.operations.length === 0) {
|
|
242
|
+
return initial;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const freeInputs = this.oss.operations
|
|
246
|
+
.filter(
|
|
247
|
+
operation =>
|
|
248
|
+
operation.parent === null &&
|
|
249
|
+
(operation.operation_type !== OperationType.SYNTHESIS || operation.arguments.length === 0)
|
|
250
|
+
)
|
|
251
|
+
.map(operation => operation.nodeID);
|
|
252
|
+
let inputsPositions = this.layout.filter(pos => freeInputs.includes(pos.nodeID));
|
|
253
|
+
if (inputsPositions.length === 0) {
|
|
254
|
+
inputsPositions = this.layout.filter(pos => pos.nodeID.startsWith('o'));
|
|
255
|
+
}
|
|
256
|
+
const maxX = Math.max(...inputsPositions.map(node => node.x));
|
|
257
|
+
const minY = Math.min(...inputsPositions.map(node => node.y));
|
|
258
|
+
return {
|
|
259
|
+
x: maxX + OPERATION_NODE_WIDTH + MIN_DISTANCE + GRID_SIZE,
|
|
260
|
+
y: minY
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private calculatePositionForFreeBlock(initial: Rectangle2D): Rectangle2D {
|
|
265
|
+
const rootBlocks = this.oss.blocks.filter(block => block.parent === null).map(block => block.nodeID);
|
|
266
|
+
const blocksPositions = this.layout.filter(pos => rootBlocks.includes(pos.nodeID));
|
|
267
|
+
if (blocksPositions.length === 0) {
|
|
268
|
+
return initial;
|
|
269
|
+
}
|
|
270
|
+
const maxX = Math.max(...blocksPositions.map(node => node.x + node.width));
|
|
271
|
+
const minY = Math.min(...blocksPositions.map(node => node.y));
|
|
272
|
+
return { ...initial, x: maxX + MIN_DISTANCE, y: minY };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private calculateOffsetForParentChange(target: NodePosition, parent: NodePosition | null): Position2D {
|
|
276
|
+
const newPosition = { ...target };
|
|
277
|
+
if (parent === null) {
|
|
278
|
+
const rootElements = this.oss.hierarchy.rootNodes();
|
|
279
|
+
const positions = this.layout.filter(pos => rootElements.includes(pos.nodeID));
|
|
280
|
+
preventOverlap(newPosition, positions);
|
|
281
|
+
} else if (!rectanglesStrictOverlap(target, parent)) {
|
|
282
|
+
newPosition.x = parent.x + MIN_DISTANCE;
|
|
283
|
+
newPosition.y = parent.y + MIN_DISTANCE;
|
|
284
|
+
|
|
285
|
+
const siblings = this.oss.hierarchy.at(parent.nodeID)?.outputs ?? [];
|
|
286
|
+
const siblingsPositions = this.layout.filter(pos => siblings.includes(pos.nodeID));
|
|
287
|
+
preventOverlap(newPosition, siblingsPositions);
|
|
288
|
+
}
|
|
289
|
+
return { x: newPosition.x - target.x, y: newPosition.y - target.y };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ======= Internals =======
|
|
294
|
+
function rectanglesOverlap(a: Rectangle2D, b: Rectangle2D): boolean {
|
|
295
|
+
return !(
|
|
296
|
+
a.x + a.width + MIN_DISTANCE <= b.x ||
|
|
297
|
+
b.x + b.width + MIN_DISTANCE <= a.x ||
|
|
298
|
+
a.y + a.height + MIN_DISTANCE <= b.y ||
|
|
299
|
+
b.y + b.height + MIN_DISTANCE <= a.y
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function rectanglesStrictOverlap(a: Rectangle2D, b: Rectangle2D): boolean {
|
|
304
|
+
return !(a.x + a.width <= b.x || b.x + b.width <= a.x || a.y + a.height <= b.y || b.y + b.height <= a.y);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function preventOverlap(
|
|
308
|
+
target: Rectangle2D,
|
|
309
|
+
fixedRectangles: Rectangle2D[],
|
|
310
|
+
options: { moveX?: boolean; moveY?: boolean } = { moveX: true, moveY: true }
|
|
311
|
+
) {
|
|
312
|
+
if ((!options.moveX && !options.moveY) || fixedRectangles.length === 0) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
let hasOverlap: boolean;
|
|
316
|
+
do {
|
|
317
|
+
hasOverlap = false;
|
|
318
|
+
for (const fixed of fixedRectangles) {
|
|
319
|
+
if (rectanglesOverlap(target, fixed)) {
|
|
320
|
+
hasOverlap = true;
|
|
321
|
+
if (options.moveX) {
|
|
322
|
+
target.x += MIN_DISTANCE;
|
|
323
|
+
}
|
|
324
|
+
if (options.moveY) {
|
|
325
|
+
target.y += MIN_DISTANCE;
|
|
326
|
+
}
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
} while (hasOverlap);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function calculatePositionFromArgs(args: NodePosition[]): Position2D {
|
|
334
|
+
const maxY = Math.max(...args.map(node => node.y));
|
|
335
|
+
const minX = Math.min(...args.map(node => node.x));
|
|
336
|
+
const maxX = Math.max(...args.map(node => node.x));
|
|
337
|
+
return {
|
|
338
|
+
x: Math.ceil((maxX + minX) / 2 / GRID_SIZE) * GRID_SIZE,
|
|
339
|
+
y: maxY + 2 * OPERATION_NODE_HEIGHT + MIN_DISTANCE
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function calculatePositionFromChildren(
|
|
344
|
+
initial: Rectangle2D,
|
|
345
|
+
operations: NodePosition[],
|
|
346
|
+
blocks: NodePosition[]
|
|
347
|
+
): Rectangle2D {
|
|
348
|
+
const allNodes = [...blocks, ...operations];
|
|
349
|
+
if (allNodes.length === 0) {
|
|
350
|
+
return initial;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const left = Math.min(...allNodes.map(n => n.x)) - MIN_DISTANCE;
|
|
354
|
+
const top = Math.min(...allNodes.map(n => n.y)) - MIN_DISTANCE;
|
|
355
|
+
const right = Math.max(...allNodes.map(n => n.x + n.width)) + MIN_DISTANCE;
|
|
356
|
+
const bottom = Math.max(...allNodes.map(n => n.y + n.height)) + MIN_DISTANCE;
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
x: left,
|
|
360
|
+
y: top,
|
|
361
|
+
width: right - left,
|
|
362
|
+
height: bottom - top
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function findClosestNodeByDistance(nodes: NodePosition[], target: NodePosition): NodePosition | null {
|
|
367
|
+
let minDist = Infinity;
|
|
368
|
+
let minNode = null;
|
|
369
|
+
for (const curr of nodes) {
|
|
370
|
+
const currDist = Math.hypot(curr.x - target.x, curr.y - target.y);
|
|
371
|
+
if (currDist < minDist) {
|
|
372
|
+
minDist = currDist;
|
|
373
|
+
minNode = curr;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return minNode;
|
|
377
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module: OSS graphical representation.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Represents XY Position. */
|
|
6
|
+
export interface Position2D {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Represents XY Position and dimensions. */
|
|
12
|
+
export interface Rectangle2D extends Position2D {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Represents a node in {@link OssLayout}. */
|
|
18
|
+
export interface NodePosition {
|
|
19
|
+
nodeID: string;
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Represents {@link OperationSchema} layout. */
|
|
27
|
+
export type OssLayout = NodePosition[];
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module: Schema of Synthesis Operations.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { type Graph } from '../graph';
|
|
6
|
+
|
|
7
|
+
import { type LibraryItem } from './library';
|
|
8
|
+
import { type NodePosition, type OssLayout } from './oss-layout';
|
|
9
|
+
|
|
10
|
+
/** Represents OSS node type. */
|
|
11
|
+
export const NodeType = {
|
|
12
|
+
OPERATION: 1,
|
|
13
|
+
BLOCK: 2
|
|
14
|
+
} as const;
|
|
15
|
+
export type NodeType = (typeof NodeType)[keyof typeof NodeType];
|
|
16
|
+
|
|
17
|
+
/** Represents OSS graph node. */
|
|
18
|
+
interface OssNode extends NodePosition {
|
|
19
|
+
nodeType: NodeType;
|
|
20
|
+
parent: number | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Represents {@link Operation} type. */
|
|
24
|
+
export const OperationType = {
|
|
25
|
+
INPUT: 'input',
|
|
26
|
+
SYNTHESIS: 'synthesis',
|
|
27
|
+
REPLICA: 'replica'
|
|
28
|
+
} as const;
|
|
29
|
+
export type OperationType = (typeof OperationType)[keyof typeof OperationType];
|
|
30
|
+
|
|
31
|
+
/** Represents {@link Substitution} extended data. */
|
|
32
|
+
export interface CstSubstituteInfo {
|
|
33
|
+
original: number;
|
|
34
|
+
substitution: number;
|
|
35
|
+
operation: number;
|
|
36
|
+
original_schema: number;
|
|
37
|
+
original_alias: string;
|
|
38
|
+
original_term: string;
|
|
39
|
+
substitution_schema: number;
|
|
40
|
+
substitution_alias: string;
|
|
41
|
+
substitution_term: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Represents Operation common attributes. */
|
|
45
|
+
interface OperationBase extends OssNode {
|
|
46
|
+
id: number;
|
|
47
|
+
alias: string;
|
|
48
|
+
title: string;
|
|
49
|
+
description: string;
|
|
50
|
+
operation_type: OperationType;
|
|
51
|
+
result: number | null;
|
|
52
|
+
nodeType: typeof NodeType.OPERATION;
|
|
53
|
+
has_additions: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Represents Input Operation. */
|
|
57
|
+
export interface OperationInput extends OperationBase {
|
|
58
|
+
operation_type: typeof OperationType.INPUT;
|
|
59
|
+
is_import: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Represents Replica Operation. */
|
|
63
|
+
interface OperationReplica extends OperationBase {
|
|
64
|
+
operation_type: typeof OperationType.REPLICA;
|
|
65
|
+
target: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Represents Synthesis Operation. */
|
|
69
|
+
export interface OperationSynthesis extends OperationBase {
|
|
70
|
+
operation_type: typeof OperationType.SYNTHESIS;
|
|
71
|
+
is_consolidation: boolean; // aka 'diamond synthesis'
|
|
72
|
+
substitutions: CstSubstituteInfo[];
|
|
73
|
+
arguments: number[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Represents Operation. */
|
|
77
|
+
export type Operation = OperationInput | OperationReplica | OperationSynthesis;
|
|
78
|
+
|
|
79
|
+
/** Represents Block. */
|
|
80
|
+
export interface Block extends OssNode {
|
|
81
|
+
id: number;
|
|
82
|
+
oss: number;
|
|
83
|
+
title: string;
|
|
84
|
+
description: string;
|
|
85
|
+
parent: number | null;
|
|
86
|
+
nodeType: typeof NodeType.BLOCK;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Represents item of OperationSchema. */
|
|
90
|
+
export type OssItem = Operation | Block;
|
|
91
|
+
|
|
92
|
+
/** Represents {@link OperationSchema} statistics. */
|
|
93
|
+
export interface OperationSchemaStats {
|
|
94
|
+
count_all: number;
|
|
95
|
+
count_inputs: number;
|
|
96
|
+
count_synthesis: number;
|
|
97
|
+
count_schemas: number;
|
|
98
|
+
count_owned: number;
|
|
99
|
+
count_block: number;
|
|
100
|
+
count_references: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Represents OperationSchema. */
|
|
104
|
+
export interface OperationSchema extends LibraryItem {
|
|
105
|
+
editors: number[];
|
|
106
|
+
operations: Operation[];
|
|
107
|
+
blocks: Block[];
|
|
108
|
+
replicas: {
|
|
109
|
+
original: number;
|
|
110
|
+
replica: number;
|
|
111
|
+
}[];
|
|
112
|
+
layout: OssLayout;
|
|
113
|
+
arguments: {
|
|
114
|
+
operation: number;
|
|
115
|
+
argument: number;
|
|
116
|
+
}[];
|
|
117
|
+
substitutions: CstSubstituteInfo[];
|
|
118
|
+
|
|
119
|
+
graph: Graph;
|
|
120
|
+
extendedGraph: Graph;
|
|
121
|
+
hierarchy: Graph<string>;
|
|
122
|
+
schemas: number[];
|
|
123
|
+
stats: OperationSchemaStats;
|
|
124
|
+
operationByID: Map<number, Operation>;
|
|
125
|
+
blockByID: Map<number, Block>;
|
|
126
|
+
itemByNodeID: Map<string, OssItem>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Represents substitution error description. */
|
|
130
|
+
export interface SubstitutionErrorDescription {
|
|
131
|
+
errorType: SubstitutionErrorType;
|
|
132
|
+
params: string[];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Represents Substitution table error types. */
|
|
136
|
+
export const SubstitutionErrorType = {
|
|
137
|
+
invalidIDs: 0,
|
|
138
|
+
incorrectCst: 1,
|
|
139
|
+
invalidClasses: 2,
|
|
140
|
+
invalidBasic: 3,
|
|
141
|
+
invalidConstant: 4,
|
|
142
|
+
typificationCycle: 5,
|
|
143
|
+
baseSubstitutionNotSet: 6,
|
|
144
|
+
unequalTypification: 7,
|
|
145
|
+
unequalExpressions: 8,
|
|
146
|
+
unequalArgsCount: 9,
|
|
147
|
+
unequalArgs: 10,
|
|
148
|
+
invalidNominal: 11
|
|
149
|
+
} as const;
|
|
150
|
+
export type SubstitutionErrorType = (typeof SubstitutionErrorType)[keyof typeof SubstitutionErrorType];
|