UNPKG

9.05 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 SectorReshapingTool class lets the user interactively modify the angles of a "pie"-shaped sector of a circle.
17 * When a node is selected, this shows two handles for changing the angles of the sides of the sector and one handle for changing the radius.
18 *
19 * This depends on there being three data properties, "angle", "sweep", and "radius",
20 * that hold the needed information to be able to reproduce the sector.
21 *
22 * If you want to experiment with this extension, try the <a href="../../extensionsJSM/SectorReshaping.html">Sector Reshaping</a> sample.
23 * @category Tool Extension
24 */
25export class SectorReshapingTool extends go.Tool {
26 private _handle: go.GraphObject | null = null;
27 private _originalRadius: number = 0;
28 private _originalAngle: number = 0;
29 private _originalSweep: number = 0;
30
31 private _radiusProperty: string = 'radius';
32 private _angleProperty: string = 'angle';
33 private _sweepProperty: string = 'sweep';
34
35 /**
36 * Constructs a SectorReshapingTool and sets the name for the tool.
37 */
38 constructor() {
39 super();
40 this.name = 'SectorReshaping';
41 }
42
43 /**
44 * Gets or sets the name of the data property for the sector radius.
45 *
46 * The default value is "radius".
47 */
48 get radiusProperty(): string { return this._radiusProperty; }
49 set radiusProperty(val: string) { this._radiusProperty = val; }
50
51 /**
52 * Gets or sets the name of the data property for the sector start angle.
53 *
54 * The default value is "angle".
55 */
56 get angleProperty(): string { return this._angleProperty; }
57 set angleProperty(val: string) { this._angleProperty = val; }
58
59 /**
60 * Gets or sets the name of the data property for the sector sweep angle.
61 *
62 * The default value is "sweep".
63 */
64 get sweepProperty(): string { return this._sweepProperty; }
65 set sweepProperty(val: string) { this._sweepProperty = val; }
66
67 /**
68 * This tool can only start if Diagram.allowReshape is true and the mouse-down event
69 * is at a tool handle created by this tool.
70 */
71 public canStart(): boolean {
72 if (!this.isEnabled) return false;
73 const diagram = this.diagram;
74 if (diagram.isReadOnly) return false;
75 if (!diagram.allowReshape) return false;
76 const h = this.findToolHandleAt(diagram.firstInput.documentPoint, this.name);
77 return (h !== null);
78 }
79
80 /**
81 * If the Part is selected, show two angle-changing tool handles and one radius-changing tool handle.
82 */
83 public updateAdornments(part: go.Part): void {
84 const data = part.data;
85 if (part.isSelected && data !== null && !this.diagram.isReadOnly) {
86 let ad = part.findAdornment(this.name);
87 if (ad === null) {
88 const $ = go.GraphObject.make;
89 ad =
90 $(go.Adornment, 'Spot',
91 $(go.Placeholder),
92 $(go.Shape, 'Diamond',
93 { name: 'RADIUS', fill: 'lime', width: 10, height: 10, cursor: 'move' },
94 new go.Binding('alignment', '', (d) => {
95 const angle = SectorReshapingTool.getAngle(d);
96 const sweep = SectorReshapingTool.getSweep(d);
97 const p = new go.Point(0.5, 0).rotate(angle + sweep / 2);
98 return new go.Spot(0.5 + p.x, 0.5 + p.y);
99 })),
100 $(go.Shape, 'Circle',
101 { name: 'ANGLE', fill: 'lime', width: 8, height: 8, cursor: 'move' },
102 new go.Binding('alignment', '', (d) => {
103 const angle = SectorReshapingTool.getAngle(d);
104 const p = new go.Point(0.5, 0).rotate(angle);
105 return new go.Spot(0.5 + p.x, 0.5 + p.y);
106 })),
107 $(go.Shape, 'Circle',
108 { name: 'SWEEP', fill: 'lime', width: 8, height: 8, cursor: 'move' },
109 new go.Binding('alignment', '', (d) => {
110 const angle = SectorReshapingTool.getAngle(d);
111 const sweep = SectorReshapingTool.getSweep(d);
112 const p = new go.Point(0.5, 0).rotate(angle + sweep);
113 return new go.Spot(0.5 + p.x, 0.5 + p.y);
114 }))
115 ) as go.Adornment;
116 ad.adornedObject = part.locationObject;
117 part.addAdornment(this.name, ad);
118 } else {
119 ad.location = part.position;
120 const ns = part.naturalBounds;
121 if (ad.placeholder !== null) ad.placeholder.desiredSize = new go.Size((ns.width) * part.scale, (ns.height) * part.scale);
122 ad.updateTargetBindings();
123 }
124 } else {
125 part.removeAdornment(this.name);
126 }
127 }
128
129 /**
130 * Remember the original angles and radius and start a transaction.
131 */
132 public doActivate(): void {
133 const diagram = this.diagram;
134 this._handle = this.findToolHandleAt(diagram.firstInput.documentPoint, this.name);
135 if (this._handle === null) return;
136 const part = (this._handle.part as go.Adornment).adornedPart;
137 if (part === null || part.data === null) return;
138
139 const data = part.data;
140 this._originalRadius = SectorReshapingTool.getRadius(data);
141 this._originalAngle = SectorReshapingTool.getAngle(data);
142 this._originalSweep = SectorReshapingTool.getSweep(data);
143
144 this.startTransaction(this.name);
145 this.isActive = true;
146 }
147
148 /**
149 * Stop the transaction.
150 */
151 public doDeactivate(): void {
152 this.stopTransaction();
153
154 this._handle = null;
155 this.isActive = false;
156 }
157
158 /**
159 * Restore the original angles and radius and then stop this tool.
160 */
161 public doCancel(): void {
162 if (this._handle !== null) {
163 const part = (this._handle.part as go.Adornment).adornedPart;
164 if (part !== null) {
165 const model = this.diagram.model;
166 model.setDataProperty(part.data, this._radiusProperty, this._originalRadius);
167 model.setDataProperty(part.data, this._angleProperty, this._originalAngle);
168 model.setDataProperty(part.data, this._sweepProperty, this._originalSweep);
169 }
170 }
171 this.stopTool();
172 }
173
174 /**
175 * Depending on the current handle being dragged, update the "radius", the "angle", or the "sweep"
176 * properties on the model data.
177 * Those property names are currently parameterized as static members of SectorReshapingTool.
178 */
179 public doMouseMove(): void {
180 const diagram = this.diagram;
181 const h = this._handle;
182 if (this.isActive && h !== null) {
183 const adorned = (h.part as go.Adornment).adornedObject;
184 if (adorned === null) return;
185 const center = adorned.getDocumentPoint(go.Spot.Center);
186 const node = adorned.part;
187 if (node === null || node.data === null) return;
188 const mouse = diagram.lastInput.documentPoint;
189 if (h.name === 'RADIUS') {
190 const dst = Math.sqrt(center.distanceSquaredPoint(mouse));
191 diagram.model.setDataProperty(node.data, this._radiusProperty, dst);
192 } else if (h.name === 'ANGLE') {
193 const dir = center.directionPoint(mouse);
194 diagram.model.setDataProperty(node.data, this._angleProperty, dir);
195 } else if (h.name === 'SWEEP') {
196 const dir = center.directionPoint(mouse);
197 const ang = SectorReshapingTool.getAngle(node.data);
198 let swp = (dir - ang + 360) % 360;
199 if (swp > 359) swp = 360; // make it easier to get a full circle
200 diagram.model.setDataProperty(node.data, this._sweepProperty, swp);
201 }
202 }
203 }
204
205 /**
206 * Finish the transaction and stop the tool.
207 */
208 public doMouseUp(): void {
209 const diagram = this.diagram;
210 if (this.isActive) {
211 this.transactionResult = this.name; // successful finish
212 }
213 this.stopTool();
214 }
215
216 // static functions for getting data
217 /** @hidden @internal */
218 public static getRadius(data: go.ObjectData): number {
219 let radius = data['radius'];
220 if (!(typeof radius === 'number') || isNaN(radius) || radius <= 0) radius = 50;
221 return radius;
222 }
223
224 /** @hidden @internal */
225 public static getAngle(data: go.ObjectData): number {
226 let angle = data['angle'];
227 if (!(typeof angle === 'number') || isNaN(angle)) angle = 0;
228 else angle = angle % 360;
229 return angle;
230 }
231
232 /** @hidden @internal */
233 public static getSweep(data: go.ObjectData): number {
234 let sweep = data['sweep'];
235 if (!(typeof sweep === 'number') || isNaN(sweep)) sweep = 360;
236 return sweep;
237 }
238}