@nasser-sw/fabric 7.0.1-beta3 → 7.0.1-beta5

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 (105) hide show
  1. package/0 +0 -0
  2. package/dist/index.js +345 -162
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.min.js +1 -1
  5. package/dist/index.min.js.map +1 -1
  6. package/dist/index.min.mjs +1 -1
  7. package/dist/index.min.mjs.map +1 -1
  8. package/dist/index.mjs +345 -162
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/index.node.cjs +345 -162
  11. package/dist/index.node.cjs.map +1 -1
  12. package/dist/index.node.mjs +345 -162
  13. package/dist/index.node.mjs.map +1 -1
  14. package/dist/package.json.min.mjs +1 -1
  15. package/dist/package.json.mjs +1 -1
  16. package/dist/src/shapes/Line.d.ts +32 -86
  17. package/dist/src/shapes/Line.d.ts.map +1 -1
  18. package/dist/src/shapes/Line.min.mjs +1 -1
  19. package/dist/src/shapes/Line.min.mjs.map +1 -1
  20. package/dist/src/shapes/Line.mjs +345 -161
  21. package/dist/src/shapes/Line.mjs.map +1 -1
  22. package/dist-extensions/src/shapes/CustomLine.d.ts +10 -0
  23. package/dist-extensions/src/shapes/CustomLine.d.ts.map +1 -0
  24. package/dist-extensions/src/shapes/Line.d.ts +32 -86
  25. package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
  26. package/fabric-test-editor.html +157 -8
  27. package/fabric-test2.html +513 -0
  28. package/fabric.ts +182 -182
  29. package/package.json +1 -1
  30. package/src/shapes/Line.ts +397 -164
  31. package/debug/konva/CHANGELOG.md +0 -1474
  32. package/debug/konva/LICENSE +0 -22
  33. package/debug/konva/README.md +0 -205
  34. package/debug/konva/gulpfile.mjs +0 -110
  35. package/debug/konva/package.json +0 -139
  36. package/debug/konva/release.sh +0 -65
  37. package/debug/konva/resources/doc-includes/ContainerParams.txt +0 -6
  38. package/debug/konva/resources/doc-includes/NodeParams.txt +0 -20
  39. package/debug/konva/resources/doc-includes/ShapeParams.txt +0 -53
  40. package/debug/konva/resources/jsdoc.conf.json +0 -28
  41. package/debug/konva/rollup.config.mjs +0 -32
  42. package/debug/konva/src/Animation.ts +0 -237
  43. package/debug/konva/src/BezierFunctions.ts +0 -826
  44. package/debug/konva/src/Canvas.ts +0 -193
  45. package/debug/konva/src/Container.ts +0 -649
  46. package/debug/konva/src/Context.ts +0 -1017
  47. package/debug/konva/src/Core.ts +0 -5
  48. package/debug/konva/src/DragAndDrop.ts +0 -173
  49. package/debug/konva/src/Factory.ts +0 -246
  50. package/debug/konva/src/FastLayer.ts +0 -29
  51. package/debug/konva/src/Global.ts +0 -210
  52. package/debug/konva/src/Group.ts +0 -31
  53. package/debug/konva/src/Layer.ts +0 -546
  54. package/debug/konva/src/Node.ts +0 -3477
  55. package/debug/konva/src/PointerEvents.ts +0 -67
  56. package/debug/konva/src/Shape.ts +0 -2081
  57. package/debug/konva/src/Stage.ts +0 -1000
  58. package/debug/konva/src/Tween.ts +0 -811
  59. package/debug/konva/src/Util.ts +0 -1123
  60. package/debug/konva/src/Validators.ts +0 -210
  61. package/debug/konva/src/_CoreInternals.ts +0 -85
  62. package/debug/konva/src/_FullInternals.ts +0 -171
  63. package/debug/konva/src/canvas-backend.ts +0 -36
  64. package/debug/konva/src/filters/Blur.ts +0 -388
  65. package/debug/konva/src/filters/Brighten.ts +0 -48
  66. package/debug/konva/src/filters/Brightness.ts +0 -30
  67. package/debug/konva/src/filters/Contrast.ts +0 -75
  68. package/debug/konva/src/filters/Emboss.ts +0 -207
  69. package/debug/konva/src/filters/Enhance.ts +0 -154
  70. package/debug/konva/src/filters/Grayscale.ts +0 -25
  71. package/debug/konva/src/filters/HSL.ts +0 -108
  72. package/debug/konva/src/filters/HSV.ts +0 -106
  73. package/debug/konva/src/filters/Invert.ts +0 -23
  74. package/debug/konva/src/filters/Kaleidoscope.ts +0 -274
  75. package/debug/konva/src/filters/Mask.ts +0 -220
  76. package/debug/konva/src/filters/Noise.ts +0 -44
  77. package/debug/konva/src/filters/Pixelate.ts +0 -107
  78. package/debug/konva/src/filters/Posterize.ts +0 -46
  79. package/debug/konva/src/filters/RGB.ts +0 -82
  80. package/debug/konva/src/filters/RGBA.ts +0 -103
  81. package/debug/konva/src/filters/Sepia.ts +0 -27
  82. package/debug/konva/src/filters/Solarize.ts +0 -29
  83. package/debug/konva/src/filters/Threshold.ts +0 -44
  84. package/debug/konva/src/index.ts +0 -3
  85. package/debug/konva/src/shapes/Arc.ts +0 -176
  86. package/debug/konva/src/shapes/Arrow.ts +0 -231
  87. package/debug/konva/src/shapes/Circle.ts +0 -76
  88. package/debug/konva/src/shapes/Ellipse.ts +0 -121
  89. package/debug/konva/src/shapes/Image.ts +0 -319
  90. package/debug/konva/src/shapes/Label.ts +0 -386
  91. package/debug/konva/src/shapes/Line.ts +0 -364
  92. package/debug/konva/src/shapes/Path.ts +0 -1013
  93. package/debug/konva/src/shapes/Rect.ts +0 -79
  94. package/debug/konva/src/shapes/RegularPolygon.ts +0 -167
  95. package/debug/konva/src/shapes/Ring.ts +0 -94
  96. package/debug/konva/src/shapes/Sprite.ts +0 -370
  97. package/debug/konva/src/shapes/Star.ts +0 -125
  98. package/debug/konva/src/shapes/Text.ts +0 -1065
  99. package/debug/konva/src/shapes/TextPath.ts +0 -583
  100. package/debug/konva/src/shapes/Transformer.ts +0 -1889
  101. package/debug/konva/src/shapes/Wedge.ts +0 -129
  102. package/debug/konva/src/skia-backend.ts +0 -35
  103. package/debug/konva/src/types.ts +0 -84
  104. package/debug/konva/tsconfig.json +0 -31
  105. package/debug/konva/tsconfig.test.json +0 -7
