1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | import * as go from '../release/go-module.js';
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | export class SnapLinkReshapingTool extends go.LinkReshapingTool {
|
24 | private _gridCellSize = new go.Size(NaN, NaN);
|
25 | private _gridOrigin = new go.Point(NaN, NaN);
|
26 | private _isGridSnapEnabled = true;
|
27 | private _avoidsNodes = true;
|
28 |
|
29 | private _safePoint = new go.Point(NaN, NaN);
|
30 | private _prevSegHoriz = false;
|
31 | private _nextSegHoriz = false;
|
32 |
|
33 | |
34 |
|
35 |
|
36 |
|
37 |
|
38 | get gridCellSize(): go.Size { return this._gridCellSize; }
|
39 | set gridCellSize(val: go.Size) {
|
40 | if (!(val instanceof go.Size)) throw new Error('new value for SnapLinkReshapingTool.gridCellSize must be a Size, not: ' + val);
|
41 | this._gridCellSize = val.copy();
|
42 | }
|
43 |
|
44 | |
45 |
|
46 |
|
47 |
|
48 |
|
49 | get gridOrigin(): go.Point { return this._gridOrigin; }
|
50 | set gridOrigin(val: go.Point) {
|
51 | if (!(val instanceof go.Point)) throw new Error('new value for SnapLinkReshapingTool.gridOrigin must be a Point, not: ' + val);
|
52 | this._gridOrigin = val.copy();
|
53 | }
|
54 |
|
55 | |
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | get isGridSnapEnabled(): boolean { return this._isGridSnapEnabled; }
|
62 | set isGridSnapEnabled(val: boolean) {
|
63 | if (typeof val !== 'boolean') throw new Error('new value for SnapLinkReshapingTool.isGridSnapEnabled must be a boolean, not: ' + val);
|
64 | this._isGridSnapEnabled = val;
|
65 | }
|
66 |
|
67 | |
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 | get avoidsNodes(): boolean { return this._avoidsNodes; }
|
75 | set avoidsNodes(val: boolean) {
|
76 | if (typeof val !== 'boolean') throw new Error('new value for SnapLinkReshapingTool.avoidsNodes must be a boolean, not: ' + val);
|
77 | this._avoidsNodes = val;
|
78 | }
|
79 |
|
80 | |
81 |
|
82 |
|
83 |
|
84 | public doActivate(): void {
|
85 | super.doActivate();
|
86 | if (this.isActive && this.avoidsNodes && this.adornedLink !== null && this.adornedLink.isOrthogonal && this.handle !== null) {
|
87 |
|
88 | this._safePoint = this.diagram.lastInput.documentPoint.copy();
|
89 | const link = this.adornedLink;
|
90 | const idx = this.handle.segmentIndex;
|
91 | this._prevSegHoriz = Math.abs(link.getPoint(idx-1).y - link.getPoint(idx).y) < 0.5;
|
92 | this._nextSegHoriz = Math.abs(link.getPoint(idx+1).y - link.getPoint(idx).y) < 0.5;
|
93 | }
|
94 | };
|
95 |
|
96 | |
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | public computeReshape(p: go.Point): go.Point {
|
106 | let pt = p;
|
107 | const diagram = this.diagram;
|
108 | if (this.isGridSnapEnabled) {
|
109 |
|
110 | let cell = this.gridCellSize;
|
111 | let orig = this.gridOrigin;
|
112 | if (!cell.isReal() || cell.width === 0 || cell.height === 0) cell = diagram.grid.gridCellSize;
|
113 | if (!orig.isReal()) orig = diagram.grid.gridOrigin;
|
114 |
|
115 | pt = p.copy().snapToGrid(orig.x, orig.y, cell.width, cell.height);
|
116 | }
|
117 | if (this.avoidsNodes && this.adornedLink !== null && this.adornedLink.isOrthogonal) {
|
118 | if (this._checkSegmentsOverlap(pt)) {
|
119 | this._safePoint = pt.copy();
|
120 | } else {
|
121 | pt = this._safePoint.copy();
|
122 | }
|
123 | }
|
124 |
|
125 | return super.computeReshape(pt);
|
126 | }
|
127 |
|
128 | |
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 | private _checkSegmentsOverlap(pt: go.Point): boolean {
|
135 | if (this.handle === null) return true;
|
136 | if (this.adornedLink === null) return true;
|
137 | const index = this.handle.segmentIndex;
|
138 |
|
139 | if (index >= 1) {
|
140 | const p1 = this.adornedLink.getPoint(index-1);
|
141 | const r = new go.Rect(pt.x, pt.y, 0, 0);
|
142 | const q1 = p1.copy();
|
143 | if (this._prevSegHoriz) {
|
144 | q1.y = pt.y;
|
145 | } else {
|
146 | q1.x = pt.x;
|
147 | }
|
148 | r.unionPoint(q1);
|
149 | const overlaps = this.diagram.findPartsIn(r, true, false);
|
150 | if (overlaps.any(function(p) { return p instanceof go.Node && p.avoidable; })) return false;
|
151 |
|
152 | if (index >= 2) {
|
153 | const p0 = this.adornedLink.getPoint(index-2);
|
154 | const r = new go.Rect(q1.x, q1.y, 0, 0);
|
155 | if (this._prevSegHoriz) {
|
156 | r.unionPoint(new go.Point(q1.x, p0.y));
|
157 | } else {
|
158 | r.unionPoint(new go.Point(p0.x, q1.y));
|
159 | }
|
160 | const overlaps = this.diagram.findPartsIn(r, true, false);
|
161 | if (overlaps.any(function(p) { return p instanceof go.Node && p.avoidable; })) return false;
|
162 | }
|
163 | }
|
164 |
|
165 | if (index < this.adornedLink.pointsCount-1) {
|
166 | const p2 = this.adornedLink.getPoint(index+1);
|
167 | const r = new go.Rect(pt.x, pt.y, 0, 0);
|
168 | const q2 = p2.copy();
|
169 | if (this._nextSegHoriz) {
|
170 | q2.y = pt.y;
|
171 | } else {
|
172 | q2.x = pt.x;
|
173 | }
|
174 | r.unionPoint(q2);
|
175 | const overlaps = this.diagram.findPartsIn(r, true, false);
|
176 | if (overlaps.any(function(p) { return p instanceof go.Node && p.avoidable; })) return false;
|
177 |
|
178 | if (index < this.adornedLink.pointsCount-2) {
|
179 | const p3 = this.adornedLink.getPoint(index+2);
|
180 | const r = new go.Rect(q2.x, q2.y, 0, 0);
|
181 | if (this._nextSegHoriz) {
|
182 | r.unionPoint(new go.Point(q2.x, p3.y));
|
183 | } else {
|
184 | r.unionPoint(new go.Point(p3.x, q2.y));
|
185 | }
|
186 | const overlaps = this.diagram.findPartsIn(r, true, false);
|
187 | if (overlaps.any(function(p) { return p instanceof go.Node && p.avoidable; })) return false;
|
188 | }
|
189 | }
|
190 |
|
191 | return true;
|
192 | };
|
193 | }
|