UNPKG

6.48 kBPlain TextView Raw
1/*
2* Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
3*/
4
5/*
6* This is an extension and not part of the main GoJS library.
7* Note that the API for this class may change with any version, even point releases.
8* If you intend to use an extension in production, you should copy the code to your own source directory.
9* Extensions can be found in the GoJS kit under the extensions or extensionsTS folders.
10* See the Extensions intro page (https://gojs.net/latest/intro/extensions.html) for more information.
11*/
12
13import * as go from '../release/go-module.js';
14
15/**
16 * The OrthogonalLinkReshapingTool class lets a user drag a tool handle along the link segment, which will move the whole segment.
17 *
18 * If you want to experiment with this extension, try the <a href="../../extensionsJSM/OrthogonalLinkReshaping.html">Orthogonal Link Reshaping</a> sample.
19 * @category Tool Extension
20 */
21export class OrthogonalLinkReshapingTool extends go.LinkReshapingTool {
22 /**
23 * Constructs an OrthogonalLinkReshapingTool and sets the name for the tool.
24 */
25 constructor() {
26 super();
27 this.name = 'OrthogonalLinkReshaping';
28 }
29
30 private _alreadyAddedPoint: boolean = false;
31
32 /**
33 * @hidden @internal
34 * For orthogonal, straight links, create the handles and set reshaping behavior.
35 */
36 public makeAdornment(pathshape: go.Shape): go.Adornment {
37 const link = pathshape.part as go.Link;
38
39 // add all normal handles first
40 const adornment = super.makeAdornment(pathshape);
41
42 // add long reshaping handles for orthogonal, straight links
43 if (link !== null && link.isOrthogonal && link.curve !== go.Link.Bezier) {
44 const firstindex = link.firstPickIndex + (link.resegmentable ? 0 : 1);
45 const lastindex = link.lastPickIndex - (link.resegmentable ? 0 : 1);
46 for (let i = firstindex; i < lastindex; i++) {
47 this.makeSegmentDragHandle(link, adornment, i);
48 }
49 }
50 return adornment;
51 }
52
53 /**
54 * This stops the current reshaping operation and updates any link handles.
55 */
56 public doDeactivate(): void {
57 this._alreadyAddedPoint = false;
58 // when we finish, recreate adornment to ensure proper reshaping behavior/cursor
59 const link = this.adornedLink;
60 if (link !== null && link.isOrthogonal && link.curve !== go.Link.Bezier) {
61 const pathshape = link.path;
62 if (pathshape !== null) {
63 const adornment = this.makeAdornment(pathshape);
64 if (adornment !== null) {
65 link.addAdornment(this.name, adornment);
66 adornment.location = link.position;
67 }
68 }
69 }
70 super.doDeactivate();
71 }
72
73 /**
74 * Change the route of the {@link #adornedLink} by moving the segment corresponding to the current
75 * {@link #handle} to be at the given {@link Point}.
76 */
77 public reshape(newpt: go.Point): void {
78 const link = this.adornedLink;
79
80 // identify if the handle being dragged is a segment dragging handle
81 if (link !== null && link.isOrthogonal && link.curve !== go.Link.Bezier && this.handle !== null && this.handle.toMaxLinks === 999) {
82 link.startRoute();
83 let index = this.handle.segmentIndex; // for these handles, firstPickIndex <= index < lastPickIndex
84 if (!this._alreadyAddedPoint && link.resegmentable) { // only change the number of points if Link.resegmentable
85 this._alreadyAddedPoint = true;
86 if (index === link.firstPickIndex) {
87 link.insertPoint(index, link.getPoint(index).copy());
88 index++;
89 this.handle.segmentIndex = index;
90 } else if (index === link.lastPickIndex - 1) {
91 link.insertPoint(index, link.getPoint(index).copy());
92 if (index - 1 === link.firstPickIndex + 1) this.handle.segmentIndex = index - 1;
93 }
94 }
95 const behavior = this.getReshapingBehavior(this.handle);
96 if (behavior === go.LinkReshapingTool.Vertical) {
97 // move segment vertically
98 link.setPointAt(index, link.getPoint(index - 1).x, newpt.y);
99 link.setPointAt(index + 1, link.getPoint(index + 2).x, newpt.y);
100 } else if (behavior === go.LinkReshapingTool.Horizontal) {
101 // move segment horizontally
102 link.setPointAt(index, newpt.x, link.getPoint(index - 1).y);
103 link.setPointAt(index + 1, newpt.x, link.getPoint(index + 2).y);
104 }
105 link.commitRoute();
106 } else {
107 super.reshape(newpt);
108 }
109 }
110
111 /**
112 * Create the segment dragging handles.
113 * There are two parts: one invisible handle that spans the segment, and a visible handle at the middle of the segment.
114 * These are inserted at the front of the adornment such that the normal handles have priority.
115 */
116 public makeSegmentDragHandle(link: go.Link, adornment: go.Adornment, index: number): void {
117 const a = link.getPoint(index);
118 let b = link.getPoint(index + 1);
119 const seglength = Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y));
120 // determine segment orientation
121 let orient = '';
122 if (this.isApprox(a.x, b.x) && this.isApprox(a.y, b.y)) {
123 b = link.getPoint(index - 1);
124 if (this.isApprox(a.x, b.x)) {
125 orient = 'vertical';
126 } else if (this.isApprox(a.y, b.y)) {
127 orient = 'horizontal';
128 }
129 } else {
130 if (this.isApprox(a.x, b.x)) {
131 orient = 'vertical';
132 } else if (this.isApprox(a.y, b.y)) {
133 orient = 'horizontal';
134 }
135 }
136
137 // make an invisible handle along the whole segment
138 const h = new go.Shape();
139 h.strokeWidth = 6;
140 h.opacity = 0;
141 h.segmentOrientation = go.Link.OrientAlong;
142 h.segmentIndex = index;
143 h.segmentFraction = 0.5;
144 h.toMaxLinks = 999; // set this unsused property to easily identify that we have a segment dragging handle
145 if (orient === 'horizontal') {
146 this.setReshapingBehavior(h, go.LinkReshapingTool.Vertical);
147 h.cursor = 'n-resize';
148 } else {
149 this.setReshapingBehavior(h, go.LinkReshapingTool.Horizontal);
150 h.cursor = 'w-resize';
151 }
152 h.geometryString = 'M 0 0 L ' + seglength + ' 0';
153 adornment.insertAt(0, h);
154 }
155
156 /**
157 * Compare two numbers to ensure they are almost equal.
158 * Used in this class for comparing coordinates of Points.
159 */
160 public isApprox(x: number, y: number): boolean {
161 const d = x - y;
162 return d < 0.5 && d > -0.5;
163 }
164}