@eclipse-glsp/layout-elk 0.9.0-next.0193b16.17

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,89 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2022 STMicroelectronics and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+ import { DefaultModelState, GGraph, GModelElementConstructor, ModelState } from '@eclipse-glsp/server-node';
17
+ import { StubDiagramConfiguration } from '@eclipse-glsp/server-node/lib/test/mock-util';
18
+ import { expect } from 'chai';
19
+ import { Container, ContainerModule, injectable } from 'inversify';
20
+ import * as sinon from 'sinon';
21
+ import { AbstractLayoutConfigurator } from '.';
22
+ import { configureELKLayoutModule } from './di.config';
23
+ import { DefaultElementFilter, ElementFilter } from './element-filter';
24
+ import { FallbackLayoutConfigurator, LayoutConfigurator } from './layout-configurator';
25
+
26
+ @injectable()
27
+ class CustomLayoutConfigurator extends AbstractLayoutConfigurator {}
28
+
29
+ @injectable()
30
+ class CustomElementFilter extends DefaultElementFilter {}
31
+
32
+ describe('test configureELKLayoutModule', () => {
33
+ const sandbox = sinon.createSandbox();
34
+ const mockDiagramConfiguration = new StubDiagramConfiguration();
35
+ const typeMappings = new Map<string, GModelElementConstructor>();
36
+
37
+ typeMappings.set('graph', GGraph);
38
+ sandbox.stub(mockDiagramConfiguration, 'typeMapping').value(typeMappings);
39
+ const modelState = new DefaultModelState();
40
+ const baseModule = new ContainerModule(bind => {
41
+ bind(ModelState).toConstantValue(modelState);
42
+ });
43
+ it('configure with minimal options', () => {
44
+ const algorithm = 'layered';
45
+ const elkModule = configureELKLayoutModule({ algorithms: [algorithm] });
46
+ const container = new Container();
47
+ container.load(baseModule, elkModule);
48
+ const filter = container.get<ElementFilter>(ElementFilter);
49
+ expect(filter).to.be.an.instanceOf(DefaultElementFilter);
50
+ const configurator = container.get<LayoutConfigurator>(LayoutConfigurator);
51
+ expect(configurator).to.be.an.instanceOf(FallbackLayoutConfigurator);
52
+ const graphOptions = configurator.apply(new GGraph());
53
+ expect(graphOptions).not.to.be.undefined;
54
+ expect(graphOptions!['elk.algorithm']).to.equal(algorithm);
55
+ });
56
+
57
+ it('configure with additional default layout options', () => {
58
+ const algorithm = 'layered';
59
+ const defaultLayoutOptions = {
60
+ 'elk.direction': 'LEFT',
61
+ 'elk.edgeRouting': 'POLYLINE'
62
+ };
63
+ const elkModule = configureELKLayoutModule({ algorithms: [algorithm], defaultLayoutOptions });
64
+ const container = new Container();
65
+ container.load(baseModule, elkModule);
66
+ const configurator = container.get<LayoutConfigurator>(LayoutConfigurator);
67
+ const graphOptions = configurator.apply(new GGraph());
68
+ expect(graphOptions).not.to.be.undefined;
69
+ expect(graphOptions).to.include(defaultLayoutOptions);
70
+ });
71
+
72
+ it('configure with custom layout configurator', () => {
73
+ const algorithm = 'layered';
74
+ const elkModule = configureELKLayoutModule({ algorithms: [algorithm], layoutConfigurator: CustomLayoutConfigurator });
75
+ const container = new Container();
76
+ container.load(baseModule, elkModule);
77
+ const configurator = container.get<LayoutConfigurator>(LayoutConfigurator);
78
+ expect(configurator).to.be.an.instanceOf(CustomLayoutConfigurator);
79
+ });
80
+
81
+ it('configure with custom element filter', () => {
82
+ const algorithm = 'layered';
83
+ const elkModule = configureELKLayoutModule({ algorithms: [algorithm], elementFilter: CustomElementFilter });
84
+ const container = new Container();
85
+ container.load(baseModule, elkModule);
86
+ const filter = container.get<ElementFilter>(ElementFilter);
87
+ expect(filter).to.be.an.instanceOf(CustomElementFilter);
88
+ });
89
+ });
@@ -0,0 +1,104 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2022 STMicroelectronics and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+ import { LayoutEngine, ModelState } from '@eclipse-glsp/server-node';
17
+ import ElkConstructor, { LayoutOptions } from 'elkjs/lib/elk.bundled';
18
+ import { ContainerModule } from 'inversify';
19
+ import { ElkFactory } from 'sprotty-elk';
20
+ import { ElementFilter, LayoutConfigurator } from '.';
21
+ import { DefaultElementFilter } from './element-filter';
22
+ import { GlspElkLayoutEngine } from './glsp-elk-layout-engine';
23
+ import { FallbackLayoutConfigurator } from './layout-configurator';
24
+
25
+ type Constructor<T> = new (...args: any[]) => T;
26
+
27
+ /**
28
+ * Configuration options for the {@link configureELKLayoutModule} function.
29
+ */
30
+ export interface ElkModuleOptions {
31
+ /**
32
+ * The set of elk algorithms that is used by the {@link GlspElkLayoutEngine}.
33
+ */
34
+ algorithms: string[];
35
+ /**
36
+ * Additional default layout options. This options are passed to the underlying {@link ElkFactory}.
37
+ * In addition, they are used to configure the {@link FallbackGlspLayoutConfigurator}.
38
+ */
39
+ defaultLayoutOptions?: LayoutOptions;
40
+ /**
41
+ * The custom {@link LayoutConfigurator} class that should be bound. If this option is not set
42
+ * the {@link FallbackLayoutConfigurator} is bound instead.
43
+ */
44
+ layoutConfigurator?: Constructor<LayoutConfigurator>;
45
+ /**
46
+ * The custom {@link ElementFilter} class that should be bound. If this option is not set, the default implementation
47
+ * is bound.
48
+ */
49
+ elementFilter?: Constructor<ElementFilter>;
50
+ }
51
+
52
+ /**
53
+ * Utility method to create a DI module that provides all necessary bindings to use the {@link GlspElkLayoutEngine} in a node GLSP server
54
+ * implementation. A set of configuration options is provided to enable easy customization. In most cases at least
55
+ * the custom {@link layoutConfigurator} binding should be provided (in addition to the required `algorithms' property) via these options.
56
+ *
57
+ * The constructed module is not intended for standalone use cases and only works in combination with a GLSPDiagramModule.
58
+ *
59
+ * * The following bindings are provided:
60
+ * - {@link ILayoutConfigurator}
61
+ * - {@link IElementFilter}
62
+ * - {@link LayoutEngine}
63
+ * - {@link ElkFactory}
64
+ *
65
+ * @param options The configuration options
66
+ * @returns A DI module that can be loaded as additional module when configuring a diagram module for a GLSP server.
67
+ */
68
+ export function configureELKLayoutModule(options: ElkModuleOptions): ContainerModule {
69
+ return new ContainerModule(bind => {
70
+ if (options.elementFilter) {
71
+ bind(ElementFilter).to(options.elementFilter).inSingletonScope();
72
+ } else {
73
+ bind(ElementFilter).to(DefaultElementFilter).inSingletonScope();
74
+ }
75
+
76
+ if (options.layoutConfigurator) {
77
+ bind(LayoutConfigurator).to(options.layoutConfigurator);
78
+ } else {
79
+ bind(LayoutConfigurator)
80
+ .toDynamicValue(context => new FallbackLayoutConfigurator(options.algorithms, options.defaultLayoutOptions))
81
+ .inSingletonScope();
82
+ }
83
+
84
+ const elkFactory: ElkFactory = () =>
85
+ new ElkConstructor({
86
+ algorithms: options.algorithms,
87
+ defaultLayoutOptions: options.defaultLayoutOptions
88
+ });
89
+
90
+ bind(ElkFactory).toConstantValue(elkFactory);
91
+
92
+ bind(GlspElkLayoutEngine)
93
+ .toDynamicValue(context => {
94
+ const container = context.container;
95
+ const factory = container.get<ElkFactory>(ElkFactory);
96
+ const filter = container.get<ElementFilter>(ElementFilter);
97
+ const configurator = container.get<LayoutConfigurator>(LayoutConfigurator);
98
+ const modelState = container.get<ModelState>(ModelState);
99
+ return new GlspElkLayoutEngine(factory, filter, configurator, modelState);
100
+ })
101
+ .inSingletonScope();
102
+ bind(LayoutEngine).toService(GlspElkLayoutEngine);
103
+ });
104
+ }
@@ -0,0 +1,84 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2018-2022 TypeFox and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+ import { GEdge, GLabel, GModelElement, GNode, GPort, ModelState } from '@eclipse-glsp/server-node';
17
+ import { inject, injectable } from 'inversify';
18
+
19
+ export const ElementFilter = Symbol('ElementFilter');
20
+ /**
21
+ * Filter used to determine which model elements should be included in the automatic layout.
22
+ */
23
+ export interface ElementFilter {
24
+ /**
25
+ * Applies the element filter on the given element,
26
+ * @param element The element on which the filter should be applied.
27
+ * @returns `true` if the element should be included in the automatic layout, `false` otherwise.
28
+ */
29
+ apply(element: GModelElement): boolean;
30
+ }
31
+
32
+ /**
33
+ * Default implementation of {@link ElementFilter}.
34
+ * Without further configuration this filter includes all basic model elements (nodes,edges,labels,ports) that are
35
+ * part of the graphical model. For each basic type a custom filter behavior is in place.
36
+ * Edges that have no source or target are filtered out. In addition, edges that are connected to a filtered element are filtered out
37
+ * as well. The filter behavior for each of the basic types can be customized by overriding the corresponding `filter` method.
38
+ * (e.g. {@link DefaultElementFilter.filterNode})
39
+ */
40
+ @injectable()
41
+ export class DefaultElementFilter implements ElementFilter {
42
+ @inject(ModelState)
43
+ protected modelState: ModelState;
44
+
45
+ apply(element: GModelElement): boolean {
46
+ if (element instanceof GNode) {
47
+ return this.filterNode(element);
48
+ } else if (element instanceof GEdge) {
49
+ return this.filterEdge(element);
50
+ } else if (element instanceof GLabel) {
51
+ this.filterLabel(element);
52
+ } else if (element instanceof GPort) {
53
+ this.filterPort(element);
54
+ }
55
+ return true;
56
+ }
57
+
58
+ protected filterNode(node: GNode): boolean {
59
+ return true;
60
+ }
61
+
62
+ protected filterEdge(edge: GEdge): boolean {
63
+ const source = this.modelState.index.get(edge.sourceId);
64
+
65
+ if (!source || (source instanceof GNode && !this.filterNode(source)) || (source instanceof GPort && !this.filterPort(source))) {
66
+ return false;
67
+ }
68
+
69
+ const target = this.modelState.index.get(edge.targetId);
70
+
71
+ if (!target || (target instanceof GNode && !this.filterNode(target)) || (target instanceof GPort && !this.filterPort(target))) {
72
+ return false;
73
+ }
74
+ return true;
75
+ }
76
+
77
+ protected filterLabel(label: GLabel): boolean {
78
+ return true;
79
+ }
80
+
81
+ protected filterPort(port: GPort): boolean {
82
+ return true;
83
+ }
84
+ }
@@ -0,0 +1,332 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2018-2022 TypeFox and others.
3
+ * This program and the accompanying materials are made available under the
4
+ * terms of the Eclipse Public License v. 2.0 which is available at
5
+ * http://www.eclipse.org/legal/epl-2.0.
6
+ *
7
+ * This Source Code may also be made available under the following Secondary
8
+ * Licenses when the conditions for such availability set forth in the Eclipse
9
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
10
+ * with the GNU Classpath Exception which is available at
11
+ * https://www.gnu.org/software/classpath/license.html.
12
+ *
13
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
14
+ ********************************************************************************/
15
+ import {
16
+ findParent,
17
+ findParentByClass,
18
+ GCompartment,
19
+ GEdge,
20
+ GGraph,
21
+ GLabel,
22
+ GModelElement,
23
+ GModelElementConstructor,
24
+ GModelRoot,
25
+ GNode,
26
+ GPort,
27
+ GShapeElement,
28
+ LayoutEngine,
29
+ MaybePromise,
30
+ ModelState,
31
+ Point
32
+ } from '@eclipse-glsp/server-node';
33
+ import { ELK, ElkEdge, ElkExtendedEdge, ElkGraphElement, ElkLabel, ElkNode, ElkPort, ElkPrimitiveEdge, ElkShape } from 'elkjs/lib/elk-api';
34
+ import { injectable } from 'inversify';
35
+ import { ElkFactory } from 'sprotty-elk';
36
+ import { ElementFilter } from './element-filter';
37
+ import { LayoutConfigurator } from './layout-configurator';
38
+
39
+ /**
40
+ * An implementation of GLSP's {@link LayoutEngine} interface that retrieves the graphical model from the {@link ModelState},
41
+ * transforms this model into an ELK graph and then invokes the underlying ELK instance for layout computation.
42
+ */
43
+ @injectable()
44
+ export class GlspElkLayoutEngine implements LayoutEngine {
45
+ protected readonly elk: ELK;
46
+
47
+ protected elkEdges: ElkEdge[];
48
+ protected idToElkElement: Map<string, ElkGraphElement>;
49
+
50
+ constructor(
51
+ elkFactory: ElkFactory,
52
+ protected readonly filter: ElementFilter,
53
+ protected readonly configurator: LayoutConfigurator,
54
+ protected modelState: ModelState
55
+ ) {
56
+ this.elk = elkFactory();
57
+ }
58
+ layout(): MaybePromise<GModelRoot> {
59
+ const root = this.modelState.root;
60
+ if (!(root instanceof GGraph)) {
61
+ return root;
62
+ }
63
+
64
+ this.elkEdges = [];
65
+ this.idToElkElement = new Map();
66
+ const elkGraph = this.transformToElk(root) as ElkNode;
67
+ return this.elk.layout(elkGraph).then(result => {
68
+ this.applyLayout(result);
69
+ return root;
70
+ });
71
+ }
72
+
73
+ protected transformToElk(model: GModelElement): ElkGraphElement {
74
+ if (model instanceof GGraph) {
75
+ const graph = this.transformGraph(model);
76
+ this.elkEdges.forEach(elkEdge => {
77
+ const parent = this.findCommonAncestor(elkEdge as ElkPrimitiveEdge);
78
+ if (parent) {
79
+ parent.edges!.push(elkEdge);
80
+ }
81
+ });
82
+ return graph;
83
+ } else if (model instanceof GNode) {
84
+ return this.transformNode(model);
85
+ } else if (model instanceof GEdge) {
86
+ return this.transformEdge(model);
87
+ } else if (model instanceof GLabel) {
88
+ return this.transformPort(model);
89
+ } else if (model instanceof GPort) {
90
+ return this.transformPort(model);
91
+ }
92
+
93
+ throw new Error('Type not supported: ' + model.type);
94
+ }
95
+
96
+ /**
97
+ * Searches for all children of the given element that are an instance of the given {@link GModelElementConstructor}
98
+ * and are included by the {@link ElementFilter}. Also considers children that are nested inside of {@link GCompartment}s.
99
+ * @param element The element whose children should be queried.
100
+ * @param constructor The class instance that should be matched
101
+ * @returns A list of all matching children.
102
+ */
103
+ protected findChildren<G extends GModelElement>(element: GModelElement, constructor: GModelElementConstructor<G>): G[] {
104
+ const result: G[] = [];
105
+ element.children.forEach(child => {
106
+ if (child instanceof constructor && this.filter.apply(element)) {
107
+ result.push(child);
108
+ } else if (child instanceof GCompartment) {
109
+ result.push(...this.findChildren(child, constructor));
110
+ }
111
+ });
112
+
113
+ return result;
114
+ }
115
+
116
+ protected findCommonAncestor(elkEdge: ElkPrimitiveEdge): ElkNode | undefined {
117
+ const source = this.modelState.index.get(elkEdge.source);
118
+ const target = this.modelState.index.get(elkEdge.target);
119
+ if (!source || !target) {
120
+ return undefined;
121
+ }
122
+
123
+ const sourceParent = findParent(source.parent, parent => parent instanceof GNode || parent instanceof GGraph);
124
+ const targetParent = findParent(target.parent, parent => parent instanceof GNode || parent instanceof GGraph);
125
+
126
+ if (!sourceParent || !targetParent) {
127
+ return undefined;
128
+ }
129
+
130
+ if (sourceParent === targetParent) {
131
+ return this.idToElkElement.get(sourceParent.id);
132
+ } else if (source === targetParent) {
133
+ return this.idToElkElement.get(source.id);
134
+ } else if (target === sourceParent) {
135
+ return this.idToElkElement.get(target.id);
136
+ }
137
+ return undefined;
138
+ }
139
+
140
+ protected transformGraph(graph: GGraph): ElkGraphElement {
141
+ const elkGraph: ElkNode = {
142
+ id: graph.id,
143
+ layoutOptions: this.configurator.apply(graph)
144
+ };
145
+ if (graph.children) {
146
+ elkGraph.children = this.findChildren(graph, GNode).map(child => this.transformToElk(child)) as ElkNode[];
147
+ elkGraph.edges = [];
148
+ this.elkEdges.push(...(this.findChildren(graph, GEdge).map(child => this.transformToElk(child)) as ElkEdge[]));
149
+ }
150
+
151
+ this.idToElkElement.set(graph.id, elkGraph);
152
+ return elkGraph;
153
+ }
154
+
155
+ protected transformNode(node: GNode): ElkNode {
156
+ const elkNode: ElkNode = {
157
+ id: node.id,
158
+ layoutOptions: this.configurator.apply(node)
159
+ };
160
+
161
+ if (node.children) {
162
+ elkNode.children = this.findChildren(node, GNode).map(child => this.transformToElk(child)) as ElkNode[];
163
+ elkNode.edges = [];
164
+ this.elkEdges.push(...(this.findChildren(node, GEdge).map(child => this.transformToElk(child)) as ElkEdge[]));
165
+
166
+ elkNode.labels = this.findChildren(node, GLabel).map(child => this.transformToElk(child)) as ElkLabel[];
167
+ elkNode.ports = this.findChildren(node, GPort).map(child => this.transformToElk(child)) as ElkPort[];
168
+ }
169
+
170
+ this.transformShape(elkNode, node);
171
+ this.idToElkElement.set(node.id, elkNode);
172
+
173
+ return elkNode;
174
+ }
175
+
176
+ protected transformShape(elkShape: ElkShape, shape: GShapeElement): void {
177
+ if (shape.position) {
178
+ elkShape.x = shape.position.x;
179
+ elkShape.y = shape.position.y;
180
+ }
181
+ if (shape.size) {
182
+ elkShape.width = shape.size.width;
183
+ elkShape.height = shape.size.height;
184
+ }
185
+ }
186
+
187
+ protected transformEdge(edge: GEdge): ElkEdge {
188
+ const elkEdge: ElkPrimitiveEdge = {
189
+ id: edge.id,
190
+ source: edge.sourceId,
191
+ target: edge.targetId,
192
+ layoutOptions: this.configurator.apply(edge)
193
+ };
194
+ const sourceElement = this.modelState.index.get(edge.sourceId);
195
+ if (sourceElement instanceof GPort) {
196
+ const parentNode = findParentByClass(sourceElement, GNode);
197
+ if (parentNode) {
198
+ elkEdge.source = parentNode.id;
199
+ elkEdge.sourcePort = sourceElement.id;
200
+ }
201
+ }
202
+
203
+ const targetElement = this.modelState.index.get(edge.targetId);
204
+ if (sourceElement instanceof GPort) {
205
+ const parentNode = findParentByClass(targetElement, GNode);
206
+ if (parentNode) {
207
+ elkEdge.target = parentNode.id;
208
+ elkEdge.targetPort = targetElement.id;
209
+ }
210
+ }
211
+
212
+ if (edge.children) {
213
+ elkEdge.labels = this.findChildren(edge, GLabel).map(child => this.transformToElk(child)) as ElkLabel[];
214
+ }
215
+ const points = edge.routingPoints;
216
+ if (points && points.length >= 2) {
217
+ elkEdge.sourcePoint = points[0];
218
+ elkEdge.bendPoints = points.slice(1, points.length - 1);
219
+ elkEdge.targetPoint = points[points.length - 1];
220
+ }
221
+ this.idToElkElement.set(edge.id, elkEdge);
222
+ return elkEdge;
223
+ }
224
+
225
+ protected transformLabel(label: GLabel): ElkLabel {
226
+ const elkLabel: ElkLabel = {
227
+ id: label.id,
228
+ text: label.text,
229
+ layoutOptions: this.configurator.apply(label)
230
+ };
231
+ this.transformShape(elkLabel, label);
232
+ this.idToElkElement.set(label.id, elkLabel);
233
+ return elkLabel;
234
+ }
235
+
236
+ protected transformPort(port: GPort): ElkPort {
237
+ const elkPort: ElkPort = {
238
+ id: port.id,
239
+ layoutOptions: this.configurator.apply(port)
240
+ };
241
+ if (port.children) {
242
+ elkPort.labels = this.findChildren(port, GLabel).map(child => this.transformToElk(child)) as ElkLabel[];
243
+ }
244
+ this.transformShape(elkPort, port);
245
+ this.idToElkElement.set(port.id, elkPort);
246
+ return elkPort;
247
+ }
248
+
249
+ protected applyLayout(elkNode: ElkNode): void {
250
+ const element = this.modelState.index.get(elkNode.id);
251
+ if (element instanceof GNode) {
252
+ this.applyShape(element, elkNode);
253
+ }
254
+ if (elkNode.children) {
255
+ for (const child of elkNode.children) {
256
+ this.applyLayout(child);
257
+ }
258
+ }
259
+ if (elkNode.edges) {
260
+ for (const elkEdge of elkNode.edges) {
261
+ const edge = this.modelState.index.get(elkEdge.id);
262
+ if (edge instanceof GEdge) {
263
+ this.applyEdge(edge, elkEdge);
264
+ }
265
+ }
266
+ }
267
+
268
+ if (elkNode.ports) {
269
+ for (const elkPort of elkNode.ports) {
270
+ const port = this.modelState.index.findByClass(elkPort.id, GPort);
271
+ if (port) {
272
+ this.applyShape(port, elkPort);
273
+ }
274
+ }
275
+ }
276
+ }
277
+
278
+ protected applyShape(shape: GShapeElement, elkShape: ElkShape): void {
279
+ if (elkShape.x !== undefined && elkShape.y !== undefined) {
280
+ shape.position = { x: elkShape.x, y: elkShape.y };
281
+ }
282
+ if (elkShape.width !== undefined && elkShape.height !== undefined) {
283
+ shape.size = { width: elkShape.width, height: elkShape.height };
284
+ }
285
+
286
+ if (elkShape.labels) {
287
+ for (const elkLabel of elkShape.labels) {
288
+ const label = this.modelState.index.findByClass(elkLabel.id, GLabel);
289
+ if (label) {
290
+ this.applyShape(label, elkLabel);
291
+ }
292
+ }
293
+ }
294
+ }
295
+
296
+ protected applyEdge(edge: GEdge, elkEdge: ElkEdge): void {
297
+ const points: Point[] = [];
298
+ if ((elkEdge as any).sections && (elkEdge as any).sections.length > 0) {
299
+ const section = (elkEdge as ElkExtendedEdge).sections[0];
300
+ if (section.startPoint) {
301
+ points.push(section.startPoint);
302
+ }
303
+ if (section.bendPoints) {
304
+ points.push(...section.bendPoints);
305
+ }
306
+ if (section.endPoint) {
307
+ points.push(section.endPoint);
308
+ }
309
+ } else {
310
+ const section = elkEdge as ElkPrimitiveEdge;
311
+ if (section.sourcePoint) {
312
+ points.push(section.sourcePoint);
313
+ }
314
+ if (section.bendPoints) {
315
+ points.push(...section.bendPoints);
316
+ }
317
+ if (section.targetPoint) {
318
+ points.push(section.targetPoint);
319
+ }
320
+ }
321
+ edge.routingPoints = points;
322
+
323
+ if (elkEdge.labels) {
324
+ elkEdge.labels.forEach(elkLabel => {
325
+ const label = this.modelState.index.findByClass(elkLabel.id, GLabel);
326
+ if (label) {
327
+ this.applyShape(label, elkLabel);
328
+ }
329
+ });
330
+ }
331
+ }
332
+ }
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2022 STMicroelectronics and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+ export * from './di.config';
17
+ export * from './element-filter';
18
+ export * from './glsp-elk-layout-engine';
19
+ export * from './layout-configurator';
20
+ export * from './sprotty-elk';