(function() { var degreesToRadians = fabric.util.degreesToRadians; fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ { /** * Returns true if object intersects with an area formed by 2 points * @param {Object} selectionTL * @param {Object} selectionBR * @return {Boolean} */ intersectsWithRect: function(selectionTL, selectionBR) { var oCoords = this.oCoords, tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y), tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y), bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y), br = new fabric.Point(oCoords.br.x, oCoords.br.y); var intersection = fabric.Intersection.intersectPolygonRectangle( [tl, tr, br, bl], selectionTL, selectionBR ); return intersection.status === 'Intersection'; }, /** * Returns true if object intersects with another object * @param {Object} other Object to test * @return {Boolean} */ intersectsWithObject: function(other) { // extracts coords function getCoords(oCoords) { return { tl: new fabric.Point(oCoords.tl.x, oCoords.tl.y), tr: new fabric.Point(oCoords.tr.x, oCoords.tr.y), bl: new fabric.Point(oCoords.bl.x, oCoords.bl.y), br: new fabric.Point(oCoords.br.x, oCoords.br.y) }; } var thisCoords = getCoords(this.oCoords), otherCoords = getCoords(other.oCoords); var intersection = fabric.Intersection.intersectPolygonPolygon( [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl], [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl] ); return intersection.status === 'Intersection'; }, /** * Returns true if object is fully contained within area of another object * @param {Object} other Object to test * @return {Boolean} */ isContainedWithinObject: function(other) { return this.isContainedWithinRect(other.oCoords.tl, other.oCoords.br); }, /** * Returns true if object is fully contained within area formed by 2 points * @param {Object} selectionTL * @param {Object} selectionBR * @return {Boolean} */ isContainedWithinRect: function(selectionTL, selectionBR) { var oCoords = this.oCoords, tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y), tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y), bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y); return tl.x > selectionTL.x && tr.x < selectionBR.x && tl.y > selectionTL.y && bl.y < selectionBR.y; }, /** * Returns width of an object's bounding rectangle * @deprecated since 1.0.4 * @return {Number} width value */ getBoundingRectWidth: function() { return this.getBoundingRect().width; }, /** * Returns height of an object's bounding rectangle * @deprecated since 1.0.4 * @return {Number} height value */ getBoundingRectHeight: function() { return this.getBoundingRect().height; }, /** * Returns coordinates of object's bounding rectangle (left, top, width, height) * @return {Object} Object with left, top, width, height properties */ getBoundingRect: function() { this.oCoords || this.setCoords(); var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x]; var minX = fabric.util.array.min(xCoords); var maxX = fabric.util.array.max(xCoords); var width = Math.abs(minX - maxX); var yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y]; var minY = fabric.util.array.min(yCoords); var maxY = fabric.util.array.max(yCoords); var height = Math.abs(minY - maxY); return { left: minX, top: minY, width: width, height: height }; }, /** * Returns width of an object * @return {Number} width value */ getWidth: function() { return this.width * this.scaleX; }, /** * Returns height of an object * @return {Number} height value */ getHeight: function() { return this.height * this.scaleY; }, /** * Makes sure the scale is valid and modifies it if necessary * @private * @param {Number} value * @return {Number} */ _constrainScale: function(value) { if (Math.abs(value) < this.minScaleLimit) { if (value < 0) return -this.minScaleLimit; else return this.minScaleLimit; } return value; }, /** * Scales an object (equally by x and y) * @param value {Number} scale factor * @return {fabric.Object} thisArg * @chainable */ scale: function(value) { value = this._constrainScale(value); if (value < 0) { this.flipX = !this.flipX; this.flipY = !this.flipY; value *= -1; } this.scaleX = value; this.scaleY = value; this.setCoords(); return this; }, /** * Scales an object to a given width, with respect to bounding box (scaling by x/y equally) * @param value {Number} new width value * @return {fabric.Object} thisArg * @chainable */ scaleToWidth: function(value) { // adjust to bounding rect factor so that rotated shapes would fit as well var boundingRectFactor = this.getBoundingRectWidth() / this.getWidth(); return this.scale(value / this.width / boundingRectFactor); }, /** * Scales an object to a given height, with respect to bounding box (scaling by x/y equally) * @param value {Number} new height value * @return {fabric.Object} thisArg * @chainable */ scaleToHeight: function(value) { // adjust to bounding rect factor so that rotated shapes would fit as well var boundingRectFactor = this.getBoundingRectHeight() / this.getHeight(); return this.scale(value / this.height / boundingRectFactor); }, /** * Sets corner position coordinates based on current angle, width and height * @return {fabric.Object} thisArg * @chainable */ setCoords: function() { var strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0, padding = this.padding, theta = degreesToRadians(this.angle); this.currentWidth = (this.width + strokeWidth) * this.scaleX + padding * 2; this.currentHeight = (this.height + strokeWidth) * this.scaleY + padding * 2; // If width is negative, make postive. Fixes path selection issue if (this.currentWidth < 0) { this.currentWidth = Math.abs(this.currentWidth); } var _hypotenuse = Math.sqrt( Math.pow(this.currentWidth / 2, 2) + Math.pow(this.currentHeight / 2, 2)); var _angle = Math.atan(this.currentHeight / this.currentWidth); // offset added for rotate and scale actions var offsetX = Math.cos(_angle + theta) * _hypotenuse, offsetY = Math.sin(_angle + theta) * _hypotenuse, sinTh = Math.sin(theta), cosTh = Math.cos(theta); var coords = this.getCenterPoint(); var tl = { x: coords.x - offsetX, y: coords.y - offsetY }; var tr = { x: tl.x + (this.currentWidth * cosTh), y: tl.y + (this.currentWidth * sinTh) }; var br = { x: tr.x - (this.currentHeight * sinTh), y: tr.y + (this.currentHeight * cosTh) }; var bl = { x: tl.x - (this.currentHeight * sinTh), y: tl.y + (this.currentHeight * cosTh) }; var ml = { x: tl.x - (this.currentHeight/2 * sinTh), y: tl.y + (this.currentHeight/2 * cosTh) }; var mt = { x: tl.x + (this.currentWidth/2 * cosTh), y: tl.y + (this.currentWidth/2 * sinTh) }; var mr = { x: tr.x - (this.currentHeight/2 * sinTh), y: tr.y + (this.currentHeight/2 * cosTh) }; var mb = { x: bl.x + (this.currentWidth/2 * cosTh), y: bl.y + (this.currentWidth/2 * sinTh) }; var mtr = { x: mt.x, y: mt.y }; // debugging // setTimeout(function() { // canvas.contextTop.fillStyle = 'green'; // canvas.contextTop.fillRect(mb.x, mb.y, 3, 3); // canvas.contextTop.fillRect(bl.x, bl.y, 3, 3); // canvas.contextTop.fillRect(br.x, br.y, 3, 3); // canvas.contextTop.fillRect(tl.x, tl.y, 3, 3); // canvas.contextTop.fillRect(tr.x, tr.y, 3, 3); // canvas.contextTop.fillRect(ml.x, ml.y, 3, 3); // canvas.contextTop.fillRect(mr.x, mr.y, 3, 3); // canvas.contextTop.fillRect(mt.x, mt.y, 3, 3); // }, 50); this.oCoords = { // corners tl: tl, tr: tr, br: br, bl: bl, // middle ml: ml, mt: mt, mr: mr, mb: mb, // rotating point mtr: mtr }; // set coordinates of the draggable boxes in the corners used to scale/rotate the image this._setCornerCoords(); return this; } }); })();