@eclipse-glsp/client 2.7.0-next.2 → 2.7.0-next.21
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/lib/base/action-dispatcher.d.ts.map +1 -1
- package/lib/base/action-dispatcher.js +9 -3
- package/lib/base/action-dispatcher.js.map +1 -1
- package/lib/base/default.module.d.ts.map +1 -1
- package/lib/base/default.module.js +2 -0
- package/lib/base/default.module.js.map +1 -1
- package/lib/base/editor-context-service.d.ts +4 -3
- package/lib/base/editor-context-service.d.ts.map +1 -1
- package/lib/base/editor-context-service.js +11 -1
- package/lib/base/editor-context-service.js.map +1 -1
- package/lib/base/model/glsp-model-source.d.ts +5 -2
- package/lib/base/model/glsp-model-source.d.ts.map +1 -1
- package/lib/base/model/glsp-model-source.js +28 -1
- package/lib/base/model/glsp-model-source.js.map +1 -1
- package/lib/base/selection-service.d.ts +4 -3
- package/lib/base/selection-service.d.ts.map +1 -1
- package/lib/base/selection-service.js +6 -1
- package/lib/base/selection-service.js.map +1 -1
- package/lib/base/shortcuts/available-shortcuts-extension.d.ts +1 -1
- package/lib/base/shortcuts/available-shortcuts-extension.d.ts.map +1 -1
- package/lib/base/shortcuts/available-shortcuts-extension.js +3 -4
- package/lib/base/shortcuts/available-shortcuts-extension.js.map +1 -1
- package/lib/base/view/mouse-tool.d.ts +1 -1
- package/lib/base/view/mouse-tool.d.ts.map +1 -1
- package/lib/base/view/mouse-tool.js +3 -19
- package/lib/base/view/mouse-tool.js.map +1 -1
- package/lib/features/change-bounds/move-element-handler.d.ts +2 -3
- package/lib/features/change-bounds/move-element-handler.d.ts.map +1 -1
- package/lib/features/change-bounds/move-element-handler.js +2 -3
- package/lib/features/change-bounds/move-element-handler.js.map +1 -1
- package/lib/features/change-bounds/resize/resize-handler.d.ts +2 -3
- package/lib/features/change-bounds/resize/resize-handler.d.ts.map +1 -1
- package/lib/features/change-bounds/resize/resize-handler.js +2 -3
- package/lib/features/change-bounds/resize/resize-handler.js.map +1 -1
- package/lib/features/helper-lines/helper-line-feedback.d.ts +1 -1
- package/lib/features/helper-lines/helper-line-feedback.d.ts.map +1 -1
- package/lib/features/helper-lines/helper-line-feedback.js +2 -3
- package/lib/features/helper-lines/helper-line-feedback.js.map +1 -1
- package/lib/features/label-edit-ui/label-edit-ui.d.ts +4 -1
- package/lib/features/label-edit-ui/label-edit-ui.d.ts.map +1 -1
- package/lib/features/label-edit-ui/label-edit-ui.js +38 -1
- package/lib/features/label-edit-ui/label-edit-ui.js.map +1 -1
- package/lib/features/routing/edge-router.d.ts +2 -1
- package/lib/features/routing/edge-router.d.ts.map +1 -1
- package/lib/features/routing/edge-router.js +2 -1
- package/lib/features/routing/edge-router.js.map +1 -1
- package/lib/features/routing/routing-module.d.ts +1 -1
- package/lib/features/routing/routing-module.d.ts.map +1 -1
- package/lib/features/routing/routing-module.js +7 -1
- package/lib/features/routing/routing-module.js.map +1 -1
- package/lib/features/routing/sticky-manhattan-anchors.d.ts +29 -0
- package/lib/features/routing/sticky-manhattan-anchors.d.ts.map +1 -0
- package/lib/features/routing/sticky-manhattan-anchors.js +62 -0
- package/lib/features/routing/sticky-manhattan-anchors.js.map +1 -0
- package/lib/features/routing/sticky-manhattan-edge-router.d.ts +107 -0
- package/lib/features/routing/sticky-manhattan-edge-router.d.ts.map +1 -0
- package/lib/features/routing/sticky-manhattan-edge-router.js +626 -0
- package/lib/features/routing/sticky-manhattan-edge-router.js.map +1 -0
- package/lib/features/search-palette/search-palette.d.ts +2 -2
- package/lib/features/search-palette/search-palette.d.ts.map +1 -1
- package/lib/features/search-palette/search-palette.js +3 -4
- package/lib/features/search-palette/search-palette.js.map +1 -1
- package/lib/features/select/select-module.d.ts +1 -1
- package/lib/features/select/select-module.js +1 -1
- package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts +2 -3
- package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts.map +1 -1
- package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js +2 -3
- package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js.map +1 -1
- package/lib/index.d.ts +5 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/views/gedge-view-with-gaps-on-intersections.d.ts +39 -0
- package/lib/views/gedge-view-with-gaps-on-intersections.d.ts.map +1 -0
- package/lib/views/gedge-view-with-gaps-on-intersections.js +85 -0
- package/lib/views/gedge-view-with-gaps-on-intersections.js.map +1 -0
- package/lib/views/rounded-corner-manhattan-edge-view.d.ts +58 -0
- package/lib/views/rounded-corner-manhattan-edge-view.d.ts.map +1 -0
- package/lib/views/rounded-corner-manhattan-edge-view.js +170 -0
- package/lib/views/rounded-corner-manhattan-edge-view.js.map +1 -0
- package/package.json +4 -6
- package/src/base/action-dispatcher.ts +6 -3
- package/src/base/default.module.ts +4 -0
- package/src/base/editor-context-service.ts +17 -5
- package/src/base/model/glsp-model-source.ts +35 -1
- package/src/base/selection-service.ts +11 -2
- package/src/base/shortcuts/available-shortcuts-extension.ts +3 -4
- package/src/base/view/mouse-tool.ts +4 -19
- package/src/features/change-bounds/move-element-handler.ts +3 -2
- package/src/features/change-bounds/resize/resize-handler.ts +4 -3
- package/src/features/helper-lines/helper-line-feedback.ts +6 -4
- package/src/features/label-edit-ui/label-edit-ui.ts +36 -3
- package/src/features/routing/edge-router.ts +2 -2
- package/src/features/routing/routing-module.ts +8 -1
- package/src/features/routing/sticky-manhattan-anchors.ts +53 -0
- package/src/features/routing/sticky-manhattan-edge-router.spec.ts +212 -0
- package/src/features/routing/sticky-manhattan-edge-router.ts +713 -0
- package/src/features/search-palette/search-palette.ts +3 -4
- package/src/features/select/select-module.ts +1 -1
- package/src/features/tools/change-bounds/change-bounds-tool-move-feedback.ts +13 -3
- package/src/index.ts +5 -1
- package/src/views/gedge-view-with-gaps-on-intersections.tsx +99 -0
- package/src/views/rounded-corner-manhattan-edge-view.ts +171 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/********************************************************************************
|
|
2
|
-
* Copyright (c) 2024 EclipseSource and others.
|
|
2
|
+
* Copyright (c) 2024-2026 EclipseSource and others.
|
|
3
3
|
*
|
|
4
4
|
* This program and the accompanying materials are made available under the
|
|
5
5
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
@@ -13,15 +13,32 @@
|
|
|
13
13
|
*
|
|
14
14
|
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
********************************************************************************/
|
|
16
|
-
import { EditLabelUI } from '@eclipse-glsp/sprotty';
|
|
17
|
-
import { injectable } from 'inversify';
|
|
16
|
+
import { Bounds, EditLabelUI } from '@eclipse-glsp/sprotty';
|
|
17
|
+
import { inject, injectable } from 'inversify';
|
|
18
|
+
import { EditorContextService } from '../../base/editor-context-service';
|
|
18
19
|
import { CSS_HIDDEN_EXTENSION_CLASS, CSS_UI_EXTENSION_CLASS } from '../../base/ui-extension/ui-extension';
|
|
19
20
|
|
|
20
21
|
@injectable()
|
|
21
22
|
export class GlspEditLabelUI extends EditLabelUI {
|
|
23
|
+
@inject(EditorContextService)
|
|
24
|
+
protected editorContextService: EditorContextService;
|
|
25
|
+
|
|
22
26
|
protected override initializeContents(containerElement: HTMLElement): void {
|
|
23
27
|
super.initializeContents(containerElement);
|
|
24
28
|
containerElement.classList.add(CSS_UI_EXTENSION_CLASS);
|
|
29
|
+
this.editorContextService.onViewportChanged(() => {
|
|
30
|
+
window.requestAnimationFrame(() => {
|
|
31
|
+
if (this.isActive && this.containerElement) {
|
|
32
|
+
if (this.isLabelInVisibleViewport()) {
|
|
33
|
+
this.setPosition(this.containerElement);
|
|
34
|
+
this.applyFontStyling();
|
|
35
|
+
} else {
|
|
36
|
+
// Cancel editing if the label moved out of the visible viewport
|
|
37
|
+
this.hide();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
25
42
|
}
|
|
26
43
|
|
|
27
44
|
protected override setContainerVisible(visible: boolean): void {
|
|
@@ -32,4 +49,20 @@ export class GlspEditLabelUI extends EditLabelUI {
|
|
|
32
49
|
this.containerElement?.classList.add(CSS_HIDDEN_EXTENSION_CLASS);
|
|
33
50
|
}
|
|
34
51
|
}
|
|
52
|
+
|
|
53
|
+
protected isLabelInVisibleViewport(): boolean {
|
|
54
|
+
if (!this.labelElement) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const canvasBounds = this.editorContextService.canvasBounds;
|
|
58
|
+
const scroll = { x: window.scrollX, y: window.scrollY };
|
|
59
|
+
const canvasScreenBounds: Bounds = {
|
|
60
|
+
x: canvasBounds.x - scroll.x,
|
|
61
|
+
y: canvasBounds.y - scroll.y,
|
|
62
|
+
width: canvasBounds.width,
|
|
63
|
+
height: canvasBounds.height
|
|
64
|
+
};
|
|
65
|
+
const labelBounds = this.labelElement.getBoundingClientRect();
|
|
66
|
+
return Bounds.overlap(labelBounds, canvasScreenBounds);
|
|
67
|
+
}
|
|
35
68
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/********************************************************************************
|
|
2
|
-
* Copyright (c) 2024 EclipseSource and others.
|
|
2
|
+
* Copyright (c) 2024-2026 EclipseSource and others.
|
|
3
3
|
*
|
|
4
4
|
* This program and the accompanying materials are made available under the
|
|
5
5
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
@@ -114,7 +114,7 @@ export class GLSPBezierEdgeRouter extends BezierEdgeRouter {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
function ensureBounds(element?: GConnectableElement): boolean {
|
|
117
|
+
export function ensureBounds(element?: GConnectableElement): boolean {
|
|
118
118
|
if (!element) {
|
|
119
119
|
return false;
|
|
120
120
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/********************************************************************************
|
|
2
|
-
* Copyright (c) 2019-
|
|
2
|
+
* Copyright (c) 2019-2026 EclipseSource and others.
|
|
3
3
|
*
|
|
4
4
|
* This program and the accompanying materials are made available under the
|
|
5
5
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
@@ -32,6 +32,8 @@ import {
|
|
|
32
32
|
configureCommand
|
|
33
33
|
} from '@eclipse-glsp/sprotty';
|
|
34
34
|
import { GLSPBezierEdgeRouter, GLSPManhattanEdgeRouter, GLSPPolylineEdgeRouter } from './edge-router';
|
|
35
|
+
import { StickyManhattanDiamondAnchor, StickyManhattanEllipticAnchor, StickyManhattanRectangularAnchor } from './sticky-manhattan-anchors';
|
|
36
|
+
import { GLSPStickyManhattanEdgeRouter } from './sticky-manhattan-edge-router';
|
|
35
37
|
|
|
36
38
|
export const routingModule = new FeatureModule(
|
|
37
39
|
(bind, unbind, isBound, rebind) => {
|
|
@@ -54,6 +56,11 @@ export const routingModule = new FeatureModule(
|
|
|
54
56
|
bindAsService(context, TYPES.IAnchorComputer, BezierRectangleAnchor);
|
|
55
57
|
bindAsService(context, TYPES.IAnchorComputer, BezierDiamondAnchor);
|
|
56
58
|
|
|
59
|
+
bindAsService(context, TYPES.IEdgeRouter, GLSPStickyManhattanEdgeRouter);
|
|
60
|
+
bindAsService(context, TYPES.IAnchorComputer, StickyManhattanEllipticAnchor);
|
|
61
|
+
bindAsService(context, TYPES.IAnchorComputer, StickyManhattanRectangularAnchor);
|
|
62
|
+
bindAsService(context, TYPES.IAnchorComputer, StickyManhattanDiamondAnchor);
|
|
63
|
+
|
|
57
64
|
configureCommand({ bind, isBound }, AddRemoveBezierSegmentCommand);
|
|
58
65
|
},
|
|
59
66
|
{ featureId: Symbol('routing') }
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 EclipseSource 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 {
|
|
17
|
+
DIAMOND_ANCHOR_KIND,
|
|
18
|
+
ELLIPTIC_ANCHOR_KIND,
|
|
19
|
+
ManhattanDiamondAnchor,
|
|
20
|
+
ManhattanEllipticAnchor,
|
|
21
|
+
ManhattanRectangularAnchor,
|
|
22
|
+
RECTANGULAR_ANCHOR_KIND
|
|
23
|
+
} from '@eclipse-glsp/sprotty';
|
|
24
|
+
import { injectable } from 'inversify';
|
|
25
|
+
import { GLSPStickyManhattanEdgeRouter } from './sticky-manhattan-edge-router';
|
|
26
|
+
|
|
27
|
+
// Anchor computers are keyed by `<routerKind>:<anchorKind>` in the AnchorComputerRegistry.
|
|
28
|
+
// The sticky router has its own routerKind, so dedicated subclasses must be registered even
|
|
29
|
+
// though their geometry behavior is identical to the standard Manhattan anchors.
|
|
30
|
+
|
|
31
|
+
@injectable()
|
|
32
|
+
export class StickyManhattanRectangularAnchor extends ManhattanRectangularAnchor {
|
|
33
|
+
static override readonly KIND = GLSPStickyManhattanEdgeRouter.KIND + ':' + RECTANGULAR_ANCHOR_KIND;
|
|
34
|
+
override get kind(): string {
|
|
35
|
+
return StickyManhattanRectangularAnchor.KIND;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@injectable()
|
|
40
|
+
export class StickyManhattanDiamondAnchor extends ManhattanDiamondAnchor {
|
|
41
|
+
static override readonly KIND = GLSPStickyManhattanEdgeRouter.KIND + ':' + DIAMOND_ANCHOR_KIND;
|
|
42
|
+
override get kind(): string {
|
|
43
|
+
return StickyManhattanDiamondAnchor.KIND;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@injectable()
|
|
48
|
+
export class StickyManhattanEllipticAnchor extends ManhattanEllipticAnchor {
|
|
49
|
+
static override readonly KIND = GLSPStickyManhattanEdgeRouter.KIND + ':' + ELLIPTIC_ANCHOR_KIND;
|
|
50
|
+
override get kind(): string {
|
|
51
|
+
return StickyManhattanEllipticAnchor.KIND;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 EclipseSource 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 { DefaultAnchors, EdgeRouterRegistry, GNode, GRoutableElement, Point, Side } from '@eclipse-glsp/sprotty';
|
|
17
|
+
import { expect } from 'chai';
|
|
18
|
+
import { Container } from 'inversify';
|
|
19
|
+
import { GEdge, GGraph } from '../../model';
|
|
20
|
+
import { routingModule } from './routing-module';
|
|
21
|
+
import { GLSPStickyManhattanEdgeRouter, StickyManhattanRouterOptions } from './sticky-manhattan-edge-router';
|
|
22
|
+
|
|
23
|
+
/** Exposes the protected helpers as public so the spec can exercise them directly. */
|
|
24
|
+
class TestableStickyManhattanEdgeRouter extends GLSPStickyManhattanEdgeRouter {
|
|
25
|
+
public override manhattanify(points: Point[], edge: GRoutableElement): void {
|
|
26
|
+
super.manhattanify(points, edge);
|
|
27
|
+
}
|
|
28
|
+
public override getOptions(edge: GRoutableElement): StickyManhattanRouterOptions {
|
|
29
|
+
return super.getOptions(edge);
|
|
30
|
+
}
|
|
31
|
+
public override getBestConnectionAnchors(
|
|
32
|
+
sourceAnchors: DefaultAnchors,
|
|
33
|
+
targetAnchors: DefaultAnchors,
|
|
34
|
+
options: StickyManhattanRouterOptions
|
|
35
|
+
): { source: Side; target: Side } {
|
|
36
|
+
return super.getBestConnectionAnchors(sourceAnchors, targetAnchors, options);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function newNode(id: string, x: number, y: number, width = 40, height = 30): GNode {
|
|
41
|
+
const node = new GNode();
|
|
42
|
+
node.id = id;
|
|
43
|
+
node.position = { x, y };
|
|
44
|
+
node.size = { width, height };
|
|
45
|
+
return node;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function setupEdge(opts: { sourcePos?: Point; targetPos?: Point; routingPoints?: Point[] }): {
|
|
49
|
+
graph: GGraph;
|
|
50
|
+
edge: GEdge;
|
|
51
|
+
router: GLSPStickyManhattanEdgeRouter;
|
|
52
|
+
} {
|
|
53
|
+
const graph = new GGraph();
|
|
54
|
+
const source = newNode('source', opts.sourcePos?.x ?? 0, opts.sourcePos?.y ?? 0);
|
|
55
|
+
const target = newNode('target', opts.targetPos?.x ?? 200, opts.targetPos?.y ?? 100);
|
|
56
|
+
graph.add(source);
|
|
57
|
+
graph.add(target);
|
|
58
|
+
|
|
59
|
+
const edge = new GEdge();
|
|
60
|
+
edge.id = 'edge';
|
|
61
|
+
edge.sourceId = 'source';
|
|
62
|
+
edge.targetId = 'target';
|
|
63
|
+
edge.routerKind = GLSPStickyManhattanEdgeRouter.KIND;
|
|
64
|
+
if (opts.routingPoints) {
|
|
65
|
+
edge.routingPoints = opts.routingPoints.slice();
|
|
66
|
+
}
|
|
67
|
+
graph.add(edge);
|
|
68
|
+
|
|
69
|
+
const container = new Container();
|
|
70
|
+
container.load(routingModule);
|
|
71
|
+
const registry = container.get<EdgeRouterRegistry>(EdgeRouterRegistry);
|
|
72
|
+
const router = registry.get(GLSPStickyManhattanEdgeRouter.KIND) as GLSPStickyManhattanEdgeRouter;
|
|
73
|
+
return { graph, edge, router };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
describe('GLSPStickyManhattanEdgeRouter', () => {
|
|
77
|
+
describe('route()', () => {
|
|
78
|
+
it('returns an empty route when the source node cannot be resolved', () => {
|
|
79
|
+
const graph = new GGraph();
|
|
80
|
+
const target = newNode('target', 200, 100);
|
|
81
|
+
graph.add(target);
|
|
82
|
+
const edge = new GEdge();
|
|
83
|
+
edge.id = 'edge';
|
|
84
|
+
edge.sourceId = 'missing';
|
|
85
|
+
edge.targetId = 'target';
|
|
86
|
+
edge.routerKind = GLSPStickyManhattanEdgeRouter.KIND;
|
|
87
|
+
graph.add(edge);
|
|
88
|
+
|
|
89
|
+
const container = new Container();
|
|
90
|
+
container.load(routingModule);
|
|
91
|
+
const router = container
|
|
92
|
+
.get<EdgeRouterRegistry>(EdgeRouterRegistry)
|
|
93
|
+
.get(GLSPStickyManhattanEdgeRouter.KIND) as GLSPStickyManhattanEdgeRouter;
|
|
94
|
+
expect(router.route(edge)).to.deep.equal([]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('produces a source-first, target-last sequence with intermediate linear points', () => {
|
|
98
|
+
const { edge, router } = setupEdge({});
|
|
99
|
+
const route = router.route(edge);
|
|
100
|
+
expect(route[0].kind).to.equal('source');
|
|
101
|
+
expect(route[route.length - 1].kind).to.equal('target');
|
|
102
|
+
expect(route.slice(1, -1).every(p => p.kind === 'linear')).to.equal(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('computes a two-corner default route for horizontally separated nodes', () => {
|
|
106
|
+
const { edge, router } = setupEdge({ sourcePos: { x: 0, y: 0 }, targetPos: { x: 200, y: 100 } });
|
|
107
|
+
const route = router.route(edge);
|
|
108
|
+
// source RIGHT -> target LEFT with different Y: two corners at midX.
|
|
109
|
+
const interior = route.slice(1, -1);
|
|
110
|
+
expect(interior).to.have.lengthOf(2);
|
|
111
|
+
expect(interior[0].x).to.equal(interior[1].x);
|
|
112
|
+
expect(interior[0].y).to.not.equal(interior[1].y);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('sticky behavior', () => {
|
|
117
|
+
it('preserves interior bend points when the source node moves vertically', () => {
|
|
118
|
+
const { edge, router } = setupEdge({
|
|
119
|
+
sourcePos: { x: 0, y: 0 },
|
|
120
|
+
targetPos: { x: 300, y: 200 },
|
|
121
|
+
routingPoints: [
|
|
122
|
+
{ x: 150, y: 15 },
|
|
123
|
+
{ x: 150, y: 215 }
|
|
124
|
+
]
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Prime the position snapshot.
|
|
128
|
+
router.route(edge);
|
|
129
|
+
|
|
130
|
+
// Move the source node down by 50px.
|
|
131
|
+
edge.source!.position = { x: 0, y: 50 };
|
|
132
|
+
const route = router.route(edge);
|
|
133
|
+
const interior = route.slice(1, -1);
|
|
134
|
+
|
|
135
|
+
// The shared x=150 spine must stay put — no recomputed midX.
|
|
136
|
+
expect(interior.every(p => p.x === 150)).to.equal(true);
|
|
137
|
+
// The target-side bend must not have moved.
|
|
138
|
+
expect(interior[interior.length - 1].y).to.equal(215);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe('cleanupRoutingPoints()', () => {
|
|
143
|
+
it('removes leading routing points that fall inside the source bounds', () => {
|
|
144
|
+
const { edge, router } = setupEdge({
|
|
145
|
+
sourcePos: { x: 0, y: 0 },
|
|
146
|
+
targetPos: { x: 300, y: 100 },
|
|
147
|
+
routingPoints: [
|
|
148
|
+
{ x: 10, y: 10 }, // inside source bounds (40x30 at origin)
|
|
149
|
+
{ x: 150, y: 20 },
|
|
150
|
+
{ x: 150, y: 110 }
|
|
151
|
+
]
|
|
152
|
+
});
|
|
153
|
+
const points = edge.routingPoints.slice();
|
|
154
|
+
router.cleanupRoutingPoints(edge, points, false, false);
|
|
155
|
+
expect(points).to.not.deep.include({ x: 10, y: 10 });
|
|
156
|
+
expect(points[0]).to.deep.equal({ x: 150, y: 20 });
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('collapses degenerate segments shorter than minimalPointDistance', () => {
|
|
160
|
+
const { edge, router } = setupEdge({ sourcePos: { x: 0, y: 0 }, targetPos: { x: 300, y: 100 } });
|
|
161
|
+
const points: Point[] = [
|
|
162
|
+
{ x: 100, y: 20 },
|
|
163
|
+
{ x: 101, y: 21 }, // manhattan distance 2 < default minimal of 3
|
|
164
|
+
{ x: 250, y: 20 }
|
|
165
|
+
];
|
|
166
|
+
router.cleanupRoutingPoints(edge, points, false, false);
|
|
167
|
+
expect(points).to.have.lengthOf(1);
|
|
168
|
+
expect(points[0]).to.deep.equal({ x: 250, y: 20 });
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('manhattanify()', () => {
|
|
173
|
+
it('inserts an intermediate corner so every segment is strictly orthogonal', () => {
|
|
174
|
+
const { edge, router } = setupEdge({});
|
|
175
|
+
const testable = router as TestableStickyManhattanEdgeRouter;
|
|
176
|
+
const points: Point[] = [
|
|
177
|
+
{ x: 0, y: 0 },
|
|
178
|
+
{ x: 50, y: 50 } // diagonal
|
|
179
|
+
];
|
|
180
|
+
testable.manhattanify(points, edge);
|
|
181
|
+
expect(points).to.deep.equal([
|
|
182
|
+
{ x: 0, y: 0 },
|
|
183
|
+
{ x: 0, y: 50 },
|
|
184
|
+
{ x: 50, y: 50 }
|
|
185
|
+
]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('leaves strictly orthogonal routes untouched', () => {
|
|
189
|
+
const { edge, router } = setupEdge({});
|
|
190
|
+
const testable = router as TestableStickyManhattanEdgeRouter;
|
|
191
|
+
const points: Point[] = [
|
|
192
|
+
{ x: 0, y: 0 },
|
|
193
|
+
{ x: 50, y: 0 },
|
|
194
|
+
{ x: 50, y: 40 }
|
|
195
|
+
];
|
|
196
|
+
const before = points.map(p => ({ ...p }));
|
|
197
|
+
testable.manhattanify(points, edge);
|
|
198
|
+
expect(points).to.deep.equal(before);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('getBestConnectionAnchors()', () => {
|
|
203
|
+
it('picks RIGHT/LEFT when source is clearly to the left of target', () => {
|
|
204
|
+
const { edge } = setupEdge({ sourcePos: { x: 0, y: 0 }, targetPos: { x: 400, y: 0 } });
|
|
205
|
+
const router = new TestableStickyManhattanEdgeRouter();
|
|
206
|
+
const sourceAnchors = new DefaultAnchors(edge.source!, edge.parent, 'source');
|
|
207
|
+
const targetAnchors = new DefaultAnchors(edge.target!, edge.parent, 'target');
|
|
208
|
+
const result = router.getBestConnectionAnchors(sourceAnchors, targetAnchors, router.getOptions(edge));
|
|
209
|
+
expect(result).to.deep.equal({ source: Side.RIGHT, target: Side.LEFT });
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
});
|