UNPKG

9.85 kBPlain TextView Raw
1/*
2* Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
3*/
4
5import * as go from '../release/go-module.js';
6
7// A custom Tool to change the scale of an object in a Part.
8
9/*
10* This is an extension and not part of the main GoJS library.
11* Note that the API for this class may change with any version, even point releases.
12* If you intend to use an extension in production, you should copy the code to your own source directory.
13* Extensions can be found in the GoJS kit under the extensions or extensionsTS folders.
14* See the Extensions intro page (https://gojs.net/latest/intro/extensions.html) for more information.
15*/
16
17/**
18* A custom tool for rescaling an object.
19*
20* Install the RescalingTool as a mouse-down tool by calling:
21* myDiagram.toolManager.mouseDownTools.add(new RescalingTool());
22*
23* Normally it would not make sense for the same object to be both resizable and rescalable.
24*
25* Note that there is no <code>Part.rescaleObjectName</code> property and there is no <code>Part.rescalable</code> property.
26* So although you cannot customize any Node to affect this tool, you can set
27* <a>RescalingTool.rescaleObjectName</a> and set <a>RescalingTool.isEnabled</a> to control
28* whether objects are rescalable and when.
29*
30* If you want to experiment with this extension, try the <a href="../../extensionsJSM/Rescaling.html">Rescaling</a> sample.
31* @category Tool Extension
32*/
33export class RescalingTool extends go.Tool {
34 private _rescaleObjectName: string = "";
35 private _handleArchetype: go.GraphObject;
36
37 // internal state
38 private _adornedObject: go.GraphObject | null = null;
39
40 private _handle: go.GraphObject | null = null;
41
42 private originalPoint = new go.Point();
43 private originalTopLeft = new go.Point();
44 private originalScale = 1.0;
45
46 constructor() {
47 super();
48 this.name = "Rescaling";
49
50 var h = new go.Shape();
51 h.desiredSize = new go.Size(8, 8);
52 h.fill = "lightblue";
53 h.stroke = "dodgerblue";
54 h.strokeWidth = 1;
55 h.cursor = "nwse-resize";
56 this._handleArchetype = h;
57 }
58 /**
59 * Gets the {@link GraphObject} that is being rescaled.
60 * This may be the same object as the selected {@link Part} or it may be contained within that Part.
61 *
62 * This property is also settable, but should only be set when overriding functions
63 * in RescalingTool, and not during normal operation.
64 */
65 get adornedObject(): go.GraphObject | null { return this._adornedObject; }
66 set adornedObject(val: go.GraphObject | null) { this._adornedObject = val; }
67
68 /**
69 * Gets or sets a small GraphObject that is copied as a rescale handle for the selected part.
70 * By default this is a {@link Shape} that is a small blue square.
71 * Setting this property does not raise any events.
72 *
73 * Here is an example of changing the default handle to be green "X":
74 * ```js
75 * tool.handleArchetype =
76 * $(go.Shape, "XLine",
77 * { width: 8, height: 8, stroke: "green", fill: "transparent" });
78 * ```
79 */
80 get handleArchetype(): go.GraphObject { return this._handleArchetype; }
81 set handleArchetype(val: go.GraphObject ) { this._handleArchetype = val; }
82
83 /**
84 * This property returns the {@link GraphObject} that is the tool handle being dragged by the user.
85 * This will be contained by an {@link Adornment} whose category is "RescalingTool".
86 * Its {@link Adornment#adornedObject} is the same as the {@link #adornedObject}.
87 *
88 * This property is also settable, but should only be set either within an override of {@link #doActivate}
89 * or prior to calling {@link #doActivate}.
90 */
91 get handle(): go.GraphObject | null { return this._handle; }
92 set handle(val: go.GraphObject | null) { this._handle = val; }
93
94 /**
95 * This property returns the name of the GraphObject that identifies the object to be rescaled by this tool.
96 *
97 * The default value is the empty string, resulting in the whole Node being rescaled.
98 * This property is used by findRescaleObject when calling {@link Panel#findObject}.
99 */
100 get rescaleObjectName(): string { return this._rescaleObjectName; }
101 set rescaleObjectName(val: string) { this._rescaleObjectName = val; }
102
103 /**
104 * @this {RescalingTool}
105 * @param {Part} part
106 */
107 updateAdornments(part: go.Part | null) {
108 if (part === null || part instanceof go.Link) return;
109 if (part.isSelected && !this.diagram.isReadOnly) {
110 var rescaleObj = this.findRescaleObject(part);
111 if (rescaleObj !== null && part.actualBounds.isReal() && part.isVisible() &&
112 rescaleObj.actualBounds.isReal() && rescaleObj.isVisibleObject()) {
113 var adornment = part.findAdornment(this.name);
114 if (adornment === null || adornment.adornedObject !== rescaleObj) {
115 adornment = this.makeAdornment(rescaleObj);
116 }
117 if (adornment !== null) {
118 adornment.location = rescaleObj.getDocumentPoint(go.Spot.BottomRight);
119 part.addAdornment(this.name, adornment);
120 return;
121 }
122 }
123 }
124 part.removeAdornment(this.name);
125 }
126
127 /**
128 * @this {RescalingTool}
129 * @param {GraphObject} rescaleObj
130 * @return {Adornment}
131 */
132 makeAdornment(rescaleObj: go.GraphObject | null): go.Adornment {
133 var adornment = new go.Adornment();
134 adornment.type = go.Panel.Position;
135 adornment.locationSpot = go.Spot.Center;
136 adornment.add(this._handleArchetype.copy());
137 adornment.adornedObject = rescaleObj;
138 return adornment;
139 }
140
141 /**
142 * Return the GraphObject to be rescaled by the user.
143 * @this {RescalingTool}
144 * @return {GraphObject}
145 */
146 findRescaleObject(part: go.Part): go.GraphObject {
147 var obj = part.findObject(this.rescaleObjectName);
148 if (obj) return obj;
149 return part;
150 }
151
152 /**
153 * This tool can start running if the mouse-down happens on a "Rescaling" handle.
154 * @this {RescalingTool}
155 * @return {boolean}
156 */
157 canStart(): boolean {
158 var diagram = this.diagram;
159 if (diagram === null || diagram.isReadOnly) return false;
160 if (!diagram.lastInput.left) return false;
161 var h = this.findToolHandleAt(diagram.firstInput.documentPoint, this.name);
162 return (h !== null);
163 }
164
165 /**
166 * Activating this tool remembers the {@link #handle} that was dragged,
167 * the {@link #adornedObject} that is being rescaled,
168 * starts a transaction, and captures the mouse.
169 * @this {RescalingTool}
170 */
171 doActivate(): void {
172 var diagram = this.diagram;
173 if (diagram === null) return;
174 this._handle = this.findToolHandleAt(diagram.firstInput.documentPoint, this.name);
175 if (this._handle === null) return;
176 var ad = this._handle.part;
177 this._adornedObject = (ad instanceof go.Adornment) ? ad.adornedObject : null;
178 if (!this._adornedObject) return;
179 this.originalPoint = this._handle.getDocumentPoint(go.Spot.Center);
180 this.originalTopLeft = this._adornedObject.getDocumentPoint(go.Spot.TopLeft);
181 this.originalScale = this._adornedObject.scale;
182 diagram.isMouseCaptured = true;
183 diagram.delaysLayout = true;
184 this.startTransaction(this.name);
185 this.isActive = true;
186 }
187
188 /**
189 * Stop the current transaction, forget the {@link #handle} and {@link #adornedObject}, and release the mouse.
190 * @this {RescalingTool}
191 */
192 doDeactivate(): void {
193 var diagram = this.diagram;
194 if (diagram === null) return;
195 this.stopTransaction();
196 this._handle = null;
197 this._adornedObject = null;
198 diagram.isMouseCaptured = false;
199 this.isActive = false;
200 };
201
202 /**
203 * Restore the original {@link GraphObject#scale} of the adorned object.
204 * @this {RescalingTool}
205 */
206 doCancel(): void {
207 var diagram = this.diagram;
208 if (diagram !== null) diagram.delaysLayout = false;
209 this.scale(this.originalScale);
210 this.stopTool();
211 }
212
213 /**
214 * Call {@link #scale} with a new scale determined by the current mouse point.
215 * This determines the new scale by calling {@link #computeScale}.
216 * @this {RescalingTool}
217 */
218 doMouseMove(): void {
219 var diagram = this.diagram;
220 if (this.isActive && diagram !== null) {
221 var newScale = this.computeScale(diagram.lastInput.documentPoint);
222 this.scale(newScale);
223 }
224 }
225
226 /**
227 * Call {@link #scale} with a new scale determined by the most recent mouse point,
228 * and commit the transaction.
229 * @this {RescalingTool}
230 */
231 doMouseUp(): void {
232 var diagram = this.diagram;
233 if (this.isActive && diagram !== null) {
234 diagram.delaysLayout = false;
235 var newScale = this.computeScale(diagram.lastInput.documentPoint);
236 this.scale(newScale);
237 this.transactionResult = this.name;
238 }
239 this.stopTool();
240 }
241
242 /**
243 * Set the {@link GraphObject#scale} of the {@link #findRescaleObject}.
244 * @this {RescalingTool}
245 * @param {number} newScale
246 */
247 scale(newScale: number): void {
248 if (this._adornedObject !== null) {
249 this._adornedObject.scale = newScale;
250 }
251 }
252
253 /**
254 * Compute the new scale given a point.
255 *
256 * This method is called by both {@link #doMouseMove} and {@link #doMouseUp}.
257 * This method may be overridden.
258 * Please read the Introduction page on <a href="../../intro/extensions.html">Extensions</a> for how to override methods and how to call this base method.
259 * @this {RescalingTool}
260 * @param {Point} newPoint in document coordinates
261 */
262 computeScale(newPoint: go.Point): number {
263 var scale = this.originalScale;
264 var origdist = Math.sqrt(this.originalPoint.distanceSquaredPoint(this.originalTopLeft));
265 var newdist = Math.sqrt(newPoint.distanceSquaredPoint(this.originalTopLeft));
266 return scale * (newdist/origdist);
267 }
268}