@syncfusion/ej2-image-editor 30.2.5 → 31.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/ej2-image-editor.umd.min.js +2 -2
  2. package/dist/ej2-image-editor.umd.min.js.map +1 -1
  3. package/dist/es6/ej2-image-editor.es2015.js +7 -39
  4. package/dist/es6/ej2-image-editor.es2015.js.map +1 -1
  5. package/dist/es6/ej2-image-editor.es5.js +7 -39
  6. package/dist/es6/ej2-image-editor.es5.js.map +1 -1
  7. package/dist/global/ej2-image-editor.min.js +2 -2
  8. package/dist/global/ej2-image-editor.min.js.map +1 -1
  9. package/dist/global/index.d.ts +1 -1
  10. package/dist/ts/image-editor/action/crop.d.ts +44 -0
  11. package/dist/ts/image-editor/action/crop.ts +867 -0
  12. package/dist/ts/image-editor/action/draw.d.ts +187 -0
  13. package/dist/ts/image-editor/action/draw.ts +4924 -0
  14. package/dist/ts/image-editor/action/export.d.ts +29 -0
  15. package/dist/ts/image-editor/action/export.ts +509 -0
  16. package/dist/ts/image-editor/action/filter.d.ts +48 -0
  17. package/dist/ts/image-editor/action/filter.ts +872 -0
  18. package/dist/ts/image-editor/action/freehand-draw.d.ts +68 -0
  19. package/dist/ts/image-editor/action/freehand-draw.ts +1135 -0
  20. package/dist/ts/image-editor/action/index.d.ts +9 -0
  21. package/dist/ts/image-editor/action/index.ts +9 -0
  22. package/dist/ts/image-editor/action/selection.d.ts +178 -0
  23. package/dist/ts/image-editor/action/selection.ts +5241 -0
  24. package/dist/ts/image-editor/action/shape.d.ts +130 -0
  25. package/dist/ts/image-editor/action/shape.ts +3917 -0
  26. package/dist/ts/image-editor/action/transform.d.ts +77 -0
  27. package/dist/ts/image-editor/action/transform.ts +2008 -0
  28. package/dist/ts/image-editor/action/undo-redo.d.ts +52 -0
  29. package/dist/ts/image-editor/action/undo-redo.ts +1169 -0
  30. package/dist/ts/image-editor/base/enum.d.ts +277 -0
  31. package/dist/ts/image-editor/base/enum.ts +288 -0
  32. package/dist/ts/image-editor/base/image-editor-model.d.ts +770 -0
  33. package/dist/ts/image-editor/base/image-editor.d.ts +1928 -0
  34. package/dist/ts/image-editor/base/image-editor.ts +5496 -0
  35. package/dist/ts/image-editor/base/index.d.ts +4 -0
  36. package/dist/ts/image-editor/base/index.ts +4 -0
  37. package/dist/ts/image-editor/base/interface.d.ts +1637 -0
  38. package/dist/ts/image-editor/base/interface.ts +1709 -0
  39. package/dist/ts/image-editor/index.d.ts +3 -0
  40. package/dist/ts/image-editor/index.ts +3 -0
  41. package/dist/ts/image-editor/renderer/index.d.ts +1 -0
  42. package/dist/ts/image-editor/renderer/index.ts +1 -0
  43. package/dist/ts/image-editor/renderer/toolbar.d.ts +171 -0
  44. package/dist/ts/image-editor/renderer/toolbar.ts +6356 -0
  45. package/dist/ts/index.d.ts +4 -0
  46. package/dist/ts/index.ts +4 -0
  47. package/package.json +47 -15
  48. package/src/image-editor/action/export.js +1 -1
  49. package/src/image-editor/action/freehand-draw.d.ts +0 -1
  50. package/src/image-editor/action/freehand-draw.js +3 -25
  51. package/src/image-editor/action/undo-redo.js +3 -13
