mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 10:45:21 +08:00
split up methods from the PathHelper class and moved them to Path and Geometry. Path specific methods went to Path, and general purpose geometric utility methods went to the Geometry utility class. TextPath now inherits methods from Path
This commit is contained in:
parent
c4a359cd48
commit
9631d6e1bb
2
Thorfile
2
Thorfile
@ -4,7 +4,7 @@ class Build < Thor
|
||||
# This is the list of files to concatenate. The first file will appear at the top of the final file. All files are relative to the lib directory.
|
||||
FILES = [
|
||||
"license.js", "src/Global.js", "src/Transition.js", "src/filters/Grayscale.js",
|
||||
"src/util/Type.js", "src/util/Canvas.js", "src/util/Class.js", "src/util/Tween.js", "src/util/Transform.js",
|
||||
"src/util/Type.js", "src/util/Canvas.js", "src/util/Class.js", "src/util/Tween.js", "src/util/Transform.js", "src/util/Geometry.js",
|
||||
"src/Animation.js", "src/Node.js", "src/Container.js", "src/Stage.js", "src/Layer.js", "src/Group.js", "src/Shape.js",
|
||||
"src/shapes/Rect.js", "src/shapes/Ellipse.js", "src/shapes/Image.js", "src/shapes/Polygon.js", "src/shapes/Text.js", "src/shapes/Line.js", "src/shapes/Sprite.js"
|
||||
]
|
||||
|
107
dist/kinetic-core.js
vendored
107
dist/kinetic-core.js
vendored
@ -1083,6 +1083,109 @@ Kinetic.Transform.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Utility methods written by jfollas to
|
||||
* handle length and point measurements
|
||||
*/
|
||||
Kinetic.Geometry = {
|
||||
getLineLength: function(x1, y1, x2, y2) {
|
||||
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
|
||||
},
|
||||
getPointOnLine: function(dist, P1x, P1y, P2x, P2y, fromX, fromY) {
|
||||
if(fromX === undefined) {
|
||||
fromX = P1x;
|
||||
}
|
||||
if(fromY === undefined) {
|
||||
fromY = P1y;
|
||||
}
|
||||
|
||||
var m = (P2y - P1y) / ((P2x - P1x) + 0.00000001);
|
||||
var run = Math.sqrt(dist * dist / (1 + m * m));
|
||||
var rise = m * run;
|
||||
var pt;
|
||||
|
||||
if((fromY - P1y) / ((fromX - P1x) + 0.00000001) === m) {
|
||||
pt = {
|
||||
x: fromX + run,
|
||||
y: fromY + rise
|
||||
};
|
||||
}
|
||||
else {
|
||||
var ix, iy;
|
||||
|
||||
var len = this.getLineLength(P1x, P1y, P2x, P2y);
|
||||
if(len < 0.00000001) {
|
||||
return undefined;
|
||||
}
|
||||
var u = (((fromX - P1x) * (P2x - P1x)) + ((fromY - P1y) * (P2y - P1y)));
|
||||
u = u / (len * len);
|
||||
ix = P1x + u * (P2x - P1x);
|
||||
iy = P1y + u * (P2y - P1y);
|
||||
|
||||
var pRise = this.getLineLength(fromX, fromY, ix, iy);
|
||||
var pRun = Math.sqrt(dist * dist - pRise * pRise);
|
||||
run = Math.sqrt(pRun * pRun / (1 + m * m));
|
||||
rise = m * run;
|
||||
pt = {
|
||||
x: ix + run,
|
||||
y: iy + rise
|
||||
};
|
||||
}
|
||||
|
||||
return pt;
|
||||
},
|
||||
getPointOnCubicBezier: function(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
|
||||
function CB1(t) {
|
||||
return t * t * t;
|
||||
}
|
||||
function CB2(t) {
|
||||
return 3 * t * t * (1 - t);
|
||||
}
|
||||
function CB3(t) {
|
||||
return 3 * t * (1 - t) * (1 - t);
|
||||
}
|
||||
function CB4(t) {
|
||||
return (1 - t) * (1 - t) * (1 - t);
|
||||
}
|
||||
var x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
|
||||
var y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
|
||||
|
||||
return {
|
||||
x: x,
|
||||
y: y
|
||||
};
|
||||
},
|
||||
getPointOnQuadraticBezier: function(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
|
||||
function QB1(t) {
|
||||
return t * t;
|
||||
}
|
||||
function QB2(t) {
|
||||
return 2 * t * (1 - t);
|
||||
}
|
||||
function QB3(t) {
|
||||
return (1 - t) * (1 - t);
|
||||
}
|
||||
var x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
|
||||
var y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
|
||||
|
||||
return {
|
||||
x: x,
|
||||
y: y
|
||||
};
|
||||
},
|
||||
getPointOnEllipticalArc: function(cx, cy, rx, ry, theta, psi) {
|
||||
var cosPsi = Math.cos(psi), sinPsi = Math.sin(psi);
|
||||
var pt = {
|
||||
x: rx * Math.cos(theta),
|
||||
y: ry * Math.sin(theta)
|
||||
};
|
||||
return {
|
||||
x: cx + (pt.x * cosPsi - pt.y * sinPsi),
|
||||
y: cy + (pt.x * sinPsi + pt.y * cosPsi)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Animation
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
@ -4559,9 +4662,7 @@ Kinetic.Ellipse = Kinetic.Shape.extend({
|
||||
|
||||
// call super constructor
|
||||
this._super(config);
|
||||
|
||||
this._convertRadius();
|
||||
|
||||
var that = this;
|
||||
this.on('radiusChange.kinetic', function() {
|
||||
that._convertRadius();
|
||||
@ -4891,7 +4992,6 @@ Kinetic.Text = Kinetic.Shape.extend({
|
||||
var attr = attrs[n];
|
||||
this.on(attr + 'Change.kinetic', that._setTextData);
|
||||
}
|
||||
|
||||
that._setTextData();
|
||||
},
|
||||
drawFunc: function(context) {
|
||||
@ -5403,7 +5503,6 @@ Kinetic.Sprite = Kinetic.Shape.extend({
|
||||
config.drawFunc = this.drawFunc;
|
||||
// call super constructor
|
||||
this._super(config);
|
||||
|
||||
var that = this;
|
||||
this.on('animationChange.kinetic', function() {
|
||||
// reset index when animation changes
|
||||
|
29
dist/kinetic-core.min.js
vendored
29
dist/kinetic-core.min.js
vendored
File diff suppressed because one or more lines are too long
@ -15,14 +15,11 @@ Kinetic.Plugins.Path = Kinetic.Shape.extend({
|
||||
var that = this;
|
||||
|
||||
config.drawFunc = this.drawFunc;
|
||||
|
||||
// call super constructor
|
||||
this._super(config);
|
||||
|
||||
this.dataArray = Kinetic.Plugins.PathHelper.parsePathData(this.attrs.data);
|
||||
|
||||
this.dataArray = this.parsePathData(this.attrs.data);
|
||||
this.on('dataChange', function() {
|
||||
that.dataArray = Kinetic.Plugins.PathHelper.parsePathData(that.attrs.data);
|
||||
that.dataArray = this.parsePathData(that.attrs.data);
|
||||
});
|
||||
},
|
||||
drawFunc: function(context) {
|
||||
@ -68,6 +65,368 @@ Kinetic.Plugins.Path = Kinetic.Shape.extend({
|
||||
}
|
||||
this.fill(context);
|
||||
this.stroke(context);
|
||||
},
|
||||
/**
|
||||
* get parsed data array from the data
|
||||
* string. V, v, H, h, and l data are converted to
|
||||
* L data for the purpose of high performance Path
|
||||
* rendering
|
||||
*/
|
||||
parsePathData: function(data) {
|
||||
// Path Data Segment must begin with a moveTo
|
||||
//m (x y)+ Relative moveTo (subsequent points are treated as lineTo)
|
||||
//M (x y)+ Absolute moveTo (subsequent points are treated as lineTo)
|
||||
//l (x y)+ Relative lineTo
|
||||
//L (x y)+ Absolute LineTo
|
||||
//h (x)+ Relative horizontal lineTo
|
||||
//H (x)+ Absolute horizontal lineTo
|
||||
//v (y)+ Relative vertical lineTo
|
||||
//V (y)+ Absolute vertical lineTo
|
||||
//z (closepath)
|
||||
//Z (closepath)
|
||||
//c (x1 y1 x2 y2 x y)+ Relative Bezier curve
|
||||
//C (x1 y1 x2 y2 x y)+ Absolute Bezier curve
|
||||
//q (x1 y1 x y)+ Relative Quadratic Bezier
|
||||
//Q (x1 y1 x y)+ Absolute Quadratic Bezier
|
||||
//t (x y)+ Shorthand/Smooth Relative Quadratic Bezier
|
||||
//T (x y)+ Shorthand/Smooth Absolute Quadratic Bezier
|
||||
//s (x2 y2 x y)+ Shorthand/Smooth Relative Bezier curve
|
||||
//S (x2 y2 x y)+ Shorthand/Smooth Absolute Bezier curve
|
||||
//a (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Relative Elliptical Arc
|
||||
//A (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Absolute Elliptical Arc
|
||||
|
||||
// return early if data is not defined
|
||||
if(!data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// command string
|
||||
var cs = data;
|
||||
|
||||
// command chars
|
||||
var cc = ['m', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'];
|
||||
// convert white spaces to commas
|
||||
cs = cs.replace(new RegExp(' ', 'g'), ',');
|
||||
// create pipes so that we can split the data
|
||||
for(var n = 0; n < cc.length; n++) {
|
||||
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
|
||||
}
|
||||
// create array
|
||||
var arr = cs.split('|');
|
||||
var ca = [];
|
||||
// init context point
|
||||
var cpx = 0;
|
||||
var cpy = 0;
|
||||
for(var n = 1; n < arr.length; n++) {
|
||||
var str = arr[n];
|
||||
var c = str.charAt(0);
|
||||
str = str.slice(1);
|
||||
// remove ,- for consistency
|
||||
str = str.replace(new RegExp(',-', 'g'), '-');
|
||||
// add commas so that it's easy to split
|
||||
str = str.replace(new RegExp('-', 'g'), ',-');
|
||||
str = str.replace(new RegExp('e,-', 'g'), 'e-');
|
||||
var p = str.split(',');
|
||||
if(p.length > 0 && p[0] === '') {
|
||||
p.shift();
|
||||
}
|
||||
// convert strings to floats
|
||||
for(var i = 0; i < p.length; i++) {
|
||||
p[i] = parseFloat(p[i]);
|
||||
}
|
||||
while(p.length > 0) {
|
||||
if(isNaN(p[0]))// case for a trailing comma before next command
|
||||
break;
|
||||
|
||||
var cmd = null;
|
||||
var points = [];
|
||||
var startX = cpx, startY = cpy;
|
||||
|
||||
// convert l, H, h, V, and v to L
|
||||
switch (c) {
|
||||
|
||||
// Note: Keep the lineTo's above the moveTo's in this switch
|
||||
case 'l':
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'L';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'L':
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
|
||||
// Note: lineTo handlers need to be above this point
|
||||
case 'm':
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'M';
|
||||
points.push(cpx, cpy);
|
||||
c = 'l';
|
||||
// subsequent points are treated as relative lineTo
|
||||
break;
|
||||
case 'M':
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
cmd = 'M';
|
||||
points.push(cpx, cpy);
|
||||
c = 'L';
|
||||
// subsequent points are treated as absolute lineTo
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
cpx += p.shift();
|
||||
cmd = 'L';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'H':
|
||||
cpx = p.shift();
|
||||
cmd = 'L';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'v':
|
||||
cpy += p.shift();
|
||||
cmd = 'L';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'V':
|
||||
cpy = p.shift();
|
||||
cmd = 'L';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'C':
|
||||
points.push(p.shift(), p.shift(), p.shift(), p.shift());
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'c':
|
||||
points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'C';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'S':
|
||||
var ctlPtx = cpx, ctlPty = cpy;
|
||||
var prevCmd = ca[ca.length - 1];
|
||||
if(prevCmd.command === 'C') {
|
||||
ctlPtx = cpx + (cpx - prevCmd.points[2]);
|
||||
ctlPty = cpy + (cpy - prevCmd.points[3]);
|
||||
}
|
||||
points.push(ctlPtx, ctlPty, p.shift(), p.shift());
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
cmd = 'C';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 's':
|
||||
var ctlPtx = cpx, ctlPty = cpy;
|
||||
var prevCmd = ca[ca.length - 1];
|
||||
if(prevCmd.command === 'C') {
|
||||
ctlPtx = cpx + (cpx - prevCmd.points[2]);
|
||||
ctlPty = cpy + (cpy - prevCmd.points[3]);
|
||||
}
|
||||
points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'C';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'Q':
|
||||
points.push(p.shift(), p.shift());
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'q':
|
||||
points.push(cpx + p.shift(), cpy + p.shift());
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'Q';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'T':
|
||||
var ctlPtx = cpx, ctlPty = cpy;
|
||||
var prevCmd = ca[ca.length - 1];
|
||||
if(prevCmd.command === 'Q') {
|
||||
ctlPtx = cpx + (cpx - prevCmd.points[0]);
|
||||
ctlPty = cpy + (cpy - prevCmd.points[1]);
|
||||
}
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
cmd = 'Q';
|
||||
points.push(ctlPtx, ctlPty, cpx, cpy);
|
||||
break;
|
||||
case 't':
|
||||
var ctlPtx = cpx, ctlPty = cpy;
|
||||
var prevCmd = ca[ca.length - 1];
|
||||
if(prevCmd.command === 'Q') {
|
||||
ctlPtx = cpx + (cpx - prevCmd.points[0]);
|
||||
ctlPty = cpy + (cpy - prevCmd.points[1]);
|
||||
}
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'Q';
|
||||
points.push(ctlPtx, ctlPty, cpx, cpy);
|
||||
break;
|
||||
case 'A':
|
||||
var rx = p.shift(), ry = p.shift(), psi = p.shift(), fa = p.shift(), fs = p.shift();
|
||||
var x1 = cpx, y1 = cpy; cpx = p.shift(), cpy = p.shift();
|
||||
cmd = 'A';
|
||||
points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
|
||||
break;
|
||||
case 'a':
|
||||
var rx = p.shift(), ry = p.shift(), psi = p.shift(), fa = p.shift(), fs = p.shift();
|
||||
var x1 = cpx, y1 = cpy; cpx += p.shift(), cpy += p.shift();
|
||||
cmd = 'A';
|
||||
points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
|
||||
break;
|
||||
}
|
||||
|
||||
ca.push({
|
||||
command: cmd || c,
|
||||
points: points,
|
||||
start: {
|
||||
x: startX,
|
||||
y: startY
|
||||
},
|
||||
pathLength: this.calcLength(startX, startY, cmd || c, points)
|
||||
});
|
||||
}
|
||||
|
||||
if(c === 'z' || c === 'Z')
|
||||
ca.push({
|
||||
command: 'z',
|
||||
points: [],
|
||||
start: undefined,
|
||||
pathLength: 0
|
||||
});
|
||||
}
|
||||
|
||||
return ca;
|
||||
},
|
||||
calcLength: function(x, y, cmd, points) {
|
||||
var len, p1, p2;
|
||||
var g = Kinetic.Geometry;
|
||||
|
||||
switch (cmd) {
|
||||
case 'L':
|
||||
return g.getLineLength(x, y, points[0], points[1]);
|
||||
case 'C':
|
||||
// Approximates by breaking curve into 100 line segments
|
||||
len = 0.0;
|
||||
p1 = g.getPointOnCubicBezier(0, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
|
||||
for( t = 0.01; t <= 1; t += 0.01) {
|
||||
p2 = g.getPointOnCubicBezier(t, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
|
||||
len += g.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
||||
p1 = p2;
|
||||
}
|
||||
return len;
|
||||
case 'Q':
|
||||
// Approximates by breaking curve into 100 line segments
|
||||
len = 0.0;
|
||||
p1 = g.getPointOnQuadraticBezier(0, x, y, points[0], points[1], points[2], points[3]);
|
||||
for( t = 0.01; t <= 1; t += 0.01) {
|
||||
p2 = g.getPointOnQuadraticBezier(t, x, y, points[0], points[1], points[2], points[3]);
|
||||
len += g.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
||||
p1 = p2;
|
||||
}
|
||||
return len;
|
||||
case 'A':
|
||||
// Approximates by breaking curve into line segments
|
||||
len = 0.0;
|
||||
var start = points[4];
|
||||
// 4 = theta
|
||||
var dTheta = points[5];
|
||||
// 5 = dTheta
|
||||
var end = points[4] + dTheta;
|
||||
var inc = Math.PI / 180.0;
|
||||
// 1 degree resolution
|
||||
if(Math.abs(start - end) < inc) {
|
||||
inc = Math.abs(start - end);
|
||||
}
|
||||
// Note: for purpose of calculating arc length, not going to worry about rotating X-axis by angle psi
|
||||
p1 = g.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], start, 0);
|
||||
if(dTheta < 0) {// clockwise
|
||||
for( t = start - inc; t > end; t -= inc) {
|
||||
p2 = g.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
|
||||
len += g.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
||||
p1 = p2;
|
||||
}
|
||||
}
|
||||
else {// counter-clockwise
|
||||
for( t = start + inc; t < end; t += inc) {
|
||||
p2 = g.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
|
||||
len += g.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
||||
p1 = p2;
|
||||
}
|
||||
}
|
||||
p2 = g.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], end, 0);
|
||||
len += g.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
convertEndpointToCenterParameterization: function(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
|
||||
// Derived from: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||
var psi = psiDeg * (Math.PI / 180.0);
|
||||
var xp = Math.cos(psi) * (x1 - x2) / 2.0 + Math.sin(psi) * (y1 - y2) / 2.0;
|
||||
var yp = -1 * Math.sin(psi) * (x1 - x2) / 2.0 + Math.cos(psi) * (y1 - y2) / 2.0;
|
||||
|
||||
var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
|
||||
|
||||
if(lambda > 1) {
|
||||
rx *= Math.sqrt(lambda);
|
||||
ry *= Math.sqrt(lambda);
|
||||
}
|
||||
|
||||
var f = Math.sqrt((((rx * rx) * (ry * ry)) - ((rx * rx) * (yp * yp)) - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp) + (ry * ry) * (xp * xp)));
|
||||
|
||||
if(fa == fs) {
|
||||
f *= -1;
|
||||
}
|
||||
if(isNaN(f)) {
|
||||
f = 0;
|
||||
}
|
||||
|
||||
var cxp = f * rx * yp / ry;
|
||||
var cyp = f * -ry * xp / rx;
|
||||
|
||||
var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
|
||||
var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
|
||||
|
||||
var vMag = function(v) {
|
||||
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
|
||||
};
|
||||
var vRatio = function(u, v) {
|
||||
return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
|
||||
};
|
||||
var vAngle = function(u, v) {
|
||||
return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
|
||||
};
|
||||
var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
|
||||
var u = [(xp - cxp) / rx, (yp - cyp) / ry];
|
||||
var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
|
||||
var dTheta = vAngle(u, v);
|
||||
|
||||
if(vRatio(u, v) <= -1) {
|
||||
dTheta = Math.PI;
|
||||
}
|
||||
if(vRatio(u, v) >= 1) {
|
||||
dTheta = 0;
|
||||
}
|
||||
if(fs === 0 && dTheta > 0) {
|
||||
dTheta = dTheta - 2 * Math.PI;
|
||||
}
|
||||
if(fs == 1 && dTheta < 0) {
|
||||
dTheta = dTheta + 2 * Math.PI;
|
||||
}
|
||||
return [cx, cy, rx, ry, theta, dTheta, psi, fs];
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
*/
|
||||
Kinetic.Plugins.TextPath = Kinetic.Shape.extend({
|
||||
init: function(config) {
|
||||
|
||||
this.setDefaultAttrs({
|
||||
fontFamily: 'Calibri',
|
||||
fontSize: 12,
|
||||
@ -25,14 +24,11 @@ Kinetic.Plugins.TextPath = Kinetic.Shape.extend({
|
||||
var that = this;
|
||||
|
||||
config.drawFunc = this.drawFunc;
|
||||
|
||||
// call super constructor
|
||||
this._super(config);
|
||||
|
||||
this.dataArray = Kinetic.Plugins.PathHelper.parsePathData(this.attrs.data);
|
||||
|
||||
this.dataArray = this.parsePathData(this.attrs.data);
|
||||
this.on('dataChange', function() {
|
||||
that.dataArray = Kinetic.Plugins.PathHelper.parsePathData(that.attrs.data);
|
||||
that.dataArray = this.parsePathData(this.attrs.data);
|
||||
});
|
||||
// update text data for certain attr changes
|
||||
var attrs = ['text', 'textStroke', 'textStrokeWidth'];
|
||||
@ -40,7 +36,6 @@ Kinetic.Plugins.TextPath = Kinetic.Shape.extend({
|
||||
var attr = attrs[n];
|
||||
this.on(attr + 'Change', that._setTextData);
|
||||
}
|
||||
|
||||
that._setTextData();
|
||||
},
|
||||
drawFunc: function(context) {
|
||||
@ -83,6 +78,9 @@ Kinetic.Plugins.TextPath = Kinetic.Shape.extend({
|
||||
|
||||
context.restore();
|
||||
},
|
||||
parsePathData: Kinetic.Plugins.Path.prototype.parsePathData,
|
||||
calcLength: Kinetic.Plugins.Path.prototype.calcLength,
|
||||
convertEndpointToCenterParameterization: Kinetic.Plugins.Path.prototype.convertEndpointToCenterParameterization,
|
||||
/**
|
||||
* get text width in pixels
|
||||
* @name getTextWidth
|
||||
@ -181,8 +179,8 @@ Kinetic.Plugins.TextPath = Kinetic.Shape.extend({
|
||||
|
||||
switch (pathCmd.command) {
|
||||
case 'L':
|
||||
if(Kinetic.Plugins.PathHelper.getLineLength(p0.x, p0.y, pathCmd.points[0], pathCmd.points[1]) > glyphWidth) {
|
||||
p1 = Kinetic.Plugins.PathHelper.getPointOnLine(glyphWidth, p0.x, p0.y, pathCmd.points[0], pathCmd.points[1], p0.x, p0.y);
|
||||
if(Kinetic.Geometry.getLineLength(p0.x, p0.y, pathCmd.points[0], pathCmd.points[1]) > glyphWidth) {
|
||||
p1 = Kinetic.Geometry.getPointOnLine(glyphWidth, p0.x, p0.y, pathCmd.points[0], pathCmd.points[1], p0.x, p0.y);
|
||||
}
|
||||
else
|
||||
pathCmd = undefined;
|
||||
@ -207,7 +205,7 @@ Kinetic.Plugins.TextPath = Kinetic.Shape.extend({
|
||||
currentT = end;
|
||||
needNewSegment = true;
|
||||
}
|
||||
p1 = Kinetic.Plugins.PathHelper.getPointOnEllipticalArc(pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], currentT, pathCmd.points[6]);
|
||||
p1 = Kinetic.Geometry.getPointOnEllipticalArc(pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], currentT, pathCmd.points[6]);
|
||||
break;
|
||||
case 'C':
|
||||
if(currentT === 0) {
|
||||
@ -225,7 +223,7 @@ Kinetic.Plugins.TextPath = Kinetic.Shape.extend({
|
||||
currentT = 1.0;
|
||||
needNewSegment = true;
|
||||
}
|
||||
p1 = Kinetic.Plugins.PathHelper.getPointOnCubicBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], pathCmd.points[4], pathCmd.points[5]);
|
||||
p1 = Kinetic.Geometry.getPointOnCubicBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], pathCmd.points[4], pathCmd.points[5]);
|
||||
break;
|
||||
case 'Q':
|
||||
if(currentT === 0)
|
||||
@ -239,13 +237,13 @@ Kinetic.Plugins.TextPath = Kinetic.Shape.extend({
|
||||
currentT = 1.0;
|
||||
needNewSegment = true;
|
||||
}
|
||||
p1 = Kinetic.Plugins.PathHelper.getPointOnQuadraticBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3]);
|
||||
p1 = Kinetic.Geometry.getPointOnQuadraticBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if(p1 !== undefined) {
|
||||
currLen = Kinetic.Plugins.PathHelper.getLineLength(p0.x, p0.y, p1.x, p1.y);
|
||||
currLen = Kinetic.Geometry.getLineLength(p0.x, p0.y, p1.x, p1.y);
|
||||
}
|
||||
|
||||
if(needNewSegment) {
|
||||
@ -262,7 +260,7 @@ Kinetic.Plugins.TextPath = Kinetic.Shape.extend({
|
||||
if(p0 === undefined || p1 === undefined)
|
||||
break;
|
||||
|
||||
var width = Kinetic.Plugins.PathHelper.getLineLength(p0.x, p0.y, p1.x, p1.y);
|
||||
var width = Kinetic.Geometry.getLineLength(p0.x, p0.y, p1.x, p1.y);
|
||||
|
||||
// Note: Since glyphs are rendered one at a time, any kerning pair data built into the font will not be used.
|
||||
// Can foresee having a rough pair table built in that the developer can override as needed.
|
||||
@ -270,7 +268,7 @@ Kinetic.Plugins.TextPath = Kinetic.Shape.extend({
|
||||
var kern = 0;
|
||||
// placeholder for future implementation
|
||||
|
||||
var midpoint = Kinetic.Plugins.PathHelper.getPointOnLine(kern + width / 2.0, p0.x, p0.y, p1.x, p1.y);
|
||||
var midpoint = Kinetic.Geometry.getPointOnLine(kern + width / 2.0, p0.x, p0.y, p1.x, p1.y);
|
||||
|
||||
var rotation = Math.atan2((p1.y - p0.y), (p1.x - p0.x));
|
||||
this.glyphInfo.push({
|
||||
|
@ -1,495 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// PathHelper
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
Kinetic.Plugins.PathHelper = {
|
||||
|
||||
/**
|
||||
* get parsed data array from the data
|
||||
* string. V, v, H, h, and l data are converted to
|
||||
* L data for the purpose of high performance Path
|
||||
* rendering
|
||||
*/
|
||||
parsePathData : function (data) {
|
||||
|
||||
// Path Data Segment must begin with a moveTo
|
||||
//m (x y)+ Relative moveTo (subsequent points are treated as lineTo)
|
||||
//M (x y)+ Absolute moveTo (subsequent points are treated as lineTo)
|
||||
//l (x y)+ Relative lineTo
|
||||
//L (x y)+ Absolute LineTo
|
||||
//h (x)+ Relative horizontal lineTo
|
||||
//H (x)+ Absolute horizontal lineTo
|
||||
//v (y)+ Relative vertical lineTo
|
||||
//V (y)+ Absolute vertical lineTo
|
||||
//z (closepath)
|
||||
//Z (closepath)
|
||||
//c (x1 y1 x2 y2 x y)+ Relative Bezier curve
|
||||
//C (x1 y1 x2 y2 x y)+ Absolute Bezier curve
|
||||
//q (x1 y1 x y)+ Relative Quadratic Bezier
|
||||
//Q (x1 y1 x y)+ Absolute Quadratic Bezier
|
||||
//t (x y)+ Shorthand/Smooth Relative Quadratic Bezier
|
||||
//T (x y)+ Shorthand/Smooth Absolute Quadratic Bezier
|
||||
//s (x2 y2 x y)+ Shorthand/Smooth Relative Bezier curve
|
||||
//S (x2 y2 x y)+ Shorthand/Smooth Absolute Bezier curve
|
||||
//a (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Relative Elliptical Arc
|
||||
//A (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Absolute Elliptical Arc
|
||||
|
||||
// return early if data is not defined
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// command string
|
||||
var cs = data;
|
||||
|
||||
// command chars
|
||||
var cc = ['m', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'];
|
||||
// convert white spaces to commas
|
||||
cs = cs.replace(new RegExp(' ', 'g'), ',');
|
||||
// create pipes so that we can split the data
|
||||
for (var n = 0; n < cc.length; n++) {
|
||||
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
|
||||
}
|
||||
// create array
|
||||
var arr = cs.split('|');
|
||||
var ca = [];
|
||||
// init context point
|
||||
var cpx = 0;
|
||||
var cpy = 0;
|
||||
for (var n = 1; n < arr.length; n++) {
|
||||
var str = arr[n];
|
||||
var c = str.charAt(0);
|
||||
str = str.slice(1);
|
||||
// remove ,- for consistency
|
||||
str = str.replace(new RegExp(',-', 'g'), '-');
|
||||
// add commas so that it's easy to split
|
||||
str = str.replace(new RegExp('-', 'g'), ',-');
|
||||
str = str.replace(new RegExp('e,-', 'g'), 'e-');
|
||||
var p = str.split(',');
|
||||
if (p.length > 0 && p[0] === '') {
|
||||
p.shift();
|
||||
}
|
||||
// convert strings to floats
|
||||
for (var i = 0; i < p.length; i++) {
|
||||
p[i] = parseFloat(p[i]);
|
||||
}
|
||||
while (p.length > 0) {
|
||||
if (isNaN(p[0])) // case for a trailing comma before next command
|
||||
break;
|
||||
|
||||
var cmd = null;
|
||||
var points = [];
|
||||
var startX = cpx,
|
||||
startY = cpy;
|
||||
|
||||
// convert l, H, h, V, and v to L
|
||||
switch (c) {
|
||||
|
||||
// Note: Keep the lineTo's above the moveTo's in this switch
|
||||
case 'l':
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'L';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'L':
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
|
||||
// Note: lineTo handlers need to be above this point
|
||||
case 'm':
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'M';
|
||||
points.push(cpx, cpy);
|
||||
c = 'l';
|
||||
// subsequent points are treated as relative lineTo
|
||||
break;
|
||||
case 'M':
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
cmd = 'M';
|
||||
points.push(cpx, cpy);
|
||||
c = 'L';
|
||||
// subsequent points are treated as absolute lineTo
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
cpx += p.shift();
|
||||
cmd = 'L';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'H':
|
||||
cpx = p.shift();
|
||||
cmd = 'L';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'v':
|
||||
cpy += p.shift();
|
||||
cmd = 'L';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'V':
|
||||
cpy = p.shift();
|
||||
cmd = 'L';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'C':
|
||||
points.push(p.shift(), p.shift(), p.shift(), p.shift());
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'c':
|
||||
points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'C';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'S':
|
||||
var ctlPtx = cpx,
|
||||
ctlPty = cpy;
|
||||
var prevCmd = ca[ca.length - 1];
|
||||
if (prevCmd.command === 'C') {
|
||||
ctlPtx = cpx + (cpx - prevCmd.points[2]);
|
||||
ctlPty = cpy + (cpy - prevCmd.points[3]);
|
||||
}
|
||||
points.push(ctlPtx, ctlPty, p.shift(), p.shift());
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
cmd = 'C';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 's':
|
||||
var ctlPtx = cpx,
|
||||
ctlPty = cpy;
|
||||
var prevCmd = ca[ca.length - 1];
|
||||
if (prevCmd.command === 'C') {
|
||||
ctlPtx = cpx + (cpx - prevCmd.points[2]);
|
||||
ctlPty = cpy + (cpy - prevCmd.points[3]);
|
||||
}
|
||||
points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'C';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'Q':
|
||||
points.push(p.shift(), p.shift());
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'q':
|
||||
points.push(cpx + p.shift(), cpy + p.shift());
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'Q';
|
||||
points.push(cpx, cpy);
|
||||
break;
|
||||
case 'T':
|
||||
var ctlPtx = cpx,
|
||||
ctlPty = cpy;
|
||||
var prevCmd = ca[ca.length - 1];
|
||||
if (prevCmd.command === 'Q') {
|
||||
ctlPtx = cpx + (cpx - prevCmd.points[0]);
|
||||
ctlPty = cpy + (cpy - prevCmd.points[1]);
|
||||
}
|
||||
cpx = p.shift();
|
||||
cpy = p.shift();
|
||||
cmd = 'Q';
|
||||
points.push(ctlPtx, ctlPty, cpx, cpy);
|
||||
break;
|
||||
case 't':
|
||||
var ctlPtx = cpx,
|
||||
ctlPty = cpy;
|
||||
var prevCmd = ca[ca.length - 1];
|
||||
if (prevCmd.command === 'Q') {
|
||||
ctlPtx = cpx + (cpx - prevCmd.points[0]);
|
||||
ctlPty = cpy + (cpy - prevCmd.points[1]);
|
||||
}
|
||||
cpx += p.shift();
|
||||
cpy += p.shift();
|
||||
cmd = 'Q';
|
||||
points.push(ctlPtx, ctlPty, cpx, cpy);
|
||||
break;
|
||||
case 'A':
|
||||
var rx = p.shift(),
|
||||
ry = p.shift(),
|
||||
psi = p.shift(),
|
||||
fa = p.shift(),
|
||||
fs = p.shift();
|
||||
var x1 = cpx,
|
||||
y1 = cpy;
|
||||
cpx = p.shift(),
|
||||
cpy = p.shift();
|
||||
cmd = 'A';
|
||||
points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
|
||||
break;
|
||||
case 'a':
|
||||
var rx = p.shift(),
|
||||
ry = p.shift(),
|
||||
psi = p.shift(),
|
||||
fa = p.shift(),
|
||||
fs = p.shift();
|
||||
var x1 = cpx,
|
||||
y1 = cpy;
|
||||
cpx += p.shift(),
|
||||
cpy += p.shift();
|
||||
cmd = 'A';
|
||||
points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
|
||||
break;
|
||||
}
|
||||
|
||||
ca.push({
|
||||
command : cmd || c,
|
||||
points : points,
|
||||
start : {
|
||||
x : startX,
|
||||
y : startY
|
||||
},
|
||||
pathLength : this.calcLength(startX, startY, cmd || c, points)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (c === 'z' || c === 'Z')
|
||||
ca.push({
|
||||
command : 'z',
|
||||
points : [],
|
||||
start : undefined,
|
||||
pathLength : 0
|
||||
});
|
||||
}
|
||||
|
||||
return ca;
|
||||
},
|
||||
calcLength : function (x, y, cmd, points) {
|
||||
var len,
|
||||
p1,
|
||||
p2;
|
||||
|
||||
switch (cmd) {
|
||||
case 'L':
|
||||
return this.getLineLength(x, y, points[0], points[1]);
|
||||
case 'C':
|
||||
// Approximates by breaking curve into 100 line segments
|
||||
len = 0.0;
|
||||
p1 = this.getPointOnCubicBezier(0, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
|
||||
for (t = 0.01; t <= 1; t += 0.01) {
|
||||
p2 = this.getPointOnCubicBezier(t, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
|
||||
len += this.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
||||
p1 = p2;
|
||||
}
|
||||
return len;
|
||||
case 'Q':
|
||||
// Approximates by breaking curve into 100 line segments
|
||||
len = 0.0;
|
||||
p1 = this.getPointOnQuadraticBezier(0, x, y, points[0], points[1], points[2], points[3]);
|
||||
for (t = 0.01; t <= 1; t += 0.01) {
|
||||
p2 = this.getPointOnQuadraticBezier(t, x, y, points[0], points[1], points[2], points[3]);
|
||||
len += this.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
||||
p1 = p2;
|
||||
}
|
||||
return len;
|
||||
case 'A':
|
||||
// Approximates by breaking curve into line segments
|
||||
len = 0.0;
|
||||
var start = points[4]; // 4 = theta
|
||||
var dTheta = points[5]; // 5 = dTheta
|
||||
var end = points[4] + dTheta;
|
||||
var inc = Math.PI / 180.0; // 1 degree resolution
|
||||
if (Math.abs(start - end) < inc) {
|
||||
inc = Math.abs(start - end);
|
||||
}
|
||||
// Note: for purpose of calculating arc length, not going to worry about rotating X-axis by angle psi
|
||||
|
||||
p1 = this.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], start, 0);
|
||||
if (dTheta < 0) { // clockwise
|
||||
for (t = start - inc; t > end; t -= inc) {
|
||||
p2 = this.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
|
||||
len += this.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
||||
p1 = p2;
|
||||
}
|
||||
} else { // counter-clockwise
|
||||
for (t = start + inc; t < end; t += inc) {
|
||||
p2 = this.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
|
||||
len += this.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
||||
p1 = p2;
|
||||
}
|
||||
}
|
||||
|
||||
p2 = this.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], end, 0);
|
||||
len += this.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
getLineLength : function (x1, y1, x2, y2) {
|
||||
|
||||
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
|
||||
},
|
||||
getPointOnLine : function (dist, P1x, P1y, P2x, P2y, fromX, fromY) {
|
||||
|
||||
if (fromX === undefined) {
|
||||
fromX = P1x;
|
||||
}
|
||||
if (fromY === undefined) {
|
||||
fromY = P1y;
|
||||
}
|
||||
|
||||
var m = (P2y - P1y) / ((P2x - P1x) + 0.00000001);
|
||||
|
||||
var run = Math.sqrt(dist * dist / (1 + m * m));
|
||||
var rise = m * run;
|
||||
|
||||
var pt;
|
||||
|
||||
if ((fromY - P1y) / ((fromX - P1x) + 0.00000001) === m) {
|
||||
pt = {
|
||||
x : fromX + run,
|
||||
y : fromY + rise
|
||||
};
|
||||
} else {
|
||||
var ix,
|
||||
iy;
|
||||
|
||||
var len = this.getLineLength(P1x, P1y, P2x, P2y);
|
||||
if (len < 0.00000001) {
|
||||
return undefined;
|
||||
}
|
||||
var u = (((fromX - P1x) * (P2x - P1x)) + ((fromY - P1y) * (P2y - P1y)));
|
||||
u = u / (len * len);
|
||||
|
||||
ix = P1x + u * (P2x - P1x);
|
||||
iy = P1y + u * (P2y - P1y);
|
||||
|
||||
var pRise = this.getLineLength(fromX, fromY, ix, iy);
|
||||
var pRun = Math.sqrt(dist * dist - pRise * pRise);
|
||||
|
||||
run = Math.sqrt(pRun * pRun / (1 + m * m));
|
||||
rise = m * run;
|
||||
|
||||
pt = {
|
||||
x : ix + run,
|
||||
y : iy + rise
|
||||
};
|
||||
}
|
||||
|
||||
return pt;
|
||||
},
|
||||
getPointOnCubicBezier : function (pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
|
||||
function CB1(t) {
|
||||
return t * t * t;
|
||||
}
|
||||
function CB2(t) {
|
||||
return 3 * t * t * (1 - t);
|
||||
}
|
||||
function CB3(t) {
|
||||
return 3 * t * (1 - t) * (1 - t);
|
||||
}
|
||||
function CB4(t) {
|
||||
return (1 - t) * (1 - t) * (1 - t);
|
||||
}
|
||||
|
||||
var x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
|
||||
var y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
|
||||
|
||||
return {
|
||||
x : x,
|
||||
y : y
|
||||
};
|
||||
},
|
||||
getPointOnQuadraticBezier : function (pct, P1x, P1y, P2x, P2y, P3x, P3y) {
|
||||
function QB1(t) {
|
||||
return t * t;
|
||||
}
|
||||
function QB2(t) {
|
||||
return 2 * t * (1 - t);
|
||||
}
|
||||
function QB3(t) {
|
||||
return (1 - t) * (1 - t);
|
||||
}
|
||||
|
||||
var x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
|
||||
var y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
|
||||
|
||||
return {
|
||||
x : x,
|
||||
y : y
|
||||
};
|
||||
},
|
||||
getPointOnEllipticalArc : function (cx, cy, rx, ry, theta, psi) {
|
||||
var cosPsi = Math.cos(psi),
|
||||
sinPsi = Math.sin(psi);
|
||||
var pt = {
|
||||
x : rx * Math.cos(theta),
|
||||
y : ry * Math.sin(theta)
|
||||
};
|
||||
return {
|
||||
x : cx + (pt.x * cosPsi - pt.y * sinPsi),
|
||||
y : cy + (pt.x * sinPsi + pt.y * cosPsi)
|
||||
};
|
||||
},
|
||||
convertEndpointToCenterParameterization : function (x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
|
||||
|
||||
// Derived from: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||
|
||||
var psi = psiDeg * (Math.PI / 180.0);
|
||||
|
||||
var xp = Math.cos(psi) * (x1 - x2) / 2.0 + Math.sin(psi) * (y1 - y2) / 2.0;
|
||||
var yp = -1 * Math.sin(psi) * (x1 - x2) / 2.0 + Math.cos(psi) * (y1 - y2) / 2.0;
|
||||
|
||||
var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
|
||||
|
||||
if (lambda > 1) {
|
||||
rx *= Math.sqrt(lambda);
|
||||
ry *= Math.sqrt(lambda);
|
||||
}
|
||||
|
||||
var f = Math.sqrt((((rx * rx) * (ry * ry)) - ((rx * rx) * (yp * yp)) - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp) + (ry * ry) * (xp * xp)));
|
||||
|
||||
if (fa == fs)
|
||||
f *= -1;
|
||||
if (isNaN(f))
|
||||
f = 0;
|
||||
|
||||
var cxp = f * rx * yp / ry;
|
||||
var cyp = f * -ry * xp / rx;
|
||||
|
||||
var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
|
||||
var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
|
||||
|
||||
var vMag = function (v) {
|
||||
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
|
||||
};
|
||||
var vRatio = function (u, v) {
|
||||
return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
|
||||
};
|
||||
var vAngle = function (u, v) {
|
||||
return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
|
||||
};
|
||||
var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
|
||||
|
||||
var u = [(xp - cxp) / rx, (yp - cyp) / ry];
|
||||
var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
|
||||
var dTheta = vAngle(u, v);
|
||||
|
||||
if (vRatio(u, v) <= -1)
|
||||
dTheta = Math.PI;
|
||||
if (vRatio(u, v) >= 1)
|
||||
dTheta = 0;
|
||||
|
||||
if (fs === 0 && dTheta > 0)
|
||||
dTheta = dTheta - 2 * Math.PI;
|
||||
if (fs == 1 && dTheta < 0)
|
||||
dTheta = dTheta + 2 * Math.PI;
|
||||
|
||||
return [cx, cy, rx, ry, theta, dTheta, psi, fs];
|
||||
}
|
||||
|
||||
};
|
@ -21,9 +21,7 @@ Kinetic.Ellipse = Kinetic.Shape.extend({
|
||||
|
||||
// call super constructor
|
||||
this._super(config);
|
||||
|
||||
this._convertRadius();
|
||||
|
||||
var that = this;
|
||||
this.on('radiusChange.kinetic', function() {
|
||||
that._convertRadius();
|
||||
|
@ -17,7 +17,6 @@ Kinetic.Sprite = Kinetic.Shape.extend({
|
||||
config.drawFunc = this.drawFunc;
|
||||
// call super constructor
|
||||
this._super(config);
|
||||
|
||||
var that = this;
|
||||
this.on('animationChange.kinetic', function() {
|
||||
// reset index when animation changes
|
||||
|
@ -38,7 +38,6 @@ Kinetic.Text = Kinetic.Shape.extend({
|
||||
var attr = attrs[n];
|
||||
this.on(attr + 'Change.kinetic', that._setTextData);
|
||||
}
|
||||
|
||||
that._setTextData();
|
||||
},
|
||||
drawFunc: function(context) {
|
||||
|
102
src/util/Geometry.js
Normal file
102
src/util/Geometry.js
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Utility methods written by jfollas to
|
||||
* handle length and point measurements
|
||||
*/
|
||||
Kinetic.Geometry = {
|
||||
getLineLength: function(x1, y1, x2, y2) {
|
||||
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
|
||||
},
|
||||
getPointOnLine: function(dist, P1x, P1y, P2x, P2y, fromX, fromY) {
|
||||
if(fromX === undefined) {
|
||||
fromX = P1x;
|
||||
}
|
||||
if(fromY === undefined) {
|
||||
fromY = P1y;
|
||||
}
|
||||
|
||||
var m = (P2y - P1y) / ((P2x - P1x) + 0.00000001);
|
||||
var run = Math.sqrt(dist * dist / (1 + m * m));
|
||||
var rise = m * run;
|
||||
var pt;
|
||||
|
||||
if((fromY - P1y) / ((fromX - P1x) + 0.00000001) === m) {
|
||||
pt = {
|
||||
x: fromX + run,
|
||||
y: fromY + rise
|
||||
};
|
||||
}
|
||||
else {
|
||||
var ix, iy;
|
||||
|
||||
var len = this.getLineLength(P1x, P1y, P2x, P2y);
|
||||
if(len < 0.00000001) {
|
||||
return undefined;
|
||||
}
|
||||
var u = (((fromX - P1x) * (P2x - P1x)) + ((fromY - P1y) * (P2y - P1y)));
|
||||
u = u / (len * len);
|
||||
ix = P1x + u * (P2x - P1x);
|
||||
iy = P1y + u * (P2y - P1y);
|
||||
|
||||
var pRise = this.getLineLength(fromX, fromY, ix, iy);
|
||||
var pRun = Math.sqrt(dist * dist - pRise * pRise);
|
||||
run = Math.sqrt(pRun * pRun / (1 + m * m));
|
||||
rise = m * run;
|
||||
pt = {
|
||||
x: ix + run,
|
||||
y: iy + rise
|
||||
};
|
||||
}
|
||||
|
||||
return pt;
|
||||
},
|
||||
getPointOnCubicBezier: function(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
|
||||
function CB1(t) {
|
||||
return t * t * t;
|
||||
}
|
||||
function CB2(t) {
|
||||
return 3 * t * t * (1 - t);
|
||||
}
|
||||
function CB3(t) {
|
||||
return 3 * t * (1 - t) * (1 - t);
|
||||
}
|
||||
function CB4(t) {
|
||||
return (1 - t) * (1 - t) * (1 - t);
|
||||
}
|
||||
var x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
|
||||
var y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
|
||||
|
||||
return {
|
||||
x: x,
|
||||
y: y
|
||||
};
|
||||
},
|
||||
getPointOnQuadraticBezier: function(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
|
||||
function QB1(t) {
|
||||
return t * t;
|
||||
}
|
||||
function QB2(t) {
|
||||
return 2 * t * (1 - t);
|
||||
}
|
||||
function QB3(t) {
|
||||
return (1 - t) * (1 - t);
|
||||
}
|
||||
var x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
|
||||
var y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
|
||||
|
||||
return {
|
||||
x: x,
|
||||
y: y
|
||||
};
|
||||
},
|
||||
getPointOnEllipticalArc: function(cx, cy, rx, ry, theta, psi) {
|
||||
var cosPsi = Math.cos(psi), sinPsi = Math.sin(psi);
|
||||
var pt = {
|
||||
x: rx * Math.cos(theta),
|
||||
y: ry * Math.sin(theta)
|
||||
};
|
||||
return {
|
||||
x: cx + (pt.x * cosPsi - pt.y * sinPsi),
|
||||
y: cy + (pt.x * sinPsi + pt.y * cosPsi)
|
||||
};
|
||||
}
|
||||
};
|
@ -11,6 +11,7 @@
|
||||
<!-- assets -->
|
||||
<script src="../js/Test.js"></script>
|
||||
<script src="../js/performanceTests.js"></script>
|
||||
<script src="../assets/worldMap.js"></script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
var test = new Test();
|
||||
|
@ -22,8 +22,8 @@ Test.prototype.tests = {
|
||||
layer.add(rect);
|
||||
}
|
||||
stage.add(layer);
|
||||
|
||||
console.profileEnd();
|
||||
|
||||
console.profileEnd();
|
||||
endTimer('add and draw 1,000 Kinetic rectangles');
|
||||
|
||||
},
|
||||
@ -59,9 +59,9 @@ Test.prototype.tests = {
|
||||
});
|
||||
|
||||
stage.start();
|
||||
|
||||
|
||||
setTimeout(function() {
|
||||
//stage.stop();
|
||||
//stage.stop();
|
||||
}, 1000)
|
||||
},
|
||||
'DRAWING - draw rect vs image from image data': function(containerId) {
|
||||
@ -219,5 +219,53 @@ Test.prototype.tests = {
|
||||
endTimer('draw 1,000 cached stars');
|
||||
}
|
||||
});
|
||||
},
|
||||
'*PATH - add map path': function(containerId) {
|
||||
startTimer();
|
||||
var stage = new Kinetic.Stage({
|
||||
container: containerId,
|
||||
width: 1024,
|
||||
height: 480,
|
||||
throttle: 80,
|
||||
scale: 0.5,
|
||||
x: 50,
|
||||
y: 10
|
||||
});
|
||||
var mapLayer = new Kinetic.Layer();
|
||||
|
||||
for(var key in worldMap.shapes) {
|
||||
var c = worldMap.shapes[key];
|
||||
|
||||
var path = new Kinetic.Plugins.Path({
|
||||
data: c,
|
||||
fill: '#ccc',
|
||||
stroke: '#999',
|
||||
strokeWidth: 1
|
||||
});
|
||||
|
||||
if(key === 'US')
|
||||
test(path.dataArray[0].command === 'M', 'first command should be a moveTo');
|
||||
|
||||
path.on('mouseover', function() {
|
||||
this.setFill('red');
|
||||
mapLayer.draw();
|
||||
});
|
||||
|
||||
path.on('mouseout', function() {
|
||||
this.setFill('#ccc');
|
||||
mapLayer.draw();
|
||||
});
|
||||
|
||||
mapLayer.add(path);
|
||||
}
|
||||
|
||||
stage.add(mapLayer);
|
||||
|
||||
endTimer('time build and to draw map');
|
||||
|
||||
mapLayer.beforeDraw(startTimer);
|
||||
mapLayer.afterDraw(function() {
|
||||
endTimer('redraw layer');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user