UNPKG

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