@@ -0,0 +1,1135 @@
1
+ import { EventHandler, extend, isNullOrUndefined } from '@syncfusion/ej2-base';
2
+ import { ActivePoint } from '@syncfusion/ej2-inputs';
3
+ import { FreehandDraw, Point, SelectionPoint, ShapeChangeEventArgs, ShapeSettings, ShapeType, StrokeSettings } from '../index';
4
+ import { CurrentObject, ImageDimension, ImageEditor } from '../index';
5
+
6
+ export class FreehandDrawing {
7
+ private parent: ImageEditor;
8
+ private lowerContext: CanvasRenderingContext2D;
9
+ private upperContext: CanvasRenderingContext2D;
10
+ private fhdObj: FreehandDraw = {lastWidth: 0, lastVelocity: 0, time: 0, pointX: 0, pointY: 0};
11
+ private isFreehandDrawing: boolean = false;
12
+ private freehandDownPoint: Point = {x: 0, y: 0};
13
+ private isFreehandPointMoved: boolean = false;
14
+ private fhdHovIdx: number; // Specifies current hovered index of freehand drawing
15
+ private pointCounter: number = 0;
16
+ // eslint-disable-next-line
17
+ private selPointColl: any = {};
18
+ private penStrokeWidth: number;
19
+ private currFHDIdx: number = 0; // Specifies id for every freehand drawing - uses while deleting
20
+ private selPoints: Point[] = [];
21
+ private dummyPoints: Point[] = [];
22
+ private fhdSelID: string;
23
+ private tempFHDStyles: StrokeSettings = {strokeColor: null, fillColor: null, strokeWidth: null};
24
+ private fhdSelIdx: number;
25
+ private straightenPoint: Point = {x: null, y: null, ratioX: null, ratioY: null };
26
+ private prevStraightenObj: SelectionPoint;
27
+ private straightenPointAngle: number = 0;
28
+ private isMasking: boolean = false;
29
+
30
+ constructor(parent: ImageEditor) {
31
+ this.parent = parent;
32
+ this.addEventListener();
33
+ }
34
+
35
+ public destroy(): void {
36
+ if (this.parent.isDestroyed) { return; }
37
+ this.removeEventListener();
38
+ }
39
+
40
+ private addEventListener(): void {
41
+ this.parent.on('freehand-draw', this.draw, this);
42
+ this.parent.on('destroyed', this.destroy, this);
43
+ }
44
+
45
+ private removeEventListener(): void {
46
+ this.parent.off('freehand-draw', this.draw);
47
+ this.parent.off('destroyed', this.destroy);
48
+ }
49
+
50
+ private draw(args?: { onPropertyChange: boolean, prop: string, value?: object }): void {
51
+ this.updateFhdPvtVar();
52
+ switch (args.prop) {
53
+ case 'hoverFhd': {
54
+ this.hoverFhd(args.value['strokeColor'], args.value['strokeWidth']);
55
+ break;
56
+ }
57
+ case 'freehandDownHandler':
58
+ this.freehandDownHandler(args.value['e'], args.value['canvas']);
59
+ break;
60
+ case 'freehandUpHandler':
61
+ this.freehandUpHandler(args.value['e'], args.value['canvas'], args.value['context']);
62
+ break;
63
+ case 'handle-freehand-draw': {
64
+ const id: number = parseInt(args.value['id'].split('_')[1], 10) - 1;
65
+ if (this.isFHDIdx(id)) {
66
+ this.deleteFhd(id, true);
67
+ }
68
+ break;
69
+ }
70
+ case 'freehandRedraw':
71
+ this.freehandRedraw(args.value['context'], args.value['points']);
72
+ break;
73
+ case 'deleteFhd': {
74
+ const id: number = parseInt(args.value['id'].split('_')[1], 10) - 1;
75
+ this.deleteFhd(id, true);
76
+ break;
77
+ }
78
+ case 'selectFhd': {
79
+ let id: number = null;
80
+ if (args.value['id']) {
81
+ id = parseInt(args.value['id'].split('_')[1], 10) - 1;
82
+ }
83
+ this.selectFhd(id);
84
+ break;
85
+ }
86
+ case 'applyFhd':
87
+ this.applyFhd();
88
+ break;
89
+ case 'cancelFhd':
90
+ this.cancelFhd();
91
+ break;
92
+ case 'updateFHDCurPts':
93
+ this.updateFHDCurPts();
94
+ break;
95
+ case 'rotateFhdColl':
96
+ this.rotateFhdColl();
97
+ break;
98
+ case 'flipFHDColl':
99
+ this.flipFHDColl(args.value['value']);
100
+ break;
101
+ case 'panFHDColl':
102
+ this.panFHDColl(args.value['xDiff'], args.value['yDiff'], args.value['panRegion']);
103
+ break;
104
+ case 'updateFHDColl':
105
+ if (args.value && args.value['isPreventApply']) {
106
+ this.updateFHDColl(args.value['isPreventApply']);
107
+ } else {
108
+ this.updateFHDColl();
109
+ }
110
+ break;
111
+ case 'zoomFHDColl':
112
+ this.zoomFHDColl(args.value['isPreventApply']);
113
+ break;
114
+ case 'apply-pen-draw':
115
+ this.applyPenDraw();
116
+ break;
117
+ case 'freeHandDraw':
118
+ this.freeHandDraw(args.value['value']);
119
+ break;
120
+ case 'isFHDIdx':
121
+ this.isFHDIdx(args.value['index'], args.value['obj']);
122
+ break;
123
+ case 'getSqPtFD':
124
+ this.getSqPtFD(args.value['idx'], args.value['obj']);
125
+ break;
126
+ case 'getSelPointColl':
127
+ args.value['obj']['selPointColl'] = extend([], this.selPointColl);
128
+ break;
129
+ case 'setSelPointColl':
130
+ this.selPointColl = extend([], args.value['obj']['selPointColl']);
131
+ break;
132
+ case 'pushSelPointColl':
133
+ this.selPointColl.push(extend([], args.value['obj']['selPointColl']));
134
+ break;
135
+ case 'setFreehandDrawHoveredIndex':
136
+ this.fhdHovIdx = args.value['index'];
137
+ break;
138
+ case 'getFreehandDrawHoveredIndex':
139
+ args.value['obj']['index'] = this.fhdHovIdx;
140
+ break;
141
+ case 'setPointCounter':
142
+ this.pointCounter = args.value['value'];
143
+ break;
144
+ case 'getPenStrokeWidth':
145
+ args.value['obj']['penStrokeWidth'] = this.penStrokeWidth;
146
+ break;
147
+ case 'setPenStrokeWidth':
148
+ this.penStrokeWidth = args.value['value'];
149
+ break;
150
+ case 'getCurrentFreehandDrawIndex':
151
+ args.value['obj']['currentFreehandDrawIndex'] = this.currFHDIdx;
152
+ break;
153
+ case 'setCurrentFreehandDrawIndex':
154
+ this.currFHDIdx = args.value['value'];
155
+ break;
156
+ case 'updateCropPtsForSel':
157
+ this.updateCropPtsForSel();
158
+ break;
159
+ case 'getFreehandDrawSelectedId':
160
+ args.value['obj']['freehandDrawSelectedId'] = this.fhdSelID;
161
+ break;
162
+ case 'resetFreehandDrawSelectedId':
163
+ this.fhdSelID = null;
164
+ break;
165
+ case 'getTempFreeHandDrawEditingStyles':
166
+ args.value['obj']['tempFreeHandDrawEditingStyles'] = this.tempFHDStyles;
167
+ break;
168
+ case 'setFreehandSelectedIndex':
169
+ this.fhdSelIdx = args.value['index'];
170
+ break;
171
+ case 'getFreehandSelectedIndex':
172
+ args.value['obj']['freehandSelectedIndex'] = this.fhdSelIdx;
173
+ break;
174
+ case 'setCenterSelPoints':
175
+ this.setCenterSelPoints();
176
+ break;
177
+ case 'getStraightenPoint':
178
+ args.value['obj']['straightenPoint'] = extend({}, this.straightenPoint, {}, true);
179
+ break;
180
+ case 'setStraightenPoint':
181
+ this.straightenPoint.x = args.value['x'];
182
+ this.straightenPoint.y = args.value['y'];
183
+ if (args.value['ratioX'] && args.value['ratioY']) {
184
+ this.straightenPoint.ratioX = args.value['ratioX'];
185
+ this.straightenPoint.ratioY = args.value['ratioY'];
186
+ }
187
+ break;
188
+ case 'resetStraightenPoint':
189
+ this.straightenPoint = {x: null, y: null, ratioX: null, ratioY: null };
190
+ this.prevStraightenObj = null; this.straightenPointAngle = 0;
191
+ break;
192
+ case 'getStraightenPointAngle':
193
+ args.value['obj']['angle'] = this.straightenPointAngle;
194
+ break;
195
+ case 'reset':
196
+ this.reset();
197
+ break;
198
+ case 'triggerShapeChanging':
199
+ this.triggerShapeChanging(args.value['shapeChangingArgs']);
200
+ break;
201
+ case 'setMasking':
202
+ this.isMasking = args.value['value'];
203
+ break;
204
+ case 'resetSelPoints':
205
+ this.selPoints = [];
206
+ break;
207
+ }
208
+ }
209
+
210
+ private updateFhdPvtVar(): void {
211
+ const parent: ImageEditor = this.parent;
212
+ if (parent.lowerCanvas) {this.lowerContext = parent.lowerCanvas.getContext('2d'); }
213
+ if (parent.upperCanvas) {this.upperContext = parent.upperCanvas.getContext('2d'); }
214
+ }
215
+
216
+ private reset(): void {
217
+ this.fhdObj = {lastWidth: 0, lastVelocity: 0, time: 0, pointX: 0, pointY: 0};
218
+ this.isFreehandDrawing = this.isFreehandPointMoved = false; this.selPoints = []; this.dummyPoints = [];
219
+ this.freehandDownPoint = {x: 0, y: 0}; this.selPointColl = {}; this.straightenPointAngle = 0;
220
+ this.fhdHovIdx = null; this.pointCounter = 0; this.fhdSelID = null; this.isMasking = false;
221
+ this.penStrokeWidth = undefined; this.currFHDIdx = 0; this.fhdSelIdx = null;
222
+ this.tempFHDStyles = {strokeColor: null, fillColor: null, strokeWidth: null};
223
+ this.straightenPoint = {x: null, y: null, ratioX: null, ratioY: null }; this.prevStraightenObj = null;
224
+ }
225
+
226
+ public getModuleName(): string {
227
+ return 'freehand-draw';
228
+ }
229
+
230
+ private hoverFhd(fillStyle?: string, strokeWidth?: number): void {
231
+ const parent: ImageEditor = this.parent;
232
+ const context: CanvasRenderingContext2D = this.upperContext;
233
+ let idx: number = -1;
234
+ if (this.fhdHovIdx > -1) {idx = this.fhdHovIdx; }
235
+ else {idx = this.fhdSelIdx; }
236
+ parent.points = extend([], parent.pointColl[idx as number].points) as Point[];
237
+ this.pointCounter = 0;
238
+ const len: number = parent.points.length;
239
+ let controlPoint1: Point; let controlPoint2: Point; let startPoint: Point; let endPoint: Point;
240
+ let minStrokeWidth: number = 0; let maxStrokeWidth: number = 0;
241
+ context.fillStyle = fillStyle ? fillStyle : parent.pointColl[idx as number].strokeColor;
242
+ context.strokeStyle = context.fillStyle;
243
+ minStrokeWidth = maxStrokeWidth = this.penStrokeWidth = strokeWidth ?
244
+ strokeWidth : parent.pointColl[idx as number].strokeWidth;
245
+ if (len === 1) {
246
+ controlPoint1 = controlPoint2 = startPoint = endPoint = parent.points[0];
247
+ this.startDraw(context, controlPoint1, controlPoint2, startPoint, endPoint, minStrokeWidth, maxStrokeWidth);
248
+ }
249
+ for (let l: number = 0; l < len - 3; l++) {
250
+ if (parent.points[l + 1] && parent.points[l + 2] && parent.points[l + 2]) {
251
+ controlPoint1 = (this.calcCurveCP(parent.points[l + 0], parent.points[l + 1],
252
+ parent.points[l + 2])).controlPoint2;
253
+ controlPoint2 = (this.calcCurveCP(parent.points[l + 1], parent.points[l + 2],
254
+ parent.points[l + 3])).controlPoint1;
255
+ if (l === 0) {
256
+ startPoint = parent.points[l as number];
257
+ } else {
258
+ startPoint = parent.points[l + 1];
259
+ }
260
+ endPoint = parent.points[l + 2];
261
+ this.startDraw(context, controlPoint1, controlPoint2, startPoint, endPoint, minStrokeWidth, maxStrokeWidth);
262
+ }
263
+ }
264
+ context.closePath();
265
+ // Outer selection
266
+ const point: ActivePoint = this.getSqPtFD(idx);
267
+ const tempLineWidth: number = context.lineWidth;
268
+ context.lineWidth = 2;
269
+ context.strokeStyle = parent.themeColl[parent.theme]['primaryColor'];
270
+ context.beginPath();
271
+ context.rect(point.startX, point.startY, point.width, point.height);
272
+ context.stroke(); context.closePath();
273
+ context.lineWidth = tempLineWidth;
274
+ }
275
+
276
+ private freehandDownHandler(e: MouseEvent & TouchEvent, canvas: HTMLCanvasElement): void {
277
+ const parent: ImageEditor = this.parent;
278
+ parent.lowerCanvas = document.querySelector('#' + parent.element.id + '_lowerCanvas');
279
+ this.lowerContext = parent.lowerCanvas.getContext('2d');
280
+ parent.upperCanvas = document.querySelector('#' + parent.element.id + '_upperCanvas');
281
+ this.upperContext = parent.upperCanvas.getContext('2d');
282
+ this.fhdObj.time = new Date().getTime();
283
+ this.isFreehandDrawing = true;
284
+ if (e.type === 'mousedown') {this.freehandDownPoint = {x: e.clientX, y: e.clientY}; }
285
+ else {this.freehandDownPoint = {x: e.touches[0].clientX, y: e.touches[0].clientY }; }
286
+ this.isFreehandPointMoved = false;
287
+ EventHandler.add(canvas, 'mousemove touchmove', this.freehandMoveHandler, this);
288
+ const shapeSettings: ShapeSettings = {id: 'pen_' + (this.currFHDIdx + 1), type: ShapeType.FreehandDraw,
289
+ startX: this.freehandDownPoint.x, startY: this.freehandDownPoint.y,
290
+ strokeColor: parent.activeObj.strokeSettings.strokeColor, strokeWidth: this.penStrokeWidth,
291
+ points: null, index: parent.objColl.length + parent.freehandCounter + 1 };
292
+ const shapeChangingArgs: ShapeChangeEventArgs = {cancel: false, action: 'draw-start', previousShapeSettings: shapeSettings,
293
+ currentShapeSettings: shapeSettings};
294
+ this.triggerShapeChanging(shapeChangingArgs);
295
+ }
296
+
297
+ private freehandUpHandler(e: MouseEvent & TouchEvent, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D): void {
298
+ const rect: DOMRect = canvas.getBoundingClientRect() as DOMRect;
299
+ const parent: ImageEditor = this.parent;
300
+ EventHandler.remove(canvas, 'mousemove touchmove', this.freehandMoveHandler);
301
+ if (parent.points.length === 0) {
302
+ if (e.type === 'mouseup') {this.processPoint(e.clientX - rect.left, e.clientY - rect.top, true, context); }
303
+ else if (e.type === 'touchend' && e.changedTouches) {
304
+ this.processPoint(e.changedTouches[0].clientX - rect.left, e.changedTouches[0].clientY - rect.top, true, context);
305
+ } else {
306
+ if (!this.isFreehandPointMoved) {
307
+ this.processPoint(this.freehandDownPoint.x - rect.left, this.freehandDownPoint.y - rect.top, true, context);
308
+ }
309
+ }
310
+ }
311
+ context.closePath();
312
+ const prevCropObj: CurrentObject = extend({}, parent.cropObj, {}, true) as CurrentObject;
313
+ const object: Object = {currObj: {} as CurrentObject };
314
+ parent.notify('filter', { prop: 'getCurrentObj', onPropertyChange: false, value: {object: object }});
315
+ const prevObj: CurrentObject = object['currObj'];
316
+ prevObj.objColl = extend([], parent.objColl, [], true) as SelectionPoint[];
317
+ prevObj.pointColl = extend([], parent.pointColl, [], true) as Point[];
318
+ prevObj.afterCropActions = extend([], parent.afterCropActions, [], true) as string[];
319
+ const selPointCollObj: Object = {selPointColl: null };
320
+ parent.notify('freehand-draw', { prop: 'getSelPointColl', onPropertyChange: false, value: {obj: selPointCollObj }});
321
+ prevObj.selPointColl = extend([], selPointCollObj['selPointColl'], [], true) as Point[];
322
+ const fhCnt: number = parent.freehandCounter;
323
+ const order: number = parent.objColl.length + parent.freehandCounter + 1;
324
+ parent.pointColl[fhCnt as number] = { points: extend([], parent.points), strokeColor: parent.activeObj.strokeSettings.strokeColor,
325
+ strokeWidth: this.penStrokeWidth, flipState: parent.transform.currFlipState,
326
+ id: 'pen_' + (this.currFHDIdx + 1), order: order};
327
+ parent.points = []; this.dummyPoints = [];
328
+ this.selPointColl[fhCnt as number] = { points: extend([], this.selPoints) };
329
+ this.selPoints = []; this.pointCounter = 0;
330
+ parent.freehandCounter++;
331
+ this.isFreehandDrawing = false;
332
+ if (!parent.isMaskImage) {
333
+ parent.notify('undo-redo', { prop: 'updateUndoRedoColl', onPropertyChange: false,
334
+ value: {operation: 'freehand-draw', previousObj: prevObj, previousObjColl: prevObj.objColl,
335
+ previousPointColl: prevObj.pointColl, previousSelPointColl: prevObj.selPointColl,
336
+ previousCropObj: prevCropObj, previousText: null,
337
+ currentText: null, previousFilter: null, isCircleCrop: null}});
338
+ }
339
+ const shapeSettings: ShapeSettings = {id: 'pen_' + (this.currFHDIdx + 1), type: ShapeType.FreehandDraw,
340
+ startX: this.freehandDownPoint.x, startY: this.freehandDownPoint.y,
341
+ strokeColor: parent.activeObj.strokeSettings.strokeColor, strokeWidth: this.penStrokeWidth,
342
+ points: parent.pointColl[this.currFHDIdx].points, index: order };
343
+ const shapeChangingArgs: ShapeChangeEventArgs = {cancel: false, action: 'draw-end', previousShapeSettings: shapeSettings,
344
+ currentShapeSettings: shapeSettings};
345
+ this.triggerShapeChanging(shapeChangingArgs);
346
+ this.currFHDIdx++;
347
+ }
348
+
349
+ private freehandMoveHandler(e: MouseEvent & TouchEvent): void {
350
+ this.isFreehandPointMoved = true;
351
+ const rect: DOMRect = this.parent.upperCanvas.getBoundingClientRect() as DOMRect;
352
+ let x: number; let y: number;
353
+ if (e.type === 'mousemove') {
354
+ x = e.clientX - rect.left; y = e.clientY - rect.top;
355
+ } else {
356
+ x = e.touches[0].clientX - rect.left; y = e.touches[0].clientY - rect.top;
357
+ }
358
+ if (this.isFreehandDrawing) {
359
+ this.upperContext.fillStyle = this.parent.activeObj.strokeSettings.strokeColor;
360
+ if (this.parent.isMaskImage) {this.upperContext.globalCompositeOperation = 'xor'; }
361
+ this.processPoint(x, y, false, this.upperContext);
362
+ }
363
+ }
364
+
365
+ private processPoint(x: number, y: number, mouseDown: boolean, context: CanvasRenderingContext2D): void {
366
+ const parent: ImageEditor = this.parent;
367
+ let lastPoint: Point = this.point(x, y, new Date().getTime());
368
+ lastPoint = parent.points.length > 0 && parent.points[parent.points.length - 1];
369
+ const isLastPointTooClose: boolean = lastPoint ? this.distanceTo(lastPoint) <= 5 : false;
370
+ let controlPoint1: Point; let controlPoint2: Point;
371
+ let startPoint: Point; let endPoint: Point;
372
+ this.selPoints.push({x: x, y: y, ratioX: (x - parent.img.destLeft) / parent.img.destWidth,
373
+ ratioY: (y - parent.img.destTop) / parent.img.destHeight, time: this.fhdObj.time });
374
+ if (!lastPoint || !(lastPoint && isLastPointTooClose) || mouseDown) {
375
+ this.fhdObj.time = new Date().getTime();
376
+ parent.points.push({x: x, y: y, ratioX: (x - parent.img.destLeft) / parent.img.destWidth,
377
+ ratioY: (y - parent.img.destTop) / parent.img.destHeight,
378
+ time: this.fhdObj.time });
379
+ this.dummyPoints.push({x: x, y: y, ratioX: (x - parent.img.destLeft) / parent.img.destWidth,
380
+ ratioY: (y - parent.img.destTop) / parent.img.destHeight,
381
+ time: this.fhdObj.time });
382
+ if (this.dummyPoints.length > 2) {
383
+ if (this.dummyPoints.length === 3) {
384
+ this.dummyPoints.unshift(this.dummyPoints[0]);
385
+ }
386
+ const p0: Point = this.dummyPoints[0];
387
+ const p1: Point = this.dummyPoints[1];
388
+ const p2: Point = this.dummyPoints[2];
389
+ const p3: Point = this.dummyPoints[3];
390
+ controlPoint1 = this.calcCurveCP(p0, p1, p2).controlPoint2;
391
+ controlPoint2 = this.calcCurveCP(p1, p2, p3).controlPoint1;
392
+ startPoint = this.dummyPoints[1];
393
+ endPoint = this.dummyPoints[2];
394
+ let minStrokeWidth: number = 0.5; let maxStrokeWidth: number = 5;
395
+ if (!isNullOrUndefined(this.penStrokeWidth)) {
396
+ minStrokeWidth = maxStrokeWidth = this.penStrokeWidth;
397
+ }
398
+ this.startDraw(context, controlPoint1, controlPoint2, startPoint, endPoint, minStrokeWidth, maxStrokeWidth);
399
+ this.pointCounter++;
400
+ this.dummyPoints.shift();
401
+ }
402
+ if (mouseDown) {
403
+ controlPoint1 = controlPoint2 = startPoint = endPoint = {x: x, y: y, time: new Date().getTime()};
404
+ let minStrokeWidth: number = 0.5; let maxStrokeWidth: number = 5;
405
+ if (!isNullOrUndefined(this.penStrokeWidth)) {
406
+ minStrokeWidth = maxStrokeWidth = this.penStrokeWidth;
407
+ }
408
+ this.startDraw(context, controlPoint1, controlPoint2, startPoint, endPoint, minStrokeWidth, maxStrokeWidth);
409
+ }
410
+ }
411
+ }
412
+
413
+ private calcCurveCP(p1: Point, p2: Point, p3: Point): {controlPoint1: Point, controlPoint2: Point} {
414
+ if (!p2) { p2 = p1; } if (!p3) { p3 = p2; }
415
+ const dx1: number = p1.x - p2.x; const dy1: number = p1.y - p2.y;
416
+ const dx2: number = p2.x - p3.x; const dy2: number = p2.y - p3.y;
417
+ const m1: Point = { x: (p1.x + p2.x) / 2.0, y: (p1.y + p2.y) / 2.0 };
418
+ const m2: Point = { x: (p2.x + p3.x) / 2.0, y: (p2.y + p3.y) / 2.0 };
419
+ const l1: number = Math.sqrt(dx1 * dx1 + dy1 * dy1);
420
+ const l2: number = Math.sqrt(dx2 * dx2 + dy2 * dy2);
421
+ const dxm: number = (m1.x - m2.x); const dym: number = (m1.y - m2.y);
422
+ const k: number = l2 / (l1 + l2);
423
+ const cm: Point = { x: m2.x + dxm * k, y: m2.y + dym * k };
424
+ const tx: number = p2.x - cm.x; const ty: number = p2.y - cm.y;
425
+ return {
426
+ controlPoint1: this.point(m1.x + tx, m1.y + ty, 0),
427
+ controlPoint2: this.point(m2.x + tx, m2.y + ty, 0)
428
+ };
429
+ }
430
+
431
+ private point(x: number, y: number, time: number): Point {
432
+ this.fhdObj.pointX = x; this.fhdObj.pointY = y;
433
+ return {x: this.fhdObj.pointX, y: this.fhdObj.pointY, time: time};
434
+ }
435
+
436
+ private startDraw(context: CanvasRenderingContext2D, controlPoint1: Point, controlPoint2: Point,
437
+ startPoint: Point, endPoint: Point, minStrokeWidth: number, maxStrokeWidth: number): void {
438
+ let tempVelocity: number;
439
+ tempVelocity = this.pointVelocity(startPoint);
440
+ tempVelocity = 0.7 * tempVelocity + (1 - 0.7) * this.fhdObj.lastVelocity;
441
+ const newWidth: number = Math.max(maxStrokeWidth / (0.7 + 1), minStrokeWidth);
442
+ this.drawCurve(this.fhdObj.time, newWidth, context, controlPoint1, controlPoint2, startPoint, endPoint, maxStrokeWidth);
443
+ this.fhdObj.lastVelocity = tempVelocity; this.fhdObj.time = newWidth;
444
+ }
445
+
446
+ private pointVelocity(startPoint: Point): number{
447
+ return (this.fhdObj.time !== startPoint.time) ? this.distanceTo(startPoint) /
448
+ (this.fhdObj.time - startPoint.time) : 0;
449
+ }
450
+
451
+ private distanceTo(start: Point): number{
452
+ return Math.sqrt(Math.pow(this.fhdObj.pointX - start.x, 2) + Math.pow(this.fhdObj.pointY - start.y, 2));
453
+ }
454
+
455
+ private drawCurve(startWidth: number, endWidth: number, context: CanvasRenderingContext2D, controlPoint1: Point, controlPoint2: Point,
456
+ startPoint: Point, endPoint: Point, maxStrokeWidth: number): void {
457
+ let width: number; let i: number; let t1: number; let t2: number;
458
+ let t3: number; let u1: number; let u2: number; let u3: number; let x: number; let y: number;
459
+ const widthValue: number = endWidth - startWidth;
460
+ const bezierLength: number = this.bezierLength(controlPoint1, controlPoint2, startPoint, endPoint);
461
+ const drawSteps: number = Math.ceil(bezierLength) * 2;
462
+ context.beginPath();
463
+ for (i = 0; i < drawSteps; i++) {
464
+ t1 = i / drawSteps; t2 = t1 * t1; t3 = t2 * t1;
465
+ u1 = 1 - t1; u2 = u1 * u1; u3 = u2 * u1;
466
+ x = u3 * startPoint.x; x += 3 * u2 * t1 * controlPoint1.x;
467
+ x += 3 * u1 * t2 * controlPoint2.x; x += t3 * endPoint.x;
468
+ y = u3 * startPoint.y; y += 3 * u2 * t1 * controlPoint1.y;
469
+ y += 3 * u1 * t2 * controlPoint2.y; y += t3 * endPoint.y;
470
+ width = Math.min(startWidth + t3 * widthValue, maxStrokeWidth);
471
+ this.drawArc(x, y, width, context);
472
+ }
473
+ context.closePath(); context.fill();
474
+ }
475
+
476
+ private bezierLength(controlPoint1: Point, controlPoint2: Point, startPoint: Point, endPoint: Point): number {
477
+ const steps: number = 10; let length: number = 0; let i: number; let t: number;
478
+ let pointX1: number; let pointY1: number; let pointX2: number; let pointY2: number;
479
+ let pointX3: number; let pointY3: number;
480
+ for (i = 0; i <= steps; i++) {
481
+ t = i / steps;
482
+ pointX1 = this.bezierPoint(t, startPoint.x, controlPoint1.x, controlPoint2.x, endPoint.x);
483
+ pointY1 = this.bezierPoint(t, startPoint.y, controlPoint1.y, controlPoint2.y, endPoint.y);
484
+ if (i > 0) {
485
+ pointX3 = pointX1 - pointX2; pointY3 = pointY1 - pointY2;
486
+ length += Math.sqrt(pointX3 * pointX3 + pointY3 * pointY3);
487
+ }
488
+ pointX2 = pointX1; pointY2 = pointY1;
489
+ }
490
+ return length;
491
+ }
492
+
493
+ private bezierPoint(t: number, startPoint: number, cp1: number, cp2: number, endPoint: number): number {
494
+ return startPoint * (1.0 - t) * (1.0 - t) * (1.0 - t) + 3.0 * cp1 * (1.0 - t) * (1.0 - t) * t + 3.0 *
495
+ cp2 * (1.0 - t) * t * t + endPoint * t * t * t;
496
+ }
497
+
498
+ private drawArc(x: number, y: number, size: number, context: CanvasRenderingContext2D): void {
499
+ const img: ImageDimension = this.parent.img;
500
+ if ((x > img.destLeft && y > img.destTop && x < (img.destLeft + img.destWidth) &&
501
+ y < (img.destTop + img.destHeight) ||
502
+ (context !== this.lowerContext && context !== this.upperContext))) {
503
+ context.moveTo(x, y); context.arc(x, y, size, 0, 2 * Math.PI, false);
504
+ }
505
+ }
506
+
507
+ private freehandRedraw(context: CanvasRenderingContext2D, points?: Point[]): void {
508
+ const parent: ImageEditor = this.parent;
509
+ const temp: string = context.filter; context.filter = 'none';
510
+ if (points) {
511
+ parent.pointColl[parent.freehandCounter] = { points: points, strokeColor: parent.activeObj.strokeSettings.strokeColor,
512
+ strokeWidth: this.penStrokeWidth, flipState: parent.transform.currFlipState,
513
+ id: 'pen_' + (parent.freehandCounter + 1), order: parent.objColl.length + parent.freehandCounter + 1 };
514
+ this.selPointColl[parent.freehandCounter] = extend({}, parent.pointColl[parent.freehandCounter], {}, true);
515
+ parent.freehandCounter++;
516
+ }
517
+ if (parent.freehandCounter > 0) {
518
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
519
+ parent.points = extend([], parent.pointColl[n as number].points) as Point[];
520
+ this.pointCounter = 0;
521
+ const len: number = parent.points.length;
522
+ let controlPoint1: Point; let controlPoint2: Point; let startPoint: Point; let endPoint: Point;
523
+ let minStrokeWidth: number; let maxStrokeWidth: number;
524
+ if (len > 0) {
525
+ context.fillStyle = parent.pointColl[n as number].strokeColor;
526
+ minStrokeWidth = maxStrokeWidth = this.penStrokeWidth = parent.pointColl[n as number].strokeWidth;
527
+ }
528
+ if (len === 1) {
529
+ controlPoint1 = controlPoint2 = startPoint = endPoint = parent.points[0];
530
+ this.startDraw(context, controlPoint1, controlPoint2, startPoint, endPoint, minStrokeWidth, maxStrokeWidth);
531
+ }
532
+ for (let l: number = 0; l < len - 3; l++) {
533
+ if (parent.points[l + 1] && parent.points[l + 2] && parent.points[l + 2]) {
534
+ controlPoint1 = (this.calcCurveCP(parent.points[l + 0],
535
+ parent.points[l + 1], parent.points[l + 2])).controlPoint2;
536
+ controlPoint2 = (this.calcCurveCP(parent.points[l + 1], parent.points[l + 2],
537
+ parent.points[l + 3])).controlPoint1;
538
+ if (l === 0) {
539
+ startPoint = parent.points[l as number];
540
+ } else {
541
+ startPoint = parent.points[l + 1];
542
+ }
543
+ endPoint = parent.points[l + 2];
544
+ this.startDraw(context, controlPoint1, controlPoint2, startPoint, endPoint, minStrokeWidth, maxStrokeWidth);
545
+ }
546
+ }
547
+ context.closePath();
548
+ }
549
+ if (context === this.lowerContext) {
550
+ parent.notify('draw', {prop: 'applyFrame', value: {ctx: this.lowerContext, frame: parent.frameObj.type, preventImg: true}});
551
+ this.upperContext.clearRect(0, 0, parent.upperCanvas.width, parent.upperCanvas.height);
552
+ }
553
+ }
554
+ context.filter = temp;
555
+ }
556
+
557
+ private getSqPtFD(idx: number, obj?: Object): ActivePoint {
558
+ const activePoint: ActivePoint = {startX: 0, startY: 0, endX: 0, endY: 0, width: 0, height: 0};
559
+ const sPoints: Point[] = extend([], this.selPointColl[idx as number].points, []) as Point[];
560
+ this.parent.points = extend([], this.parent.pointColl[idx as number].points) as Point[];
561
+ this.pointCounter = 0; const len: number = sPoints.length;
562
+ for (let l: number = 0; l < len; l++) {
563
+ if (activePoint.startX === 0 && activePoint.startY === 0 && activePoint.endX === 0 && activePoint.endY === 0) {
564
+ activePoint.startX = sPoints[l as number].x;
565
+ activePoint.startY = sPoints[l as number].y;
566
+ activePoint.endX = sPoints[l as number].x;
567
+ activePoint.endY = sPoints[l as number].y;
568
+ } else {
569
+ activePoint.startX = Math.min(activePoint.startX, sPoints[l as number].x);
570
+ activePoint.startY = Math.min(activePoint.startY, sPoints[l as number].y);
571
+ activePoint.endX = Math.max(activePoint.endX, sPoints[l as number].x);
572
+ activePoint.endY = Math.max(activePoint.endY, sPoints[l as number].y);
573
+ }
574
+ }
575
+ activePoint.startX -= this.penStrokeWidth; activePoint.startY -= this.penStrokeWidth;
576
+ activePoint.endX += this.penStrokeWidth; activePoint.endY += this.penStrokeWidth;
577
+ activePoint.width = activePoint.endX - activePoint.startX;
578
+ activePoint.height = activePoint.endY - activePoint.startY;
579
+ if (obj) { obj['activePoint'] = activePoint; }
580
+ return activePoint;
581
+ }
582
+
583
+ private applyPenDraw(): void {
584
+ const parent: ImageEditor = this.parent;
585
+ if (parent.currObjType.shape === 'freehanddraw') {
586
+ parent.notify('shape', { prop: 'apply', onPropertyChange: false, value: {shape: null, obj: null, canvas: null}});
587
+ parent.upperCanvas.style.cursor = parent.cursor = 'default';
588
+ parent.currObjType.shape = '';
589
+ }
590
+ parent.notify('shape', {prop: 'clearActObj'});
591
+ }
592
+
593
+ private applyFhd(): void {
594
+ const parent: ImageEditor = this.parent;
595
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
596
+ const selectedPoint: any = parent.pointColl[this.fhdSelIdx];
597
+ if (selectedPoint.strokeColor === '#42a5f5') {
598
+ selectedPoint.strokeColor = this.tempFHDStyles.strokeColor;
599
+ }
600
+ parent.notify('toolbar', {prop: 'setSelectedFreehandColor', value: {color: '#42a5f5' } });
601
+ this.upperContext.clearRect(0, 0, parent.upperCanvas.width, parent.upperCanvas.height);
602
+ this.lowerContext.clearRect(0, 0, parent.lowerCanvas.width, parent.lowerCanvas.height);
603
+ parent.notify('draw', {prop: 'render-image', value: {isMouseWheel: null } });
604
+ parent.notify('toolbar', { prop: 'refresh-main-toolbar', onPropertyChange: false});
605
+ if (selectedPoint) {
606
+ selectedPoint.isSelected = false;
607
+ }
608
+ parent.notify('selection', {prop: 'resetFreehandDrawVariables'});
609
+ this.fhdHovIdx = this.fhdSelIdx = null;
610
+ }
611
+
612
+ private cancelFhd(): void {
613
+ const parent: ImageEditor = this.parent;
614
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
615
+ const selectedPoint: any = parent.pointColl[this.fhdSelIdx];
616
+ parent.notify('toolbar', {prop: 'setSelectedFreehandColor', value: {color: '#42a5f5' } });
617
+ this.upperContext.clearRect(0, 0, parent.upperCanvas.width, parent.upperCanvas.height);
618
+ this.lowerContext.clearRect(0, 0, parent.upperCanvas.width, parent.upperCanvas.height);
619
+ this.pointCounter = 0;
620
+ if (selectedPoint) {
621
+ selectedPoint.strokeColor = this.tempFHDStyles.strokeColor;
622
+ selectedPoint.strokeWidth = this.tempFHDStyles.strokeWidth;
623
+ selectedPoint.isSelected = false;
624
+ }
625
+ this.fhdHovIdx = this.fhdSelIdx = this.fhdSelID = null;
626
+ parent.notify('selection', {prop: 'resetFreehandDrawVariables'});
627
+ parent.activeObj.strokeSettings.strokeColor = this.tempFHDStyles.strokeColor;
628
+ parent.activeObj.strokeSettings.strokeWidth = this.penStrokeWidth = this.tempFHDStyles.strokeWidth;
629
+ this.tempFHDStyles = {strokeColor: null, strokeWidth: null, fillColor: null};
630
+ parent.notify('draw', {prop: 'render-image', value: {isMouseWheel: null } });
631
+ parent.notify('toolbar', { prop: 'refresh-main-toolbar', onPropertyChange: false});
632
+ }
633
+
634
+ private selectFhd(index?: number): void {
635
+ const parent: ImageEditor = this.parent;
636
+ const tempFHDStyles: StrokeSettings = extend({}, this.tempFHDStyles, {}, true) as StrokeSettings;
637
+ parent.notify('selection', { prop: 'setFreehandDrawEditing', onPropertyChange: false, value: {bool: true }});
638
+ if (index || index === 0) {
639
+ if (this.isFHDIdx(index)) {
640
+ this.fhdSelIdx = this.fhdHovIdx = index;
641
+ this.hoverFhd();
642
+ parent.upperCanvas.style.cursor = parent.cursor = 'pointer';
643
+ } else {
644
+ return;
645
+ }
646
+ }
647
+ this.fhdSelIdx = this.fhdHovIdx;
648
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
649
+ const point: any = parent.pointColl[this.fhdSelIdx];
650
+ point.isSelected = true; this.fhdSelID = point.id;
651
+ if (point.strokeColor !== '#42a5f5') {
652
+ parent.activeObj.strokeSettings.strokeColor = this.tempFHDStyles.strokeColor = point.strokeColor;
653
+ }
654
+ parent.activeObj.strokeSettings.strokeWidth = this.tempFHDStyles.strokeWidth =
655
+ parent.pointColl[this.fhdHovIdx].strokeWidth;
656
+ const obj: Object = {bool: false };
657
+ parent.notify('selection', { prop: 'getFreehandDrawEditing', onPropertyChange: false, value: {obj: obj }});
658
+ if (obj['bool']) {
659
+ const shapeSettings: ShapeSettings = {id: 'pen_' + (this.fhdSelIdx + 1), type: ShapeType.FreehandDraw,
660
+ startX: point.points[0].x, startY: point.points[0].y, strokeColor: point.strokeColor,
661
+ strokeWidth: point.strokeWidth, points: point.points, opacity: point.opacity,
662
+ index: point.order };
663
+ const shapeChangingArgs: ShapeChangeEventArgs = {cancel: false, action: 'select', previousShapeSettings: shapeSettings,
664
+ currentShapeSettings: shapeSettings};
665
+ this.triggerShapeChanging(shapeChangingArgs);
666
+ } else {
667
+ parent.okBtn(null, true);
668
+ }
669
+ if (parent.isUndoRedoStack) {
670
+ this.tempFHDStyles = tempFHDStyles;
671
+ }
672
+ }
673
+
674
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
675
+ private deleteFhd(index: number, isId?: boolean): void {
676
+ const parent: ImageEditor = this.parent;
677
+ if (this.isFHDIdx(index)) {
678
+ this.upperContext.clearRect(0, 0, parent.upperCanvas.width, parent.upperCanvas.height);
679
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
680
+ const tempPointColl: any = extend({}, parent.pointColl, {}, true);
681
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
682
+ const tempSelPointColl: any = extend({}, this.selPointColl, {}, true);
683
+ parent.pointColl = {}; this.selPointColl = {}; let count: number = 0;
684
+ for (let i: number = 0; i < parent.freehandCounter; i++) {
685
+ if (parseInt(tempPointColl[i as number].id.split('_')[1], 10) - 1 !== index) {
686
+ parent.pointColl[count as number] = tempPointColl[i as number];
687
+ this.selPointColl[count as number] = tempSelPointColl[i as number];
688
+ count++;
689
+ }
690
+ }
691
+ parent.freehandCounter -= 1; this.fhdHovIdx = this.fhdSelIdx = null;
692
+ parent.notify('selection', {prop: 'resetFreehandDrawVariables'});
693
+ parent.notify('draw', {prop: 'render-image', value: {isMouseWheel: null } });
694
+ parent.notify('toolbar', { prop: 'refresh-main-toolbar', onPropertyChange: false});
695
+ }
696
+ }
697
+
698
+ private zoomX(x: number): number {
699
+ return (x * this.parent.img.destWidth) + this.parent.img.destLeft;
700
+ }
701
+
702
+ private zoomY(y: number): number {
703
+ return (y * this.parent.img.destHeight) + this.parent.img.destTop;
704
+ }
705
+
706
+ private zoomFHDColl(isPreventApply?: boolean): void {
707
+ const parent: ImageEditor = this.parent;
708
+ const destPoints: ActivePoint = {startX: parent.img.destLeft, startY: parent.img.destTop,
709
+ width: parent.img.destWidth, height: parent.img.destHeight};
710
+ parent.notify('shape', { prop: 'straightenShapes', onPropertyChange: false});
711
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
712
+ parent.points = extend([], parent.pointColl[n as number].points, []) as Point[];
713
+ this.pointCounter = 0; const len: number = parent.points.length;
714
+ for (let l: number = 0; l < len; l++) {
715
+ const point: Point = parent.points[l as number];
716
+ point.x = this.zoomX(point.ratioX);
717
+ point.y = this.zoomY(point.ratioY);
718
+ }
719
+ }
720
+ this.updateFHDCurPts();
721
+ if (this.straightenPoint.x && this.straightenPoint.y) {
722
+ this.straightenPoint.x = this.zoomX(this.straightenPoint.ratioX);
723
+ this.straightenPoint.y = this.zoomY(this.straightenPoint.ratioY);
724
+ }
725
+ if (parent.transform.straighten !== 0) {
726
+ parent.notify('shape', { prop: 'straightenFHD', onPropertyChange: false});
727
+ }
728
+ parent.img.destLeft = destPoints.startX; parent.img.destTop = destPoints.startY;
729
+ parent.img.destWidth = destPoints.width; parent.img.destHeight = destPoints.height;
730
+ if (isNullOrUndefined(isPreventApply)) {
731
+ this.freehandRedraw(this.lowerContext, null);
732
+ }
733
+ }
734
+
735
+ private updateFHDCurPts(): void {
736
+ const parent: ImageEditor = this.parent;
737
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
738
+ if (this.selPointColl[n as number]) {
739
+ this.selPoints = extend([], this.selPointColl[n as number].points, []) as Point[];
740
+ this.pointCounter = 0; const len: number = this.selPoints.length;
741
+ for (let l: number = 0; l < len; l++) {
742
+ const point: Point = this.selPoints[l as number];
743
+ point.x = this.zoomX(point.ratioX);
744
+ point.y = this.zoomY(point.ratioY);
745
+ }
746
+ }
747
+ }
748
+ }
749
+
750
+ private rotateFhdColl(): void {
751
+ const parent: ImageEditor = this.parent;
752
+ const { destLeft, destTop, destWidth, destHeight } = parent.img;
753
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
754
+ parent.points = extend([], parent.pointColl[n as number].points, []) as Point[];
755
+ this.pointCounter = 0; const len: number = parent.points.length;
756
+ for (let l: number = 0; l < len; l++) {
757
+ const point: Point = parent.points[l as number];
758
+ point.y = destTop + (destHeight * point.ratioX);
759
+ point.x = (destLeft + destWidth) - (destWidth * point.ratioY);
760
+ point.ratioX = (point.x - destLeft) / destWidth;
761
+ point.ratioY = (point.y - destTop) / destHeight;
762
+ }
763
+ }
764
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
765
+ if (this.selPointColl[n as number]) {
766
+ this.selPoints = extend([], this.selPointColl[n as number].points, []) as Point[];
767
+ this.pointCounter = 0;
768
+ const len: number = this.selPoints.length;
769
+ for (let l: number = 0; l < len; l++) {
770
+ const point: Point = this.selPoints[l as number];
771
+ point.y = destTop + (destHeight * point.ratioX);
772
+ point.x = (destLeft + destWidth) - (destWidth * point.ratioY);
773
+ point.ratioX = (point.x - destLeft) / destWidth;
774
+ point.ratioY = (point.y - destTop) / destHeight;
775
+ }
776
+ }
777
+ }
778
+ this.updateFHDCurPts();
779
+ }
780
+
781
+ private flipFHDColl(value: string): void {
782
+ const lowercaseValue: string = value.toLowerCase();
783
+ if (lowercaseValue === 'horizontal') {
784
+ this.pointsHorizontalFlip();
785
+ } else if (lowercaseValue === 'vertical') {
786
+ this.pointsVerticalFlip();
787
+ } else {
788
+ this.pointsHorizontalFlip();
789
+ for (let i: number = 0; i < this.parent.freehandCounter; i++) {
790
+ this.parent.pointColl[i as number].shapeFlip = '';
791
+ }
792
+ this.pointsVerticalFlip();
793
+ }
794
+ }
795
+
796
+ private pointsHorizontalFlip(): void {
797
+ const parent: ImageEditor = this.parent;
798
+ const { destLeft, destTop, destWidth, destHeight } = parent.img;
799
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
800
+ if (parent.pointColl[n as number].shapeFlip !== parent.transform.currFlipState) {
801
+ parent.points = extend([], parent.pointColl[n as number].points, []) as Point[];
802
+ this.pointCounter = 0;
803
+ const len: number = parent.points.length;
804
+ for (let l: number = 0; l < len; l++) {
805
+ const point: Point = parent.points[l as number];
806
+ if (point.x <= destLeft + (destWidth / 2)) {
807
+ point.x = (destLeft + destWidth) - (point.x - destLeft);
808
+ } else if (point.x >= destLeft + (destWidth / 2)) {
809
+ point.x = destLeft + (destLeft + destWidth - point.x);
810
+ }
811
+ point.ratioX = (point.x - destLeft) / destWidth;
812
+ point.ratioY = (point.y - destTop) / destHeight;
813
+ }
814
+ parent.pointColl[n as number].shapeFlip = parent.transform.currFlipState;
815
+ }
816
+ }
817
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
818
+ if (this.selPointColl[n as number]) {
819
+ if (this.selPointColl[n as number].shapeFlip !== parent.transform.currFlipState) {
820
+ this.selPoints = extend([], this.selPointColl[n as number].points, []) as Point[];
821
+ this.pointCounter = 0;
822
+ const len: number = this.selPoints.length;
823
+ for (let l: number = 0; l < len; l++) {
824
+ const point: Point = this.selPoints[l as number];
825
+ if (point.x <= destLeft + (destWidth / 2)) {
826
+ point.x = (destLeft + destWidth) - (point.x - destLeft);
827
+ } else if (point.x >= destLeft + (destWidth / 2)) {
828
+ point.x = destLeft + (destLeft + destWidth - point.x);
829
+ }
830
+ point.ratioX = (point.x - destLeft) / destWidth;
831
+ point.ratioY = (point.y - destTop) / destHeight;
832
+ }
833
+ }
834
+ }
835
+ }
836
+ this.updateFHDCurPts();
837
+ }
838
+
839
+ private pointsVerticalFlip(): void {
840
+ const parent: ImageEditor = this.parent;
841
+ const { destLeft, destTop, destWidth, destHeight } = parent.img;
842
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
843
+ if (parent.pointColl[n as number].shapeFlip !== parent.transform.currFlipState) {
844
+ parent.points = extend([], parent.pointColl[n as number].points, []) as Point[];
845
+ this.pointCounter = 0;
846
+ const len: number = parent.points.length;
847
+ for (let l: number = 0; l < len; l++) {
848
+ const point: Point = parent.points[l as number];
849
+ if (point.y <= destTop + (destHeight / 2)) {
850
+ point.y = (destTop + destHeight) - (point.y - destTop);
851
+ } else if (point.y >= destTop + (destHeight / 2)) {
852
+ point.y = destTop + (destTop + destHeight - point.y);
853
+ }
854
+ point.ratioX = (point.x - destLeft) / destWidth;
855
+ point.ratioY = (point.y - destTop) / destHeight;
856
+ }
857
+ parent.pointColl[n as number].shapeFlip = parent.transform.currFlipState;
858
+ }
859
+ }
860
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
861
+ if (this.selPointColl[n as number]) {
862
+ if (this.selPointColl[n as number].shapeFlip !== parent.transform.currFlipState) {
863
+ this.selPoints = extend([], this.selPointColl[n as number].points, []) as Point[];
864
+ this.pointCounter = 0;
865
+ const len: number = this.selPoints.length;
866
+ for (let l: number = 0; l < len; l++) {
867
+ const point: Point = this.selPoints[l as number];
868
+ if (point.y <= destTop + (destHeight / 2)) {
869
+ point.y = (destTop + destHeight) - (point.y - destTop);
870
+ } else if (point.y >= destTop + (destHeight / 2)) {
871
+ point.y = destTop + (destTop + destHeight - point.y);
872
+ }
873
+ point.ratioX = (point.x - destLeft) / destWidth;
874
+ point.ratioY = (point.y - destTop) / destHeight;
875
+ }
876
+ }
877
+ }
878
+ }
879
+ this.updateFHDCurPts();
880
+ }
881
+
882
+ private updateFHDColl(isPreventApply?: boolean): void {
883
+ const parent: ImageEditor = this.parent;
884
+ const destPoints: ActivePoint = {startX: parent.img.destLeft, startY: parent.img.destTop,
885
+ width: parent.img.destWidth, height: parent.img.destHeight};
886
+ parent.notify('shape', { prop: 'straightenShapes', onPropertyChange: false});
887
+ const { destLeft, destTop, destWidth, destHeight } = parent.img;
888
+ for (let i: number = 0, iLen: number = parent.objColl.length; i < iLen; i++) {
889
+ const currObj: SelectionPoint = parent.objColl[i as number];
890
+ if (currObj.shape === 'line' || currObj.shape === 'arrow') {
891
+ parent.notify('shape', { prop: 'straightenShapePoints', value: {obj: currObj, isReverse: true }});
892
+ } else if (currObj.shape === 'path') {
893
+ const temp: number = parent.transform.straighten;
894
+ parent.transform.straighten = -parent.transform.straighten;
895
+ parent.notify('shape', { prop: 'straightenPath', onPropertyChange: false, value: {obj: currObj }});
896
+ parent.transform.straighten = temp;
897
+ }
898
+ currObj.imageRatio = {startX: ((currObj.activePoint.startX - destLeft) /
899
+ destWidth), startY: ((currObj.activePoint.startY - destTop) / destHeight),
900
+ endX: ((currObj.activePoint.endX - destLeft) / destWidth),
901
+ endY: ((currObj.activePoint.endY - destTop) / destHeight),
902
+ width: destWidth / currObj.activePoint.width, height: destHeight /
903
+ currObj.activePoint.height };
904
+ if (currObj.shape === 'path') {
905
+ for (let j: number = 0, jLen: number = currObj.pointColl.length; j < jLen; j++) {
906
+ currObj.pointColl[j as number].ratioX =
907
+ (currObj.pointColl[j as number].x - destLeft) / destWidth;
908
+ currObj.pointColl[j as number].ratioY =
909
+ (currObj.pointColl[j as number].y - destTop) / destHeight;
910
+ }
911
+ }
912
+ parent.notify('shape', { prop: 'refreshActiveObj', onPropertyChange: false});
913
+ }
914
+ if (parent.freehandCounter > 0 && parent.transform.straighten !== 0) {
915
+ const temp: number = parent.transform.straighten;
916
+ parent.transform.straighten = -parent.transform.straighten;
917
+ parent.notify('shape', { prop: 'straightenFHD', onPropertyChange: false});
918
+ parent.transform.straighten = temp;
919
+ }
920
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
921
+ parent.points = extend([], parent.pointColl[n as number].points, []) as Point[];
922
+ this.pointCounter = 0;
923
+ const len: number = parent.points.length;
924
+ for (let l: number = 0; l < len; l++) {
925
+ const point: Point = parent.points[l as number];
926
+ point.ratioX = (point.x - destLeft) / destWidth;
927
+ point.ratioY = (point.y - destTop) / destHeight;
928
+ }
929
+ }
930
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
931
+ if (this.selPointColl[n as number]) {
932
+ this.selPoints = extend([], this.selPointColl[n as number].points, []) as Point[];
933
+ this.pointCounter = 0;
934
+ const len: number = this.selPoints.length;
935
+ for (let l: number = 0; l < len; l++) {
936
+ const point: Point = this.selPoints[l as number];
937
+ point.ratioX = (point.x - destLeft) / destWidth;
938
+ point.ratioY = (point.y - destTop) / destHeight;
939
+ }
940
+ }
941
+ }
942
+ if (this.straightenPoint.x && this.straightenPoint.y) {
943
+ this.straightenPoint.ratioX = (this.straightenPoint.x - destLeft) / destWidth;
944
+ this.straightenPoint.ratioY = (this.straightenPoint.y - destTop) / destHeight;
945
+ }
946
+ parent.img.destLeft = destPoints.startX; parent.img.destTop = destPoints.startY;
947
+ parent.img.destWidth = destPoints.width; parent.img.destHeight = destPoints.height;
948
+ parent.notify('shape', { prop: 'drawAnnotations', onPropertyChange: false,
949
+ value: {ctx: this.lowerContext, shape: 'zoom', pen: 'zoom', isPreventApply: isPreventApply }});
950
+ }
951
+
952
+ private panFHDColl(xDiff: number, yDiff: number, panRegion: string): void {
953
+ const parent: ImageEditor = this.parent;
954
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
955
+ parent.points = extend([], parent.pointColl[n as number].points, []) as Point[];
956
+ this.pointCounter = 0;
957
+ const len: number = parent.points.length;
958
+ for (let l: number = 0; l < len; l++) {
959
+ const point: Point = parent.points[l as number];
960
+ if (panRegion === '' || panRegion === 'vertical') {
961
+ point.x += xDiff;
962
+ } else {
963
+ point.x -= xDiff;
964
+ }
965
+ if (panRegion === '' || panRegion === 'horizontal') {
966
+ point.y += yDiff;
967
+ }
968
+ else {
969
+ point.y -= yDiff;
970
+ }
971
+ }
972
+ }
973
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
974
+ if (this.selPointColl[n as number]) {
975
+ this.selPoints = extend([], this.selPointColl[n as number].points, []) as Point[];
976
+ this.pointCounter = 0;
977
+ const len: number = this.selPoints.length;
978
+ for (let l: number = 0; l < len; l++) {
979
+ const point: Point = this.selPoints[l as number];
980
+ if (panRegion === '' || panRegion === 'vertical') {
981
+ point.x += xDiff;
982
+ } else {
983
+ point.x -= xDiff;
984
+ }
985
+ if (panRegion === '' || panRegion === 'horizontal') {
986
+ point.y += yDiff;
987
+ }
988
+ else {
989
+ point.y -= yDiff;
990
+ }
991
+ }
992
+ }
993
+ }
994
+ if (this.straightenPoint.x && this.straightenPoint.y) {
995
+ if (panRegion === '' || panRegion === 'vertical') {
996
+ this.straightenPoint.x += xDiff;
997
+ } else {
998
+ this.straightenPoint.x -= xDiff;
999
+ }
1000
+ if (panRegion === '' || panRegion === 'horizontal') {
1001
+ this.straightenPoint.y += yDiff;
1002
+ }
1003
+ else {
1004
+ this.straightenPoint.y -= yDiff;
1005
+ }
1006
+ }
1007
+ this.freehandRedraw(this.lowerContext, null);
1008
+ }
1009
+
1010
+ private freeHandDraw(value: boolean): void {
1011
+ const parent: ImageEditor = this.parent;
1012
+ if (value) {
1013
+ parent.points = [];
1014
+ this.dummyPoints = [];
1015
+ parent.notify('shape', { prop: 'refreshActiveObj', onPropertyChange: false});
1016
+ parent.togglePen = true;
1017
+ parent.upperCanvas.style.cursor = parent.cursor = 'crosshair';
1018
+ parent.upperCanvas.style.display = 'block';
1019
+ if (isNullOrUndefined(parent.activeObj.strokeSettings)) {
1020
+ const obj: Object = {strokeSettings: {} as StrokeSettings };
1021
+ parent.notify('shape', { prop: 'getStrokeSettings', onPropertyChange: false,
1022
+ value: {obj: obj }});
1023
+ parent.activeObj.strokeSettings = obj['strokeSettings'];
1024
+ }
1025
+ if (isNullOrUndefined(parent.activeObj.strokeSettings.strokeWidth)) {
1026
+ parent.activeObj.strokeSettings.strokeWidth = 2;
1027
+ }
1028
+ if (parent.isMaskImage) {
1029
+ parent.notify('toolbar', { prop: 'refresh-main-toolbar', onPropertyChange: false});
1030
+ } else {
1031
+ parent.notify('toolbar', { prop: 'refresh-toolbar', onPropertyChange: false, value: {type: 'pen',
1032
+ isApplyBtn: null, isCropping: null, isZooming: null, cType: null}});
1033
+ }
1034
+ } else {
1035
+ parent.upperCanvas.style.cursor = parent.cursor = 'default';
1036
+ const strokeWidth: number = this.penStrokeWidth;
1037
+ parent.notify('shape', { prop: 'apply', onPropertyChange: false, value: {shape: null, obj: null, canvas: null}});
1038
+ parent.notify('toolbar', { prop: 'refresh-main-toolbar', onPropertyChange: false});
1039
+ parent.notify('toolbar', {prop: 'setCurrentToolbar', value: {type: 'main' }});
1040
+ parent.notify('selection', {prop: 'setFreehandDrawCustomized', value: {isFreehandDrawCustomized: false }});
1041
+ this.penStrokeWidth = strokeWidth;
1042
+ }
1043
+ }
1044
+
1045
+ private isFHDIdx(index: number, obj?: Object): boolean {
1046
+ let isIndex: boolean = false;
1047
+ for (let i: number = 0; i < this.parent.freehandCounter; i++) {
1048
+ if (this.parent.pointColl[i as number].id &&
1049
+ parseInt(this.parent.pointColl[i as number].id.split('_')[1], 10) - 1 === index) {
1050
+ isIndex = true;
1051
+ break;
1052
+ }
1053
+ }
1054
+ if (obj) { obj['isIndex'] = isIndex; }
1055
+ return isIndex;
1056
+ }
1057
+
1058
+ private updateCropPtsForSel(): void {
1059
+ const parent: ImageEditor = this.parent; const actPoint: ActivePoint = parent.activeObj.activePoint;
1060
+ for (let n: number = 0; n < parent.freehandCounter; n++) {
1061
+ const obj: Object = {selPointColl: extend([], this.selPointColl) };
1062
+ if (obj['selPointColl'][n as number]) {
1063
+ this.selPoints = extend([], obj['selPointColl'][n as number].points, []) as Point[];
1064
+ this.pointCounter = 0;
1065
+ const len: number = this.selPoints.length;
1066
+ for (let l: number = 0; l < len; l++) {
1067
+ const point: Point = this.selPoints[l as number];
1068
+ point.ratioX = (point.x - actPoint.startX) / actPoint.width;
1069
+ point.ratioY = (point.y - actPoint.startY) / actPoint.height;
1070
+ }
1071
+ }
1072
+ }
1073
+ }
1074
+
1075
+ private triggerShapeChanging(shapeChangingArgs: ShapeChangeEventArgs) : void {
1076
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
1077
+ const parent: ImageEditor = this.parent; const point: any = parent.pointColl[this.fhdSelIdx];
1078
+ parent.trigger('shapeChanging', shapeChangingArgs);
1079
+ if (parent.element.getAttribute('data-value') === 'mask-drawing' && !this.isMasking) {
1080
+ this.isMasking = true;
1081
+ parent.upperCanvas.style.cursor = 'crosshair';
1082
+ parent.notify('draw', { prop: 'updateTempObjColl'});
1083
+ parent.notify('draw', { prop: 'updateTempPointColl'});
1084
+ parent.discard();
1085
+ parent.selectMaskImage();
1086
+ return;
1087
+ }
1088
+ parent.editCompleteArgs = shapeChangingArgs;
1089
+ if (shapeChangingArgs.currentShapeSettings.id.indexOf('pen_') === -1 &&
1090
+ (shapeChangingArgs.action === 'draw-end' || shapeChangingArgs.action === 'select')) {
1091
+ const id: string = 'pen_' + shapeChangingArgs.currentShapeSettings.id;
1092
+ if (this.fhdSelIdx) {
1093
+ parent.pointColl[this.fhdSelIdx].id = id;
1094
+ } else {
1095
+ parent.pointColl[parent.freehandCounter - 1].id = id;
1096
+ }
1097
+ }
1098
+ this.penStrokeWidth = shapeChangingArgs.currentShapeSettings.strokeWidth;
1099
+ if (parent.activeObj.strokeSettings.strokeColor !== shapeChangingArgs.currentShapeSettings.strokeColor) {
1100
+ parent.activeObj.strokeSettings.strokeColor = shapeChangingArgs.currentShapeSettings.strokeColor;
1101
+ parent.notify('toolbar', { prop: 'update-toolbar-items', onPropertyChange: false });
1102
+ }
1103
+ if (this.fhdSelID && point && shapeChangingArgs.currentShapeSettings) {
1104
+ point.strokeColor = shapeChangingArgs.currentShapeSettings.strokeColor;
1105
+ point.strokeWidth = shapeChangingArgs.currentShapeSettings.strokeWidth;
1106
+ point.points = shapeChangingArgs.currentShapeSettings.points;
1107
+ point.opacity = shapeChangingArgs.currentShapeSettings.opacity;
1108
+ }
1109
+ if (shapeChangingArgs.action === 'select') {
1110
+ this.freehandRedraw(this.upperContext);
1111
+ parent.notify('toolbar', { prop: 'refresh-toolbar', onPropertyChange: false, value: {type: 'pen',
1112
+ isApplyBtn: null, isCropping: null, isZooming: null, cType: null}});
1113
+ }
1114
+ }
1115
+
1116
+ private setCenterSelPoints(): void {
1117
+ const parent: ImageEditor = this.parent;
1118
+ const destPoints: ActivePoint = {startX: parent.img.destLeft, startY: parent.img.destTop,
1119
+ width: parent.img.destWidth, height: parent.img.destHeight};
1120
+ parent.notify('shape', { prop: 'straightenShapes', onPropertyChange: false});
1121
+ const { destLeft, destTop, destWidth, destHeight } = parent.img;
1122
+ const actPoint: ActivePoint = parent.activeObj.activePoint;
1123
+ if (isNullOrUndefined(this.prevStraightenObj) ||
1124
+ (JSON.stringify(this.prevStraightenObj.activePoint) !== JSON.stringify(actPoint))) {
1125
+ this.straightenPoint = { x: actPoint.startX + (actPoint.width / 2),
1126
+ y: actPoint.startY + (actPoint.height / 2),
1127
+ ratioX: (actPoint.startX + (actPoint.width / 2) - destLeft) / destWidth,
1128
+ ratioY: (actPoint.startY + (actPoint.height / 2) - destTop) / destHeight };
1129
+ this.prevStraightenObj = extend({}, parent.activeObj, {}, true) as SelectionPoint;
1130
+ this.straightenPointAngle = parent.transform.straighten;
1131
+ }
1132
+ parent.img.destLeft = destPoints.startX; parent.img.destTop = destPoints.startY;
1133
+ parent.img.destWidth = destPoints.width; parent.img.destHeight = destPoints.height;
1134
+ }
1135
+ }