UNPKG

6.73 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/**
8* A custom RotatingTool that also supports the user moving the point about which the object is rotated.
9*
10* This tool uses two separate Adornments -- the regular one holding the rotation handle and an
11* additional one named "MovingSpot" that holds the handle for interactively moving the
12* {@link RotatingTool#rotationPoint} by changing the {@link Part#rotationSpot}.
13* @category Tool Extension
14*/
15export class SpotRotatingTool extends go.RotatingTool {
16 private _spotAdornmentTemplate: go.Adornment;
17 private _originalRotationSpot: go.Spot = go.Spot.Default;
18
19 public constructor() {
20 super();
21 const $ = go.GraphObject.make;
22 this._spotAdornmentTemplate =
23 $(go.Adornment, "Spot",
24 { locationSpot: go.Spot.Center, cursor: "move" },
25 $(go.Shape, "Circle",
26 { fill: "lightblue", stroke: "dodgerblue", width: 10, height: 10 }),
27 $(go.Shape, "Circle",
28 { fill: "dodgerblue", strokeWidth: 0, width: 4, height: 4 })
29 );
30 }
31
32 /**
33 * In addition to updating the standard "Rotating" Adornment, this updates a "MovingSpot"
34 * Adornment that the user may drag in order to move the {@link RotatingTool#rotationPoint}.
35 * @param {Part} part
36 */
37 updateAdornments(part: go.Part): void {
38 super.updateAdornments(part);
39 if (part === null) return;
40 if (part.isSelected && !this.diagram.isReadOnly) {
41 const rotateObj = part.rotateObject;
42 if (rotateObj !== null && part.canRotate() && part.actualBounds.isReal() &&
43 part.isVisible() && rotateObj.actualBounds.isReal() && rotateObj.isVisibleObject()) {
44 let ad = part.findAdornment("RotateSpot");
45 if (ad === null || ad.adornedObject !== rotateObj) {
46 ad = this._spotAdornmentTemplate.copy();
47 ad.adornedObject = part.rotateObject;
48 }
49 if (ad !== null) {
50 ad.location = this.computeRotationPoint(ad.adornedObject);
51 part.addAdornment("RotateSpot", ad);
52 return;
53 }
54 }
55 }
56 part.removeAdornment("RotateSpot");
57 };
58
59 /**
60 * Change the positioning of the "Rotating" Adornment to adapt to the rotation point
61 * potentially being well outside of the object being rotated.
62 *
63 * This assumes that {@link RotatingTool#handleAngle} is zero.
64 * @param {GraphObject} obj the object being rotated
65 * @returns Point in document coordinates
66 */
67 computeAdornmentLocation(obj: go.GraphObject): go.Point {
68 let p = this.rotationPoint;
69 if (!p.isReal()) p = this.computeRotationPoint(obj);
70 const q = obj.getLocalPoint(p);
71 //??? ignores this.handleAngle
72 q.x = Math.max(obj.naturalBounds.right, q.x) + this.handleDistance;
73 return obj.getDocumentPoint(q);
74 }
75
76 /**
77 * In addition to the standard behavior of {@link RotatingTool#canStart},
78 * also start when the user starts dragging the "MovingSpot" adornment/handle.
79 * @returns boolean
80 */
81 canStart(): boolean {
82 if (!this.isEnabled) return false;
83 const diagram = this.diagram;
84 if (diagram.isReadOnly) return false;
85 if (!diagram.allowRotate) return false;
86 if (!diagram.lastInput.left) return false;
87
88 let h = this.findToolHandleAt(diagram.firstInput.documentPoint, this.name);
89 if (h !== null) return true;
90
91 h = this.findToolHandleAt(diagram.firstInput.documentPoint, "RotateSpot");
92 return (h !== null);
93 }
94
95 /**
96 * @hidden @internal
97 */
98 doActivate(): void {
99 // might be dragging the spot handle instead of the rotate handle
100 this.handle = this.findToolHandleAt(this.diagram.firstInput.documentPoint, "RotateSpot");
101 if (this.handle !== null) {
102 const ad = this.handle.part as go.Adornment;
103 if (ad.adornedObject !== null) {
104 const part = ad.adornedPart;
105 if (part !== null) this._originalRotationSpot = part.rotationSpot;
106 }
107 }
108 // doActivate uses this.handle if it is set beforehand, rather than searching for a rotate handle
109 super.doActivate();
110 }
111
112 /**
113 * @hidden @internal
114 */
115 doCancel(): void {
116 if (this.adornedObject !== null) {
117 const part = this.adornedObject.part;
118 if (part !== null) {
119 part.rotationSpot = this._originalRotationSpot;
120 this.rotationPoint.set(this.computeRotationPoint(this.adornedObject));
121 this.updateAdornments(part);
122 }
123 }
124 super.doCancel();
125 }
126
127 /**
128 * @hidden @internal
129 */
130 doMouseMove(): void {
131 if (this.isActive) {
132 if (this.handle !== null && this.handle.part && this.handle.part.category === "RotateSpot") {
133 // modify part.rotationSpot and this.rotationPoint
134 this.shiftRotationPoint();
135 } else {
136 super.doMouseMove();
137 }
138 }
139 }
140
141 /**
142 * @hidden @internal
143 */
144 doMouseUp(): void {
145 if (this.isActive) {
146 if (this.handle !== null && this.handle.part && this.handle.part.category === "RotateSpot") {
147 // modify part.rotationSpot and this.rotationPoint
148 this.shiftRotationPoint();
149 this.transactionResult = "Shifted rotationSpot";
150 this.stopTool();
151 } else {
152 super.doMouseUp();
153 }
154 }
155 }
156
157 /**
158 * This is called by mouse moves and mouse up events when the handle being dragged is "MovingSpot".
159 * This needs to update the {@link Part#rotationSpot} and {@link RotatingTool#rotationPoint} properties.
160 *
161 * For each of the X and Y directions, when the handle is within the bounds of the rotated object,
162 * the new rotation Spot will be purely fractional; when it is outside the Spot will be limited to
163 * a fraction of zero or one (whichever is closer) and an absolute offset that places the rotation point
164 * where the handle is.
165 * @expose
166 */
167 shiftRotationPoint(): void {
168 const dp = this.diagram.lastInput.documentPoint;
169 const obj = this.adornedObject;
170 if (obj === null) return;
171 const w = obj.naturalBounds.width || 1; // disallow zero
172 const h = obj.naturalBounds.height || 1;
173 const part = obj.part;
174 if (part === null) return;
175 const op = obj.getLocalPoint(dp);
176 const fx = (op.x < 0) ? 0 : (op.x > w ? 1 : op.x/w);
177 const fy = (op.y < 0) ? 0 : (op.y > h ? 1 : op.y/h);
178 const ox = (op.x < 0) ? op.x : (op.x > w ? op.x-w : 0);
179 const oy = (op.y < 0) ? op.y : (op.y > h ? op.y-h : 0);
180 part.rotationSpot = new go.Spot(fx, fy, ox, oy);
181 this.rotationPoint.set(this.computeRotationPoint(obj));
182 this.updateAdornments(part);
183 }
184}