diff --git a/build.js b/build.js index 175cdd2f..1f391c72 100644 --- a/build.js +++ b/build.js @@ -112,6 +112,8 @@ var filesToInclude = [ 'src/color.class.js', 'src/static_canvas.class.js', + + ifSpecifiedInclude('interaction', 'src/freedrawing.class.js'), ifSpecifiedInclude('interaction', 'src/canvas.class.js'), 'src/canvas.animation.js', @@ -162,4 +164,4 @@ appendFileContents(filesToInclude, function() { }); }); }); -}); \ No newline at end of file +}); diff --git a/dist/all.js b/dist/all.js index 23a5d164..63c55a20 100644 --- a/dist/all.js +++ b/dist/all.js @@ -4536,6 +4536,13 @@ fabric.util.string = { return Math.sqrt(dx * dx + dy * dy); }, + /** + * Return the point between A (x,y) and B (x,y) + */ + midPointFrom: function (that) { + return new Point(this.x + (that.x - this.x)/2, this.y + (that.y - this.y)/2); + }, + min: function (that) { return new Point(Math.min(this.x, that.x), Math.min(this.y, that.y)); }, @@ -4569,6 +4576,7 @@ fabric.util.string = { }; })(typeof exports !== 'undefined' ? exports : this); + (function(global) { "use strict"; @@ -6191,6 +6199,282 @@ fabric.util.string = { fabric.StaticCanvas.prototype.toJSON = fabric.StaticCanvas.prototype.toObject; })(); + +(function(global) { + + "use strict"; + + var fabric = global.fabric || (global.fabric = { }); + + var utilMin = fabric.util.array.min, + utilMax = fabric.util.array.max; + + if (fabric.FreeDrawing) { + fabric.warn('fabric.FreeDrawin is already defined'); + return; + } + + fabric.FreeDrawing = FreeDrawing; + + function FreeDrawing(fabricCanvas) { + this.init(fabricCanvas); + } + + FreeDrawing.prototype =  { + constructor: FreeDrawing, + + /** + * Free Drawer handles scribbling on a fabricCanvas. + * It converts the hand writting to a SVG Path and adds this + * path to the canvas. + * + * @metod init + * @param fabricCavnas {FabricCanvas} + * + */ + init: function(fabricCanvas) { + this.canvas = fabricCanvas; + this._points = []; + this._color = this.canvas.freeDrawingColor; + this._strokeWidth = this.canvas.freeDrawingLineWidth; + }, + + /** + * Set path color + * @method setColor + * @param color {String/rgb/rgba} + * + */ + setColor: function(color) { + this._color = color; + }, + + /** + * Set path thichness (strokeWidth) + * @method setThickness + * @param thickness {int} + * + */ + setThickness: function(thickness) { + this._strokeWidth = thickness; + }, + + /** + * @private + * @method _addPoint + * + */ + _addPoint: function(point) { + this._points.push(point); + }, + + /** + * Clear points array and set contextTop canvas + * style. + * + * @private + * @method _reset + * + */ + _reset: function() { + this._points.length = 0; + var ctx = this.canvas.contextTop; + + // set freehanddrawing line canvas parameters + ctx.strokeStyle = this._color; + ctx.lineWidth = this._strokeWidth; + ctx.lineCap = ctx.lineJoin = 'round'; + }, + + /** + * @method _prepareForDrawing + */ + _prepareForDrawing: function(pointer) { + + this.canvas._isCurrentlyDrawing = true; + this.canvas.discardActiveObject().renderAll(); + + var p = new fabric.Point(pointer.x, pointer.y); + this._reset(); + this._addPoint(p); + this.canvas.contextTop.moveTo(p.x, p.y); + + }, + + + /** + * @private + * @method _captureDrawingPath + * + * @param point {pointer} (fabric.util.pointer) actual mouse position + * related to the canvas. + */ + _captureDrawingPath: function(pointer) { + var MIN_POINT_DISTANCE = 2; + var pointerPoint = new fabric.Point(pointer.x, pointer.y); + // Only push point if min_point_distance is reached + var lastPoint = this._points[this._points.length -1]; + + if (pointerPoint.distanceFrom(lastPoint) > MIN_POINT_DISTANCE) + this._addPoint(pointerPoint); + + if (this._points.length < 2) + this._addPoint(pointerPoint); + }, + + /** + * Draw a smooth path on the topCanvas using + * quadraticCurveTo. + * + * @private + * @method _render + * + */ + _render: function() { + var ctx = this.canvas.contextTop; + ctx.beginPath(); + + var p1 = this._points[0]; + var p2 = this._points[1]; + + for (var i = 1, len = this._points.length; i < len; i++) { + // we pick the point between pi+1 & pi+2 as the + // end point and p1 as our control point. + var midPoint = p1.midPointFrom(p2); + ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); + + var p1 = this._points[i]; + var p2 = this._points[i+1]; + } + // Draw last line as a straight line while + // we wait for the next point to be able to calculate + // the bezier control point + ctx.lineTo(p1.x, p1.y); + ctx.stroke(); + }, + + /** + * Return an SVG path based on our + * captured points and their boundinb box. + * + * @private + * @method _getSVGPathData + * + */ + _getSVGPathData: function() { + this.box = this.getPathBoundingBox(this._points); + return this.convertPointsToSVGPath(this._points, this.box.minx, this.box.maxx, this.box.miny, this.box.maxy); + }, + + /** + * @method getPathBoundingBox + * @param points {Array of points} + */ + getPathBoundingBox: function(points) { + var xBounds = [], + yBounds = [], + p1 = points[0], + p2 = points[1], + startPoint = p1; + + for (var i = 1, len = points.length; i < len; i++) { + var midPoint = p1.midPointFrom(p2); + // with startPoint, p1 as control point, midpoint as end point + xBounds.push(startPoint.x); + xBounds.push(midPoint.x); + yBounds.push(startPoint.y); + yBounds.push(midPoint.y); + + p1 = points[i]; + p2 = points[i+1]; + startPoint = midPoint; + } // end for + + xBounds.push(p1.x); + yBounds.push(p1.y); + + return { + minx: utilMin(xBounds), + miny: utilMin(yBounds), + maxx: utilMax(xBounds), + maxy: utilMax(yBounds) + }; + }, + + /** + * @method convertPointsToSVGPath + * @param points {Array of points} + */ + convertPointsToSVGPath: function(points, minX, maxX, minY, maxY) { + var path = []; + var p1 = new fabric.Point(points[0].x - minX, points[0].y - minY); + var p2 = new fabric.Point(points[1].x - minX, points[1].y - minY); + + path.push('M ', points[0].x - minX, ' ', points[0].y - minY, ' '); + for (var i = 1, len = points.length; i < len; i++) { + var midPoint = p1.midPointFrom(p2); + // p1 is our bezier control point + // midpoint is our endpoint + // start point is p(i-1) value. + path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' '); + p1 = new fabric.Point(points[i].x - minX, points[i].y - minY); + if ((i+1) < points.length) + p2 = new fabric.Point(points[i+1].x - minX, points[i+1].y - minY); + } + path.push('L ', p1.x, ' ', p1.y, ' '); + return path; + }, + + /** + * On mouseup after drawing the path on contextTop canvas + * we use the points captured to create an new fabric path object + * and add it to the fabric canvas. + * + * @method _finalizeAndAddPath + * + */ + + _finalizeAndAddPath: function() { + var ctx = this.canvas.contextTop; + ctx.closePath(); + var path = this._getSVGPathData(); + path = path.join(''); + + if (path === "M 0 0 Q 0 0 0 0 L 0 0") { + // do not create 0 width/height paths, as they are + // rendered inconsistently across browsers + // Firefox 4, for example, renders a dot, + // whereas Chrome 10 renders nothing + fabricCanvas.renderAll(); + return; + } + + var p = new fabric.Path(path); + p.fill = null; + p.stroke = this._color; + p.strokeWidth = this._strokeWidth; + this.canvas.add(p); + + // set path origin coordinates based on our bouding box + var originLeft = this.box.minx + (this.box.maxx - this.box.minx) /2; + var originTop = this.box.miny + (this.box.maxy - this.box.miny) /2; + + this.canvas.contextTop.arc(originLeft, originTop, 3); + + p.set("left", originLeft); + p.set("top", originTop) + // does not change position + p.setCoords(); + + this.canvas.renderAll(); + + // fire event 'path' created + this.canvas.fire('path:created', { path: p }); + } + }; + +})(typeof exports !== 'undefined' ? exports : this); + (function() { var extend = fabric.util.object.extend, @@ -6364,8 +6648,7 @@ fabric.util.string = { _initInteractive: function() { this._currentTransform = null; this._groupSelector = null; - this._freeDrawingXPoints = [ ]; - this._freeDrawingYPoints = [ ]; + this.freeDrawing = new fabric.FreeDrawing(this); this._initWrapperElement(); this._createUpperCanvas(); this._initEvents(); @@ -6442,7 +6725,8 @@ fabric.util.string = { var target; if (this.isDrawingMode && this._isCurrentlyDrawing) { - this._finalizeDrawingPath(); + this._isCurrentlyDrawing = false; + this.freeDrawing._finalizeAndAddPath(); this.fire('mouse:up', { e: e }); return; } @@ -6517,10 +6801,13 @@ fabric.util.string = { if (!isLeftClick && !fabric.isTouchSupported) return; if (this.isDrawingMode) { - this._prepareForDrawing(e); + var pointer = this.getPointer(e); + this.freeDrawing._prepareForDrawing(pointer); + + // capture coordinates immediately; + // this allows to draw dots (when movement never occurs) + this.freeDrawing._captureDrawingPath(pointer); - // capture coordinates immediately; this allows to draw dots (when movement never occurs) - this._captureDrawingPath(e); this.fire('mouse:down', { e: e }); return; } @@ -6590,7 +6877,13 @@ fabric.util.string = { if (this.isDrawingMode) { if (this._isCurrentlyDrawing) { - this._captureDrawingPath(e); + var pointer = this.getPointer(e); + this.freeDrawing._captureDrawingPath(pointer); + + // redraw curve + // clear top canvas + this.clearContext(this.contextTop); + this.freeDrawing._render(this.contextTop); } this.fire('mouse:move', { e: e }); return; @@ -6902,91 +7195,6 @@ fabric.util.string = { } }, - /** - * @private - * @method _prepareForDrawing - */ - _prepareForDrawing: function(e) { - - this._isCurrentlyDrawing = true; - - this.discardActiveObject().renderAll(); - - var pointer = this.getPointer(e); - - this._freeDrawingXPoints.length = this._freeDrawingYPoints.length = 0; - - this._freeDrawingXPoints.push(pointer.x); - this._freeDrawingYPoints.push(pointer.y); - - this.contextTop.beginPath(); - this.contextTop.moveTo(pointer.x, pointer.y); - this.contextTop.strokeStyle = this.freeDrawingColor; - this.contextTop.lineWidth = this.freeDrawingLineWidth; - this.contextTop.lineCap = this.contextTop.lineJoin = 'round'; - }, - - /** - * @private - * @method _captureDrawingPath - */ - _captureDrawingPath: function(e) { - var pointer = this.getPointer(e); - - this._freeDrawingXPoints.push(pointer.x); - this._freeDrawingYPoints.push(pointer.y); - - this.contextTop.lineTo(pointer.x, pointer.y); - this.contextTop.stroke(); - }, - - /** - * @private - * @method _finalizeDrawingPath - */ - _finalizeDrawingPath: function() { - - this.contextTop.closePath(); - - this._isCurrentlyDrawing = false; - - var minX = utilMin(this._freeDrawingXPoints), - minY = utilMin(this._freeDrawingYPoints), - maxX = utilMax(this._freeDrawingXPoints), - maxY = utilMax(this._freeDrawingYPoints), - path = [ ], - xPoints = this._freeDrawingXPoints, - yPoints = this._freeDrawingYPoints; - - path.push('M ', xPoints[0] - minX, ' ', yPoints[0] - minY, ' '); - - for (var i = 1, len = xPoints.length; i < len; i++) { - path.push('L ', xPoints[i] - minX, ' ', yPoints[i] - minY, ' '); - } - - // TODO (kangax): maybe remove Path creation from here, to decouple fabric.Canvas from fabric.Path, - // and instead fire something like "drawing:completed" event with path string - - path = path.join(''); - - if (path === "M 0 0 L 0 0 ") { - // do not create 0 width/height paths, as they are rendered inconsistently across browsers - // Firefox 4, for example, renders a dot, whereas Chrome 10 renders nothing - this.renderAll(); - return; - } - - var p = new fabric.Path(path); - - p.fill = null; - p.stroke = this.freeDrawingColor; - p.strokeWidth = this.freeDrawingLineWidth; - this.add(p); - p.set("left", minX + (maxX - minX) / 2).set("top", minY + (maxY - minY) / 2).setCoords(); - this.renderAll(); - this.fire('path:created', { path: p }); - }, - /** * Translates object by "setting" its left/top * @method _translateObject @@ -11550,6 +11758,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, { }; })(typeof exports !== 'undefined' ? exports : this); + (function(global) { "use strict"; diff --git a/src/canvas.class.js b/src/canvas.class.js index ae5e4c85..3d077ddf 100644 --- a/src/canvas.class.js +++ b/src/canvas.class.js @@ -171,8 +171,7 @@ _initInteractive: function() { this._currentTransform = null; this._groupSelector = null; - this._freeDrawingXPoints = [ ]; - this._freeDrawingYPoints = [ ]; + this.freeDrawing = new fabric.FreeDrawing(this); this._initWrapperElement(); this._createUpperCanvas(); this._initEvents(); @@ -249,7 +248,8 @@ var target; if (this.isDrawingMode && this._isCurrentlyDrawing) { - this._finalizeDrawingPath(); + this._isCurrentlyDrawing = false; + this.freeDrawing._finalizeAndAddPath(); this.fire('mouse:up', { e: e }); return; } @@ -324,10 +324,13 @@ if (!isLeftClick && !fabric.isTouchSupported) return; if (this.isDrawingMode) { - this._prepareForDrawing(e); + var pointer = this.getPointer(e); + this.freeDrawing._prepareForDrawing(pointer); + + // capture coordinates immediately; + // this allows to draw dots (when movement never occurs) + this.freeDrawing._captureDrawingPath(pointer); - // capture coordinates immediately; this allows to draw dots (when movement never occurs) - this._captureDrawingPath(e); this.fire('mouse:down', { e: e }); return; } @@ -397,7 +400,13 @@ if (this.isDrawingMode) { if (this._isCurrentlyDrawing) { - this._captureDrawingPath(e); + var pointer = this.getPointer(e); + this.freeDrawing._captureDrawingPath(pointer); + + // redraw curve + // clear top canvas + this.clearContext(this.contextTop); + this.freeDrawing._render(this.contextTop); } this.fire('mouse:move', { e: e }); return; @@ -709,91 +718,6 @@ } }, - /** - * @private - * @method _prepareForDrawing - */ - _prepareForDrawing: function(e) { - - this._isCurrentlyDrawing = true; - - this.discardActiveObject().renderAll(); - - var pointer = this.getPointer(e); - - this._freeDrawingXPoints.length = this._freeDrawingYPoints.length = 0; - - this._freeDrawingXPoints.push(pointer.x); - this._freeDrawingYPoints.push(pointer.y); - - this.contextTop.beginPath(); - this.contextTop.moveTo(pointer.x, pointer.y); - this.contextTop.strokeStyle = this.freeDrawingColor; - this.contextTop.lineWidth = this.freeDrawingLineWidth; - this.contextTop.lineCap = this.contextTop.lineJoin = 'round'; - }, - - /** - * @private - * @method _captureDrawingPath - */ - _captureDrawingPath: function(e) { - var pointer = this.getPointer(e); - - this._freeDrawingXPoints.push(pointer.x); - this._freeDrawingYPoints.push(pointer.y); - - this.contextTop.lineTo(pointer.x, pointer.y); - this.contextTop.stroke(); - }, - - /** - * @private - * @method _finalizeDrawingPath - */ - _finalizeDrawingPath: function() { - - this.contextTop.closePath(); - - this._isCurrentlyDrawing = false; - - var minX = utilMin(this._freeDrawingXPoints), - minY = utilMin(this._freeDrawingYPoints), - maxX = utilMax(this._freeDrawingXPoints), - maxY = utilMax(this._freeDrawingYPoints), - path = [ ], - xPoints = this._freeDrawingXPoints, - yPoints = this._freeDrawingYPoints; - - path.push('M ', xPoints[0] - minX, ' ', yPoints[0] - minY, ' '); - - for (var i = 1, len = xPoints.length; i < len; i++) { - path.push('L ', xPoints[i] - minX, ' ', yPoints[i] - minY, ' '); - } - - // TODO (kangax): maybe remove Path creation from here, to decouple fabric.Canvas from fabric.Path, - // and instead fire something like "drawing:completed" event with path string - - path = path.join(''); - - if (path === "M 0 0 L 0 0 ") { - // do not create 0 width/height paths, as they are rendered inconsistently across browsers - // Firefox 4, for example, renders a dot, whereas Chrome 10 renders nothing - this.renderAll(); - return; - } - - var p = new fabric.Path(path); - - p.fill = null; - p.stroke = this.freeDrawingColor; - p.strokeWidth = this.freeDrawingLineWidth; - this.add(p); - p.set("left", minX + (maxX - minX) / 2).set("top", minY + (maxY - minY) / 2).setCoords(); - this.renderAll(); - this.fire('path:created', { path: p }); - }, - /** * Translates object by "setting" its left/top * @method _translateObject diff --git a/src/freedrawing.class.js b/src/freedrawing.class.js new file mode 100644 index 00000000..36f5affc --- /dev/null +++ b/src/freedrawing.class.js @@ -0,0 +1,274 @@ +(function(global) { + + "use strict"; + + var fabric = global.fabric || (global.fabric = { }); + + var utilMin = fabric.util.array.min, + utilMax = fabric.util.array.max; + + if (fabric.FreeDrawing) { + fabric.warn('fabric.FreeDrawin is already defined'); + return; + } + + fabric.FreeDrawing = FreeDrawing; + + function FreeDrawing(fabricCanvas) { + this.init(fabricCanvas); + } + + FreeDrawing.prototype =  { + constructor: FreeDrawing, + + /** + * Free Drawer handles scribbling on a fabricCanvas. + * It converts the hand writting to a SVG Path and adds this + * path to the canvas. + * + * @metod init + * @param fabricCavnas {FabricCanvas} + * + */ + init: function(fabricCanvas) { + this.canvas = fabricCanvas; + this._points = []; + this._color = this.canvas.freeDrawingColor; + this._strokeWidth = this.canvas.freeDrawingLineWidth; + }, + + /** + * Set path color + * @method setColor + * @param color {String/rgb/rgba} + * + */ + setColor: function(color) { + this._color = color; + }, + + /** + * Set path thichness (strokeWidth) + * @method setThickness + * @param thickness {int} + * + */ + setThickness: function(thickness) { + this._strokeWidth = thickness; + }, + + /** + * @private + * @method _addPoint + * + */ + _addPoint: function(point) { + this._points.push(point); + }, + + /** + * Clear points array and set contextTop canvas + * style. + * + * @private + * @method _reset + * + */ + _reset: function() { + this._points.length = 0; + var ctx = this.canvas.contextTop; + + // set freehanddrawing line canvas parameters + ctx.strokeStyle = this._color; + ctx.lineWidth = this._strokeWidth; + ctx.lineCap = ctx.lineJoin = 'round'; + }, + + /** + * @method _prepareForDrawing + */ + _prepareForDrawing: function(pointer) { + + this.canvas._isCurrentlyDrawing = true; + this.canvas.discardActiveObject().renderAll(); + + var p = new fabric.Point(pointer.x, pointer.y); + this._reset(); + this._addPoint(p); + this.canvas.contextTop.moveTo(p.x, p.y); + + }, + + + /** + * @private + * @method _captureDrawingPath + * + * @param point {pointer} (fabric.util.pointer) actual mouse position + * related to the canvas. + */ + _captureDrawingPath: function(pointer) { + var MIN_POINT_DISTANCE = 2; + var pointerPoint = new fabric.Point(pointer.x, pointer.y); + // Only push point if min_point_distance is reached + var lastPoint = this._points[this._points.length -1]; + + if (pointerPoint.distanceFrom(lastPoint) > MIN_POINT_DISTANCE) + this._addPoint(pointerPoint); + + if (this._points.length < 2) + this._addPoint(pointerPoint); + }, + + /** + * Draw a smooth path on the topCanvas using + * quadraticCurveTo. + * + * @private + * @method _render + * + */ + _render: function() { + var ctx = this.canvas.contextTop; + ctx.beginPath(); + + var p1 = this._points[0]; + var p2 = this._points[1]; + + for (var i = 1, len = this._points.length; i < len; i++) { + // we pick the point between pi+1 & pi+2 as the + // end point and p1 as our control point. + var midPoint = p1.midPointFrom(p2); + ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); + + var p1 = this._points[i]; + var p2 = this._points[i+1]; + } + // Draw last line as a straight line while + // we wait for the next point to be able to calculate + // the bezier control point + ctx.lineTo(p1.x, p1.y); + ctx.stroke(); + }, + + /** + * Return an SVG path based on our + * captured points and their boundinb box. + * + * @private + * @method _getSVGPathData + * + */ + _getSVGPathData: function() { + this.box = this.getPathBoundingBox(this._points); + return this.convertPointsToSVGPath(this._points, this.box.minx, this.box.maxx, this.box.miny, this.box.maxy); + }, + + /** + * @method getPathBoundingBox + * @param points {Array of points} + */ + getPathBoundingBox: function(points) { + var xBounds = [], + yBounds = [], + p1 = points[0], + p2 = points[1], + startPoint = p1; + + for (var i = 1, len = points.length; i < len; i++) { + var midPoint = p1.midPointFrom(p2); + // with startPoint, p1 as control point, midpoint as end point + xBounds.push(startPoint.x); + xBounds.push(midPoint.x); + yBounds.push(startPoint.y); + yBounds.push(midPoint.y); + + p1 = points[i]; + p2 = points[i+1]; + startPoint = midPoint; + } // end for + + xBounds.push(p1.x); + yBounds.push(p1.y); + + return { + minx: utilMin(xBounds), + miny: utilMin(yBounds), + maxx: utilMax(xBounds), + maxy: utilMax(yBounds) + }; + }, + + /** + * @method convertPointsToSVGPath + * @param points {Array of points} + */ + convertPointsToSVGPath: function(points, minX, maxX, minY, maxY) { + var path = []; + var p1 = new fabric.Point(points[0].x - minX, points[0].y - minY); + var p2 = new fabric.Point(points[1].x - minX, points[1].y - minY); + + path.push('M ', points[0].x - minX, ' ', points[0].y - minY, ' '); + for (var i = 1, len = points.length; i < len; i++) { + var midPoint = p1.midPointFrom(p2); + // p1 is our bezier control point + // midpoint is our endpoint + // start point is p(i-1) value. + path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' '); + p1 = new fabric.Point(points[i].x - minX, points[i].y - minY); + if ((i+1) < points.length) + p2 = new fabric.Point(points[i+1].x - minX, points[i+1].y - minY); + } + path.push('L ', p1.x, ' ', p1.y, ' '); + return path; + }, + + /** + * On mouseup after drawing the path on contextTop canvas + * we use the points captured to create an new fabric path object + * and add it to the fabric canvas. + * + * @method _finalizeAndAddPath + * + */ + + _finalizeAndAddPath: function() { + var ctx = this.canvas.contextTop; + ctx.closePath(); + var path = this._getSVGPathData(); + path = path.join(''); + + if (path === "M 0 0 Q 0 0 0 0 L 0 0") { + // do not create 0 width/height paths, as they are + // rendered inconsistently across browsers + // Firefox 4, for example, renders a dot, + // whereas Chrome 10 renders nothing + fabricCanvas.renderAll(); + return; + } + + var p = new fabric.Path(path); + p.fill = null; + p.stroke = this._color; + p.strokeWidth = this._strokeWidth; + this.canvas.add(p); + + // set path origin coordinates based on our bouding box + var originLeft = this.box.minx + (this.box.maxx - this.box.minx) /2; + var originTop = this.box.miny + (this.box.maxy - this.box.miny) /2; + + this.canvas.contextTop.arc(originLeft, originTop, 3); + + p.set("left", originLeft); + p.set("top", originTop) + // does not change position + p.setCoords(); + + this.canvas.renderAll(); + + // fire event 'path' created + this.canvas.fire('path:created', { path: p }); + } + }; + +})(typeof exports !== 'undefined' ? exports : this); diff --git a/src/path.class.js b/src/path.class.js index ac8ad65e..90a20a98 100644 --- a/src/path.class.js +++ b/src/path.class.js @@ -760,4 +760,4 @@ return new fabric.Path(parsedAttributes.d, extend(parsedAttributes, options)); }; -})(typeof exports !== 'undefined' ? exports : this); \ No newline at end of file +})(typeof exports !== 'undefined' ? exports : this); diff --git a/src/point.class.js b/src/point.class.js index ba455a46..1bc3f473 100644 --- a/src/point.class.js +++ b/src/point.class.js @@ -161,6 +161,13 @@ return Math.sqrt(dx * dx + dy * dy); }, + /** + * Return the point between A (x,y) and B (x,y) + */ + midPointFrom: function (that) { + return new Point(this.x + (that.x - this.x)/2, this.y + (that.y - this.y)/2); + }, + min: function (that) { return new Point(Math.min(this.x, that.x), Math.min(this.y, that.y)); }, @@ -193,4 +200,4 @@ } }; -})(typeof exports !== 'undefined' ? exports : this); \ No newline at end of file +})(typeof exports !== 'undefined' ? exports : this); diff --git a/src/static_canvas.class.js b/src/static_canvas.class.js index 4e4418a4..d502a966 100644 --- a/src/static_canvas.class.js +++ b/src/static_canvas.class.js @@ -1188,4 +1188,4 @@ */ fabric.StaticCanvas.prototype.toJSON = fabric.StaticCanvas.prototype.toObject; -})(); \ No newline at end of file +})();