transformer work

This commit is contained in:
Anton Lavrenov 2020-05-05 10:37:58 -05:00
commit 0314182992
11 changed files with 512 additions and 434 deletions

View File

@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## Not released:
## 5.0.3 - 2020-05-01
* Fixes for `boundBoxFunc` of `Konva.Transformer`.
## 5.0.2 - 2020-04-23
* Deatach fixes for `Konva.Transformer`

View File

@ -155,6 +155,7 @@ See file `konva-node/demo.js` file in this repo as a sample.
# Backers
[myposter GmbH](https://www.myposter.de/)
[queue.gg]()https://queue.gg/
# Change log

296
konva.js
View File

@ -499,7 +499,7 @@
result.rotation = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
result.scaleX = r;
result.scaleY = delta / r;
result.skewX = Math.atan((a * c + b * d) / (r * r));
result.skewX = (a * c + b * d) / (r * r);
result.skewY = 0;
}
else if (c != 0 || d != 0) {
@ -509,11 +509,24 @@
result.scaleX = delta / s;
result.scaleY = s;
result.skewX = 0;
result.skewY = Math.atan((a * c + b * d) / (s * s));
result.skewY = (a * c + b * d) / (s * s);
}
result.rotation = Util._getRotation(result.rotation);
return result;
};
Transform.prototype.qrDecompose = function () {
var angle = Math.atan2(this.m[1], this.m[0]), denom = Math.pow(this.m[0], 2) + Math.pow(this.m[1], 2), scaleX = Math.sqrt(denom), scaleY = (this.m[0] * this.m[3] - this.m[2] * this.m[1]) / scaleX, skewX = Math.atan2(this.m[0] * this.m[2] + this.m[1] * this.m[3], denom);
var rotation = Util._getRotation(angle);
return {
rotation: rotation,
scaleX: scaleX,
scaleY: scaleY,
skewX: skewX / (Math.PI / 180),
skewY: 0,
x: this.m[4],
y: this.m[5]
};
};
return Transform;
}());
// CONSTANTS
@ -13379,7 +13392,7 @@
*/
Factory.addGetterSetter(TextPath, 'letterSpacing', 0, getNumberValidator());
/**
* get/set text baselineg. The default is 'middle'. Can be 'top', 'bottom', 'middle', 'alphabetic', 'hanging'
* get/set text baseline. The default is 'middle'. Can be 'top', 'bottom', 'middle', 'alphabetic', 'hanging'
* @name Konva.TextPath#textBaseline
* @method
* @param {String} textBaseline
@ -13577,51 +13590,6 @@
var center = getCenter(shape);
return rotateAroundPoint(shape, deltaRad, center);
}
function getShapeRect(shape) {
var angleRad = shape.rotation;
var x1 = shape.x;
var y1 = shape.y;
var x2 = x1 + shape.width * Math.cos(angleRad);
var y2 = y1 + shape.width * Math.sin(angleRad);
var x3 = shape.x +
shape.width * Math.cos(angleRad) +
shape.height * Math.sin(-angleRad);
var y3 = shape.y +
shape.height * Math.cos(angleRad) +
shape.width * Math.sin(angleRad);
var x4 = shape.x + shape.height * Math.sin(-angleRad);
var y4 = shape.y + shape.height * Math.cos(angleRad);
var leftX = Math.min(x1, x2, x3, x4);
var rightX = Math.max(x1, x2, x3, x4);
var topY = Math.min(y1, y2, y3, y4);
var bottomY = Math.max(y1, y2, y3, y4);
return {
x: leftX,
y: topY,
width: rightX - leftX,
height: bottomY - topY
};
}
function getShapesRect(shapes) {
var x1 = 9999999999;
var y1 = 9999999999;
var x2 = -999999999;
var y2 = -999999999;
shapes.forEach(function (shape) {
var rect = getShapeRect(shape);
x1 = Math.min(x1, rect.x);
y1 = Math.min(y1, rect.y);
x2 = Math.max(x2, rect.x + rect.width);
y2 = Math.max(y2, rect.y + rect.height);
});
return {
x: x1,
y: y1,
width: x2 - x1,
height: y2 - y1,
rotation: 0
};
}
function getSnap(snaps, newRotationRad, tol) {
var snapped = newRotationRad;
for (var i = 0; i < snaps.length; i++) {
@ -13816,6 +13784,7 @@
Transformer.prototype._getNodeRect = function () {
return this._getCache(NODES_RECT, this.__getNodeRect);
};
// return absolute rotated bounding rectangle
Transformer.prototype.__getNodeShape = function (node, rot, relative) {
if (rot === void 0) { rot = this.rotation(); }
var rect = node.getClientRect({
@ -13854,14 +13823,56 @@
rotation: 0
};
}
var shapes = this.nodes().map(function (node) {
return _this.__getNodeShape(node);
var totalPoints = [];
this.nodes().map(function (node) {
var box = node.getClientRect({
skipTransform: true,
skipShadow: true,
skipStroke: _this.ignoreStroke()
});
var points = [
{ x: box.x, y: box.y },
{ x: box.x + box.width, y: box.y },
{ x: box.x + box.width, y: box.y + box.height },
{ x: box.x, y: box.y + box.height }
];
var trans = node.getAbsoluteTransform();
points.forEach(function (point) {
var transformed = trans.point(point);
totalPoints.push(transformed);
});
});
var box = getShapesRect(shapes);
return rotateAroundPoint(box, Konva.getAngle(this.rotation()), {
x: 0,
y: 0
var tr = new Transform();
tr.rotate(-Konva.getAngle(this.rotation()));
var minX, minY, maxX, maxY;
totalPoints.forEach(function (point) {
var transformed = tr.point(point);
if (minX === undefined) {
minX = maxX = transformed.x;
minY = maxY = transformed.y;
}
minX = Math.min(minX, transformed.x);
minY = Math.min(minY, transformed.y);
maxX = Math.max(maxX, transformed.x);
maxY = Math.max(maxY, transformed.y);
});
tr.invert();
var p = tr.point({ x: minX, y: minY });
return {
x: p.x,
y: p.y,
width: maxX - minX,
height: maxY - minY,
rotation: Konva.getAngle(this.rotation())
};
// const shapes = this.nodes().map(node => {
// return this.__getNodeShape(node);
// });
// const box = getShapesRect(shapes);
// return rotateAroundPoint(box, Konva.getAngle(this.rotation()), {
// x: 0,
// y: 0
// });
};
Transformer.prototype.getX = function () {
return this._getNodeRect().x;
@ -14171,7 +14182,6 @@
}
};
Transformer.prototype._fitNodesInto = function (newAttrs, evt) {
var _this = this;
var oldAttrs = this._getNodeRect();
var minSize = 1;
if (Util._inRange(newAttrs.width, -this.padding() * 2 - minSize, minSize)) {
@ -14236,17 +14246,30 @@
this._anchorDragOffset.y -= offset.y;
newAttrs.height += this.padding() * 2;
}
// const pure = this.findOne('.back').getClientRect({
// skipStroke: true
// });
// var scaleX = pure.width ? newAttrs.width / pure.width : 1;
// var scaleY = pure.height ? newAttrs.height / pure.height : 1;
// var dx = pure.x * scaleX;
// var dy = pure.y * scaleY;
// let's find delta transform
var dx = newAttrs.x - oldAttrs.x, dy = newAttrs.y - oldAttrs.y, angle = newAttrs.rotation - oldAttrs.rotation, scaleX = newAttrs.width / oldAttrs.width, scaleY = newAttrs.height / oldAttrs.height;
// dt1.invert();
// const dt2 = new Transform();
// dt2.translate(newAttrs.x, newAttrs.y);
// dt2.rotate(newAttrs.rotation);
// var dx = newAttrs.x - oldAttrs.x,
// dy = newAttrs.y - oldAttrs.y,
// var angle = newAttrs.rotation - oldAttrs.rotation;
// scaleX = newAttrs.width / oldAttrs.width,
// scaleY = newAttrs.height / oldAttrs.height;
// const x = newAttrs.x - (dx * Math.cos(angle) + dy * Math.sin(-angle));
// const y = newAttrs.y - (dy * Math.cos(angle) + dx * Math.sin(angle));
// // dt1.invert();
// const tr = new Transform();
// tr.translate(x, y);
// tr.rotate(angle);
// // console.log(dt.point(newAttrs));
// // dt.translate(newAttrs.x, newAttrs.y);
// // console.log(dt.decompose());
// // dt.rotate(newAttrs.rotation);
// dt2.scale(scaleX, scaleY);
// tr.scale(scaleX, scaleY);
// dt1.multiply(dt2);
// dt.translate(dx, dy);
// dt.rotate(angle);
@ -14257,103 +14280,37 @@
// ) {
// debugger;
// }
var base = 10000000;
var oldTr = new Transform();
oldTr.translate(oldAttrs.x, oldAttrs.y);
oldTr.rotate(oldAttrs.rotation);
oldTr.scale(oldAttrs.width / base, oldAttrs.height / base);
var newTr = new Transform();
newTr.translate(newAttrs.x, newAttrs.y);
newTr.rotate(newAttrs.rotation);
newTr.scale(newAttrs.width / base, newAttrs.height / base);
var delta = newTr.multiply(oldTr.invert());
this._nodes.forEach(function (node) {
// var oldRect = this.__getNodeShape(node, 0);
// var newRect = transformAndRotateShape(oldRect, oldAttrs, newAttrs);
// this._fitNodeInto(node, newRect, evt);
// var m = new Transform(),
// x = node.x(),
// y = node.y(),
// rotation = Konva.getAngle(node.rotation()),
// scaleX = node.scaleX(),
// scaleY = node.scaleY(),
// skewX = node.skewX(),
// skewY = node.skewY(),
// offsetX = node.offsetX(),
// offsetY = node.offsetY();
// // if (x !== 0 || y !== 0) {
// // m.translate(x, y);
// // }
// if (rotation !== 0) {
// m.rotate(rotation);
var pt = node.getParent().getAbsoluteTransform();
var selfTr = node.getTransform().copy();
selfTr.translate(node.offsetX(), node.offsetY());
var newLocal = new Transform();
newLocal
.multiply(delta)
.multiply(pt)
.multiply(pt.copy().invert())
.multiply(selfTr);
// node._cache.set('transform', newLocal);
// node._cache.set('absoluteTransform', newLocal);
// console.log();
var attrs = newLocal.decompose();
// if (Math.abs(attrs.skewX - node.skewX()) < 0.00000001) {
// attrs.skewX = node.skewX();
// }
// if (skewX !== 0 || skewY !== 0) {
// m.skew(skewX, skewY);
// if (Math.abs(attrs.skewY - node.skewY()) < 0.00000001) {
// attrs.skewY = node.skewY();
// }
// if (scaleX !== 1 || scaleY !== 1) {
// m.scale(scaleX, scaleY);
// }
// if (offsetX !== 0 || offsetY !== 0) {
// m.translate(-1 * offsetX, -1 * offsetY);
// }
// return m;
// t.translate(dx, dy);
// t.rotate(Konva.getAngle(node.rotation()) + angle);
// t.scale(node.scaleX() * scaleX, node.scaleY() * scaleY);
// const t = node.getTransform();
// const dt1 = new Transform();
// dt1.translate(dx, dy);
// dt1.rotate(angle);
// dt1.scale(scaleX, scaleY);
// // const oldScale = {
// // x: dt.decompose().scaleX,
// // y: dt.decompose().scaleY
// // };
// // dt.scale(1, 1);
// t.multiply(dt1);
// dt.scale(oldScale.x, oldScale.y);
// t.translate(
// dt.getTranslation().x * t.decompose().scaleX,
// dt.getTranslation().y * t.decompose().scaleY
// );
// dt.set
// dt.setAbsolutePosition(0, 0);
// t.multiply(dt);
// console.log(
// node.rotation(),
// dt.decompose().rotation,
// t.decompose().rotation,
// Util._getRotation(newAttrs.rotation)
// );
// m.multiply(dt);
// m.translate(x + dx, y + dy);
// node.setAttrs(t.decompose());
// if (offsetX !== 0 || offsetY !== 0) {
// t.translate(
// node.offsetX() * (1 - node.scaleX() / scaleX),
// node.offsetY() * (1 - node.scaleY() / scaleY)
// );
var parentRot = Konva.getAngle(node.getParent().getAbsoluteRotation());
var rotation = Util._getRotation(newAttrs.rotation - parentRot);
var pure = node.getClientRect({
skipShadow: true,
skipStroke: _this.ignoreStroke()
});
var parentTransform = node
.getParent()
.getAbsoluteTransform()
.copy();
parentTransform.invert();
var invertedPoint = parentTransform.point({
x: newAttrs.x,
y: newAttrs.y
});
newAttrs.x = invertedPoint.x;
newAttrs.y = invertedPoint.y;
var absScale = node.getParent().getAbsoluteScale();
pure.width *= absScale.x;
pure.height *= absScale.y;
var scaleX = pure.width ? newAttrs.width / pure.width : 1;
var scaleY = pure.height ? newAttrs.height / pure.height : 1;
var dx = pure.x * scaleX - node.offsetX() * scaleX;
var dy = pure.y * scaleY - node.offsetY() * scaleY;
var x = newAttrs.x - (dx * Math.cos(rotation) + dy * Math.sin(-rotation));
var y = newAttrs.y - (dy * Math.cos(rotation) + dx * Math.sin(rotation));
var tr = new Transform();
tr.translate(x, y);
tr.rotate(rotation);
tr.scale(scaleX, scaleY);
node.setAttrs(tr.multiply(node.getTransform()).decompose());
node.setAttrs(attrs);
});
this.rotation(Util._getRotation(newAttrs.rotation));
this._resetTransformCache();
@ -14361,18 +14318,6 @@
this.getLayer().batchDraw();
};
Transformer.prototype._fitNodeInto = function (node, newAttrs, evt) {
if (this.boundBoxFunc()) {
var oldAttrs = this.__getNodeShape(node, node.rotation(), node.getParent());
var bounded = this.boundBoxFunc()(oldAttrs, newAttrs, node);
if (bounded) {
newAttrs = bounded;
}
else {
Util.warn('boundBoxFunc returned falsy. You should return new bound rect from it!');
}
}
var parentRot = Konva.getAngle(node.getParent().getAbsoluteRotation());
node.rotation(Util._getRotation(newAttrs.rotation - parentRot));
var pure = node.getClientRect({
skipTransform: true,
skipShadow: true,
@ -14387,11 +14332,24 @@
x: newAttrs.x,
y: newAttrs.y
});
var absScale = node.getParent().getAbsoluteScale();
newAttrs.x = invertedPoint.x;
newAttrs.y = invertedPoint.y;
newAttrs.width /= absScale.x;
newAttrs.height /= absScale.y;
if (this.boundBoxFunc()) {
var oldAttrs = this.__getNodeShape(node, node.rotation(), node.getParent());
var bounded = this.boundBoxFunc()(oldAttrs, newAttrs, node);
if (bounded) {
newAttrs = bounded;
}
else {
Util.warn('boundBoxFunc returned falsy. You should return new bound rect from it!');
}
}
var parentRot = Konva.getAngle(node.getParent().getAbsoluteRotation());
node.rotation(Util._getRotation(newAttrs.rotation - parentRot));
var absScale = node.getParent().getAbsoluteScale();
pure.width *= absScale.x;
pure.height *= absScale.y;
var scaleX = pure.width ? newAttrs.width / pure.width : 1;
var scaleY = pure.height ? newAttrs.height / pure.height : 1;
var rotation = Konva.getAngle(node.rotation());

6
konva.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "konva",
"version": "5.0.2",
"version": "5.0.3",
"author": "Anton Lavrenov",
"files": [
"README.md",

View File

@ -2,7 +2,6 @@ import { glob, Konva } from './Global';
import { Node } from './Node';
import { IRect, RGB, RGBA, Vector2d } from './types';
/**
* Collection constructor. Collection extends Array.
* This class is used in conjunction with {@link Konva.Container#find}
@ -344,7 +343,7 @@ export class Transform {
result.rotation = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
result.scaleX = r;
result.scaleY = delta / r;
result.skewX = Math.atan((a * c + b * d) / (r * r));
result.skewX = (a * c + b * d) / (r * r);
result.skewY = 0;
} else if (c != 0 || d != 0) {
var s = Math.sqrt(c * c + d * d);
@ -353,7 +352,7 @@ export class Transform {
result.scaleX = delta / s;
result.scaleY = s;
result.skewX = 0;
result.skewY = Math.atan((a * c + b * d) / (s * s));
result.skewY = (a * c + b * d) / (s * s);
} else {
// a = b = c = d = 0
}
@ -362,6 +361,25 @@ export class Transform {
return result;
}
qrDecompose() {
var angle = Math.atan2(this.m[1], this.m[0]),
denom = Math.pow(this.m[0], 2) + Math.pow(this.m[1], 2),
scaleX = Math.sqrt(denom),
scaleY = (this.m[0] * this.m[3] - this.m[2] * this.m[1]) / scaleX,
skewX = Math.atan2(this.m[0] * this.m[2] + this.m[1] * this.m[3], denom);
const rotation = Util._getRotation(angle);
return {
rotation,
scaleX: scaleX,
scaleY: scaleY,
skewX: skewX / (Math.PI / 180),
skewY: 0,
x: this.m[4],
y: this.m[5]
};
}
}
// CONSTANTS

View File

@ -660,7 +660,7 @@ Factory.addGetterSetter(TextPath, 'align', 'left');
Factory.addGetterSetter(TextPath, 'letterSpacing', 0, getNumberValidator());
/**
* get/set text baselineg. The default is 'middle'. Can be 'top', 'bottom', 'middle', 'alphabetic', 'hanging'
* get/set text baseline. The default is 'middle'. Can be 'top', 'bottom', 'middle', 'alphabetic', 'hanging'
* @name Konva.TextPath#textBaseline
* @method
* @param {String} textBaseline

View File

@ -480,7 +480,8 @@ export class Transformer extends Group {
return this._getCache(NODES_RECT, this.__getNodeRect);
}
__getNodeShape(node, rot = this.rotation(), relative?: Node) {
// return absolute rotated bounding rectangle
__getNodeShape(node: Node, rot = this.rotation(), relative?: Node) {
var rect = node.getClientRect({
skipTransform: true,
skipShadow: true,
@ -522,15 +523,60 @@ export class Transformer extends Group {
};
}
const shapes = this.nodes().map(node => {
return this.__getNodeShape(node);
const totalPoints = [];
this.nodes().map(node => {
const box = node.getClientRect({
skipTransform: true,
skipShadow: true,
skipStroke: this.ignoreStroke()
});
var points = [
{ x: box.x, y: box.y },
{ x: box.x + box.width, y: box.y },
{ x: box.x + box.width, y: box.y + box.height },
{ x: box.x, y: box.y + box.height }
];
var trans = node.getAbsoluteTransform();
points.forEach(function(point) {
var transformed = trans.point(point);
totalPoints.push(transformed);
});
});
const box = getShapesRect(shapes);
return rotateAroundPoint(box, Konva.getAngle(this.rotation()), {
x: 0,
y: 0
const tr = new Transform();
tr.rotate(-Konva.getAngle(this.rotation()));
var minX: number, minY: number, maxX: number, maxY: number;
totalPoints.forEach(function(point) {
var transformed = tr.point(point);
if (minX === undefined) {
minX = maxX = transformed.x;
minY = maxY = transformed.y;
}
minX = Math.min(minX, transformed.x);
minY = Math.min(minY, transformed.y);
maxX = Math.max(maxX, transformed.x);
maxY = Math.max(maxY, transformed.y);
});
tr.invert();
const p = tr.point({ x: minX, y: minY });
return {
x: p.x,
y: p.y,
width: maxX - minX,
height: maxY - minY,
rotation: Konva.getAngle(this.rotation())
};
// const shapes = this.nodes().map(node => {
// return this.__getNodeShape(node);
// });
// const box = getShapesRect(shapes);
// return rotateAroundPoint(box, Konva.getAngle(this.rotation()), {
// x: 0,
// y: 0
// });
}
getX() {
return this._getNodeRect().x;
@ -1006,23 +1052,36 @@ export class Transformer extends Group {
return;
}
}
// const pure = this.findOne('.back').getClientRect({
// skipStroke: true
// });
// var scaleX = pure.width ? newAttrs.width / pure.width : 1;
// var scaleY = pure.height ? newAttrs.height / pure.height : 1;
// var dx = pure.x * scaleX;
// var dy = pure.y * scaleY;
// let's find delta transform
var dx = newAttrs.x - oldAttrs.x,
dy = newAttrs.y - oldAttrs.y,
angle = newAttrs.rotation - oldAttrs.rotation,
scaleX = newAttrs.width / oldAttrs.width,
scaleY = newAttrs.height / oldAttrs.height;
// var dx = newAttrs.x - oldAttrs.x,
// dy = newAttrs.y - oldAttrs.y,
// var angle = newAttrs.rotation - oldAttrs.rotation;
// scaleX = newAttrs.width / oldAttrs.width,
// scaleY = newAttrs.height / oldAttrs.height;
// dt1.invert();
// const x = newAttrs.x - (dx * Math.cos(angle) + dy * Math.sin(-angle));
// const y = newAttrs.y - (dy * Math.cos(angle) + dx * Math.sin(angle));
// const dt2 = new Transform();
// dt2.translate(newAttrs.x, newAttrs.y);
// dt2.rotate(newAttrs.rotation);
// // dt1.invert();
// const tr = new Transform();
// tr.translate(x, y);
// tr.rotate(angle);
// // console.log(dt.point(newAttrs));
// // dt.translate(newAttrs.x, newAttrs.y);
// // console.log(dt.decompose());
// // dt.rotate(newAttrs.rotation);
// dt2.scale(scaleX, scaleY);
// tr.scale(scaleX, scaleY);
// dt1.multiply(dt2);
@ -1037,116 +1096,44 @@ export class Transformer extends Group {
// debugger;
// }
const base = 10000000;
const oldTr = new Transform();
oldTr.translate(oldAttrs.x, oldAttrs.y);
oldTr.rotate(oldAttrs.rotation);
oldTr.scale(oldAttrs.width / base, oldAttrs.height / base);
const newTr = new Transform();
newTr.translate(newAttrs.x, newAttrs.y);
newTr.rotate(newAttrs.rotation);
newTr.scale(newAttrs.width / base, newAttrs.height / base);
const delta = newTr.multiply(oldTr.invert());
this._nodes.forEach(node => {
// var oldRect = this.__getNodeShape(node, 0);
// var newRect = transformAndRotateShape(oldRect, oldAttrs, newAttrs);
// this._fitNodeInto(node, newRect, evt);
const pt = node.getParent().getAbsoluteTransform();
const selfTr = node.getTransform().copy();
selfTr.translate(node.offsetX(), node.offsetY());
// var m = new Transform(),
// x = node.x(),
// y = node.y(),
// rotation = Konva.getAngle(node.rotation()),
// scaleX = node.scaleX(),
// scaleY = node.scaleY(),
// skewX = node.skewX(),
// skewY = node.skewY(),
// offsetX = node.offsetX(),
// offsetY = node.offsetY();
const newLocal = new Transform();
newLocal
.multiply(delta)
.multiply(pt)
.multiply(pt.copy().invert())
.multiply(selfTr);
// // if (x !== 0 || y !== 0) {
// // m.translate(x, y);
// // }
// if (rotation !== 0) {
// m.rotate(rotation);
// node._cache.set('transform', newLocal);
// node._cache.set('absoluteTransform', newLocal);
// console.log();
const attrs = newLocal.decompose();
// if (Math.abs(attrs.skewX - node.skewX()) < 0.00000001) {
// attrs.skewX = node.skewX();
// }
// if (skewX !== 0 || skewY !== 0) {
// m.skew(skewX, skewY);
// if (Math.abs(attrs.skewY - node.skewY()) < 0.00000001) {
// attrs.skewY = node.skewY();
// }
// if (scaleX !== 1 || scaleY !== 1) {
// m.scale(scaleX, scaleY);
// }
// if (offsetX !== 0 || offsetY !== 0) {
// m.translate(-1 * offsetX, -1 * offsetY);
// }
// return m;
// t.translate(dx, dy);
// t.rotate(Konva.getAngle(node.rotation()) + angle);
// t.scale(node.scaleX() * scaleX, node.scaleY() * scaleY);
// const t = node.getTransform();
// const dt1 = new Transform();
// dt1.translate(dx, dy);
// dt1.rotate(angle);
// dt1.scale(scaleX, scaleY);
// // const oldScale = {
// // x: dt.decompose().scaleX,
// // y: dt.decompose().scaleY
// // };
// // dt.scale(1, 1);
// t.multiply(dt1);
// dt.scale(oldScale.x, oldScale.y);
// t.translate(
// dt.getTranslation().x * t.decompose().scaleX,
// dt.getTranslation().y * t.decompose().scaleY
// );
// dt.set
// dt.setAbsolutePosition(0, 0);
// t.multiply(dt);
// console.log(
// node.rotation(),
// dt.decompose().rotation,
// t.decompose().rotation,
// Util._getRotation(newAttrs.rotation)
// );
// m.multiply(dt);
// m.translate(x + dx, y + dy);
// node.setAttrs(t.decompose());
// if (offsetX !== 0 || offsetY !== 0) {
// t.translate(
// node.offsetX() * (1 - node.scaleX() / scaleX),
// node.offsetY() * (1 - node.scaleY() / scaleY)
// );
const parentRot = Konva.getAngle(node.getParent().getAbsoluteRotation());
const rotation = Util._getRotation(newAttrs.rotation - parentRot);
var pure = node.getClientRect({
skipShadow: true,
skipStroke: this.ignoreStroke()
});
const parentTransform = node
.getParent()
.getAbsoluteTransform()
.copy();
parentTransform.invert();
const invertedPoint = parentTransform.point({
x: newAttrs.x,
y: newAttrs.y
});
newAttrs.x = invertedPoint.x;
newAttrs.y = invertedPoint.y;
var absScale = node.getParent().getAbsoluteScale();
pure.width *= absScale.x;
pure.height *= absScale.y;
var scaleX = pure.width ? newAttrs.width / pure.width : 1;
var scaleY = pure.height ? newAttrs.height / pure.height : 1;
var dx = pure.x * scaleX - node.offsetX() * scaleX;
var dy = pure.y * scaleY - node.offsetY() * scaleY;
var x = newAttrs.x - (dx * Math.cos(rotation) + dy * Math.sin(-rotation));
var y = newAttrs.y - (dy * Math.cos(rotation) + dx * Math.sin(rotation));
var tr = new Transform();
tr.translate(x, y);
tr.rotate(rotation);
tr.scale(scaleX, scaleY);
node.setAttrs(tr.multiply(node.getTransform()).decompose());
node.setAttrs(attrs);
});
this.rotation(Util._getRotation(newAttrs.rotation));
this._resetTransformCache();
@ -1154,6 +1141,29 @@ export class Transformer extends Group {
this.getLayer().batchDraw();
}
_fitNodeInto(node: Node, newAttrs, evt) {
var pure = node.getClientRect({
skipTransform: true,
skipShadow: true,
skipStroke: this.ignoreStroke()
});
const parentTransform = node
.getParent()
.getAbsoluteTransform()
.copy();
parentTransform.invert();
const invertedPoint = parentTransform.point({
x: newAttrs.x,
y: newAttrs.y
});
var absScale = node.getParent().getAbsoluteScale();
newAttrs.x = invertedPoint.x;
newAttrs.y = invertedPoint.y;
newAttrs.width /= absScale.x;
newAttrs.height /= absScale.y;
if (this.boundBoxFunc()) {
const oldAttrs = this.__getNodeShape(
node,
@ -1173,28 +1183,8 @@ export class Transformer extends Group {
const parentRot = Konva.getAngle(node.getParent().getAbsoluteRotation());
node.rotation(Util._getRotation(newAttrs.rotation - parentRot));
var pure = node.getClientRect({
skipTransform: true,
skipShadow: true,
skipStroke: this.ignoreStroke()
});
const parentTransform = node
.getParent()
.getAbsoluteTransform()
.copy();
parentTransform.invert();
const invertedPoint = parentTransform.point({
x: newAttrs.x,
y: newAttrs.y
});
newAttrs.x = invertedPoint.x;
newAttrs.y = invertedPoint.y;
var absScale = node.getParent().getAbsoluteScale();
pure.width *= absScale.x;
pure.height *= absScale.y;
var scaleX = pure.width ? newAttrs.width / pure.width : 1;
var scaleY = pure.height ? newAttrs.height / pure.height : 1;

View File

@ -1492,6 +1492,37 @@ suite('Shape', function() {
assert.equal(absRect.height, 100);
});
test.only('getClientRect with skew', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var shape = new Konva.Rect({
x: 150,
y: 50,
width: 100,
height: 100,
scaleX: 0.63,
skewX: -1.6,
fill: 'green'
});
layer.add(shape);
var back = new Konva.Rect({
stroke: 'red'
});
back.setAttrs(shape.getClientRect());
layer.add(back);
layer.draw();
var absRect = shape.getClientRect();
assert.equal(absRect.x, 10);
assert.equal(absRect.y, 0);
assert.equal(absRect.width, 100);
assert.equal(absRect.height, 100);
});
test('shadow should respect pixel ratio', function() {
var stage = addStage();
var layer = new Konva.Layer();

View File

@ -315,6 +315,32 @@ suite('TextPath', function() {
assert.equal(layer.getContext().getTrace(true), trace);
});
test.skip('Text path with center align - arc', function() {
var stage = addStage();
var layer = new Konva.Layer();
var textpath = new Konva.TextPath({
fill: '#333',
fontSize: 20,
text: 'Hello World',
align: 'right',
data: 'M 50 200 a 100 100 0 0 1 200 0'
});
layer.add(textpath);
var path = new Konva.Path({
stroke: '#000',
data: 'M 50 200 a 100 100 0 0 1 200 0'
});
layer.add(path);
stage.add(layer);
var trace =
'restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();restore();restore();';
assert.equal(layer.getContext().getTrace(true), trace);
});
test('Text path with align right', function() {
var stage = addStage();
var layer = new Konva.Layer();

View File

@ -1,4 +1,4 @@
suite.only('Transformer', function() {
suite('Transformer', function() {
function isClose(a, b) {
return Math.abs(a - b) < 0.000001;
}
@ -138,7 +138,7 @@ suite.only('Transformer', function() {
layer.draw();
});
test.only('try to fit simple rectangle', function() {
test('try to fit simple rectangle', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
@ -159,21 +159,23 @@ suite.only('Transformer', function() {
layer.draw();
// tr._fitNodesInto({
// x: 120,
// y: 60,
// width: 50,
// height: 50,
// rotation: Konva.getAngle(45)
// });
tr._fitNodesInto({
x: 120,
y: 60,
width: 50,
height: 50,
rotation: Konva.getAngle(45)
});
// assert.equal(tr.x(), rect.x());
// assert.equal(Math.round(tr.y()), rect.y());
// assert.equal(tr.width(), 50);
// assert.equal(tr.height(), 50);
// assert.equal(tr.rotation(), rect.rotation());
assert.equal(tr.x(), rect.x());
assert.equal(Math.round(tr.y()), rect.y());
assert.equal(tr.width(), 50);
assert.equal(tr.height(), 50);
assert.equal(tr.rotation(), rect.rotation());
});
test('try to fit simple rotated rectangle', function() {
var stage = addStage();
var layer = new Konva.Layer();
@ -351,6 +353,43 @@ suite.only('Transformer', function() {
layer.draw();
});
test.only('try to fit rectangle with skew', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var rect = new Konva.Rect({
x: 100,
y: 60,
draggable: true,
width: 100,
height: 100,
fill: 'yellow',
skewX: 1,
});
layer.add(rect);
var tr = new Konva.Transformer();
layer.add(tr);
tr.nodes([rect]);
layer.draw();
// tr._fitNodesInto({
// x: 120,
// y: 60,
// width: 50,
// height: 50,
// rotation: Konva.getAngle(45)
// });
// assert.equal(tr.x(), rect.x());
// assert.equal(Math.round(tr.y()), rect.y());
// assert.equal(tr.width(), 50);
// assert.equal(tr.height(), 50);
// assert.equal(tr.rotation(), rect.rotation());
});
test('try to resize in draggable stage', function() {
var stage = addStage();
stage.draggable(true);
@ -452,13 +491,13 @@ suite.only('Transformer', function() {
tr._fitNodesInto(box);
assert.equal(rect.x(), 100);
assert.equal(rect.y(), 0);
assert.equal(rect.width(), 100);
assert.equal(rect.scaleX(), -1);
assert.equal(rect.height(), 100);
assert.equal(rect.scaleY(), 1);
assert.equal(rect.rotation(), 0);
assert.almostEqual(rect.x(), 100);
assert.almostEqual(rect.y(), 0);
assert.almostEqual(rect.width(), 100);
assert.almostEqual(rect.scaleX(), 1);
assert.almostEqual(rect.height(), 100);
assert.almostEqual(rect.scaleY(), -1);
assert.almostEqual(rect.rotation(), -180);
layer.draw();
});
@ -497,11 +536,11 @@ suite.only('Transformer', function() {
rotation: 0
});
assert.equal(rect.x(), 20);
assert.equal(rect.y(), 20);
assert.equal(rect.width(), 100);
assert.equal(rect.height(), 100);
assert.equal(rect.scaleX(), 2);
assert.almostEqual(rect.x(), 20);
assert.almostEqual(rect.y(), 20);
assert.almostEqual(rect.width(), 100);
assert.almostEqual(rect.height(), 100);
assert.almostEqual(rect.scaleX(), 2);
});
test('listen shape changes', function() {
@ -569,7 +608,7 @@ suite.only('Transformer', function() {
assert.equal(tr.rotation(), rect.rotation());
});
test.only('try to fit a transformed rect', function() {
test('try to fit a transformed rect', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
@ -600,11 +639,11 @@ suite.only('Transformer', function() {
rotation: 0
});
assert.equal(rect.x(), 100);
assert.equal(rect.y(), 70);
assert.equal(rect.width() * rect.scaleX(), 100);
assert.equal(rect.height() * rect.scaleY(), 100);
assert.equal(rect.rotation(), rect.rotation());
assert.almostEqual(rect.x(), 100);
assert.almostEqual(rect.y(), 70);
assert.almostEqual(rect.width() * rect.scaleX(), 100);
assert.almostEqual(rect.height() * rect.scaleY(), 100);
assert.almostEqual(rect.rotation(), rect.rotation());
});
test('add transformer for transformed rect with offset', function() {
@ -670,17 +709,17 @@ suite.only('Transformer', function() {
});
layer.draw();
assert.equal(rect.x(), 100);
assert.equal(rect.y(), 50);
assert.equal(rect.width() * rect.scaleX(), 200);
assert.equal(rect.height() * rect.scaleY(), 100);
assert.equal(rect.rotation(), rect.rotation());
assert.almostEqual(rect.x(), 100);
assert.almostEqual(rect.y(), 50);
assert.almostEqual(rect.width() * rect.scaleX(), 200);
assert.almostEqual(rect.height() * rect.scaleY(), 100);
assert.almostEqual(rect.rotation(), rect.rotation());
assert.equal(tr.x(), 0);
assert.equal(tr.y(), 0);
assert.equal(tr.width(), 200);
assert.equal(tr.height(), 100);
assert.equal(rect.rotation(), rect.rotation());
assert.almostEqual(tr.x(), 0);
assert.almostEqual(tr.y(), 0);
assert.almostEqual(tr.width(), 200);
assert.almostEqual(tr.height(), 100);
assert.almostEqual(rect.rotation(), rect.rotation());
});
test('add transformer for circle', function() {
@ -938,16 +977,16 @@ suite.only('Transformer', function() {
var rect = group.getClientRect();
assert.equal(group.x(), 50);
assert.equal(group.y(), 50);
assert.equal(rect.width, 100);
assert.equal(rect.height, 100);
assert.equal(group.rotation(), 90);
assert.almostEqual(group.x(), 50);
assert.almostEqual(group.y(), 50);
assert.almostEqual(rect.width, 100);
assert.almostEqual(rect.height, 100);
assert.almostEqual(group.rotation(), 90);
assert.equal(tr.x(), 100);
assert.equal(tr.y(), 0);
assert.equal(tr.width(), 100);
assert.equal(tr.height(), 100);
assert.almostEqual(tr.x(), 100);
assert.almostEqual(tr.y(), 0);
assert.almostEqual(tr.width(), 100);
assert.almostEqual(tr.height(), 100);
});
test('add transformer to another group', function() {
@ -1040,15 +1079,15 @@ suite.only('Transformer', function() {
var rect = group.getClientRect();
assert.equal(group.x(), 100);
assert.equal(group.y(), 50);
assert.equal(rect.width, 200);
assert.equal(rect.height, 100);
assert.almostEqual(group.x(), 100);
assert.almostEqual(group.y(), 50);
assert.almostEqual(rect.width, 200);
assert.almostEqual(rect.height, 100);
assert.equal(tr.x(), 0);
assert.equal(tr.y(), 0);
assert.equal(tr.width(), 200);
assert.equal(tr.height(), 100);
assert.almostEqual(tr.x(), 0);
assert.almostEqual(tr.y(), 0);
assert.almostEqual(tr.width(), 200);
assert.almostEqual(tr.height(), 100);
});
test('toJSON should not save attached node and children', function() {
@ -1203,12 +1242,12 @@ suite.only('Transformer', function() {
y: 150
});
assert.equal(rect.x(), 80);
assert.equal(rect.y(), 30);
assert.equal(rect.width(), 100);
assert.equal(rect.scaleX(), 0.5);
assert.equal(rect.height(), 100);
assert.equal(rect.scaleY(), 1);
assert.almostEqual(rect.x(), 80);
assert.almostEqual(rect.y(), 30);
assert.almostEqual(rect.width(), 100);
assert.almostEqual(rect.scaleX(), 0.5);
assert.almostEqual(rect.height(), 100);
assert.almostEqual(rect.scaleY(), 1);
});
test('keep ratio should allow negative scaling', function() {
@ -1235,28 +1274,22 @@ suite.only('Transformer', function() {
var anchor = tr.findOne('.top-right');
var pos = anchor.getAbsolutePosition();
stage.simulateMouseDown({
tr.simulateMouseDown({
x: pos.x,
y: pos.y
});
var box = stage.content.getBoundingClientRect();
tr._handleMouseMove({
clientX: box.left + pos.x - 100,
clientY: box.top + pos.y + 100
tr.simulateMouseMove({
x: pos.x - 100,
y: pos.y + 100
});
// here is duplicate, because transformer is listening window events
tr._handleMouseUp({
clientX: box.left + pos.x - 100,
clientY: box.top + pos.y + 100
});
stage.simulateMouseUp({
tr.simulateMouseUp({
x: pos.x - 100,
y: pos.y + 100
});
assert.equal(rect.scaleX(), -1);
assert.equal(rect.scaleY(), -1);
assert.almostEqual(rect.scaleX(), 1);
assert.almostEqual(rect.scaleY(), 1);
assert.almostEqual(rect.rotation(), -180);
});
test.skip('visual test', function(done) {
@ -1349,24 +1382,24 @@ suite.only('Transformer', function() {
x: 0,
y: 199
});
assert.equal(rect.rotation(), 270);
assert.almostEqual(rect.rotation(), -90);
tr.simulateMouseMove({
x: 0,
y: 50
});
assert.equal(rect.rotation(), 270);
assert.almostEqual(rect.rotation(), -90);
tr.simulateMouseMove({
x: 0,
y: 45
});
assert.equal(rect.rotation(), 270);
assert.almostEqual(rect.rotation(), -90);
tr.simulateMouseMove({
x: 0,
y: 1
});
assert.equal(rect.rotation(), 270);
assert.almostEqual(rect.rotation(), -90);
tr.simulateMouseMove({
x: 1,
@ -1410,12 +1443,12 @@ suite.only('Transformer', function() {
y: 60
});
assert.equal(rect.x(), 115);
assert.equal(rect.y(), 10);
assert.equal(rect.width(), 100);
assert.equal(rect.scaleX(), -0.05);
assert.equal(rect.height(), 100);
assert.equal(rect.rotation(), 0);
assert.almostEqual(rect.x(), 115);
assert.almostEqual(rect.y(), 10);
assert.almostEqual(rect.width(), 100);
assert.almostEqual(rect.scaleX(), -0.05);
assert.almostEqual(rect.height(), 100);
assert.almostEqual(rect.rotation(), 0);
tr.simulateMouseMove({
x: 125,
@ -1478,24 +1511,24 @@ suite.only('Transformer', function() {
y: 125
});
assert.equal(rect.x(), 10);
assert.equal(rect.y(), 115);
assert.equal(rect.width(), 100);
assert.equal(rect.scaleY(), -0.05);
assert.equal(rect.height(), 100);
assert.equal(rect.rotation(), 0);
assert.almostEqual(rect.x(), 10);
assert.almostEqual(rect.y(), 115);
assert.almostEqual(rect.width(), 100);
assert.almostEqual(rect.scaleY(), -0.05);
assert.almostEqual(rect.height(), 100);
assert.almostEqual(rect.rotation(), 0);
tr.simulateMouseMove({
x: 60,
y: 125
});
assert.equal(rect.x(), 10);
assert.equal(rect.y(), 115);
assert.equal(rect.width(), 100);
assert.equal(rect.scaleY(), -0.05);
assert.equal(rect.height(), 100);
assert.equal(rect.rotation(), 0);
assert.almostEqual(rect.x(), 10);
assert.almostEqual(rect.y(), 115);
assert.almostEqual(rect.width(), 100);
assert.almostEqual(rect.scaleY(), -0.05);
assert.almostEqual(rect.height(), 100);
assert.almostEqual(rect.rotation(), 0);
// switch again
tr.simulateMouseMove({
@ -1503,13 +1536,13 @@ suite.only('Transformer', function() {
y: 90
});
assert.equal(rect.x(), 10);
assert.equal(rect.y(), 100);
assert.equal(rect.width(), 100);
assert.equal(rect.scaleX(), 1);
assert.equal(rect.scaleY(), 0.1);
assert.equal(rect.height(), 100);
assert.equal(rect.rotation(), 0);
assert.almostEqual(rect.x(), 10);
assert.almostEqual(rect.y(), 100);
assert.almostEqual(rect.width(), 100);
assert.almostEqual(rect.scaleX(), 1);
assert.almostEqual(rect.scaleY(), 0.1);
assert.almostEqual(rect.height(), 100);
assert.almostEqual(rect.rotation(), 0);
tr.simulateMouseUp();
});
@ -2884,8 +2917,6 @@ suite.only('Transformer', function() {
});
layer.draw();
console.log(start, end);
// move from start to end
tr.simulateMouseDown(start);
tr.simulateMouseMove(end);
@ -4186,7 +4217,12 @@ suite.only('Transformer', function() {
test('boundBoxFox should work in local coordinates', function() {
var stage = addStage();
var layer = new Konva.Layer();
var layer = new Konva.Layer({
x: 10,
y: 10,
scaleX: 2,
scaleY: 2
});
stage.add(layer);
var rect1 = new Konva.Rect({
@ -4221,6 +4257,13 @@ suite.only('Transformer', function() {
height: 50,
rotation: 0
});
assert.deepEqual(newBox, {
x: 0,
y: 0,
width: 50,
height: 50,
rotation: 0
});
} else {
assert.deepEqual(oldBox, {
x: 50,
@ -4229,6 +4272,13 @@ suite.only('Transformer', function() {
height: 50,
rotation: 0
});
assert.deepEqual(newBox, {
x: 50,
y: 50,
width: 50,
height: 50,
rotation: 0
});
}
return newBox;
}
@ -4236,10 +4286,10 @@ suite.only('Transformer', function() {
layer.add(tr);
tr._fitNodesInto({
x: 0,
y: 0,
width: 100,
height: 100,
x: 10,
y: 10,
width: 200,
height: 200,
rotation: 0
});
});