Misc changes (#3614)

* misc changes
This commit is contained in:
Andrea Bogazzi 2017-01-30 15:24:32 +01:00 committed by GitHub
parent 6a30d81fdb
commit c378fa5b04
16 changed files with 629 additions and 495 deletions

View file

@ -61,6 +61,7 @@ fabric.SHARED_ATTRIBUTES = [
fabric.DPI = 96;
fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)';
fabric.fontPaths = { };
fabric.iMatrix = [1, 0, 0, 1, 0, 0];
/**
* Cache Object for widths of chars in text rendering.

View file

@ -1,11 +1,11 @@
(function() {
function getCoords(oCoords) {
function getCoords(coords) {
return [
new fabric.Point(oCoords.tl.x, oCoords.tl.y),
new fabric.Point(oCoords.tr.x, oCoords.tr.y),
new fabric.Point(oCoords.br.x, oCoords.br.y),
new fabric.Point(oCoords.bl.x, oCoords.bl.y)
new fabric.Point(coords.tl.x, coords.tl.y),
new fabric.Point(coords.tr.x, coords.tr.y),
new fabric.Point(coords.br.x, coords.br.y),
new fabric.Point(coords.bl.x, coords.bl.y)
];
}
@ -15,22 +15,56 @@
fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
/**
* Object containing coordinates of object's controls
* @type Object
* @default
* Describe object's corner position in canvas element coordinates.
* properties are tl,mt,tr,ml,mr,bl,mb,br,mtr for the main controls.
* each property is an object with x, y and corner.
* The `corner` property contains in a similar manner the 4 points of the
* interactive area of the corner.
* The coordinates depends from this properties: width, height, scaleX, scaleY
* skewX, skewY, angle, strokeWidth, viewportTransform, top, left, padding.
* The coordinates get updated with @method setCoords.
* You can calculate them without updating with @method calcCoords;
* @memberOf fabric.Object.prototype
*/
oCoords: null,
/**
* Describe object's corner position in canvas object absolute coordinates
* properties are tl,tr,bl,br and describe the four main corner.
* each property is an object with x, y, instance of Fabric.Point.
* The coordinates depends from this properties: width, height, scaleX, scaleY
* skewX, skewY, angle, strokeWidth, top, left.
* Those coordinates are usefull to understand where an object is. They get updated
* with oCoords but they do not need to be updated when zoom or panning change.
* The coordinates get updated with @method setCoords.
* You can calculate them without updating with @method calcCoords(true);
* @memberOf fabric.Object.prototype
*/
aCoords: null,
/**
* return correct set of coordinates for intersection
*/
getCoords: function(absolute, calculate) {
if (!this.oCoords) {
this.setCoords();
}
var coords = absolute ? this.aCoords : this.oCoords;
return getCoords(calculate ? this.calcCoords(absolute) : coords);
},
/**
* Checks if object intersects with an area formed by 2 points
* @param {Object} pointTL top-left point of area
* @param {Object} pointBR bottom-right point of area
* @param {Boolean} [absolute] use coordinates without viewportTransform
* @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
* @return {Boolean} true if object intersects with an area formed by 2 points
*/
intersectsWithRect: function(pointTL, pointBR) {
var oCoords = getCoords(this.oCoords),
intersectsWithRect: function(pointTL, pointBR, absolute, calculate) {
var coords = this.getCoords(absolute, calculate),
intersection = fabric.Intersection.intersectPolygonRectangle(
oCoords,
coords,
pointTL,
pointBR
);
@ -40,29 +74,35 @@
/**
* Checks if object intersects with another object
* @param {Object} other Object to test
* @param {Boolean} [absolute] use coordinates without viewportTransform
* @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
* @return {Boolean} true if object intersects with another object
*/
intersectsWithObject: function(other) {
intersectsWithObject: function(other, absolute, calculate) {
var intersection = fabric.Intersection.intersectPolygonPolygon(
getCoords(this.oCoords),
getCoords(other.oCoords)
this.getCoords(absolute, calculate),
other.getCoords(absolute, calculate)
);
return intersection.status === 'Intersection'
|| other.isContainedWithinObject(this)
|| this.isContainedWithinObject(other);
|| other.isContainedWithinObject(this, absolute, calculate)
|| this.isContainedWithinObject(other, absolute, calculate);
},
/**
* Checks if object is fully contained within area of another object
* @param {Object} other Object to test
* @param {Boolean} [absolute] use coordinates without viewportTransform
* @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
* @return {Boolean} true if object is fully contained within area of another object
*/
isContainedWithinObject: function(other) {
var points = getCoords(this.oCoords),
i = 0;
isContainedWithinObject: function(other, absolute, calculate) {
var points = this.getCoords(absolute, calculate),
i = 0, lines = other._getImageLines(
calculate ? other.calcCoords(absolute) : absolute ? other.aCoords : other.oCoords
);
for (; i < 4; i++) {
if (!other.containsPoint(points[i])) {
if (!other.containsPoint(points[i], lines)) {
return false;
}
}
@ -73,10 +113,12 @@
* Checks if object is fully contained within area formed by 2 points
* @param {Object} pointTL top-left point of area
* @param {Object} pointBR bottom-right point of area
* @param {Boolean} [absolute] use coordinates without viewportTransform
* @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
* @return {Boolean} true if object is fully contained within area formed by 2 points
*/
isContainedWithinRect: function(pointTL, pointBR) {
var boundingRect = this.getBoundingRect();
isContainedWithinRect: function(pointTL, pointBR, absolute, calculate) {
var boundingRect = this.getBoundingRect(absolute, calculate);
return (
boundingRect.left >= pointTL.x &&
@ -89,19 +131,42 @@
/**
* Checks if point is inside the object
* @param {fabric.Point} point Point to check against
* @param {Object} [lines] object returned from @method _getImageLines
* @param {Boolean} [absolute] use coordinates without viewportTransform
* @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
* @return {Boolean} true if point is inside the object
*/
containsPoint: function(point) {
if (!this.oCoords) {
this.setCoords();
}
var lines = this._getImageLines(this.oCoords),
containsPoint: function(point, lines, absolute, calculate) {
var lines = lines || this._getImageLines(
calculate ? this.calcCoords(absolute) : absolute ? this.aCoords : this.oCoords
),
xPoints = this._findCrossPoints(point, lines);
// if xPoints is odd then point is inside the object
return (xPoints !== 0 && xPoints % 2 === 1);
},
/**
* Checks if object is contained within the canvas with current viewportTransform
* the check is done stopping at first point that appear on screen
* @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
* @return {Boolean} true if object is fully contained within canvas
*/
isOnScreen: function(calculate) {
if (!this.canvas) {
return false;
}
var pointTL = this.canvas.vptCoords.tl, pointBR = this.canvas.vptCoords.br;
var points = this.getCoords(true, calculate), point;
for (var i = 0; i < 4; i++) {
point = points[i];
if (point.x <= pointBR.x && point.x >= pointTL.x && point.y <= pointBR.y && point.y >= pointTL.y) {
return true;
}
}
return false;
},
/**
* Method that returns an object with the object edges in it, given the coordinates of the corners
* @private
@ -133,16 +198,16 @@
* and the horizontal line determined by a point on canvas
* @private
* @param {fabric.Point} point Point to check
* @param {Object} oCoords Coordinates of the object being evaluated
* @param {Object} lines Coordinates of the object being evaluated
*/
// remove yi, not used but left code here just in case.
_findCrossPoints: function(point, oCoords) {
_findCrossPoints: function(point, lines) {
var b1, b2, a1, a2, xi, // yi,
xcount = 0,
iLine;
for (var lineKey in oCoords) {
iLine = oCoords[lineKey];
for (var lineKey in lines) {
iLine = lines[lineKey];
// optimisation 1: line below point. no cross
if ((iLine.o.y < point.y) && (iLine.d.y < point.y)) {
continue;
@ -199,17 +264,13 @@
/**
* Returns coordinates of object's bounding rectangle (left, top, width, height)
* the box is intented as aligned to axis of canvas.
* @param {Boolean} ignoreVpt bounding box will not be affected by viewportTransform
* @param {Boolean} [absolute] use coordinates without viewportTransform
* @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
* @return {Object} Object with left, top, width, height properties
*/
getBoundingRect: function(ignoreVpt) {
var coords = this.calcCoords(ignoreVpt);
return fabric.util.makeBoundingBoxFromPoints([
coords.tl,
coords.tr,
coords.br,
coords.bl
]);
getBoundingRect: function(absolute, calculate) {
var coords = this.getCoords(absolute, calculate);
return fabric.util.makeBoundingBoxFromPoints(coords);
},
/**
@ -296,10 +357,10 @@
* @return {Object} Object with tl, tr, br, bl ....
* @chainable
*/
calcCoords: function(ignoreVpt) {
calcCoords: function(absolute) {
var theta = degreesToRadians(this.angle),
vpt = this.getViewportTransform(),
dim = ignoreVpt ? this._getTransformedDimensions() : this._calculateCurrentDimensions(),
dim = absolute ? this._getTransformedDimensions() : this._calculateCurrentDimensions(),
currentWidth = dim.x, currentHeight = dim.y,
sinTh = Math.sin(theta),
cosTh = Math.cos(theta),
@ -309,12 +370,12 @@
offsetY = Math.sin(_angle + theta) * _hypotenuse,
center = this.getCenterPoint(),
// offset added for rotate and scale actions
coords = ignoreVpt ? center : fabric.util.transformPoint(center, vpt),
coords = absolute ? center : fabric.util.transformPoint(center, vpt),
tl = new fabric.Point(coords.x - offsetX, coords.y - offsetY),
tr = new fabric.Point(tl.x + (currentWidth * cosTh), tl.y + (currentWidth * sinTh)),
bl = new fabric.Point(tl.x - (currentHeight * sinTh), tl.y + (currentHeight * cosTh)),
br = new fabric.Point(coords.x + offsetX, coords.y + offsetY);
if (!ignoreVpt) {
if (!absolute) {
var ml = new fabric.Point((tl.x + bl.x) / 2, (tl.y + bl.y) / 2),
mt = new fabric.Point((tr.x + tl.x) / 2, (tr.y + tl.y) / 2),
mr = new fabric.Point((br.x + tr.x) / 2, (br.y + tr.y) / 2),
@ -341,7 +402,7 @@
// corners
tl: tl, tr: tr, br: br, bl: bl,
};
if (!ignoreVpt) {
if (!absolute) {
// middle
coords.ml = ml;
coords.mt = mt;
@ -356,22 +417,24 @@
/**
* Sets corner position coordinates based on current angle, width and height
* See https://github.com/kangax/fabric.js/wiki/When-to-call-setCoords
* @param {Boolean} [ignoreZoom] set oCoords with or without the viewport transform.
* @param {Boolean} [skipAbsolute] skip calculation of aCoords, usefull in setViewportTransform
* @return {fabric.Object} thisArg
* @chainable
*/
setCoords: function(ignoreZoom, skipAbsolute) {
this.oCoords = this.calcCoords(ignoreZoom);
if (!skipAbsolute && !ignoreZoom) {
this.absoluteCoords = this.calcCoords(true);
if (!skipAbsolute) {
this.aCoords = this.calcCoords(true);
}
// set coordinates of the draggable boxes in the corners used to scale/rotate the image
ignoreZoom || this._setCornerCoords && this._setCornerCoords();
ignoreZoom || (this._setCornerCoords && this._setCornerCoords());
return this;
},
/*
/**
* calculate rotation matrix of an object
* @return {Array} rotation matrix for the object
*/
@ -380,20 +443,21 @@
var theta = degreesToRadians(this.angle), cos = Math.cos(theta), sin = Math.sin(theta);
return [cos, sin, -sin, cos, 0, 0];
}
return [1, 0, 0, 1, 0, 0];
return fabric.iMatrix.concat();
},
/*
/**
* calculate trasform Matrix that represent current transformation from
* object properties.
* @param {Boolean} [skipGroup] return transformMatrix for object and not go upward with parents
* @return {Array} matrix Transform Matrix for the object
*/
calcTransformMatrix: function() {
calcTransformMatrix: function(skipGroup) {
var center = this.getCenterPoint(),
translateMatrix = [1, 0, 0, 1, center.x, center.y],
rotateMatrix = this._calcRotateMatrix(),
dimensionMatrix = this._calcDimensionsTransformMatrix(this.skewX, this.skewY, true),
matrix = this.group ? this.group.calcTransformMatrix() : [1, 0, 0, 1, 0, 0];
matrix = this.group && !skipGroup ? this.group.calcTransformMatrix() : fabric.iMatrix.concat();
matrix = multiplyMatrices(matrix, translateMatrix);
matrix = multiplyMatrices(matrix, rotateMatrix);
matrix = multiplyMatrices(matrix, dimensionMatrix);

View file

@ -176,14 +176,6 @@
this.radius = value;
return this.set('width', value * 2).set('height', value * 2);
},
/**
* Returns complexity of an instance
* @return {Number} complexity of this instance
*/
complexity: function() {
return 1;
}
});
/* _FROM_SVG_START_ */

View file

@ -156,14 +156,6 @@
this._renderFill(ctx);
this._renderStroke(ctx);
},
/**
* Returns complexity of an instance
* @return {Number} complexity
*/
complexity: function() {
return 1;
}
});
/* _FROM_SVG_START_ */

View file

@ -128,15 +128,13 @@
var objectLeft = object.getLeft(),
objectTop = object.getTop(),
ignoreZoom = true;
ignoreZoom = true, skipAbsolute = true;
object.set({
originalLeft: objectLeft,
originalTop: objectTop,
left: objectLeft - center.x,
top: objectTop - center.y
});
object.setCoords(ignoreZoom);
object.setCoords(ignoreZoom, skipAbsolute);
},
/**
@ -423,14 +421,14 @@
},
/**
* Sets coordinates of all group objects
* Sets coordinates of all objects inside group
* @return {fabric.Group} thisArg
* @chainable
*/
setObjectsCoords: function() {
var ignoreZoom = true;
var ignoreZoom = true, skipAbsolute = true;
this.forEachObject(function(object) {
object.setCoords(ignoreZoom);
object.setCoords(ignoreZoom, skipAbsolute);
});
return this;
},

View file

@ -597,14 +597,6 @@
? this.getElement().height || 0
: 0);
},
/**
* Returns complexity of an instance
* @return {Number} complexity of this instance
*/
complexity: function() {
return 1;
}
});
/**

View file

@ -280,14 +280,6 @@
return reviver ? reviver(markup.join('')) : markup.join('');
},
/* _TO_SVG_END_ */
/**
* Returns complexity of an instance
* @return {Number} complexity
*/
complexity: function() {
return 1;
}
});
/* _FROM_SVG_START_ */

View file

@ -1111,7 +1111,7 @@
if (this.canvas && this.canvas.viewportTransform) {
return this.canvas.viewportTransform;
}
return [1, 0, 0, 1, 0, 0];
return fabric.iMatrix.concat();
},
/**
@ -1497,10 +1497,10 @@
/**
* Returns complexity of an instance
* @return {Number} complexity of this instance
* @return {Number} complexity of this instance (is 1 unless subclassed)
*/
complexity: function() {
return 0;
return 1;
},
/**

View file

@ -3,26 +3,20 @@
'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
min = fabric.util.array.min,
max = fabric.util.array.max,
toFixed = fabric.util.toFixed;
extend = fabric.util.object.extend;
if (fabric.Polygon) {
fabric.warn('fabric.Polygon is already defined');
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push('points');
/**
* Polygon class
* @class fabric.Polygon
* @extends fabric.Object
* @extends fabric.Polyline
* @see {@link fabric.Polygon#initialize} for constructor definition
*/
fabric.Polygon = fabric.util.createClass(fabric.Object, /** @lends fabric.Polygon.prototype */ {
fabric.Polygon = fabric.util.createClass(fabric.Polyline, /** @lends fabric.Polygon.prototype */ {
/**
* Type of an object
@ -31,109 +25,6 @@
*/
type: 'polygon',
/**
* Points array
* @type Array
* @default
*/
points: null,
/**
* Minimum X from points values, necessary to offset points
* @type Number
* @default
*/
minX: 0,
/**
* Minimum Y from points values, necessary to offset points
* @type Number
* @default
*/
minY: 0,
cacheProperties: cacheProperties,
/**
* Constructor
* @param {Array} points Array of points
* @param {Object} [options] Options object
* @return {fabric.Polygon} thisArg
*/
initialize: function(points, options) {
options = options || {};
this.points = points || [];
this.callSuper('initialize', options);
this._calcDimensions();
if (!('top' in options)) {
this.top = this.minY;
}
if (!('left' in options)) {
this.left = this.minX;
}
this.pathOffset = {
x: this.minX + this.width / 2,
y: this.minY + this.height / 2
};
},
/**
* @private
*/
_calcDimensions: function() {
var points = this.points,
minX = min(points, 'x'),
minY = min(points, 'y'),
maxX = max(points, 'x'),
maxY = max(points, 'y');
this.width = (maxX - minX) || 0;
this.height = (maxY - minY) || 0;
this.minX = minX || 0;
this.minY = minY || 0;
},
/**
* Returns object representation of an instance
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} Object representation of an instance
*/
toObject: function(propertiesToInclude) {
return extend(this.callSuper('toObject', propertiesToInclude), {
points: this.points.concat()
});
},
/* _TO_SVG_START_ */
/**
* Returns svg representation of an instance
* @param {Function} [reviver] Method for further parsing of svg representation.
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var points = [], addTransform,
markup = this._createBaseSVGMarkup();
for (var i = 0, len = this.points.length; i < len; i++) {
points.push(toFixed(this.points[i].x, 2), ',', toFixed(this.points[i].y, 2), ' ');
}
if (!(this.group && this.group.type === 'path-group')) {
addTransform = ' translate(' + (-this.pathOffset.x) + ', ' + (-this.pathOffset.y) + ') ';
}
markup.push(
'<', this.type, ' ', this.getSvgId(),
'points="', points.join(''),
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(), addTransform,
' ', this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
},
/* _TO_SVG_END_ */
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
@ -143,35 +34,9 @@
if (!this.commonRender(ctx, noTransform)) {
return;
}
ctx.closePath();
this._renderFill(ctx);
if (this.stroke || this.strokeDashArray) {
ctx.closePath();
this._renderStroke(ctx);
}
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Boolean} noTransform
*/
commonRender: function(ctx, noTransform) {
var point, len = this.points.length,
x = noTransform ? 0 : this.pathOffset.x,
y = noTransform ? 0 : this.pathOffset.y;
if (!len || isNaN(this.points[len - 1].y)) {
// do not draw if no points or odd points
// NaN comes from parseFloat of a empty string in parser
return false;
}
ctx.beginPath();
ctx.moveTo(this.points[0].x - x, this.points[0].y - y);
for (var i = 0; i < len; i++) {
point = this.points[i];
ctx.lineTo(point.x - x, point.y - y);
}
return true;
this._renderStroke(ctx);
},
/**
@ -179,17 +44,9 @@
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_renderDashedStroke: function(ctx) {
fabric.Polyline.prototype._renderDashedStroke.call(this, ctx);
this.callSuper('_renderDashedStroke', ctx);
ctx.closePath();
},
/**
* Returns complexity of an instance
* @return {Number} complexity of this instance
*/
complexity: function() {
return this.points.length;
}
});
/* _FROM_SVG_START_ */

View file

@ -2,7 +2,11 @@
'use strict';
var fabric = global.fabric || (global.fabric = { });
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
min = fabric.util.array.min,
max = fabric.util.array.max,
toFixed = fabric.util.toFixed;
if (fabric.Polyline) {
fabric.warn('fabric.Polyline is already defined');
@ -70,14 +74,37 @@
* });
*/
initialize: function(points, options) {
return fabric.Polygon.prototype.initialize.call(this, points, options);
options = options || {};
this.points = points || [];
this.callSuper('initialize', options);
this._calcDimensions();
if (!('top' in options)) {
this.top = this.minY;
}
if (!('left' in options)) {
this.left = this.minX;
}
this.pathOffset = {
x: this.minX + this.width / 2,
y: this.minY + this.height / 2
};
},
/**
* @private
*/
_calcDimensions: function() {
return fabric.Polygon.prototype._calcDimensions.call(this);
var points = this.points,
minX = min(points, 'x'),
minY = min(points, 'y'),
maxX = max(points, 'x'),
maxY = max(points, 'y');
this.width = (maxX - minX) || 0;
this.height = (maxY - minY) || 0;
this.minX = minX || 0;
this.minY = minY || 0;
},
/**
@ -86,27 +113,72 @@
* @return {Object} Object representation of an instance
*/
toObject: function(propertiesToInclude) {
return fabric.Polygon.prototype.toObject.call(this, propertiesToInclude);
return extend(this.callSuper('toObject', propertiesToInclude), {
points: this.points.concat()
});
},
/* _TO_SVG_START_ */
/**
* Returns SVG representation of an instance
* Returns svg representation of an instance
* @param {Function} [reviver] Method for further parsing of svg representation.
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
return fabric.Polygon.prototype.toSVG.call(this, reviver);
var points = [], addTransform,
markup = this._createBaseSVGMarkup();
for (var i = 0, len = this.points.length; i < len; i++) {
points.push(toFixed(this.points[i].x, 2), ',', toFixed(this.points[i].y, 2), ' ');
}
if (!(this.group && this.group.type === 'path-group')) {
addTransform = ' translate(' + (-this.pathOffset.x) + ', ' + (-this.pathOffset.y) + ') ';
}
markup.push(
'<', this.type, ' ', this.getSvgId(),
'points="', points.join(''),
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(), addTransform,
' ', this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
},
/* _TO_SVG_END_ */
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Boolean} noTransform
*/
commonRender: function(ctx, noTransform) {
var point, len = this.points.length,
x = noTransform ? 0 : this.pathOffset.x,
y = noTransform ? 0 : this.pathOffset.y;
if (!len || isNaN(this.points[len - 1].y)) {
// do not draw if no points or odd points
// NaN comes from parseFloat of a empty string in parser
return false;
}
ctx.beginPath();
ctx.moveTo(this.points[0].x - x, this.points[0].y - y);
for (var i = 0; i < len; i++) {
point = this.points[i];
ctx.lineTo(point.x - x, point.y - y);
}
return true;
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Boolean} noTransform
*/
_render: function(ctx, noTransform) {
if (!fabric.Polygon.prototype.commonRender.call(this, ctx, noTransform)) {
if (!this.commonRender(ctx, noTransform)) {
return;
}
this._renderFill(ctx);

View file

@ -175,14 +175,6 @@
return reviver ? reviver(markup.join('')) : markup.join('');
},
/* _TO_SVG_END_ */
/**
* Returns complexity of an instance
* @return {Number} complexity
*/
complexity: function() {
return 1;
}
});
/* _FROM_SVG_START_ */

View file

@ -97,14 +97,6 @@
return reviver ? reviver(markup.join('')) : markup.join('');
},
/* _TO_SVG_END_ */
/**
* Returns complexity of an instance
* @return {Number} complexity of this instance
*/
complexity: function() {
return 1;
}
});
/**

View file

@ -12,6 +12,8 @@
getElementOffset = fabric.util.getElementOffset,
removeFromArray = fabric.util.removeFromArray,
toFixed = fabric.util.toFixed,
transformPoint = fabric.util.transformPoint,
invertTransform = fabric.util.invertTransform,
CANVAS_INIT_ERROR = new Error('Could not initialize `canvas` element');
@ -138,7 +140,7 @@
* @type Array
* @default
*/
viewportTransform: [1, 0, 0, 1, 0, 0],
viewportTransform: fabric.iMatrix.concat(),
/**
* if set to false background image is not affected by viewport transform
@ -168,6 +170,17 @@
*/
enableRetinaScaling: true,
/**
* Describe canvas element extension over design
* properties are tl,tr,bl,br.
* if canvas is not zoomed/panned those points are the four corner of canvas
* if canvas is viewportTransformed you those points indicate the extension
* of canvas element in plain untrasformed coordinates
* The coordinates get updated with @method calcViewportBoundaries.
* @memberOf fabric.StaticCanvas.prototype
*/
vptCoords: { },
/**
* @private
* @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
@ -648,6 +661,7 @@
if (activeGroup) {
activeGroup.setCoords(ingoreVpt, skipAbsolute);
}
this.calcViewportBoundaries();
this.renderAll();
return this;
},
@ -662,10 +676,10 @@
zoomToPoint: function (point, value) {
// TODO: just change the scale, preserve other transformations
var before = point, vpt = this.viewportTransform.slice(0);
point = fabric.util.transformPoint(point, fabric.util.invertTransform(this.viewportTransform));
point = transformPoint(point, invertTransform(this.viewportTransform));
vpt[0] = value;
vpt[3] = value;
var after = fabric.util.transformPoint(point, vpt);
var after = transformPoint(point, vpt);
vpt[4] += before.x - after.x;
vpt[5] += before.y - after.y;
return this.setViewportTransform(vpt);
@ -782,7 +796,7 @@
},
/**
* Renders both the canvas.
* Renders the canvas
* @return {fabric.Canvas} instance
* @chainable
*/
@ -792,6 +806,24 @@
return this;
},
/**
* Calculate the position of the 4 corner of canvas with current viewportTransform.
* helps to determinate when an object is in the current rendering viewport using
* object absolute coordinates ( aCoords )
* @return {Object} points.tl
* @chainable
*/
calcViewportBoundaries: function() {
var points = { }, width = this.getWidth(), height = this.getHeight(),
iVpt = invertTransform(this.viewportTransform);
points.tl = transformPoint({ x: 0, y: 0 }, iVpt);
points.br = transformPoint({ x: width, y: height }, iVpt);
points.tr = new fabric.Point(points.br.x, points.tl.y);
points.bl = new fabric.Point(points.tl.x, points.br.y);
this.vptCoords = points;
return points;
},
/**
* Renders background, objects, overlay and controls.
* @param {CanvasRenderingContext2D} ctx
@ -800,6 +832,7 @@
* @chainable
*/
renderCanvas: function(ctx, objects) {
this.calcViewportBoundaries();
this.clearContext(ctx);
this.fire('before:render');
if (this.clipTo) {
@ -973,8 +1006,8 @@
*/
getVpCenter: function() {
var center = this.getCenter(),
iVpt = fabric.util.invertTransform(this.viewportTransform);
return fabric.util.transformPoint({ x: center.left, y: center.top }, iVpt);
iVpt = invertTransform(this.viewportTransform);
return transformPoint({ x: center.left, y: center.top }, iVpt);
},
/**

View file

@ -1399,7 +1399,7 @@
});
test('relativePan', function() {
ok(typeof canvas.relativePan == 'function');
ok(typeof canvas.relativePan === 'function');
deepEqual(canvas.viewportTransform, [1, 0, 0, 1, 0, 0], 'initial viewport is identity matrix');
var point = new fabric.Point(-50, -50);
canvas.relativePan(point);
@ -1410,11 +1410,38 @@
});
test('getContext', function() {
ok(typeof canvas.getContext == 'function');
ok(typeof canvas.getContext === 'function');
var context = canvas.getContext();
equal(context, canvas.contextContainer, 'should return the context container');
});
test('calcViewportBoundaries', function() {
ok(typeof canvas.calcViewportBoundaries === 'function');
canvas.calcViewportBoundaries();
deepEqual(canvas.vptCoords.tl, new fabric.Point(0, 0), 'tl is 0,0');
deepEqual(canvas.vptCoords.tr, new fabric.Point(canvas.getWidth(), 0), 'tr is width, 0');
deepEqual(canvas.vptCoords.bl, new fabric.Point(0, canvas.getHeight()), 'bl is 0, height');
deepEqual(canvas.vptCoords.br, new fabric.Point(canvas.getWidth(), canvas.getHeight()), 'tl is width, height');
});
test('calcViewportBoundaries with zoom', function() {
ok(typeof canvas.calcViewportBoundaries === 'function');
canvas.setViewportTransform([2, 0, 0, 2, 0, 0]);
deepEqual(canvas.vptCoords.tl, new fabric.Point(0, 0), 'tl is 0,0');
deepEqual(canvas.vptCoords.tr, new fabric.Point(canvas.getWidth() / 2, 0), 'tl is 0,0');
deepEqual(canvas.vptCoords.bl, new fabric.Point(0, canvas.getHeight() / 2), 'tl is 0,0');
deepEqual(canvas.vptCoords.br, new fabric.Point(canvas.getWidth() / 2, canvas.getHeight() / 2), 'tl is 0,0');
});
test('calcViewportBoundaries with zoom and translation', function() {
ok(typeof canvas.calcViewportBoundaries === 'function');
canvas.setViewportTransform([2, 0, 0, 2, -60, 60]);
deepEqual(canvas.vptCoords.tl, new fabric.Point(30, -30), 'tl is 0,0');
deepEqual(canvas.vptCoords.tr, new fabric.Point(30 + canvas.getWidth() / 2, -30), 'tl is 0,0');
deepEqual(canvas.vptCoords.bl, new fabric.Point(30, canvas.getHeight() / 2 - 30), 'tl is 0,0');
deepEqual(canvas.vptCoords.br, new fabric.Point(30 + canvas.getWidth() / 2, canvas.getHeight() / 2 - 30), 'tl is 0,0');
});
//how to test with an exception?
/*asyncTest('options in setBackgroundImage from invalid URL', function() {
canvas.backgroundImage = null;

View file

@ -578,17 +578,11 @@
start();
}
else {
var image;
setTimeout(function() {
cObj.cloneAsImage(function(image) {
ok(image);
ok(image instanceof fabric.Image);
equal(image.width, 100, 'the image has same dimension of object');
start();
}, 500);
cObj.cloneAsImage(function(i) {
image = i;
});
}
});
@ -601,18 +595,12 @@
start();
}
else {
var image;
setTimeout(function() {
cObj.cloneAsImage(function(image) {
ok(image);
ok(image instanceof fabric.Image);
equal(image.width, 200, 'the image has been scaled by retina');
fabric.devicePixelRatio = 1;
start();
}, 500);
cObj.cloneAsImage(function(i) {
image = i;
}, { enableRetinaScaling: true });
}
});
@ -679,54 +667,6 @@
}
});
test('intersectsWithRectangle', function() {
var cObj = new fabric.Object({ left: 50, top: 50, width: 100, height: 100 });
cObj.setCoords();
ok(typeof cObj.intersectsWithRect == 'function');
var point1 = new fabric.Point(110, 100),
point2 = new fabric.Point(210, 200),
point3 = new fabric.Point(0, 0),
point4 = new fabric.Point(10, 10);
ok(cObj.intersectsWithRect(point1, point2));
ok(!cObj.intersectsWithRect(point3, point4));
});
test('intersectsWithObject', function() {
var cObj = new fabric.Object({ left: 50, top: 50, width: 100, height: 100 });
cObj.setCoords();
ok(typeof cObj.intersectsWithObject == 'function', 'has intersectsWithObject method');
var cObj2 = new fabric.Object({ left: -150, top: -150, width: 200, height: 200 });
cObj2.setCoords();
ok(cObj.intersectsWithObject(cObj2), 'cobj2 does intersect with cobj');
ok(cObj2.intersectsWithObject(cObj), 'cobj2 does intersect with cobj');
var cObj3 = new fabric.Object({ left: 392.5, top: 339.5, width: 13, height: 33 });
cObj3.setCoords();
ok(!cObj.intersectsWithObject(cObj3), 'cobj3 does not intersect with cobj (external)');
ok(!cObj3.intersectsWithObject(cObj), 'cobj3 does not intersect with cobj (external)');
var cObj4 = new fabric.Object({ left: 0, top: 0, width: 200, height: 200 });
cObj4.setCoords();
ok(cObj4.intersectsWithObject(cObj), 'overlapping objects are considered intersecting');
ok(cObj.intersectsWithObject(cObj4), 'overlapping objects are considered intersecting');
});
test('isContainedWithinRect', function() {
var cObj = new fabric.Object({ left: 20, top: 20, width: 10, height: 10 });
cObj.setCoords();
ok(typeof cObj.isContainedWithinRect == 'function');
// fully contained
ok(cObj.isContainedWithinRect(new fabric.Point(10,10), new fabric.Point(100,100)));
// only intersects
ok(!cObj.isContainedWithinRect(new fabric.Point(10,10), new fabric.Point(25, 25)));
// doesn't intersect
ok(!cObj.isContainedWithinRect(new fabric.Point(100,100), new fabric.Point(110, 110)));
});
test('isType', function() {
var cObj = new fabric.Object();
ok(typeof cObj.isType == 'function');
@ -1274,163 +1214,6 @@
equal(object.get('fill'), '123456');
});
test('intersectsWithRect', function() {
var object = new fabric.Object({ left: 0, top: 0, width: 40, height: 50, angle: 160 }),
point1 = new fabric.Point(-10, -10),
point2 = new fabric.Point(20, 30),
point3 = new fabric.Point(10, 15),
point4 = new fabric.Point(30, 35),
point5 = new fabric.Point(50, 60),
point6 = new fabric.Point(70, 80);
object.setCoords();
// object and area intersects
equal(object.intersectsWithRect(point1, point2), true);
// area is contained in object (no intersection)
equal(object.intersectsWithRect(point3, point4), false);
// area is outside of object (no intersection)
equal(object.intersectsWithRect(point5, point6), false);
});
test('intersectsWithObject', function() {
var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 230, strokeWidth: 0 }),
object1 = new fabric.Object({ left: 20, top: 30, width: 60, height: 30, angle: 10, strokeWidth: 0 }),
object2 = new fabric.Object({ left: 25, top: 35, width: 20, height: 20, angle: 50, strokeWidth: 0 }),
object3 = new fabric.Object({ left: 50, top: 50, width: 20, height: 20, angle: 0, strokeWidth: 0 });
object.set({ originX: 'center', originY: 'center' }).setCoords();
object1.set({ originX: 'center', originY: 'center' }).setCoords();
object2.set({ originX: 'center', originY: 'center' }).setCoords();
object3.set({ originX: 'center', originY: 'center' }).setCoords();
equal(object.intersectsWithObject(object1), true, 'object and object1 intersects');
equal(object.intersectsWithObject(object2), true, 'object2 is contained in object');
equal(object.intersectsWithObject(object3), false, 'object3 is outside of object (no intersection)');
});
test('isContainedWithinObject', function() {
var object = new fabric.Object({ left: 0, top: 0, width: 40, height: 40, angle: 0 }),
object1 = new fabric.Object({ left: 1, top: 1, width: 38, height: 38, angle: 0 }),
object2 = new fabric.Object({ left: 20, top: 20, width: 40, height: 40, angle: 0 }),
object3 = new fabric.Object({ left: 50, top: 50, width: 40, height: 40, angle: 0 });
object.setCoords();
object1.setCoords();
object2.setCoords();
object3.setCoords();
equal(object1.isContainedWithinObject(object), true, 'object1 is fully contained within object');
equal(object2.isContainedWithinObject(object), false, 'object2 intersects object (not fully contained)');
equal(object3.isContainedWithinObject(object), false, 'object3 is outside of object (not fully contained)');
object1.angle = 45;
object1.setCoords();
equal(object1.isContainedWithinObject(object), false, 'object1 rotated is not contained within object');
var rect1 = new fabric.Rect({
width: 50,
height: 50,
left: 50,
top: 50
});
var rect2 = new fabric.Rect({
width: 100,
height: 100,
left: 100,
top: 0,
angle: 45,
});
rect1.setCoords();
rect2.setCoords();
equal(rect1.isContainedWithinObject(rect2), false, 'rect1 rotated is not contained within rect2');
});
test('isContainedWithinRect', function() {
var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160 }),
point1 = new fabric.Point(0, 0),
point2 = new fabric.Point(80, 80),
point3 = new fabric.Point(0, 0),
point4 = new fabric.Point(80, 60),
point5 = new fabric.Point(80, 80),
point6 = new fabric.Point(90, 90);
object.set({ originX: 'center', originY: 'center' }).setCoords();
// area is contained in object (no intersection)
equal(object.isContainedWithinRect(point1, point2), true);
// object and area intersects
equal(object.isContainedWithinRect(point3, point4), false);
// area is outside of object (no intersection)
equal(object.isContainedWithinRect(point5, point6), false);
});
test('isContainedWithinRect', function() {
var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160 }),
point1 = new fabric.Point(0, 0),
point2 = new fabric.Point(80, 80),
point3 = new fabric.Point(0, 0),
point4 = new fabric.Point(80, 60),
point5 = new fabric.Point(80, 80),
point6 = new fabric.Point(90, 90);
object.set({ originX: 'center', originY: 'center' }).setCoords();
// area is contained in object (no intersection)
equal(object.isContainedWithinRect(point1, point2), true);
// object and area intersects
equal(object.isContainedWithinRect(point3, point4), false);
// area is outside of object (no intersection)
equal(object.isContainedWithinRect(point5, point6), false);
});
test('containsPoint', function() {
var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160, strokeWidth: 0 }),
point1 = new fabric.Point(30, 30),
point2 = new fabric.Point(60, 30),
point3 = new fabric.Point(45, 65),
point4 = new fabric.Point(15, 40),
point5 = new fabric.Point(30, 15);
object.set({ originX: 'center', originY: 'center' }).setCoords();
// point1 is contained in object
equal(object.containsPoint(point1), true);
// point2 is outside of object (right)
equal(object.containsPoint(point2), false);
// point3 is outside of object (bottom)
equal(object.containsPoint(point3), false);
// point4 is outside of object (left)
equal(object.containsPoint(point4), false);
// point5 is outside of object (top)
equal(object.containsPoint(point5), false);
});
test('containsPoint with padding', function() {
var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160, padding: 5 }),
point1 = new fabric.Point(30, 30),
point2 = new fabric.Point(10, 20),
point3 = new fabric.Point(65, 30),
point4 = new fabric.Point(45, 75),
point5 = new fabric.Point(10, 40),
point6 = new fabric.Point(30, 5);
object.set({ originX: 'center', originY: 'center' }).setCoords();
// point1 is contained in object
equal(object.containsPoint(point1), true);
// point2 is contained in object (padding area)
equal(object.containsPoint(point2), true);
// point2 is outside of object (right)
equal(object.containsPoint(point3), false);
// point3 is outside of object (bottom)
equal(object.containsPoint(point4), false);
// point4 is outside of object (left)
equal(object.containsPoint(point5), false);
// point5 is outside of object (top)
equal(object.containsPoint(point6), false);
});
test('clipTo', function() {
var object = new fabric.Object({
left: 40,

View file

@ -1,12 +1,264 @@
(function() {
var canvas = this.canvas = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.StaticCanvas();
QUnit.module('fabric.ObjectGeometry');
test('intersectsWithRectangle', function() {
var cObj = new fabric.Object({ left: 50, top: 50, width: 100, height: 100 });
cObj.setCoords();
ok(typeof cObj.intersectsWithRect == 'function');
var point1 = new fabric.Point(110, 100),
point2 = new fabric.Point(210, 200),
point3 = new fabric.Point(0, 0),
point4 = new fabric.Point(10, 10);
ok(cObj.intersectsWithRect(point1, point2));
ok(!cObj.intersectsWithRect(point3, point4));
});
test('intersectsWithRectangle absolute', function() {
var cObj = new fabric.Rect({ left: 10, top: 10, width: 20, height: 20 });
var absolute = true;
canvas.add(cObj);
canvas.viewportTransform = [2, 0, 0, 2, 0, 0];
cObj.setCoords();
canvas.calcViewportBoundaries();
var point1 = new fabric.Point(5, 5),
point2 = new fabric.Point(15, 15),
point3 = new fabric.Point(25, 25),
point4 = new fabric.Point(35, 35);
ok(!cObj.intersectsWithRect(point1, point2), 'Does not intersect because there is a 2x zoom');
ok(!cObj.intersectsWithRect(point3, point4), 'Does not intersect because there is a 2x zoom');
ok(cObj.intersectsWithRect(point1, point2, absolute), 'absolute coordinates intersect');
ok(cObj.intersectsWithRect(point3, point4, absolute), 'absolute coordinates intersect');
});
test('intersectsWithObject', function() {
var cObj = new fabric.Object({ left: 50, top: 50, width: 100, height: 100 });
cObj.setCoords();
ok(typeof cObj.intersectsWithObject == 'function', 'has intersectsWithObject method');
var cObj2 = new fabric.Object({ left: -150, top: -150, width: 200, height: 200 });
cObj2.setCoords();
ok(cObj.intersectsWithObject(cObj2), 'cobj2 does intersect with cobj');
ok(cObj2.intersectsWithObject(cObj), 'cobj2 does intersect with cobj');
var cObj3 = new fabric.Object({ left: 392.5, top: 339.5, width: 13, height: 33 });
cObj3.setCoords();
ok(!cObj.intersectsWithObject(cObj3), 'cobj3 does not intersect with cobj (external)');
ok(!cObj3.intersectsWithObject(cObj), 'cobj3 does not intersect with cobj (external)');
var cObj4 = new fabric.Object({ left: 0, top: 0, width: 200, height: 200 });
cObj4.setCoords();
ok(cObj4.intersectsWithObject(cObj), 'overlapping objects are considered intersecting');
ok(cObj.intersectsWithObject(cObj4), 'overlapping objects are considered intersecting');
});
test('isContainedWithinRect', function() {
var cObj = new fabric.Object({ left: 20, top: 20, width: 10, height: 10 });
cObj.setCoords();
ok(typeof cObj.isContainedWithinRect == 'function');
// fully contained
ok(cObj.isContainedWithinRect(new fabric.Point(10,10), new fabric.Point(100,100)));
// only intersects
ok(!cObj.isContainedWithinRect(new fabric.Point(10,10), new fabric.Point(25, 25)));
// doesn't intersect
ok(!cObj.isContainedWithinRect(new fabric.Point(100,100), new fabric.Point(110, 110)));
});
test('isContainedWithinRect absolute', function() {
var cObj = new fabric.Rect({ left: 20, top: 20, width: 10, height: 10 });
var absolute = true;
canvas.add(cObj);
canvas.viewportTransform = [2, 0, 0, 2, 0, 0];
cObj.setCoords();
canvas.calcViewportBoundaries();
ok(typeof cObj.isContainedWithinRect == 'function');
// fully contained
ok(cObj.isContainedWithinRect(new fabric.Point(10,10), new fabric.Point(100,100), absolute));
// only intersects
ok(!cObj.isContainedWithinRect(new fabric.Point(10,10), new fabric.Point(25, 25), absolute));
// doesn't intersect
ok(!cObj.isContainedWithinRect(new fabric.Point(100,100), new fabric.Point(110, 110), absolute));
});
test('intersectsWithRect', function() {
var object = new fabric.Object({ left: 0, top: 0, width: 40, height: 50, angle: 160 }),
point1 = new fabric.Point(-10, -10),
point2 = new fabric.Point(20, 30),
point3 = new fabric.Point(10, 15),
point4 = new fabric.Point(30, 35),
point5 = new fabric.Point(50, 60),
point6 = new fabric.Point(70, 80);
object.setCoords();
// object and area intersects
equal(object.intersectsWithRect(point1, point2), true);
// area is contained in object (no intersection)
equal(object.intersectsWithRect(point3, point4), false);
// area is outside of object (no intersection)
equal(object.intersectsWithRect(point5, point6), false);
});
test('intersectsWithObject', function() {
var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 230, strokeWidth: 0 }),
object1 = new fabric.Object({ left: 20, top: 30, width: 60, height: 30, angle: 10, strokeWidth: 0 }),
object2 = new fabric.Object({ left: 25, top: 35, width: 20, height: 20, angle: 50, strokeWidth: 0 }),
object3 = new fabric.Object({ left: 50, top: 50, width: 20, height: 20, angle: 0, strokeWidth: 0 });
object.set({ originX: 'center', originY: 'center' }).setCoords();
object1.set({ originX: 'center', originY: 'center' }).setCoords();
object2.set({ originX: 'center', originY: 'center' }).setCoords();
object3.set({ originX: 'center', originY: 'center' }).setCoords();
equal(object.intersectsWithObject(object1), true, 'object and object1 intersects');
equal(object.intersectsWithObject(object2), true, 'object2 is contained in object');
equal(object.intersectsWithObject(object3), false, 'object3 is outside of object (no intersection)');
});
test('isContainedWithinObject', function() {
var object = new fabric.Object({ left: 0, top: 0, width: 40, height: 40, angle: 0 }),
object1 = new fabric.Object({ left: 1, top: 1, width: 38, height: 38, angle: 0 }),
object2 = new fabric.Object({ left: 20, top: 20, width: 40, height: 40, angle: 0 }),
object3 = new fabric.Object({ left: 50, top: 50, width: 40, height: 40, angle: 0 });
object.setCoords();
object1.setCoords();
object2.setCoords();
object3.setCoords();
equal(object1.isContainedWithinObject(object), true, 'object1 is fully contained within object');
equal(object2.isContainedWithinObject(object), false, 'object2 intersects object (not fully contained)');
equal(object3.isContainedWithinObject(object), false, 'object3 is outside of object (not fully contained)');
object1.angle = 45;
object1.setCoords();
equal(object1.isContainedWithinObject(object), false, 'object1 rotated is not contained within object');
var rect1 = new fabric.Rect({
width: 50,
height: 50,
left: 50,
top: 50
});
var rect2 = new fabric.Rect({
width: 100,
height: 100,
left: 100,
top: 0,
angle: 45,
});
rect1.setCoords();
rect2.setCoords();
equal(rect1.isContainedWithinObject(rect2), false, 'rect1 rotated is not contained within rect2');
});
test('isContainedWithinRect', function() {
var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160 }),
point1 = new fabric.Point(0, 0),
point2 = new fabric.Point(80, 80),
point3 = new fabric.Point(0, 0),
point4 = new fabric.Point(80, 60),
point5 = new fabric.Point(80, 80),
point6 = new fabric.Point(90, 90);
object.set({ originX: 'center', originY: 'center' }).setCoords();
// area is contained in object (no intersection)
equal(object.isContainedWithinRect(point1, point2), true);
// object and area intersects
equal(object.isContainedWithinRect(point3, point4), false);
// area is outside of object (no intersection)
equal(object.isContainedWithinRect(point5, point6), false);
});
test('isContainedWithinRect', function() {
var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160 }),
point1 = new fabric.Point(0, 0),
point2 = new fabric.Point(80, 80),
point3 = new fabric.Point(0, 0),
point4 = new fabric.Point(80, 60),
point5 = new fabric.Point(80, 80),
point6 = new fabric.Point(90, 90);
object.set({ originX: 'center', originY: 'center' }).setCoords();
// area is contained in object (no intersection)
equal(object.isContainedWithinRect(point1, point2), true);
// object and area intersects
equal(object.isContainedWithinRect(point3, point4), false);
// area is outside of object (no intersection)
equal(object.isContainedWithinRect(point5, point6), false);
});
test('containsPoint', function() {
var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160, strokeWidth: 0 }),
point1 = new fabric.Point(30, 30),
point2 = new fabric.Point(60, 30),
point3 = new fabric.Point(45, 65),
point4 = new fabric.Point(15, 40),
point5 = new fabric.Point(30, 15);
object.set({ originX: 'center', originY: 'center' }).setCoords();
// point1 is contained in object
equal(object.containsPoint(point1), true);
// point2 is outside of object (right)
equal(object.containsPoint(point2), false);
// point3 is outside of object (bottom)
equal(object.containsPoint(point3), false);
// point4 is outside of object (left)
equal(object.containsPoint(point4), false);
// point5 is outside of object (top)
equal(object.containsPoint(point5), false);
});
test('containsPoint with padding', function() {
var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160, padding: 5 }),
point1 = new fabric.Point(30, 30),
point2 = new fabric.Point(10, 20),
point3 = new fabric.Point(65, 30),
point4 = new fabric.Point(45, 75),
point5 = new fabric.Point(10, 40),
point6 = new fabric.Point(30, 5);
object.set({ originX: 'center', originY: 'center' }).setCoords();
// point1 is contained in object
equal(object.containsPoint(point1), true);
// point2 is contained in object (padding area)
equal(object.containsPoint(point2), true);
// point2 is outside of object (right)
equal(object.containsPoint(point3), false);
// point3 is outside of object (bottom)
equal(object.containsPoint(point4), false);
// point4 is outside of object (left)
equal(object.containsPoint(point5), false);
// point5 is outside of object (top)
equal(object.containsPoint(point6), false);
});
test('setCoords', function() {
var cObj = new fabric.Object({ left: 150, top: 150, width: 100, height: 100, strokeWidth: 0});
ok(typeof cObj.setCoords == 'function');
equal(cObj.setCoords(), cObj, 'chainable');
equal(cObj.oCoords.tl.x, 150);
equal(cObj.oCoords.tl.y, 150);
equal(cObj.oCoords.tr.x, 250);
equal(cObj.oCoords.tr.y, 150);
equal(cObj.oCoords.bl.x, 150);
equal(cObj.oCoords.bl.y, 250);
equal(cObj.oCoords.br.x, 250);
equal(cObj.oCoords.br.y, 250);
equal(cObj.oCoords.mtr.x, 200);
equal(cObj.oCoords.mtr.y, 110);
cObj.set('left', 250).set('top', 250);
// coords should still correspond to initial one, even after invoking `set`
@ -37,6 +289,46 @@
equal(cObj.oCoords.mtr.y, 210);
});
test('setCoords and aCoords', function() {
var cObj = new fabric.Object({ left: 150, top: 150, width: 100, height: 100, strokeWidth: 0});
cObj.canvas = {
viewportTransform: [2, 0, 0, 2, 0, 0]
};
cObj.setCoords();
equal(cObj.oCoords.tl.x, 300, 'oCoords are modified by viewportTransform');
equal(cObj.oCoords.tl.y, 300, 'oCoords are modified by viewportTransform');
equal(cObj.oCoords.tr.x, 500, 'oCoords are modified by viewportTransform');
equal(cObj.oCoords.tr.y, 300, 'oCoords are modified by viewportTransform');
equal(cObj.oCoords.bl.x, 300, 'oCoords are modified by viewportTransform');
equal(cObj.oCoords.bl.y, 500, 'oCoords are modified by viewportTransform');
equal(cObj.oCoords.br.x, 500, 'oCoords are modified by viewportTransform');
equal(cObj.oCoords.br.y, 500, 'oCoords are modified by viewportTransform');
equal(cObj.oCoords.mtr.x, 400, 'oCoords are modified by viewportTransform');
equal(cObj.oCoords.mtr.y, 260, 'oCoords are modified by viewportTransform');
equal(cObj.aCoords.tl.x, 150, 'aCoords do not interfere with viewportTransform');
equal(cObj.aCoords.tl.y, 150, 'aCoords do not interfere with viewportTransform');
equal(cObj.aCoords.tr.x, 250, 'aCoords do not interfere with viewportTransform');
equal(cObj.aCoords.tr.y, 150, 'aCoords do not interfere with viewportTransform');
equal(cObj.aCoords.bl.x, 150, 'aCoords do not interfere with viewportTransform');
equal(cObj.aCoords.bl.y, 250, 'aCoords do not interfere with viewportTransform');
equal(cObj.aCoords.br.x, 250, 'aCoords do not interfere with viewportTransform');
equal(cObj.aCoords.br.y, 250, 'aCoords do not interfere with viewportTransform');
});
test('isOnScreen', function(){
var cObj = new fabric.Object({ left: 50, top: 50, width: 100, height: 100, strokeWidth: 0});
cObj.canvas = canvas;
cObj.setCoords();
ok(cObj.isOnScreen(), 'object is onScreen');
cObj.top = 1000;
cObj.setCoords();
ok(!cObj.isOnScreen(), 'object is not onScreen with top 1000');
canvas.setZoom(0.2);
ok(cObj.isOnScreen(), 'zooming out the object is again on screen');
});
test('calcTransformMatrix', function(){
var cObj = new fabric.Object({ width: 10, height: 15, strokeWidth: 0 });
ok(typeof cObj.calcTransformMatrix == 'function', 'calcTransformMatrix should exist');
@ -72,4 +364,59 @@
ok(typeof cObj._constrainScale == 'function', '_constrainScale should exist');
});
test('getCoords return coordinate of object in canvas coordinate.', function() {
var cObj = new fabric.Object({ width: 10, height: 15, strokeWidth: 2, top: 30, left: 40 });
var coords = cObj.getCoords();
deepEqual(coords[0], new fabric.Point(40, 30), 'return top left corner');
deepEqual(coords[1], new fabric.Point(52, 30), 'return top right corner');
deepEqual(coords[2], new fabric.Point(52, 47), 'return bottom right corner');
deepEqual(coords[3], new fabric.Point(40, 47), 'return bottom left corner');
cObj.left += 5;
coords = cObj.getCoords();
deepEqual(coords[0], new fabric.Point(40, 30), 'return top left corner cached oCoords');
deepEqual(coords[1], new fabric.Point(52, 30), 'return top right corner cached oCoords');
deepEqual(coords[2], new fabric.Point(52, 47), 'return bottom right corner cached oCoords');
deepEqual(coords[3], new fabric.Point(40, 47), 'return bottom left corner cached oCoords');
coords = cObj.getCoords(false, true);
deepEqual(coords[0], new fabric.Point(45, 30), 'return top left corner recalculated');
deepEqual(coords[1], new fabric.Point(57, 30), 'return top right corner recalculated');
deepEqual(coords[2], new fabric.Point(57, 47), 'return bottom right corner recalculated');
deepEqual(coords[3], new fabric.Point(45, 47), 'return bottom left corner recalculated');
});
test('getCoords return coordinate of object in zoomed canvas coordinate.', function() {
var cObj = new fabric.Object({ width: 10, height: 15, strokeWidth: 2, top: 30, left: 40 });
cObj.canvas = {
viewportTransform: [2, 0, 0, 2, 35, 35]
};
var coords = cObj.getCoords();
deepEqual(coords[0], new fabric.Point(115, 95), 'return top left corner is influenced by canvas zoom');
deepEqual(coords[1], new fabric.Point(139, 95), 'return top right corner is influenced by canvas zoom');
deepEqual(coords[2], new fabric.Point(139, 129), 'return bottom right corner is influenced by canvas zoom');
deepEqual(coords[3], new fabric.Point(115, 129), 'return bottom left corner is influenced by canvas zoom');
});
test('getCoords return coordinate of object in absolute coordinates and ignore canvas zoom', function() {
var cObj = new fabric.Object({ width: 10, height: 15, strokeWidth: 2, top: 30, left: 40 });
cObj.canvas = {
viewportTransform: [2, 0, 0, 2, 35, 35]
};
var coords = cObj.getCoords(true);
deepEqual(coords[0], new fabric.Point(40, 30), 'return top left corner cached oCoords');
deepEqual(coords[1], new fabric.Point(52, 30), 'return top right corner cached oCoords');
deepEqual(coords[2], new fabric.Point(52, 47), 'return bottom right corner cached oCoords');
deepEqual(coords[3], new fabric.Point(40, 47), 'return bottom left corner cached oCoords');
});
// test('getCoords return coordinate of object', function() {
// var cObj = new fabric.Object({ width: 10, height: 15, strokeWidth: 2, top: 30, left: 40 });
// var coords = cObj.getCoords();
// equal(coords[0], { x: 40, y: 30 }, 'return top left corner');
// equal(coords[1], { x: 1, y: 1 }, 'return top right corner');
// equal(coords[2], { x: 1, y: 1 }, 'return bottom right corner');
// equal(coords[3], { x: 1, y: 1 }, 'return bottom left corner');
// });
})();