@@ -14,35 +14,32 @@ import { makeBoundingBoxFromPoints } from '../util/misc/boundingBoxFromPoints.mj
14
14
  import '../cache.mjs';
15
15
  import '../parser/constants.mjs';
16
16
  import '../util/animation/AnimationRegistry.mjs';
17
+ import { Control } from '../controls/Control.mjs';
17
18
  import { cacheProperties } from './Object/defaultValues.mjs';
18
19
 
19
- // @TODO this code is terrible and Line should be a special case of polyline.
20
-
21
20
  const coordProps = ['x1', 'x2', 'y1', 'y2'];
22
- /**
23
- * A Class to draw a line
24
- * A bunch of methods will be added to Polyline to handle the line case
25
- * The line class is very strange to work with, is all special, it hardly aligns
26
- * to what a developer want everytime there is an angle
27
- * @deprecated
28
- */
29
21
  class Line extends FabricObject {
30
- /**
31
- * Constructor
32
- * @param {Array} [points] Array of points
33
- * @param {Object} [options] Options object
34
- * @return {Line} thisArg
35
- */
36
22
  constructor() {
37
- let [x1, y1, x2, y2] = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [0, 0, 0, 0];
23
+ let [x1, y1, x2, y2] = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [0, 0, 100, 0];
38
24
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
39
25
  super();
40
- Object.assign(this, Line.ownDefaults);
26
+ _defineProperty(this, "hitStrokeWidth", 'auto');
27
+ _defineProperty(this, "_updatingEndpoints", false);
28
+ _defineProperty(this, "_useEndpointCoords", true);
41
29
  this.setOptions(options);
42
30
  this.x1 = x1;
43
31
  this.x2 = x2;
44
32
  this.y1 = y1;
45
33
  this.y2 = y2;
34
+ if (options.hitStrokeWidth !== undefined) {
35
+ this.hitStrokeWidth = options.hitStrokeWidth;
36
+ }
37
+ this.hasBorders = false;
38
+ this.hasControls = true;
39
+ this.selectable = true;
40
+ this.hoverCursor = 'move';
41
+ this.perPixelTargetFind = false;
42
+ this.strokeLineCap = 'butt';
46
43
  this._setWidthHeight();
47
44
  const {
48
45
  left,
@@ -50,96 +47,323 @@ class Line extends FabricObject {
50
47
  } = options;
51
48
  typeof left === 'number' && this.set(LEFT, left);
52
49
  typeof top === 'number' && this.set(TOP, top);
50
+ this._setupLineControls();
51
+ }
52
+ _setupLineControls() {
53
+ this.controls = {
54
+ p1: new Control({
55
+ x: 0,
56
+ y: 0,
57
+ cursorStyle: 'move',
58
+ actionHandler: this._endpointActionHandler.bind(this),
59
+ positionHandler: this._p1PositionHandler.bind(this),
60
+ render: this._renderEndpointControl.bind(this),
61
+ sizeX: 12,
62
+ sizeY: 12
63
+ }),
64
+ p2: new Control({
65
+ x: 0,
66
+ y: 0,
67
+ cursorStyle: 'move',
68
+ actionHandler: this._endpointActionHandler.bind(this),
69
+ positionHandler: this._p2PositionHandler.bind(this),
70
+ render: this._renderEndpointControl.bind(this),
71
+ sizeX: 12,
72
+ sizeY: 12
73
+ })
74
+ };
75
+ }
76
+ _p1PositionHandler() {
77
+ var _this$canvas;
78
+ const vpt = ((_this$canvas = this.canvas) === null || _this$canvas === void 0 ? void 0 : _this$canvas.viewportTransform) || [1, 0, 0, 1, 0, 0];
79
+ return new Point(this.x1, this.y1).transform(vpt);
80
+ }
81
+ _p2PositionHandler() {
82
+ var _this$canvas2;
83
+ const vpt = ((_this$canvas2 = this.canvas) === null || _this$canvas2 === void 0 ? void 0 : _this$canvas2.viewportTransform) || [1, 0, 0, 1, 0, 0];
84
+ return new Point(this.x2, this.y2).transform(vpt);
85
+ }
86
+ _renderEndpointControl(ctx, left, top) {
87
+ const size = 12;
88
+ ctx.save();
89
+ ctx.fillStyle = '#007bff';
90
+ ctx.strokeStyle = '#ffffff';
91
+ ctx.lineWidth = 2;
92
+ ctx.beginPath();
93
+ ctx.arc(left, top, size / 2, 0, 2 * Math.PI);
94
+ ctx.fill();
95
+ ctx.stroke();
96
+ ctx.restore();
97
+ }
98
+ drawBorders(ctx) {
99
+ let styleOverride = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
100
+ if (this._useEndpointCoords) {
101
+ this._drawLineBorders(ctx, styleOverride);
102
+ return this;
103
+ }
104
+ return super.drawBorders(ctx, styleOverride, {});
105
+ }
106
+ _drawLineBorders(ctx) {
107
+ var _this$canvas3;
108
+ let styleOverride = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
109
+ const vpt = ((_this$canvas3 = this.canvas) === null || _this$canvas3 === void 0 ? void 0 : _this$canvas3.viewportTransform) || [1, 0, 0, 1, 0, 0];
110
+ ctx.save();
111
+ ctx.setTransform(vpt[0], vpt[1], vpt[2], vpt[3], vpt[4], vpt[5]);
112
+ ctx.strokeStyle = styleOverride.borderColor || this.borderColor || 'rgba(100, 200, 200, 0.5)';
113
+ ctx.lineWidth = (this.strokeWidth || 1) + 5;
114
+ ctx.lineCap = this.strokeLineCap || 'butt';
115
+ ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
116
+ ctx.beginPath();
117
+ ctx.moveTo(this.x1, this.y1);
118
+ ctx.lineTo(this.x2, this.y2);
119
+ ctx.stroke();
120
+ ctx.restore();
121
+ }
122
+ _renderControls(ctx) {
123
+ let styleOverride = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
124
+ ctx.save();
125
+ ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
126
+ this.drawControls(ctx, styleOverride);
127
+ ctx.restore();
128
+ }
129
+ getBoundingRect() {
130
+ if (this._useEndpointCoords) {
131
+ const {
132
+ x1,
133
+ y1,
134
+ x2,
135
+ y2
136
+ } = this;
137
+ const effectiveStrokeWidth = this.hitStrokeWidth === 'auto' ? this.strokeWidth : this.hitStrokeWidth;
138
+ const padding = Math.max(effectiveStrokeWidth / 2 + 5, 10);
139
+ return {
140
+ left: Math.min(x1, x2) - padding,
141
+ top: Math.min(y1, y2) - padding,
142
+ width: Math.abs(x2 - x1) + padding * 2 || padding * 2,
143
+ height: Math.abs(y2 - y1) + padding * 2 || padding * 2
144
+ };
145
+ }
146
+ return super.getBoundingRect();
53
147
  }
148
+ setCoords() {
149
+ if (this._useEndpointCoords) {
150
+ // Set the object's center to the geometric center of the line
151
+ const center = this._findCenterFromElement();
152
+ this.left = center.x;
153
+ this.top = center.y;
54
154
 
55
- /**
56
- * @private
57
- * @param {Object} [options] Options
58
- */
59
- _setWidthHeight() {
60
- const {
61
- x1,
62
- y1,
63
- x2,
64
- y2
65
- } = this;
66
- this.width = Math.abs(x2 - x1);
67
- this.height = Math.abs(y2 - y1);
68
- const {
69
- left,
70
- top,
71
- width,
72
- height
73
- } = makeBoundingBoxFromPoints([{
74
- x: x1,
75
- y: y1
76
- }, {
77
- x: x2,
78
- y: y2
79
- }]);
80
- const position = new Point(left + width / 2, top + height / 2);
81
- this.setPositionByOrigin(position, CENTER, CENTER);
155
+ // Set width and height for hit detection and bounding box
156
+ const effectiveStrokeWidth = this.hitStrokeWidth === 'auto' ? this.strokeWidth : this.hitStrokeWidth;
157
+ const hitPadding = Math.max(effectiveStrokeWidth / 2 + 5, 10);
158
+ this.width = Math.abs(this.x2 - this.x1) + hitPadding * 2;
159
+ this.height = Math.abs(this.y2 - this.y1) + hitPadding * 2;
160
+ }
161
+ super.setCoords();
162
+ }
163
+ getCoords() {
164
+ if (this._useEndpointCoords) {
165
+ const deltaX = this.x2 - this.x1;
166
+ const deltaY = this.y2 - this.y1;
167
+ const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
168
+ if (length === 0) {
169
+ return super.getCoords();
170
+ }
171
+ const effectiveStrokeWidth = this.hitStrokeWidth === 'auto' ? this.strokeWidth : this.hitStrokeWidth;
172
+ const halfWidth = Math.max(effectiveStrokeWidth / 2 + 2, 5);
173
+
174
+ // Unit vector perpendicular to line
175
+ const perpX = -deltaY / length;
176
+ const perpY = deltaX / length;
177
+
178
+ // Four corners of oriented rectangle
179
+ return [new Point(this.x1 + perpX * halfWidth, this.y1 + perpY * halfWidth), new Point(this.x2 + perpX * halfWidth, this.y2 + perpY * halfWidth), new Point(this.x2 - perpX * halfWidth, this.y2 - perpY * halfWidth), new Point(this.x1 - perpX * halfWidth, this.y1 - perpY * halfWidth)];
180
+ }
181
+ return super.getCoords();
182
+ }
183
+ containsPoint(point) {
184
+ if (this._useEndpointCoords) {
185
+ var _this$canvas4;
186
+ if (((_this$canvas4 = this.canvas) === null || _this$canvas4 === void 0 ? void 0 : _this$canvas4.getActiveObject()) === this) {
187
+ return super.containsPoint(point);
188
+ }
189
+ const distance = this._distanceToLineSegment(point.x, point.y);
190
+ const effectiveStrokeWidth = this.hitStrokeWidth === 'auto' ? this.strokeWidth : this.hitStrokeWidth || 1;
191
+ const tolerance = Math.max(effectiveStrokeWidth / 2 + 2, 5);
192
+ return distance <= tolerance;
193
+ }
194
+ return super.containsPoint(point);
195
+ }
196
+ _distanceToLineSegment(px, py) {
197
+ const x1 = this.x1,
198
+ y1 = this.y1,
199
+ x2 = this.x2,
200
+ y2 = this.y2;
201
+ const pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
202
+ if (pd2 === 0) {
203
+ return Math.sqrt((px - x1) * (px - x1) + (py - y1) * (py - y1));
204
+ }
205
+ const u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2;
206
+ let closestX, closestY;
207
+ if (u < 0) {
208
+ closestX = x1;
209
+ closestY = y1;
210
+ } else if (u > 1) {
211
+ closestX = x2;
212
+ closestY = y2;
213
+ } else {
214
+ closestX = x1 + u * (x2 - x1);
215
+ closestY = y1 + u * (y2 - y1);
216
+ }
217
+ return Math.sqrt((px - closestX) * (px - closestX) + (py - closestY) * (py - closestY));
82
218
  }
219
+ _endpointActionHandler(eventData, transformData, x, y) {
220
+ var _this$canvas6;
221
+ const controlKey = transformData.corner;
222
+ const pointer = new Point(x, y);
223
+ let newX = pointer.x;
224
+ let newY = pointer.y;
225
+ if (eventData.shiftKey) {
226
+ const otherControl = controlKey === 'p1' ? 'p2' : 'p1';
227
+ const otherX = this[otherControl === 'p1' ? 'x1' : 'x2'];
228
+ const otherY = this[otherControl === 'p1' ? 'y1' : 'y2'];
229
+ const snapped = this._snapToAngle(otherX, otherY, newX, newY);
230
+ newX = snapped.x;
231
+ newY = snapped.y;
232
+ }
233
+ if (this._useEndpointCoords) {
234
+ var _this$canvas5;
235
+ if (controlKey === 'p1') {
236
+ this.x1 = newX;
237
+ this.y1 = newY;
238
+ } else if (controlKey === 'p2') {
239
+ this.x2 = newX;
240
+ this.y2 = newY;
241
+ }
242
+ this.dirty = true;
243
+ this.setCoords();
244
+ (_this$canvas5 = this.canvas) === null || _this$canvas5 === void 0 || _this$canvas5.requestRenderAll();
245
+ return true;
246
+ }
83
247
 
84
- /**
85
- * @private
86
- * @param {String} key
87
- * @param {*} value
88
- */
248
+ // Fallback for old system
249
+ this._updatingEndpoints = true;
250
+ if (controlKey === 'p1') {
251
+ this.x1 = newX;
252
+ this.y1 = newY;
253
+ } else if (controlKey === 'p2') {
254
+ this.x2 = newX;
255
+ this.y2 = newY;
256
+ }
257
+ this._setWidthHeight();
258
+ this.dirty = true;
259
+ this._updatingEndpoints = false;
260
+ (_this$canvas6 = this.canvas) === null || _this$canvas6 === void 0 || _this$canvas6.requestRenderAll();
261
+ this.fire('modified', {
262
+ transform: transformData,
263
+ target: this,
264
+ e: eventData
265
+ });
266
+ return true;
267
+ }
268
+ _snapToAngle(fromX, fromY, toX, toY) {
269
+ const deltaX = toX - fromX;
270
+ const deltaY = toY - fromY;
271
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
272
+ if (distance === 0) return {
273
+ x: toX,
274
+ y: toY
275
+ };
276
+ let angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
277
+ const snapIncrement = 15;
278
+ const snappedAngle = Math.round(angle / snapIncrement) * snapIncrement;
279
+ const snappedRadians = snappedAngle * (Math.PI / 180);
280
+ return {
281
+ x: fromX + Math.cos(snappedRadians) * distance,
282
+ y: fromY + Math.sin(snappedRadians) * distance
283
+ };
284
+ }
285
+ _setWidthHeight() {
286
+ let skipReposition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
287
+ this.width = Math.abs(this.x2 - this.x1) || 1;
288
+ this.height = Math.abs(this.y2 - this.y1) || 1;
289
+ if (!skipReposition && !this._updatingEndpoints) {
290
+ const {
291
+ left,
292
+ top,
293
+ width,
294
+ height
295
+ } = makeBoundingBoxFromPoints([{
296
+ x: this.x1,
297
+ y: this.y1
298
+ }, {
299
+ x: this.x2,
300
+ y: this.y2
301
+ }]);
302
+ this.setPositionByOrigin(new Point(left + width / 2, top + height / 2), CENTER, CENTER);
303
+ }
304
+ }
89
305
  _set(key, value) {
306
+ const oldLeft = this.left;
307
+ const oldTop = this.top;
90
308
  super._set(key, value);
91
309
  if (coordProps.includes(key)) {
92
- // this doesn't make sense very much, since setting x1 when top or left
93
- // are already set, is just going to show a strange result since the
94
- // line will move way more than the developer expect.
95
- // in fabric5 it worked only when the line didn't have extra transformations,
96
- // in fabric6 too. With extra transform they behave bad in different ways.
97
- // This needs probably a good rework or a tutorial if you have to create a dynamic line
98
310
  this._setWidthHeight();
311
+ this.dirty = true;
312
+ }
313
+ if ((key === 'left' || key === 'top') && this.canvas && !this._updatingEndpoints) {
314
+ const deltaX = this.left - oldLeft;
315
+ const deltaY = this.top - oldTop;
316
+ if (deltaX !== 0 || deltaY !== 0) {
317
+ this._updatingEndpoints = true;
318
+ this.x1 += deltaX;
319
+ this.y1 += deltaY;
320
+ this.x2 += deltaX;
321
+ this.y2 += deltaY;
322
+ this._updatingEndpoints = false;
323
+ }
99
324
  }
100
325
  return this;
101
326
  }
102
-
103
- /**
104
- * @private
105
- * @param {CanvasRenderingContext2D} ctx Context to render on
106
- */
327
+ render(ctx) {
328
+ if (this._useEndpointCoords) {
329
+ this._renderDirectly(ctx);
330
+ return;
331
+ }
332
+ super.render(ctx);
333
+ }
334
+ _renderDirectly(ctx) {
335
+ var _this$canvas7, _this$stroke;
336
+ if (!this.visible) return;
337
+ ctx.save();
338
+ const vpt = ((_this$canvas7 = this.canvas) === null || _this$canvas7 === void 0 ? void 0 : _this$canvas7.viewportTransform) || [1, 0, 0, 1, 0, 0];
339
+ ctx.transform(vpt[0], vpt[1], vpt[2], vpt[3], vpt[4], vpt[5]);
340
+ ctx.globalAlpha = this.opacity;
341
+ ctx.strokeStyle = ((_this$stroke = this.stroke) === null || _this$stroke === void 0 ? void 0 : _this$stroke.toString()) || '#000';
342
+ ctx.lineWidth = this.strokeWidth;
343
+ ctx.lineCap = this.strokeLineCap || 'butt';
344
+ ctx.beginPath();
345
+ ctx.moveTo(this.x1, this.y1);
346
+ ctx.lineTo(this.x2, this.y2);
347
+ ctx.stroke();
348
+ ctx.restore();
349
+ }
107
350
  _render(ctx) {
351
+ if (this._useEndpointCoords) return;
108
352
  ctx.beginPath();
109
353
  const p = this.calcLinePoints();
110
354
  ctx.moveTo(p.x1, p.y1);
111
355
  ctx.lineTo(p.x2, p.y2);
112
356
  ctx.lineWidth = this.strokeWidth;
113
-
114
- // TODO: test this
115
- // make sure setting "fill" changes color of a line
116
- // (by copying fillStyle to strokeStyle, since line is stroked, not filled)
117
357
  const origStrokeStyle = ctx.strokeStyle;
118
358
  if (isFiller(this.stroke)) {
119
359
  ctx.strokeStyle = this.stroke.toLive(ctx);
120
- } else {
121
- var _this$stroke;
122
- ctx.strokeStyle = (_this$stroke = this.stroke) !== null && _this$stroke !== void 0 ? _this$stroke : ctx.fillStyle;
123
360
  }
124
361
  this.stroke && this._renderStroke(ctx);
125
362
  ctx.strokeStyle = origStrokeStyle;
126
363
  }
127
-
128
- /**
129
- * This function is an helper for svg import. it returns the center of the object in the svg
130
- * untransformed coordinates
131
- * @private
132
- * @return {Point} center point from element coordinates
133
- */
134
364
  _findCenterFromElement() {
135
365
  return new Point((this.x1 + this.x2) / 2, (this.y1 + this.y2) / 2);
136
366
  }
137
-
138
- /**
139
- * Returns object representation of an instance
140
- * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
141
- * @return {Object} object representation of an instance
142
- */
143
367
  toObject() {
144
368
  let propertiesToInclude = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
145
369
  return {
@@ -147,32 +371,25 @@ class Line extends FabricObject {
147
371
  ...this.calcLinePoints()
148
372
  };
149
373
  }
150
-
151
- /*
152
- * Calculate object dimensions from its properties
153
- * @private
154
- */
155
374
  _getNonTransformedDimensions() {
156
375
  const dim = super._getNonTransformedDimensions();
157
- if (this.strokeLineCap === 'butt') {
158
- if (this.width === 0) {
159
- dim.y -= this.strokeWidth;
160
- }
161
- if (this.height === 0) {
162
- dim.x -= this.strokeWidth;
163
- }
376
+ if (this.strokeLineCap === 'round') {
377
+ dim.x += this.strokeWidth;
378
+ dim.y += this.strokeWidth;
164
379
  }
165
380
  return dim;
166
381
  }
167
-
168
- /**
169
- * Recalculates line points given width and height
170
- * Those points are simply placed around the center,
171
- * This is not useful outside internal render functions and svg output
172
- * Is not meant to be for the developer.
173
- * @private
174
- */
175
382
  calcLinePoints() {
383
+ if (this._updatingEndpoints) {
384
+ const centerX = (this.x1 + this.x2) / 2;
385
+ const centerY = (this.y1 + this.y2) / 2;
386
+ return {
387
+ x1: this.x1 - centerX,
388
+ y1: this.y1 - centerY,
389
+ x2: this.x2 - centerX,
390
+ y2: this.y2 - centerY
391
+ };
392
+ }
176
393
  const {
177
394
  x1: _x1,
178
395
  x2: _x2,
@@ -181,48 +398,39 @@ class Line extends FabricObject {
181
398
  width,
182
399
  height
183
400
  } = this;
184
- const xMult = _x1 <= _x2 ? -1 : 1,
185
- yMult = _y1 <= _y2 ? -1 : 1,
186
- x1 = xMult * width / 2,
187
- y1 = yMult * height / 2,
188
- x2 = xMult * -width / 2,
189
- y2 = yMult * -height / 2;
401
+ const xMult = _x1 <= _x2 ? -1 : 1;
402
+ const yMult = _y1 <= _y2 ? -1 : 1;
190
403
  return {
191
- x1,
192
- x2,
193
- y1,
194
- y2
404
+ x1: xMult * width / 2,
405
+ y1: yMult * height / 2,
406
+ x2: xMult * -width / 2,
407
+ y2: yMult * -height / 2
195
408
  };
196
409
  }
197
-
198
- /* _FROM_SVG_START_ */
199
-
200
- /**
201
- * Returns svg representation of an instance
202
- * @return {Array} an array of strings with the specific svg representation
203
- * of the instance
204
- */
205
410
  _toSVG() {
206
- const {
207
- x1,
208
- x2,
209
- y1,
210
- y2
211
- } = this.calcLinePoints();
212
- return ['<line ', 'COMMON_PARTS', `x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" />\n`];
411
+ if (this._useEndpointCoords) {
412
+ // Use absolute coordinates to bypass all Fabric.js transforms
413
+ return [`<line stroke="${this.stroke}" stroke-width="${this.strokeWidth}" stroke-linecap="${this.strokeLineCap}" `, `x1="${this.x1}" y1="${this.y1}" x2="${this.x2}" y2="${this.y2}" />\n`];
414
+ } else {
415
+ // Use standard calcLinePoints for legacy mode
416
+ const {
417
+ x1,
418
+ x2,
419
+ y1,
420
+ y2
421
+ } = this.calcLinePoints();
422
+ return ['<line ', 'COMMON_PARTS', `x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" />\n`];
423
+ }
424
+ }
425
+ toSVG(reviver) {
426
+ if (this._useEndpointCoords) {
427
+ // Override toSVG to prevent Fabric.js from adding transform wrapper
428
+ const markup = this._toSVG().join('');
429
+ return reviver ? reviver(markup) : markup;
430
+ }
431
+ // Use default behavior for legacy mode
432
+ return super.toSVG(reviver);
213
433
  }
214
-
215
- /**
216
- * List of attribute names to account for when parsing SVG element (used by {@link Line.fromElement})
217
- * @see http://www.w3.org/TR/SVG/shapes.html#LineElement
218
- */
219
-
220
- /**
221
- * Returns Line instance from an SVG element
222
- * @param {HTMLElement} element Element to parse
223
- * @param {Object} [options] Options object
224
- * @param {Function} [callback] callback function invoked after parsing
225
- */
226
434
  static async fromElement(element, options, cssRules) {
227
435
  const {
228
436
  x1 = 0,
@@ -233,14 +441,6 @@ class Line extends FabricObject {
233
441
  } = parseAttributes(element, this.ATTRIBUTE_NAMES, cssRules);
234
442
  return new this([x1, y1, x2, y2], parsedAttributes);
235
443
  }
236
-
237
- /* _FROM_SVG_END_ */
238
-
239
- /**
240
- * Returns Line instance from an object representation
241
- * @param {Object} object Object to create an instance from
242
- * @returns {Promise<Line>}
243
- */
244
444
  static fromObject(_ref) {
245
445
  let {
246
446
  x1,
@@ -257,22 +457,6 @@ class Line extends FabricObject {
257
457
  });
258
458
  }
259
459
  }
260
- /**
261
- * x value or first line edge
262
- * @type number
263
- */
264
- /**
265
- * y value or first line edge
266
- * @type number
267
- */
268
- /**
269
- * x value or second line edge
270
- * @type number
271
- */
272
- /**
273
- * y value or second line edge
274
- * @type number
275
- */
276
460
  _defineProperty(Line, "type", 'Line');
277
461
  _defineProperty(Line, "cacheProperties", [...cacheProperties, ...coordProps]);
278
462
  _defineProperty(Line, "ATTRIBUTE_NAMES", SHARED_ATTRIBUTES.concat(coordProps));