1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | import * as go from '../release/go-module.js';
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | export class PolygonDrawingTool extends go.Tool {
|
30 | private _isPolygon: boolean = true;
|
31 | private _hasArcs: boolean = false;
|
32 | private _isOrthoOnly: boolean = false;
|
33 | private _isGridSnapEnabled: boolean = false;
|
34 | private _archetypePartData: go.ObjectData= {};
|
35 |
|
36 |
|
37 | private _temporaryShape: go.Shape = go.GraphObject.make(go.Shape, { name: 'SHAPE', fill: 'lightgray', strokeWidth: 1.5 });
|
38 |
|
39 | private temp: go.Part = go.GraphObject.make(go.Part, { layerName: 'Tool' }, this._temporaryShape);
|
40 |
|
41 | |
42 |
|
43 |
|
44 | constructor() {
|
45 | super();
|
46 | this.name = 'PolygonDrawing';
|
47 | }
|
48 |
|
49 | |
50 |
|
51 |
|
52 |
|
53 |
|
54 | get isPolygon(): boolean { return this._isPolygon; }
|
55 | set isPolygon(val: boolean) { this._isPolygon = val; }
|
56 |
|
57 |
|
58 | |
59 |
|
60 |
|
61 |
|
62 |
|
63 | get hasArcs(): boolean { return this._hasArcs; }
|
64 | set hasArcs(val: boolean) { this._hasArcs = val; }
|
65 |
|
66 | |
67 |
|
68 |
|
69 |
|
70 | get isOrthoOnly(): boolean { return this._isOrthoOnly; }
|
71 | set isOrthoOnly(val: boolean) { this._isOrthoOnly = val; }
|
72 |
|
73 | |
74 |
|
75 |
|
76 |
|
77 | get isGridSnapEnabled(): boolean { return this._isGridSnapEnabled; }
|
78 | set isGridSnapEnabled(val: boolean) { this._isGridSnapEnabled = val; }
|
79 |
|
80 | |
81 |
|
82 |
|
83 |
|
84 | get archetypePartData(): go.ObjectData { return this._archetypePartData; }
|
85 | set archetypePartData(val: go.ObjectData) { this._archetypePartData = val; }
|
86 |
|
87 | |
88 |
|
89 |
|
90 |
|
91 |
|
92 | get temporaryShape(): go.Shape { return this._temporaryShape; }
|
93 | set temporaryShape(val: go.Shape) {
|
94 | if (this._temporaryShape !== val && val !== null) {
|
95 | val.name = 'SHAPE';
|
96 | const panel = this._temporaryShape.panel;
|
97 | if (panel !== null) {
|
98 | if (panel !== null) panel.remove(this._temporaryShape);
|
99 | this._temporaryShape = val;
|
100 | if (panel !== null) panel.add(this._temporaryShape);
|
101 | }
|
102 | }
|
103 | }
|
104 |
|
105 | |
106 |
|
107 |
|
108 |
|
109 |
|
110 | public canStart(): boolean {
|
111 | if (!this.isEnabled) return false;
|
112 | const diagram = this.diagram;
|
113 | if (diagram.isReadOnly || diagram.isModelReadOnly) return false;
|
114 | const model = diagram.model;
|
115 | if (model === null) return false;
|
116 |
|
117 | if (!diagram.firstInput.left) return false;
|
118 |
|
119 | const obj = diagram.findObjectAt(diagram.firstInput.documentPoint, null, null);
|
120 | return (obj === null);
|
121 | }
|
122 |
|
123 | |
124 |
|
125 |
|
126 |
|
127 |
|
128 | public doStart() {
|
129 | super.doStart();
|
130 | var diagram = this.diagram;
|
131 | if (!diagram) return;
|
132 | this.startTransaction(this.name);
|
133 | diagram.currentCursor = diagram.defaultCursor = "crosshair";
|
134 | if (!diagram.lastInput.isTouchEvent) diagram.isMouseCaptured = true;
|
135 | }
|
136 |
|
137 | |
138 |
|
139 |
|
140 |
|
141 | public doActivate(): void {
|
142 | super.doActivate();
|
143 | var diagram = this.diagram;
|
144 | if (!diagram) return;
|
145 |
|
146 | if (!diagram.lastInput.isTouchEvent) this.addPoint(diagram.lastInput.documentPoint);
|
147 | }
|
148 |
|
149 | |
150 |
|
151 |
|
152 | public doStop(): void {
|
153 | super.doStop();
|
154 | var diagram = this.diagram;
|
155 | if (!diagram) return;
|
156 | diagram.currentCursor = diagram.defaultCursor = "auto";
|
157 | if (this.temporaryShape !== null && this.temporaryShape.part !== null) {
|
158 | diagram.remove(this.temporaryShape.part);
|
159 | }
|
160 | if (diagram.isMouseCaptured) diagram.isMouseCaptured = false;
|
161 | this.stopTransaction();
|
162 | }
|
163 |
|
164 | |
165 |
|
166 |
|
167 |
|
168 | public modifyPointForGrid(p: go.Point): go.Point {
|
169 | const pregrid = p.copy();
|
170 | const grid = this.diagram.grid;
|
171 | if (grid !== null && grid.visible && this.isGridSnapEnabled) {
|
172 | const cell = grid.gridCellSize;
|
173 | const orig = grid.gridOrigin;
|
174 | p = p.copy();
|
175 | p.snapToGrid(orig.x, orig.y, cell.width, cell.height);
|
176 | }
|
177 | if (this.temporaryShape.geometry === null) return p;
|
178 | const geometry = this.temporaryShape.geometry;
|
179 | if (geometry === null) return p;
|
180 | const fig = geometry.figures.first();
|
181 | if (fig === null) return p;
|
182 | const segments = fig.segments;
|
183 | if (this.isOrthoOnly && segments.count > 0) {
|
184 | let lastPt = new go.Point(fig.startX, fig.startY);
|
185 | if (segments.count > 1) {
|
186 |
|
187 | const secondLastSegment = (segments.elt(segments.count - 2));
|
188 | lastPt = new go.Point(secondLastSegment.endX, secondLastSegment.endY);
|
189 | }
|
190 | if (pregrid.distanceSquared(lastPt.x, pregrid.y) < pregrid.distanceSquared(pregrid.x, lastPt.y)) {
|
191 | return new go.Point(lastPt.x, p.y);
|
192 | } else {
|
193 | return new go.Point(p.x, lastPt.y);
|
194 | }
|
195 | }
|
196 | return p;
|
197 | }
|
198 |
|
199 |
|
200 | |
201 |
|
202 |
|
203 |
|
204 | public addPoint(p: go.Point): void {
|
205 | const diagram = this.diagram;
|
206 | const shape = this.temporaryShape;
|
207 | if (shape === null) return;
|
208 |
|
209 |
|
210 | const viewpt = diagram.viewportBounds.position;
|
211 | const q = this.modifyPointForGrid(new go.Point(p.x - viewpt.x, p.y - viewpt.y));
|
212 |
|
213 | const part = shape.part;
|
214 | let geo: go.Geometry | null = null;
|
215 |
|
216 | if (part !== null && part.diagram === null) {
|
217 | const fig = new go.PathFigure(q.x, q.y, true);
|
218 | geo = new go.Geometry().add(fig);
|
219 | this.temporaryShape.geometry = geo;
|
220 |
|
221 | part.position = viewpt.copy().offset(-shape.strokeWidth / 2, -shape.strokeWidth / 2);
|
222 | diagram.add(part);
|
223 | } else if (shape.geometry !== null) {
|
224 |
|
225 | geo = shape.geometry.copy();
|
226 | const fig = geo.figures.first();
|
227 | if (fig !== null) {
|
228 | if (this.hasArcs) {
|
229 | const lastseg = fig.segments.last();
|
230 | if (lastseg === null) {
|
231 | fig.add(new go.PathSegment(go.PathSegment.QuadraticBezier, q.x, q.y, (fig.startX + q.x) / 2, (fig.startY + q.y) / 2));
|
232 | } else {
|
233 | fig.add(new go.PathSegment(go.PathSegment.QuadraticBezier, q.x, q.y, (lastseg.endX + q.x) / 2, (lastseg.endY + q.y) / 2));
|
234 | }
|
235 | } else {
|
236 | fig.add(new go.PathSegment(go.PathSegment.Line, q.x, q.y));
|
237 | }
|
238 | }
|
239 | }
|
240 | shape.geometry = geo;
|
241 | }
|
242 |
|
243 | |
244 |
|
245 |
|
246 |
|
247 | public moveLastPoint(p: go.Point): void {
|
248 | p = this.modifyPointForGrid(p);
|
249 | const diagram = this.diagram;
|
250 |
|
251 | const shape = this.temporaryShape;
|
252 | if (shape.geometry === null) return;
|
253 | const geo = shape.geometry.copy();
|
254 | const fig = geo.figures.first();
|
255 | if (fig === null) return;
|
256 | const segs = fig.segments;
|
257 | if (segs.count > 0) {
|
258 |
|
259 | const viewpt = diagram.viewportBounds.position;
|
260 | const seg = segs.elt(segs.count - 1);
|
261 |
|
262 | seg.endX = p.x - viewpt.x;
|
263 | seg.endY = p.y - viewpt.y;
|
264 | if (seg.type === go.PathSegment.QuadraticBezier) {
|
265 | let prevx = 0.0;
|
266 | let prevy = 0.0;
|
267 | if (segs.count > 1) {
|
268 | const prevseg = segs.elt(segs.count - 2);
|
269 | prevx = prevseg.endX;
|
270 | prevy = prevseg.endY;
|
271 | } else {
|
272 | prevx = fig.startX;
|
273 | prevy = fig.startY;
|
274 | }
|
275 | seg.point1X = (seg.endX + prevx) / 2;
|
276 | seg.point1Y = (seg.endY + prevy) / 2;
|
277 | }
|
278 | shape.geometry = geo;
|
279 | }
|
280 | }
|
281 |
|
282 | |
283 |
|
284 |
|
285 |
|
286 | public removeLastPoint(): void {
|
287 |
|
288 | const shape = this.temporaryShape;
|
289 | if (shape.geometry === null) return;
|
290 | const geo = shape.geometry.copy();
|
291 | const fig = geo.figures.first();
|
292 | if (fig === null) return;
|
293 | const segs = fig.segments;
|
294 | if (segs.count > 0) {
|
295 | segs.removeAt(segs.count - 1);
|
296 | shape.geometry = geo;
|
297 | }
|
298 | }
|
299 |
|
300 | |
301 |
|
302 |
|
303 |
|
304 | public finishShape(): void {
|
305 | const diagram = this.diagram;
|
306 | const shape: go.Shape = this.temporaryShape;
|
307 | if (shape !== null && this.archetypePartData !== null) {
|
308 |
|
309 | if (!diagram.lastInput.isTouchEvent) this.removeLastPoint();
|
310 | const tempgeo = shape.geometry;
|
311 |
|
312 | if (tempgeo !== null) {
|
313 | const tempfig = tempgeo.figures.first();
|
314 | if (tempfig !== null && tempfig.segments.count >= (this.isPolygon ? 2 : 1)) {
|
315 |
|
316 | const viewpt = diagram.viewportBounds.position;
|
317 | const copygeo = tempgeo.copy();
|
318 | const copyfig = copygeo.figures.first();
|
319 | if (this.isPolygon && copyfig !== null) {
|
320 |
|
321 | const segs = copyfig.segments;
|
322 | const seg = segs.elt(segs.count - 1);
|
323 | seg.isClosed = true;
|
324 | }
|
325 |
|
326 | const d = diagram.model.copyNodeData(this.archetypePartData);
|
327 | if (d !== null) {
|
328 |
|
329 | diagram.model.addNodeData(d);
|
330 | const part = diagram.findPartForData(d);
|
331 | if (part !== null) {
|
332 |
|
333 | const pos = copygeo.normalize();
|
334 | pos.x = viewpt.x - pos.x - shape.strokeWidth / 2;
|
335 | pos.y = viewpt.y - pos.y - shape.strokeWidth / 2;
|
336 | part.position = pos;
|
337 |
|
338 | const pShape: go.Shape = part.findObject('SHAPE') as go.Shape;
|
339 | if (pShape !== null) pShape.geometry = copygeo;
|
340 | this.transactionResult = this.name;
|
341 | }
|
342 | }
|
343 | }
|
344 | }
|
345 | }
|
346 | this.stopTool();
|
347 | }
|
348 |
|
349 | |
350 |
|
351 |
|
352 | public doMouseDown(): void {
|
353 | const diagram = this.diagram;
|
354 | if (!this.isActive) {
|
355 | this.doActivate();
|
356 | }
|
357 |
|
358 | this.addPoint(diagram.lastInput.documentPoint);
|
359 | if (!diagram.lastInput.left) {
|
360 | this.finishShape();
|
361 | } else if (diagram.lastInput.clickCount > 1) {
|
362 | this.removeLastPoint();
|
363 | this.finishShape();
|
364 | }
|
365 | }
|
366 |
|
367 | |
368 |
|
369 |
|
370 | public doMouseMove(): void {
|
371 | const diagram = this.diagram;
|
372 | if (this.isActive) {
|
373 | this.moveLastPoint(diagram.lastInput.documentPoint);
|
374 | }
|
375 | }
|
376 |
|
377 | |
378 |
|
379 |
|
380 | public doMouseUp(): void {
|
381 |
|
382 | }
|
383 |
|
384 | |
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 |
|
392 | public doKeyDown(): void {
|
393 | const diagram = this.diagram;
|
394 | if (!this.isActive) return;
|
395 | const e = diagram.lastInput;
|
396 | if (e.key === '\r') {
|
397 | this.finishShape();
|
398 | } else if (e.key === 'Z') {
|
399 | this.undo();
|
400 | } else {
|
401 | super.doKeyDown();
|
402 | }
|
403 | }
|
404 |
|
405 | |
406 |
|
407 |
|
408 | public undo(): void {
|
409 | const diagram = this.diagram;
|
410 |
|
411 | this.removeLastPoint();
|
412 | const lastInput = diagram.lastInput;
|
413 | if (lastInput.event instanceof MouseEvent) this.moveLastPoint(lastInput.documentPoint);
|
414 | }
|
415 | }
|