Initial commit

This commit is contained in:
Jonathan Puckey 2011-02-07 19:28:09 +01:00
commit b19ba2014c
29 changed files with 4031 additions and 0 deletions

View file

@ -0,0 +1,53 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<script type="text/javascript" src="../src/Bootstrap.js"></script>
<script type="text/javascript" src="../src/Item.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Point.js"></script>
<script type="text/javascript" src="../src/Rectangle.js"></script>
<script type="text/javascript" src="../src/Size.js"></script>
<script type="text/javascript" src="../src/Segment.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Path.js"></script>
<script type="text/javascript" src="../src/SegmentList.js"></script>
<script type="text/javascript" src="../src/Doc.js"></script>
<script>
var canvas, doc;
var count = 0;
window.onload = function() {
canvas = document.getElementById('canvas');
doc = new Doc(canvas);
setInterval(draw, 60);
}
function draw() {
count++;
doc.children = [];
var point = new Point(1024, 768).divide(2);
for(var i = 0; i < 35; i++) {
var vector = new Point(20 + 10 * i, 0).rotate(i * (360 / 35 * 2) * Math.sin(count / 150));
var l = vector.getLength();
var path = new Path();
// path.strokeColor = '#000000';
path.fillColor = i % 2 ? 'red' : 'black';
path.closed = true;
for(var j = 0; j < 32; j++) {
vector = vector.rotate(45 / 4);
var newPoint = point.add(vector).add(vector.rotate(-180).normalize(l * (j % 2 ? 0.1 : -0.1)));
path.add(newPoint);
}
path.smooth();
doc.children.push(path);
}
doc.children.reverse();
doc.draw();
}
</script>
</head>
<body>
<canvas id='canvas' width=1024 height=768></canvas>
</body>

48
examples/arcTo.html Normal file
View file

@ -0,0 +1,48 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<script type="text/javascript" src="../src/Bootstrap.js"></script>
<script type="text/javascript" src="../src/Item.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Point.js"></script>
<script type="text/javascript" src="../src/Rectangle.js"></script>
<script type="text/javascript" src="../src/Size.js"></script>
<script type="text/javascript" src="../src/Segment.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Path.js"></script>
<script type="text/javascript" src="../src/SegmentList.js"></script>
<script type="text/javascript" src="../src/Doc.js"></script>
<script>
window.onload = function() {
var canvas = document.getElementById('canvas');
var doc = new Doc(canvas);
var point = new Point(1024, 768).divide(2);
for(var i = 0; i < 30; i++) {
var vector = new Point(10 + 20 * i, 0).rotate(i);
var l = vector.getLength();
var path = new Path();
path.fillColor = i % 2 ? 'red' : 'black';
path.closed = true;
for(var j = 0; j < 17; j++) {
vector = vector.rotate(45 / 2);
if(j == 0) {
path.add(point.add(vector));
} else {
path.arcTo(point.add(vector), true);
}
}
doc.children.push(path);
}
doc.children.reverse();
doc.draw();
}
</script>
</head>
<body>
<canvas id='canvas' width=1024 height=768></canvas>
</body>

36
examples/circle.html Normal file
View file

@ -0,0 +1,36 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<script type="text/javascript" src="../src/Bootstrap.js"></script>
<script type="text/javascript" src="../src/Item.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Point.js"></script>
<script type="text/javascript" src="../src/Rectangle.js"></script>
<script type="text/javascript" src="../src/Size.js"></script>
<script type="text/javascript" src="../src/Segment.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Path.js"></script>
<script type="text/javascript" src="../src/SegmentList.js"></script>
<script type="text/javascript" src="../src/Doc.js"></script>
<script>
var count = 0;
var doc;
window.onload = function() {
var canvas = document.getElementById('canvas');
doc = new Doc(canvas);
var center = doc.size.divide(2);
for(var i = 0; i < 70; i++) {
var path = new Path.Circle(center, i * 5);
path.strokeColor = 'black';
doc.children.push(path);
}
doc.draw();
}
</script>
</head>
<body>
<canvas id='canvas' width=1024 height=768></canvas>
</body>

47
examples/letter.html Normal file
View file

@ -0,0 +1,47 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<script type="text/javascript" src="../src/Bootstrap.js"></script>
<script type="text/javascript" src="../src/Item.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Point.js"></script>
<script type="text/javascript" src="../src/Rectangle.js"></script>
<script type="text/javascript" src="../src/Size.js"></script>
<script type="text/javascript" src="../src/Segment.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Path.js"></script>
<script type="text/javascript" src="../src/SegmentList.js"></script>
<script type="text/javascript" src="../src/Doc.js"></script>
<script>
window.onload = function() {
var canvas = document.getElementById('canvas');
var doc = new Doc(canvas);
var letterPath = new Path(
new Segment(new Point(63.39306640625, 265.7138671875), null, new Point(10.86669921875, 6.96630859375)),
new Segment(new Point(106.58203125, 277.97412109375), new Point(-16.71826171875, 0), new Point(24.79931640625, 0)),
new Segment(new Point(145.87060546875, 245.9306640625), new Point(0, 18.947265625), new Point(0, -17.27587890625)),
new Segment(new Point(110.4833984375, 208.87158203125), new Point(25.35595703125, 9.4736328125), new Point(-30.65087890625, -11.14599609375)),
new Segment(new Point(60.88525390625, 155.37255859375), new Point(0, 26.19189453125), new Point(0, -29.25732421875)),
new Segment(new Point(121.62890625, 104.380859375), new Point(-36.501953125, 0), new Point(18.947265625, 0)),
new Segment(new Point(162.86767578125, 113.576171875), new Point(-8.08056640625, -4.73681640625)),
new Segment(new Point(156.18017578125, 133.35986328125), null, new Point(-5.8515625, -3.62255859375)),
new Segment(new Point(120.79296875, 124.443359375), new Point(16.9970703125, 0), new Point(-25.63525390625, 0)),
new Segment(new Point(85.4052734375, 152.5859375), new Point(0, -12.8173828125), new Point(0, 17.55419921875)),
new Segment(new Point(122.7431640625, 188.80908203125), new Point(-25.91357421875, -10.03076171875), new Point(31.76513671875, 12.26025390625)),
new Segment(new Point(170.39111328125, 243.97998046875), new Point(0, -27.58544921875), new Point(0, 28.97900390625)),
new Segment(new Point(104.91015625, 298.31494140625), new Point(44.30419921875, 0), new Point(-18.111328125, 0)),
new Segment(new Point(56.984375, 286.0546875), new Point(10.03076171875, 6.6875))
);
letterPath.fillColor = 'black';
doc.children.push(letterPath);
doc.draw();
}
</script>
</head>
<body>
<canvas id='canvas' width=1024 height=768></canvas>
</body>

43
examples/lines.html Normal file
View file

@ -0,0 +1,43 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<script type="text/javascript" src="../src/Bootstrap.js"></script>
<script type="text/javascript" src="../src/Item.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Point.js"></script>
<script type="text/javascript" src="../src/Rectangle.js"></script>
<script type="text/javascript" src="../src/Size.js"></script>
<script type="text/javascript" src="../src/Segment.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Path.js"></script>
<script type="text/javascript" src="../src/SegmentList.js"></script>
<script type="text/javascript" src="../src/Doc.js"></script>
<script>
var count = 0;
var doc;
window.onload = function() {
var canvas = document.getElementById('canvas');
doc = new Doc(canvas);
setInterval(draw, 20);
}
function draw() {
count++;
doc.children = [];
var point = new Point(1024, 768).divide(2);
var vector = new Point(100, 0);
for(var i = 0; i < 180; i++) {
var path = new Path.Line(point.add(vector.rotate(i * 2).normalize(30)), point.add(vector.rotate(i * 2).normalize(30 + Math.abs(Math.sin((i + count / 20)) * 300))));
path.strokeColor = i % 4 ? 'black' : 'red';
doc.children.push(path);
}
doc.draw();
}
</script>
</head>
<body>
<canvas id='canvas' width=1024 height=768></canvas>
</body>

34
examples/rectangle.html Normal file
View file

@ -0,0 +1,34 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<script type="text/javascript" src="../src/Bootstrap.js"></script>
<script type="text/javascript" src="../src/Item.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Point.js"></script>
<script type="text/javascript" src="../src/Rectangle.js"></script>
<script type="text/javascript" src="../src/Size.js"></script>
<script type="text/javascript" src="../src/Segment.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Path.js"></script>
<script type="text/javascript" src="../src/SegmentList.js"></script>
<script type="text/javascript" src="../src/Doc.js"></script>
<script>
var count = 0;
var doc;
window.onload = function() {
var canvas = document.getElementById('canvas');
doc = new Doc(canvas);
var path = new Path.Rectangle([50, 50], [100, 100]);
path.strokeColor = 'black';
console.log(path);
doc.children.push(path);
doc.draw();
}
</script>
</head>
<body>
<canvas id='canvas' width=1024 height=768></canvas>
</body>

46
examples/star.html Normal file
View file

@ -0,0 +1,46 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<script type="text/javascript" src="../src/Bootstrap.js"></script>
<script type="text/javascript" src="../src/Item.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Point.js"></script>
<script type="text/javascript" src="../src/Rectangle.js"></script>
<script type="text/javascript" src="../src/Size.js"></script>
<script type="text/javascript" src="../src/Segment.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Path.js"></script>
<script type="text/javascript" src="../src/SegmentList.js"></script>
<script type="text/javascript" src="../src/Doc.js"></script>
<script>
window.onload = function() {
var canvas = document.getElementById('canvas');
var doc = new Doc(canvas);
var point = new Point(1024, 768).divide(2);
for(var i = 0; i < 30; i++) {
var vector = new Point(1 + 10 * i, 0).rotate(i);
var l = vector.getLength();
var path = new Path();
// path.strokeColor = '#000000';
path.fillColor = i % 2 ? 'red' : 'black';
path.closed = true;
for(var j = 0; j < 32; j++) {
vector = vector.rotate(45 / 4);
var newPoint = point.add(vector).add(vector.rotate(-180).normalize(l * (j % 2 ? 0.1 : -0.1)));
path.add(newPoint);
}
path.smooth();
doc.children.push(path);
}
doc.children.reverse();
doc.draw();
}
</script>
</head>
<body>
<canvas id='canvas' width=1024 height=768></canvas>
</body>

35
examples/strokeJoin.html Normal file
View file

@ -0,0 +1,35 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<script type="text/javascript" src="../src/Bootstrap.js"></script>
<script type="text/javascript" src="../src/Item.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Point.js"></script>
<script type="text/javascript" src="../src/Rectangle.js"></script>
<script type="text/javascript" src="../src/Size.js"></script>
<script type="text/javascript" src="../src/Segment.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Path.js"></script>
<script type="text/javascript" src="../src/SegmentList.js"></script>
<script type="text/javascript" src="../src/Doc.js"></script>
<script>
var count = 0;
var doc;
window.onload = function() {
var canvas = document.getElementById('canvas');
doc = new Doc(canvas);
var path = new Path([100, 150], [150, 200], [200, 150]);
path.strokeColor = 'black';
path.strokeWidth = 30;
path.strokeJoin = 'bevel';
doc.children.push(path);
doc.draw();
}
</script>
</head>
<body>
<canvas id='canvas' width=1024 height=768></canvas>
</body>

39
examples/temp.html Normal file
View file

@ -0,0 +1,39 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<script type="text/javascript" src="../src/Bootstrap.js"></script>
<script type="text/javascript" src="../src/Item.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Point.js"></script>
<script type="text/javascript" src="../src/Rectangle.js"></script>
<script type="text/javascript" src="../src/Size.js"></script>
<script type="text/javascript" src="../src/Segment.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Path.js"></script>
<script type="text/javascript" src="../src/SegmentList.js"></script>
<script type="text/javascript" src="../src/Doc.js"></script>
<script>
var count = 0;
var doc;
window.onload = function() {
var canvas = document.getElementById('canvas');
doc = new Doc(canvas);
var rect = new Rectangle([50, 50], [200, 100])
var path = new Path.RoundRectangle(rect, 200);
path.strokeColor = 'black';
console.log(path);
doc.children.push(path);
// var path = new Path.Rectangle(rect);
// path.strokeColor = 'black';
// doc.children.push(path);
doc.draw();
}
</script>
</head>
<body>
<canvas id='canvas' width=1024 height=768></canvas>
</body>

170
src/Bootstrap.js Normal file
View file

@ -0,0 +1,170 @@
new function() {
var fix = !this.__proto__ && [Function, Number, Boolean, String, Array, Date, RegExp];
if (fix)
for (var i in fix)
fix[i].prototype.__proto__ = fix[i].prototype;
var has = {}.hasOwnProperty
? function(obj, name) {
return (!fix || name != '__proto__') && obj.hasOwnProperty(name);
}
: function(obj, name) {
return obj[name] !== (obj.__proto__ || Object.prototype)[name];
};
function inject(dest, src, enumerable, base, generics) {
function field(name, dontCheck, generics) {
var val = src[name], func = typeof val == 'function', res = val,
prev = dest[name];
if (generics && func && (!src.preserve || !generics[name])) generics[name] = function(bind) {
return bind && dest[name].apply(bind,
Array.prototype.slice.call(arguments, 1));
}
if ((dontCheck || val !== undefined && has(src, name)) && (!prev || !src.preserve)) {
if (func) {
if (prev && /\bthis\.base\b/.test(val)) {
var fromBase = base && base[name] == prev;
res = (function() {
var tmp = this.base;
this.base = fromBase ? base[name] : prev;
try { return val.apply(this, arguments); }
finally { tmp ? this.base = tmp : delete this.base; }
}).pretend(val);
}
}
dest[name] = res;
}
}
if (src) {
for (var name in src)
if (has(src, name) && !/^(statics|generics|preserve|prototype|constructor|__proto__|toString|valueOf)$/.test(name))
field(name, true, generics);
field('toString');
field('valueOf');
}
}
function extend(obj) {
function ctor(dont) {
if (fix) this.__proto__ = obj;
if (this.initialize && dont !== ctor.dont)
return this.initialize.apply(this, arguments);
}
ctor.prototype = obj;
ctor.toString = function() {
return (this.prototype.initialize || function() {}).toString();
}
return ctor;
}
inject(Function.prototype, {
inject: function(src) {
if (src) {
var proto = this.prototype, base = proto.__proto__ && proto.__proto__.constructor;
inject(proto, src, false, base && base.prototype, src.generics && this);
inject(this, src.statics, true, base);
}
for (var i = 1, l = arguments.length; i < l; i++)
this.inject(arguments[i]);
return this;
},
extend: function(src) {
var proto = new this(this.dont), ctor = proto.constructor = extend(proto);
ctor.dont = {};
inject(ctor, this, true);
return arguments.length ? this.inject.apply(ctor, arguments) : ctor;
},
pretend: function(fn) {
this.toString = function() {
return fn.toString();
}
this.valueOf = function() {
return fn.valueOf();
}
return this;
}
});
function each(obj, iter, bind) {
return obj ? (typeof obj.length == 'number'
? Array : Hash).prototype.each.call(obj, iter, bind) : bind;
}
Base = Object.extend({
has: function(name) {
return has(this, name);
},
each: function(iter, bind) {
return each(this, iter, bind);
},
inject: function() {
for (var i = 0, l = arguments.length; i < l; i++)
inject(this, arguments[i]);
return this;
},
extend: function() {
var res = new (extend(this));
return res.inject.apply(res, arguments);
},
statics: {
has: has,
each: each,
type: function(obj) {
return (obj || obj === 0) && (
obj._type || obj.nodeName && (
obj.nodeType == 1 && 'element' ||
obj.nodeType == 3 && 'textnode' ||
obj.nodeType == 9 && 'document')
|| obj.location && obj.frames && obj.history && 'window'
|| typeof obj) || null;
},
check: function(obj) {
return !!(obj || obj === 0);
},
pick: function() {
for (var i = 0, l = arguments.length; i < l; i++)
if (arguments[i] !== undefined)
return arguments[i];
return null;
},
iterator: function(iter) {
return !iter
? function(val) { return val }
: typeof iter != 'function'
? function(val) { return val == iter }
: iter;
},
stop: {}
}
}, {
generics: true,
debug: function() {
return /^(string|number|function|regexp)$/.test(Base.type(this)) ? this
: Base.each(this, function(val, key) { this.push(key + ': ' + val); }, []).join(', ');
},
clone: function() {
return Base.each(this, function(val, i) {
this[i] = val;
}, new this.constructor());
},
toQueryString: function() {
return Base.each(this, function(val, key) {
this.push(key + '=' + encodeURIComponent(val));
}, []).join('&');
}
});
}

14
src/Doc.js Normal file
View file

@ -0,0 +1,14 @@
Doc = Base.extend({
initialize: function(canvas) {
this.canvas = canvas;
this.ctx = this.canvas.getContext('2d');
this.size = new Size(canvas.offsetWidth, canvas.offsetHeight);
this.children = [];
},
draw: function() {
this.ctx.clearRect(0, 0, this.size.width, this.size.height);
for(var i = 0, l = this.children.length; i < l; i++) {
this.children[i].draw(this.ctx);
}
}
});

5
src/Item.js Normal file
View file

@ -0,0 +1,5 @@
Item = Base.extend({
initialize: function() {
this.bounds = new Rectangle();
}
});

126
src/Path.js Normal file
View file

@ -0,0 +1,126 @@
Path = PathItem.extend({
initialize: function() {
this.base.apply(this, arguments);
},
statics: {
Line: PathItem.extend({
initialize: function() {
this.base();
if(arguments.length == 2) {
console.log(new Segment(arguments[0]));
this.addSegment(new Segment(arguments[0]));
this.addSegment(new Segment(arguments[1]));
} else if(arguments.length == 4) {
this.addSegment(Segment.read(arguments[0], arguments[1]));
this.addSegment(Segment.read(arguments[2], arguments[3]));
}
}
}),
Rectangle: PathItem.extend({
initialize: function() {
this.base();
this.closed = true;
var rectangle = Rectangle.read(arguments);
var corners = ['getBottomLeft', 'getTopLeft', 'getTopRight', 'getBottomRight'];
for(var i = 0; i < 4; i++) {
this.add(rectangle[corners[i]]());
}
}
}),
RoundRectangle: PathItem.extend(new function() {
var u = 4 / 3 * (Math.sqrt(2) - 1);
return {
initialize: function() {
this.base();
var rect, size;
if(arguments.length == 2) {
rect = new Rectangle(arguments[0]);
size = new Size(arguments[1]);
} else {
rect = new Rectangle(arguments[0], arguments[1],
arguments[2], arguments[3]);
size = new Size(arguments[4], arguments[5]);
}
size = Size.min(size, rect.getSize().divide(2));
uSize = size.multiply(u);
var bl = rect.getBottomLeft();
this.add(bl.add(size.width, 0), null, [-uSize.width, 0]);
this.add(bl.subtract(0, size.height), [0, uSize.height], null);
var tl = rect.getTopLeft();
this.add(tl.add(0, size.height), null, [0, -uSize.height]);
this.add(tl.add(size.width, 0), [-uSize.width, 0], null);
var tr = rect.getTopRight();
this.add(tr.subtract(size.width, 0), null, [uSize.width, 0]);
this.add(tr.add(0, size.height), [0, -uSize.height], null);
var br = rect.getBottomRight();
this.add(br.subtract(0, size.height), null, [0, uSize.height]);
this.add(br.subtract(size.width, 0), [uSize.width, 0], null);
this.closed = true;
}
}
}),
Oval: PathItem.extend(new function() {
var u = 2 / 3 * (Math.sqrt(2) - 1);
var segments = [
{ handleOut: [0, -u], handleIn: [0, u], point: [ 0, 0.5] },
{ handleOut: [u, 0], handleIn: [-u, 0], point: [ 0.5, 0] },
{ handleOut: [0, u], handleIn: [0, -u], point: [ 1, 0.5] },
{ handleOut: [-u, 0], handleIn: [u, 0], point: [0.5, 1] }
];
return {
initialize: function() {
this.base();
var rect = Rectangle.read(arguments);
var topLeft = rect.getTopLeft();
var size = new Size(rect.width, rect.height);
for(var i = 0; i < 4; i++) {
var segment = Segment.read([segments[i]]);
segment.handleIn = segment.handleIn.multiply(size);
segment.handleOut = segment.handleOut.multiply(size);
segment.point = segment.point.multiply(size).add(topLeft);
this.segments.push(segment);
}
this.closed = true;
}
}
}),
Circle: PathItem.extend({
initialize: function() {
this.base();
var center, radius;
if(arguments.length == 3) {
center = new Point(arguments[0], arguments[1]);
radius = arguments[2];
} else {
center = new Point(arguments[0]);
radius = arguments[1];
}
var left = center.subtract(radius, 0);
this.moveTo(left);
this.arcTo(center.add(radius, 0), true);
this.arcTo(left, true);
var last = this.segments.pop();
this.segments[0].handleIn = last.handleIn;
this.closed = true;
}
}),
Arc: PathItem.extend({
initialize: function(from, through, to) {
this.base();
this.moveTo(from);
this.arcTo(through, to);
}
})
}
});

377
src/PathItem.js Normal file
View file

@ -0,0 +1,377 @@
PathItem = Item.extend(new function() {
var styleNames = {
fillColor: 'fillStyle',
strokeColor: 'strokeStyle',
strokeWidth: 'lineWidth',
strokeJoin: 'lineJoin',
strokeCap: 'lineCap',
miterLimit: 'miterLimit'
};
/**
* Solves a tri-diagonal system for one of coordinates (x or y) of first
* bezier control points.
*
* @param rhs right hand side vector.
* @return Solution vector.
*/
var getFirstControlPoints = function(rhs) {
var n = rhs.length;
var x = []; // Solution vector.
var tmp = []; // Temporary workspace.
var b = 2;
x[0] = rhs[0] / b;
// Decomposition and forward substitution.
for (var i = 1; i < n; i++) {
tmp[i] = 1 / b;
b = (i < n - 1 ? 4.0 : 2.0) - tmp[i];
x[i] = (rhs[i] - x[i - 1]) / b;
}
// Back-substitution.
for (var i = 1; i < n; i++) {
x[n - i - 1] -= tmp[n - i] * x[n - i];
}
return x;
};
return {
initialize: function() {
this.closed = false;
this.segments = [];//new SegmentList(this);
this.bounds = new Rectangle();
for(var i = 0, l = arguments.length; i < l; i++) {
var segment = new Segment(arguments[i]);
this.addSegment(segment);
}
},
addSegment: function(segment) {
segment.path = this;
this.segments.push(segment);
},
add: function() {
var segment = Segment.read(arguments);
if(segment)
this.addSegment(segment);
},
moveTo: function() {
var segment = Segment.read(arguments);
if(segment && !this.segments.length)
this.addSegment(segment);
},
lineTo: function() {
var segment = Segment.read(arguments);
if(segment && this.segments.length)
this.addSegment(segment);
},
/**
* Adds a cubic bezier curve to the path, defined by two handles and a to
* point.
*/
cubicCurveTo: function(handle1, handle2, to) {
// First modify the current segment:
var current = getCurrentSegment();
// Convert to relative values:
current.handleOut.set(
handle1.x - current.point.x,
handle1.y - current.point.y);
// And add the new segment, with handleIn set to c2
this.segments.push(
new Segment(to, handle2.subtract(to), new Point())
);
},
/**
* Adds a quadratic bezier curve to the path, defined by a handle and a to
* point.
*/
quadraticCurveTo: function(handle, to) {
// This is exact:
// If we have the three quad points: A E D,
// and the cubic is A B C D,
// B = E + 1/3 (A - E)
// C = E + 1/3 (D - E)
var current = this.segments[this.segments.length - 1];
var x1 = current.point.x;
var y1 = current.point.y;
cubicCurveTo(
handle.add(current.point.subtract(handle).multiply(1/3)),
handle.add(to.subtract(handle).multiply(1/3)),
to
);
},
curveTo: function(through, to, parameter) {
through = new Point(through);
to = new Point(to);
if(parameter == null)
parameter = 0.5;
var current = this.segments[this.segments.length - 1];
// handle = (through - (1 - t)^2 * current - t^2 * to) / (2 * (1 - t) * t)
var t1 = 1 - parameter;
var handle = through.subtract(
current.multiply(t1 * t1)).subtract(
to.multiply(parameter * parameter)).divide(
2.0 * parameter * t1);
if (handle.isNaN())
throw new Error(
"Cannot put a curve through points with parameter="
+ parameter);
this.quadraticCurveTo(handle, to);
},
arcTo: function(to, clockwise) {
var through, to;
if(arguments[1] && typeof arguments[1] != 'boolean') {
through = new Point(arguments[0]);
to = new Point(arguments[1]);
} else {
if(clockwise === null)
clockwise = true;
var current = this.segments[this.segments.length - 1].point;
var middle = current.add(to).divide(2);
var step = middle.subtract(current);
through = clockwise
? middle.subtract(-step.y, step.x)
: middle.add(-step.y, step.x);
}
// Get the start point:
var current = this.segments[this.segments.length - 1];
var x1 = current.point.x, x2 = through.x, x3 = to.x;
var y1 = current.point.y, y2 = through.y, y3 = to.y;
var f = x3 * x3 - x3 * x2 - x1 * x3 + x1 * x2 + y3 * y3 - y3 * y2
- y1 * y3 + y1 * y2;
var g = x3 * y1 - x3 * y2 + x1 * y2 - x1 * y3 + x2 * y3 - x2 * y1;
var m = g == 0 ? 0 : f / g;
var c = (m * y2) - x2 - x1 - (m * y1);
var d = (m * x1) - y1 - y2 - (x2 * m);
var e = (x1 * x2) + (y1 * y2) - (m * x1 * y2) + (m * x2 * y1);
var centerX = -c / 2;
var centerY = -d / 2;
var radius = Math.sqrt(centerX * centerX + centerY * centerY - e);
// Note: reversing the Y equations negates the angle to adjust
// for the upside down coordinate system.
var angle = Math.atan2(centerY - y1, x1 - centerX);
var middle = Math.atan2(centerY - y2, x2 - centerX);
var extent = Math.atan2(centerY - y3, x3 - centerX);
var diff = middle - angle;
if (diff < -Math.PI)
diff += Math.PI * 2;
else if (diff > Math.PI)
diff -= Math.PI * 2;
extent -= angle;
if (extent <= 0.0)
extent += Math.PI * 2;
if (diff < 0) extent = Math.PI * 2 - extent;
else extent = -extent;
angle = -angle;
var ext = Math.abs(extent);
var arcSegs;
if (ext >= 2 * Math.PI) arcSegs = 4;
else arcSegs = Math.ceil(ext * 2 / Math.PI);
var inc = extent;
if (inc > 2 * Math.PI) inc = 2 * Math.PI;
else if (inc < -2 * Math.PI) inc = -2 * Math.PI;
inc /= arcSegs;
var halfInc = inc / 2;
var z = 4 / 3 * Math.sin(halfInc) / (1 + Math.cos(halfInc));
for (var i = 0; i <= arcSegs; i++) {
var relx = Math.cos(angle);
var rely = Math.sin(angle);
var pt = new Point(centerX + relx * radius,
centerY + rely * radius);
var out;
if (i == arcSegs) out = null;
else out = new Point(centerX + (relx - z * rely) * radius - pt.x,
centerY + (rely + z * relx) * radius - pt.y);
if (i == 0) {
// Modify startSegment
current.handleOut = out;
} else {
// Add new Segment
var inPoint = new Point(
centerX + (relx + z * rely) * radius - pt.x,
centerY + (rely - z * relx) * radius - pt.y);
this.segments.push(new Segment(pt, inPoint, out));
}
angle += inc;
}
},
lineBy: function() {
var vector = Point.read(arguments);
if(vector) {
var current = this.segments[this.segments.length - 1];
this.lineTo(current.point.add(vector));
}
},
smooth: function() {
var segments = this.segments;
// This code is based on the work by Oleg V. Polikarpotchkin,
// http://ov-p.spaces.live.com/blog/cns!39D56F0C7A08D703!147.entry
// It was extended to support closed paths by averaging overlapping
// beginnings and ends. The result of this approach is very close to
// Polikarpotchkin's closed curve solution, but reuses the same
// algorithm as for open paths, and is probably executing faster as
// well, so it is preferred.
var size = segments.length;
if (size <= 2)
return;
var n = size;
// Add overlapping ends for averaging handles in closed paths
var overlap;
if (this.closed) {
// Overlap up to 4 points since averaging beziers affect the 4
// neighboring points
overlap = Math.min(size, 4);
n += Math.min(size, overlap) * 2;
} else {
overlap = 0;
}
var knots = [];
for (var i = 0; i < size; i++)
knots[i + overlap] = segments[i].point;
if (this.closed) {
// If we're averaging, add the 4 last points again at the beginning,
// and the 4 first ones at the end.
for (var i = 0; i < overlap; i++) {
knots[i] = segments[i + size - overlap].point;
knots[i + size + overlap] = segments[i].point;
}
} else {
n--;
}
// Calculate first Bezier control points
// Right hand side vector
var rhs = [];
// Set right hand side X values
for (var i = 1; i < n - 1; i++)
rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x;
rhs[0] = knots[0].x + 2 * knots[1].x;
rhs[n - 1] = 3 * knots[n - 1].x;
// Get first control points X-values
var x = getFirstControlPoints(rhs);
// Set right hand side Y values
for (var i = 1; i < n - 1; i++)
rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y;
rhs[0] = knots[0].y + 2 * knots[1].y;
rhs[n - 1] = 3 * knots[n - 1].y;
// Get first control points Y-values
var y = getFirstControlPoints(rhs);
if (this.closed) {
// Do the actual averaging simply by linearly fading between the
// overlapping values.
for (var i = 0, j = size; i < overlap; i++, j++) {
var f1 = (i / overlap);
var f2 = 1 - f1;
// Beginning
x[j] = x[i] * f1 + x[j] * f2;
y[j] = y[i] * f1 + y[j] * f2;
// End
var ie = i + overlap, je = j + overlap;
x[je] = x[ie] * f2 + x[je] * f1;
y[je] = y[ie] * f2 + y[je] * f1;
}
n--;
}
var handleIn = null;
// Now set the calculated handles
for (var i = overlap; i <= n - overlap; i++) {
var segment = segments[i - overlap];
if (handleIn != null)
segment.handleIn = handleIn.subtract(segment.point);
if (i < n) {
segment.handleOut =
new Point(x[i], y[i]).subtract(segment.point);
if (i < n - 1)
handleIn = new Point(
2 * knots[i + 1].x - x[i + 1],
2 * knots[i + 1].y - y[i + 1]);
else
handleIn = new Point(
(knots[n].x + x[n - 1]) / 2,
(knots[n].y + y[n - 1]) / 2);
}
}
if (closed && handleIn != null) {
var segment = get(0);
segment.handleIn = handleIn.subtract(segment.point);
}
},
curveBy: function(throughVector, toVector, parameter) {
throughVector = Point.read(throughVector);
toVector = Point.read(toVector);
var current = this.segments[this.segments.length - 1].point;
this.curveTo(current.add(throughVector), current.add(toVector), parameter);
},
arcBy: function(throughVector, toVector) {
throughVector = Point.read(throughVector);
toVector = Point.read(toVector);
var current = this.segments[this.segments.length - 1].point;
this.arcBy(current.add(throughVector), current.add(toVector));
},
setCtxStyles: function(ctx) {
for(var i in styleNames) {
var style;
if(style = this[i])
ctx[styleNames[i]] = style;
}
},
draw: function(ctx) {
ctx.beginPath();
var cp1;
for(var i = 0, l = this.segments.length; i < l; i++) {
var segment = this.segments[i];
var point = segment.point;
var handleIn = segment.handleIn ? segment.handleIn.add(point) : point;
var handleOut = segment.handleOut ? segment.handleOut.add(point) : point;
if(i == 0) {
ctx.moveTo(point.x, point.y);
} else {
ctx.bezierCurveTo(cp1.x, cp1.y, handleIn.x, handleIn.y,
point.x, point.y);
}
cp1 = handleOut;
}
if(this.closed) {
var segment = this.segments[0];
var point = segment.point;
var handleIn = segment.handleIn ? segment.handleIn.add(point) : point;
ctx.bezierCurveTo(cp1.x, cp1.y, handleIn.x, handleIn.y,
point.x, point.y);
ctx.closePath();
}
this.setCtxStyles(ctx);
if(this.fillColor) ctx.fill();
if(this.strokeColor) ctx.stroke();
}
};
});

297
src/Point.js Normal file
View file

@ -0,0 +1,297 @@
var Point = Base.extend({
initialize: function() {
if(arguments.length == 2) {
this.x = arguments[0];
this.y = arguments[1];
} else if(arguments.length == 1) {
var first = arguments[0];
if(first == null) {
this.x = this.y = 0;
} else if(first.x !== undefined) {
this.x = first.x;
this.y = first.y;
this.angle = first.angle;
} else if(first.width !== undefined) {
this.x = first.width;
this.y = first.height;
this.angle = null;
} else if(first.length !== undefined) {
this.x = first[0];
this.y = first.length > 1 ? first[1] : first[0];
} else if(typeof first === 'number') {
this.x = this.y = first;
this.angle = null;
} else {
this.x = this.y = 0;
}
} else {
this.x = this.y = 0;
}
},
clone: function() {
return new Point(this.x, this.y);
},
add: function() {
var point = Point.read(arguments);
return new Point(this.x + point.x, this.y + point.y);
},
subtract: function() {
var point = Point.read(arguments);
return new Point(this.x - point.x, this.y - point.y);
},
multiply: function() {
var point = Point.read(arguments);
return new Point(this.x * point.x, this.y * point.y);
},
divide: function() {
var point = Point.read(arguments);
return new Point(this.x / point.x, this.y / point.y);
},
modulo: function() {
var point = Point.read(arguments);
return new Point(this.x % point.x, this.y % point.y);
},
negate: function() {
return new Point(-this.x, -this.y);
},
equals: function() {
var point = Point.read(arguments);
return this.x == point.x && this.y == point.y;
},
getDistance: function() {
var point = Point.read(arguments);
var px = point.x - this.x;
var py = point.y - this.y;
return Math.sqrt(px * px + py * py);
},
getDistanceSquared: function() {
var point = Point.read(arguments);
var px = point.x - this.x;
var py = point.y - this.y;
return px * px + py * py;
},
getLength: function() {
var point = Point.read(arguments);
return Math.sqrt(this.x * this.x + this.y * this.y);
},
setLength: function(length) {
if (this.isZero()) {
if (this.angle != null) {
var a = this.angle;
this.x = Math.cos(a) * length;
this.y = Math.sin(a) * length;
} else {
// Assume angle = 0
this.x = length;
// y is already 0
}
} else {
var scale = length / this.getLength();
if (scale == 0.0) {
// Calculate angle now, so it will be preserved even when
// x and y are 0
this.getAngle();
}
this.x *= scale;
this.y *= scale;
}
},
normalize: function(length) {
if (length === null)
length = 1;
var len = this.getLength();
var scale = len != 0 ? length / len : 0;
var res = new Point(this.x * scale, this.y * scale);
// Preserve angle.
res.angle = this.angle;
return res;
},
getAngleInRadians: function() {
return Math.atan2(this.y, this.x);
},
getAngleInDegrees: function() {
return Math.atan2(this.y, this.x) * 180 / Math.PI;
},
getQuadrant: function() {
if (this.x >= 0) {
if (this.y >= 0) {
return 1;
} else {
return 4;
}
} else {
if (this.y >= 0) {
return 2;
} else {
return 3;
}
}
},
setAngle: function(angle) {
angle = this.angle = angle * Math.PI / 180;
if(!this.isZero()) {
var length = this.getLength();
this.x = Math.cos(angle) * length;
this.y = Math.sin(angle) * length;
}
},
getAngle: function() {
var angle;
if(arguments.length) {
var point = Point.read(arguments);
var div = this.getLength() * point.getLength();
if(div == 0) {
return NaN;
} else {
angle = Math.acos(this.dot(point) / div);
}
} else {
angle = this.angle = Math.atan2(this.y, this.x);
}
return angle * 180 / Math.PI;
},
getDirectedAngle: function() {
var point = Point.read(arguments);
var angle = this.getAngle() - point.getAngle();
var bounds = 180;
if(angle < - bounds) {
return angle + bounds * 2;
} else if (angle > bounds) {
return angle - bounds * 2;
} else {
return angle;
}
},
rotate: function(angle) {
angle = angle * Math.PI / 180;
var s = Math.sin(angle);
var c = Math.cos(angle);
return new Point(
this.x * c - this.y * s,
this.y * c + this.x * s
);
},
rotateAround: function(angle, center) {
center = new Point(center);
return this.subtract(center).rotate(angle).add(this);
},
interpolate: function(point, t) {
return new Point(
this.x * (1 - t) + point.x * t,
this.y * (1 - t) + point.y * t
);
},
// Need to adapt Rectangle.java first
// isInside: function(rect) {
// return rect.contains(this);
// },
isClose: function(point, tolerance) {
point = new Point(point);
return this.getDistance(point) < tolerance;
},
isParallel: function(point) {
return Math.abs(this.x / point.x - this.y / point.y) < 0.00001;
},
isZero: function() {
return this.x == 0 && this.y == 0;
},
isNaN: function() {
return isNaN(this.x) || isNaN(this.y);
},
round: function() {
return new Point(Math.round(this.x), Math.round(this.y));
},
ceil: function() {
return new Point(Math.ceil(this.x), Math.ceil(this.y));
},
floor: function() {
return new Point(Math.floor(this.x), Math.floor(this.y));
},
abs: function() {
return new Point(Math.abs(this.x), Math.abs(this.y));
},
dot: function() {
var point = Point.read(arguments);
return this.x * point.x + this.y * point.y;
},
cross: function() {
var point = Point.read(arguments);
return this.x * point.y - this.y - point.x;
},
project: function() {
var point = Point.read(arguments);
if(point.isZero()) {
return new Point(0, 0);
} else {
var scale = this.dot(point) / point.dot(point);
return new Point(
point.x * scale,
point.y * scale
);
}
},
toString: function() {
return '{ x: ' + this.x + ', y: ' + this.y + ' }';
},
statics: {
read: function(args) {
if(args.length) {
var point = new Point();
point.initialize.apply(point, args);
return point;
}
},
min: function(point1, point2) {
return new Point(
Math.min(point1.x, point2.x),
Math.min(point1.y, point2.y));
},
max: function(point1, point2) {
return new Point(
Math.max(point1.x, point2.x),
Math.max(point1.y, point2.y));
},
random: function() {
return new Point(Math.random(), Math.random());
}
}
});

287
src/Rectangle.js Normal file
View file

@ -0,0 +1,287 @@
Rectangle = Base.extend({
initialize: function() {
if(arguments.length == 1) {
var rect = arguments[0];
this.x = rect.x;
this.y = rect.y;
this.width = rect.width;
this.height = rect.height;
} else if(arguments.length == 2) {
if(arguments[1].x !== undefined) {
// new Rectangle(point1, point2)
var point1 = new Point(arguments[0]);
var point2 = new Point(arguments[1]);
this.x = point1.x;
this.y = point1.y;
this.width = point2.x - point1.x;
this.height = point2.y - point1.y;
if(this.width < 0) {
this.x = point2.x;
this.width = -this.width;
}
if(this.height < 0) {
this.y = point2.y;
this.height = -this.height;
}
} else {
// new Rectangle(point, size)
var point = new Point(arguments[0]);
var size = new Size(arguments[1]);
this.x = point.x;
this.y = point.y;
this.width = size.width;
this.height = size.height;
}
} else if(arguments.length == 4) {
// new Rectangle(x, y, width, height)
this.x = arguments[0];
this.y = arguments[1];
this.width = arguments[2];
this.height = arguments[3];
} else {
// new Rectangle()
this.x = this.y = this.width = this.height = 0;
}
},
getPoint: function() {
return new Point(this.x, this.y);
},
setPoint: function() {
var point = Point.read(arguments);
this.x = point.x;
this.y = point.y;
},
getSize: function() {
return new Size(this.width, this.height);
},
setSize: function() {
var size = Size.read(arguments);
this.width = size.width;
this.height = size.height;
},
getLeft: function() {
return this.x;
},
setLeft: function(left) {
// right should not move
this.width -= left - this.x;
this.x = left;
},
getTop: function() {
return this.y;
},
setTop: function(top) {
this.height -= top - this.y;
this.y = top;
},
getRight: function() {
return this.x + this.width;
},
setRight: function(right) {
this.width = right - this.x;
},
getBottom: function() {
return this.y + this.height;
},
setBottom: function(bottom) {
this.height = bottom - this.y;
},
getCenterX: function() {
return this.x + this.width * 0.5;
},
setCenterX: function(x) {
this.x = x - this.width * 0.5;
},
getCenterY: function() {
return this.y + this.height * 0.5;
},
setCenterY: function(y) {
this.y = y - this.height * 0.5;
},
getCenter: function() {
return new Point(this.x - this.width / 2, this.y - this.height / 2);
},
setCenter: function() {
var center = Point.read(arguments);
this.x = center.x - this.width / 2;
this.y = center.y - this.height / 2;
},
getTopLeft: function() {
return new Point(this.getLeft(), this.getTop());
},
setTopLeft: function() {
var topLeft = Point.read(arguments);
this.setLeft(topLeft.x);
this.setTop(topLeft.y);
},
getTopRight: function() {
return new Point(this.getRight(), this.getTop());
},
setTopRight: function() {
var topRight = Point.read(arguments);
this.setRight(topRight.x);
this.setTop(topRight.y);
},
getBottomLeft: function() {
return new Point(this.getLeft(), this.getBottom());
},
setBottomLeft: function() {
var bottomLeft = Point.read(arguments);
this.setLeft(bottomLeft.x);
this.setBottom(bottomLeft.y);
},
getBottomRight: function() {
return new Point(this.getRight(), this.getBottom());
},
setBottomRight: function() {
var bottomRight = Point.read(arguments);
this.setBottom(bottomRight.y);
this.setRight(bottomRight.x);
},
getLeftCenter: function() {
return new Point(this.getLeft(), this.getCenterY());
},
setLeftCenter: function() {
var leftCenter = Point.read(arguments);
this.setLeft(leftCenter.x);
this.setCenterY(leftCenter.y);
},
getTopCenter: function() {
return new Point(this.getCenterX(), this.getTop());
},
setTopCenter: function() {
var topCenter = Point.read(arguments);
this.setCenterX(topCenter.x);
this.setTop(topCenter.y);
},
getRightCenter: function() {
return new Point(this.getRight(), this.getCenterY());
},
setRightCenter: function() {
var rightCenter = Point.read(arguments);
this.setRight(rightCenter.x);
this.setCenterY(rightCenter.y);
},
getBottomCenter: function() {
return new Point(this.getCenterX(), this.getBottom());
},
setBottomCenter: function() {
var bottomCenter = Point.read(arguments);
this.setBottom(bottomCenter.y);
this.setCenterX(bottomCenter.x);
},
clone: function() {
return new Rectangle(this);
},
equals: function() {
var rect = Rectangle.read(arguments);
return this.x == rect.x && this.y == rect.y
&& this.width == rect.width && this.height == rect.height;
},
isEmpty: function() {
return this.width == 0 || this.height == 0;
},
contains: function(rect) {
if(rect.width !== undefined) {
return rect.x >= this.x && rect.y >= this.y
&& rect.x + rect.width <= this.x + this.width
&& rect.y + rect.height <= this.y + this.height;
} else {
var point = Point.read(arguments);
return point.x >= this.x && point.y >= this.y
&& point.x <= this.x + this.width
&& point.y <= this.y + this.height;
}
},
intersects: function() {
var rect = Rectangle.read(arguments);
return rect.x + rect.width > this.x
&& rect.y + rect.height > this.y
&& rect.x < this.x + this.width
&& rect.y < this.y + this.height;
},
intersect: function() {
var rect = Rectangle.read(arguments);
var x1 = Math.max(this.x, rect.x);
var y1 = Math.max(this.y, rect.y);
var x2 = Math.min(this.x + this.width, rect.x + rect.width);
var y2 = Math.min(this.y + this.height, rect.y + rect.height);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
},
unite: function() {
var rect = Rectangle.read(arguments);
var x1 = Math.min(this.x, rect.x);
var y1 = Math.min(this.y, rect.y);
var x2 = Math.max(this.x + this.width, rect.x + rect.width);
var y2 = Math.max(this.y + this.height, rect.y + rect.height);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
},
include: function() {
var point = Point.read(arguments);
var x1 = Math.min(this.x, point.x);
var y1 = Math.min(this.y, point.y);
var x2 = Math.max(this.x + this.width, point.x);
var y2 = Math.max(this.y + this.height, point.y);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
},
toString: function() {
return '{ x: ' + this.x
+ ', y: ' + this.y
+ ', width: ' + this.width
+ ', height: ' + this.height
+ ' }';
},
statics: {
read: function(args) {
if(args.length) {
var rect = new Rectangle();
rect.initialize.apply(rect, args);
return rect;
}
}
}
});

138
src/Segment.js Normal file
View file

@ -0,0 +1,138 @@
Segment = Base.extend({
initialize: function() {
if(arguments.length == 0) {
this.point = new Point();
} else if(arguments.length == 1) {
if(arguments[0].point) {
var segment = arguments[0];
this.point = new Point(segment.point);
if(segment.handleIn)
this.handleIn = new Point(segment.handleIn);
if(segment.handleOut)
this.handleOut = new Point(segment.handleOut);
} else {
this.point = new Point(arguments[0]);
}
} else if(arguments.length < 6) {
if(arguments.length == 2 && !arguments[1].x) {
this.point = new Point(arguments[0], arguments[1]);
} else {
this.point = new Point(arguments[0]);
if(arguments[1])
this.handleIn = new Point(arguments[1]);
if(arguments[2])
this.handleOut = new Point(arguments[2]);
}
} else if(arguments.length == 6) {
this.point = new Point(arguments[0], arguments[1]);
this.handleIn = new Point(arguments[2], arguments[3]);
this.handleOut = new Point(arguments[4], arguments[5]);
}
},
insert: function() {
if(this.segments && this.segments.path) {
var path = this.segments.path;
path.checkValid();
}
},
getPoint: function() {
return this.point;
},
setPoint: function() {
var point = Point.read(arguments);
this.point = point;
},
getHandleIn: function() {
return this.handleIn;
},
setHandleIn: function() {
var point = Point.read(arguments);
this.handleIn = point;
},
getHandleOut: function() {
return this.handleOut;
},
setHandleOut: function() {
var point = Point.read(arguments);
this.handleOut = point;
this.corner = !handleIn.isParallel(handleOut);
},
getIndex: function() {
var segments = this.path.segments;
for(var i = 0, l = segments.length; i < l; i++) {
if(segments[i] == this)
return i;
}
},
getPath: function() {
return this.path;
},
// todo
// getCurve: function() {
// if(this.segments && this.segments.path) {
// var curves = this.segments.path.getCurves();
// // The curves list handles closing curves, so the curves.size
// // is adjusted accordingly. just check to be in the boundaries here:
// return index < curves.length ? curves[index] : null;
// }
// },
getNext: function() {
var index = this.getIndex();
return this.path && index < this.path.segments.length - 1
? this.path.segments[index + 1] : null;
},
getPrevious: function() {
return this.path != null && index > 0 ? this.segments[this.getIndex() - 1] : null;
},
// todo
// isSelected: function() {
//
// }
//
// setSelected: function(pt, selected)
reverse: function() {
return new Segment(this.point, this.handleOut, this.handleIn);
},
clone: function() {
return new Segment(this);
},
remove: function() {
if(this.segments)
return this.path.segments.unshift(this.getIndex(), 1);
return false;
},
toString: function() {
return '{ point: ' + this.point.toString()
+ ', handleIn '+ this.handleIn.toString()
+ ', handleOut ' + this.handleOut.toString()
+ ' }';
},
statics: {
read: function(args) {
if(args.length && args[0] != null) {
var segment = new Segment();
segment.initialize.apply(segment, args);
return segment;
}
}
}
})

10
src/SegmentList.js Normal file
View file

@ -0,0 +1,10 @@
SegmentList = Array.extend({
initialize: function(path) {
this.length = 0;
this.path = path;
},
push: function() {
this.base.apply(this, arguments);
this.path.listChanged();
}
});

113
src/Size.js Normal file
View file

@ -0,0 +1,113 @@
var Size = Base.extend({
initialize: function() {
if(arguments.length == 2) {
this.width = arguments[0];
this.height = arguments[1];
} else if(arguments.length == 1) {
var first = arguments[0];
if(first.width !== undefined || first.height !== undefined) {
this.width = first.width ? first.width : 0;
this.height = first.height ? first.height : 0;
} else if(first.x !== undefined || first.y !== undefined) {
this.width = first.x ? first.x : 0;
this.height = first.y ? first.y : 0;
} else if(first.length !== undefined) {
this.width = first[0];
this.height = first.length > 1 ? first[1] : first[0];
} else if(typeof first === 'number') {
this.width = this.height = first;
} else {
this.width = this.height = 0;
}
} else {
this.width = this.height = 0;
}
},
add: function() {
var size = Size.read(arguments);
return new Size(this.width + size.width, this.height + size.height);
},
subtract: function() {
var size = Size.read(arguments);
return new Size(this.width - size.width, this.height - size.height);
},
multiply: function() {
var size = Size.read(arguments);
return new Size(this.width * size.width, this.height * size.height);
},
divide: function() {
var size = Size.read(arguments);
return new Size(this.width / size.width, this.height / size.height);
},
modulo: function() {
var size = Size.read(arguments);
return new Size(this.width % size.width, this.height % size.height);
},
negate: function() {
return new Size(-this.width, -this.height);
},
equals: function() {
var size = Size.read(arguments);
return this.width == size.width && this.height == size.height;
},
isNaN: function() {
return isNaN(this.width) || isNaN(this.height);
},
round: function() {
return new Size(Math.round(this.width), Math.round(this.height));
},
ceil: function() {
return new Size(Math.ceil(this.width), Math.ceil(this.height));
},
floor: function() {
return new Size(Math.floor(this.width), Math.floor(this.height));
},
abs: function() {
return new Size(Math.abs(this.width), Math.abs(this.height));
},
dot: function(Size) {
return this.width * size.width + this.height * size.height;
},
toString: function() {
return '{ x: ' + this.width + ', y: ' + this.height + ' }';
},
statics: {
read: function(args) {
if(args.length) {
var size = new Size();
size.initialize.apply(size, args);
return size;
}
},
min: function(Size1, Size2) {
return new Size(
Math.min(Size1.width, Size2.width),
Math.min(Size1.height, Size2.height));
},
max: function(Size1, Size2) {
return new Size(
Math.max(Size1.width, Size2.width),
Math.max(Size1.height, Size2.height));
},
random: function() {
return new Size(Math.random(), Math.random());
}
}
});

39
test/index.html Normal file
View file

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>QUnit Test Suite</title>
<link rel="stylesheet" href="qunit/qunit.css" type="text/css" media="screen">
<!-- Libraries -->
<script type="text/javascript" src="../src/Bootstrap.js"></script>
<script type="text/javascript" src="../src/Item.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Point.js"></script>
<script type="text/javascript" src="../src/Rectangle.js"></script>
<script type="text/javascript" src="../src/Size.js"></script>
<script type="text/javascript" src="../src/Segment.js"></script>
<script type="text/javascript" src="../src/PathItem.js"></script>
<script type="text/javascript" src="../src/Path.js"></script>
<script type="text/javascript" src="../src/SegmentList.js"></script>
<script type="text/javascript" src="../src/Doc.js"></script>
<script type="text/javascript" src="qunit/qunit.js"></script>
<!-- Test Helpers -->
<script type="text/javascript" src="test_functions.js"></script>
<!-- Tests -->
<script type="text/javascript" src="tests_Point.js"></script>
<script type="text/javascript" src="tests_Size.js"></script>
<script type="text/javascript" src="tests_Rectangle.js"></script>
<script type="text/javascript" src="tests_Segment.js"></script>
<script type="text/javascript" src="tests_Path_Shapes.js"></script>
<script type="text/javascript" src="tests_Path_Drawing_Commands.js"></script>
</head>
<body>
<h1 id="qunit-header">QUnit Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup</div>
</body>
</html>

197
test/qunit/qunit.css Normal file
View file

@ -0,0 +1,197 @@
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 15px 15px 0 0;
-moz-border-radius: 15px 15px 0 0;
-webkit-border-top-right-radius: 15px;
-webkit-border-top-left-radius: 15px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests ol {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
margin: 0.5em;
padding: 0.4em 0.5em 0.4em 0.5em;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #5E740B;
background-color: #fff;
border-left: 26px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 26px solid #EE5757;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Footer */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-radius: 0 0 15px 15px;
-moz-border-radius: 0 0 15px 15px;
-webkit-border-bottom-right-radius: 15px;
-webkit-border-bottom-left-radius: 15px;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
}

1415
test/qunit/qunit.js Normal file

File diff suppressed because it is too large Load diff

22
test/test_functions.js Normal file
View file

@ -0,0 +1,22 @@
function compareSegmentLists(list1, list2) {
equal(list1.length, list2.length, 'segment count');
if(list1.length == list2.length) {
for(var i = 0, l = list1.length; i < l; i++) {
compareSegments(list1[i], list2[i]);
}
}
}
function compareSegments(segment1, segment2) {
var points = ['point', 'handleIn', 'handleOut'];
for(var i = 0; i < 3; i++) {
equals(!!segment1[points[i]], !!segment2[points[i]], 'have ' + points[i]);
if(segment1[points[i]] && segment2[points[i]])
comparePoints(segment1[points[i]], segment2[points[i]], points[i]);
}
}
function comparePoints(point1, point2, message) {
equals(Math.round(point1.x * 100), Math.round(point2.x * 100), message + ' x');
equals(Math.round(point1.y * 100), Math.round(point2.y * 100), message + ' y');
}

View file

@ -0,0 +1,17 @@
module('Path Drawing Commands');
test('path.lineTo(point);', function() {
var path = new Path();
path.moveTo([50, 50]);
path.lineTo([100, 100]);
var expectedSegments = [{ point: { x: 50, y: 50 } }, { point: { x: 100, y: 100 } }];
compareSegmentLists(path.segments, expectedSegments);
});
test('path.arcTo(from, through, to);', function() {
var path = new Path();
path.moveTo([50, 50]);
path.arcTo([100, 100], [75, 75]);
var expectedSegments = [{ point: { x: 50, y: 50 }, handleOut: { x: 10.11156, y: -10.11156 } }, { point: { x: 88.5299, y: 42.33593 }, handleIn: { x: -13.21138, y: -5.47233 }, handleOut: { x: 13.21138, y: 5.47233 } }, { point: { x: 110.35534, y: 75 }, handleIn: { x: 0, y: -14.2999 } }];
compareSegmentLists(path.segments, expectedSegments);
});

40
test/tests_Path_Shapes.js Normal file
View file

@ -0,0 +1,40 @@
module('Predefined Path Shapes');
test('new Path.Rectangle([50, 50], [100, 100])', function() {
var path = new Path.Rectangle([50, 50], [100, 100]);
var expectedSegments = [new Segment(new Point(50, 150)), new Segment(new Point(50, 50)), new Segment(new Point(150, 50)), new Segment(new Point(150, 150))];
compareSegmentLists(path.segments, expectedSegments);
});
test('new Path.Circle([100, 100], 50)', function() {
var path = new Path.Circle([100, 100], 50);
var expectedSegments = [new Segment(new Point(50, 100), new Point(0, 27.6142578125), new Point(0, -27.6142578125)), new Segment(new Point(100, 50), new Point(-27.6142578125, 0), new Point(27.6142578125, 0)), new Segment(new Point(150, 100), new Point(0, -27.6142578125), new Point(0, 27.6142578125)), new Segment(new Point(100, 150), new Point(27.6142578125, 0), new Point(-27.6142578125, 0))];
compareSegmentLists(path.segments, expectedSegments);
});
test('new Path.Oval(rect)', function() {
var rect = new Rectangle([500, 500], [1000, 750])
var path = new Path.Oval(rect);
var expectedSegments = [{ point: { x: 500, y: 875 }, handleIn: { x: 0, y: 207.10645 }, handleOut: { x: 0, y: -207.10645 } }, { point: { x: 1000, y: 500 }, handleIn: { x: -276.14258, y: 0 }, handleOut: { x: 276.14258, y: 0 } }, { point: { x: 1500, y: 875 }, handleIn: { x: 0, y: -207.10645 }, handleOut: { x: 0, y: 207.10645 } }, { point: { x: 1000, y: 1250 }, handleIn: { x: 276.14258, y: 0 }, handleOut: { x: -276.14258, y: 0 } }];
compareSegmentLists(path.segments, expectedSegments);
});
test('new Path.RoundRectangle(rect, size)', function() {
var rect = new Rectangle([50, 50], [200, 100])
var path = new Path.RoundRectangle(rect, 20);
var expectedSegments = [{ point: { x: 70, y: 150 }, handleOut: { x: -11.0459, y: 0 } }, { point: { x: 50, y: 130 }, handleIn: { x: 0, y: 11.0459 } }, { point: { x: 50, y: 70 }, handleOut: { x: 0, y: -11.0459 } }, { point: { x: 70, y: 50 }, handleIn: { x: -11.0459, y: 0 } }, { point: { x: 230, y: 50 }, handleOut: { x: 11.0459, y: 0 } }, { point: { x: 250, y: 70 }, handleIn: { x: 0, y: -11.0459 } }, { point: { x: 250, y: 130 }, handleOut: { x: 0, y: 11.0459 } }, { point: { x: 230, y: 150 }, handleIn: { x: 11.0459, y: 0 } }];
compareSegmentLists(path.segments, expectedSegments);
});
test('new Path.RoundRectangle(rect, size) - too large size', function() {
var rect = new Rectangle([50, 50], [200, 100])
var path = new Path.RoundRectangle(rect, 200);
var expectedSegments = [{ point: { x: 150, y: 150 }, handleOut: { x: -55.22852, y: 0 } }, { point: { x: 50, y: 100 }, handleIn: { x: 0, y: 27.61426 } }, { point: { x: 50, y: 100 }, handleOut: { x: 0, y: -27.61426 } }, { point: { x: 150, y: 50 }, handleIn: { x: -55.22852, y: 0 } }, { point: { x: 150, y: 50 }, handleOut: { x: 55.22852, y: 0 } }, { point: { x: 250, y: 100 }, handleIn: { x: 0, y: -27.61426 } }, { point: { x: 250, y: 100 }, handleOut: { x: 0, y: 27.61426 } }, { point: { x: 150, y: 150 }, handleIn: { x: 55.22852, y: 0 } }];
compareSegmentLists(path.segments, expectedSegments);
});
test('new Path.Arc(from, through, to)', function() {
var path = new Path.Arc([50, 50], [100, 100], [75, 75]);
var expectedSegments = [{ point: { x: 50, y: 50 }, handleOut: { x: 10.11156, y: -10.11156 } }, { point: { x: 88.5299, y: 42.33593 }, handleIn: { x: -13.21138, y: -5.47233 }, handleOut: { x: 13.21138, y: 5.47233 } }, { point: { x: 110.35534, y: 75 }, handleIn: { x: 0, y: -14.2999 } }];
compareSegmentLists(path.segments, expectedSegments);
});

78
test/tests_Point.js Normal file
View file

@ -0,0 +1,78 @@
module('Point');
test('new Point(10, 20)', function(){
var point = new Point(10, 20);
equals(point.x, 10);
equals(point.y, 20);
});
test('new Point([10, 20])', function(){
var point = new Point([10, 20]);
equals(point.x, 10);
equals(point.y, 20);
});
test('new Point({x: 10, y: 20})', function(){
var point = new Point({x: 10, y: 20});
equals(point.x, 10);
equals(point.y, 20);
});
test('new Point(new Size(10, 20))', function(){
var point = new Point(new Size(10, 20));
equals(point.x, 10);
equals(point.y, 20);
});
test('new Point({ width: 10, height: 20})', function(){
var point = new Point({width: 10, height: 20});
equals(point.x, 10);
equals(point.y, 20);
});
module('Point vector operations');
test('new Point(0, 10).normalize(20)', function(){
var point = new Point(0, 10).normalize(20)
equals(point.x, 0);
equals(point.y, 20);
});
test('new Point(0, 10).setLength(20)', function(){
var point = new Point(0, 10);
point.setLength(20)
equals(point.x, 0);
equals(point.y, 20);
});
test('new Point(0, 10).getAngle()', function(){
var angle = new Point(0, 10).getAngle();
equals(angle, 90);
});
test('new Point(0, 10).getAngle([10, 10])', function(){
var angle = new Point(0, 10).getAngle([10, 10]);
equals(Math.round(angle), 45);
});
test('new Point(100, 50).rotate(90)', function(){
var point = new Point(100, 50).rotate(90);
equals(Math.round(point.x), -50);
equals(Math.round(point.y), 100);
});
test('setAngle(20)', function(){
var point = new Point(10, 20);
point.setAngle(92);
equals(point.getAngle(), 92);
});
test('setAngle(20)', function(){
var point = new Point(10, 20);
point.setAngle(92);
equals(point.getAngle(), 92);
});
test('new Point().getDirectedAngle(new Point(10, 10))', function() {
var angle = new Point().getDirectedAngle(new Point(10, 10));
equals(angle, -45);
});

231
test/tests_Rectangle.js Normal file
View file

@ -0,0 +1,231 @@
module('Rectangle');
test('new Rectangle(new Point(10, 20), new Size(30, 40));', function(){
var rect = new Rectangle(new Point(10, 20), new Size(30, 40));
equals(rect.x, 10);
equals(rect.y, 20);
equals(rect.width, 30);
equals(rect.height, 40);
});
test('new Rectangle([10, 20], [30, 40]);', function(){
var rect = new Rectangle([10, 20], [30, 40]);
equals(rect.x, 10);
equals(rect.y, 20);
equals(rect.width, 30);
equals(rect.height, 40);
});
test('new Rectangle(new Point(10, 20), new Point(30, 40));', function(){
var rect = new Rectangle(new Point(10, 20), new Point(30, 40));
equals(rect.x, 10);
equals(rect.y, 20);
equals(rect.width, 20);
equals(rect.height, 20);
});
test('new Rectangle(10, 20, 30, 40);', function(){
var rect = new Rectangle(10, 20, 30, 40);
equals(rect.x, 10);
equals(rect.y, 20);
equals(rect.width, 30);
equals(rect.height, 40);
});
test('new Rectangle({x: 10, y: 20, width: 30, height: 40});', function(){
var rect = new Rectangle({x: 10, y: 20, width: 30, height: 40});
equals(rect.x, 10);
equals(rect.y, 20);
equals(rect.width, 30);
equals(rect.height, 40);
});
test('getSize()', function(){
var rect = new Rectangle(10, 10, 20, 30);
equals(rect.getSize().equals([20, 30]), true);
});
test('setSize()', function(){
var rect = new Rectangle(10, 10, 20, 20);
rect.setSize(new Size(30, 30))
equals(rect.width, 30);
equals(rect.height, 30);
});
test('getTopLeft()', function(){
var rect = new Rectangle(10, 10, 20, 20);
var point = rect.getTopLeft();
equals(point.x, 10);
equals(point.y, 10);
});
test('setTopLeft()', function(){
var rect = new Rectangle(10, 10, 20, 20);
rect.setTopLeft(10, 15);
var point = rect.getTopLeft();
equals(point.x, 10);
equals(point.y, 15);
});
test('getTopRight()', function(){
var rect = new Rectangle(10, 10, 20, 20);
var point = rect.getTopRight();
equals(point.x, 30);
equals(point.y, 10);
});
test('setTopRight()', function(){
var rect = new Rectangle(10, 10, 20, 20);
rect.setTopRight(10, 15);
var point = rect.getTopRight();
equals(point.x, 10);
equals(point.y, 15);
});
test('getBottomLeft()', function(){
var rect = new Rectangle(10, 10, 20, 20);
var point = rect.getBottomLeft();
equals(point.x, 10);
equals(point.y, 30);
});
test('setBottomLeft()', function(){
var rect = new Rectangle(10, 10, 20, 20);
rect.setBottomLeft(10, 15);
var point = rect.getBottomLeft();
equals(point.x, 10);
equals(point.y, 15);
});
test('getBottomRight()', function(){
var rect = new Rectangle(10, 10, 20, 20);
var point = rect.getBottomRight();
equals(point.x, 30);
equals(point.y, 30);
});
test('setBottomRight()', function(){
var rect = new Rectangle(10, 10, 20, 20);
rect.setBottomRight(10, 15);
var point = rect.getBottomRight();
equals(point.x, 10);
equals(point.y, 15);
});
test('getBottomCenter()', function(){
var rect = new Rectangle(10, 10, 20, 20);
var point = rect.getBottomCenter();
equals(point.x, 20);
equals(point.y, 30);
});
test('setBottomCenter()', function(){
var rect = new Rectangle(10, 10, 20, 20);
rect.setBottomCenter(10, 15);
var point = rect.getBottomCenter();
equals(point.x, 10);
equals(point.y, 15);
});
test('getTopCenter()', function(){
var rect = new Rectangle(10, 10, 20, 20);
var point = rect.getTopCenter();
equals(point.x, 20);
equals(point.y, 10);
});
test('setTopCenter()', function(){
var rect = new Rectangle(10, 10, 20, 20);
rect.setTopCenter(10, 15);
var point = rect.getTopCenter();
equals(point.x, 10);
equals(point.y, 15);
});
test('getLeftCenter()', function(){
var rect = new Rectangle(10, 10, 20, 20);
var point = rect.getLeftCenter();
equals(point.x, 10);
equals(point.y, 20);
});
test('setLeftCenter()', function(){
var rect = new Rectangle(10, 10, 20, 20);
rect.setLeftCenter(10, 15);
var point = rect.getLeftCenter();
equals(point.x, 10);
equals(point.y, 15);
});
test('getRightCenter()', function(){
var rect = new Rectangle(10, 10, 20, 20);
var point = rect.getRightCenter();
equals(point.x, 30);
equals(point.y, 20);
});
test('setRightCenter()', function(){
var rect = new Rectangle(10, 10, 20, 20);
rect.setRightCenter(10, 15);
var point = rect.getRightCenter();
equals(point.x, 10);
equals(point.y, 15);
});
test('intersects(rect)', function() {
var rect1 = new Rectangle({ x: 160, y: 270, width: 20, height: 20 });
var rect2 = { x: 195, y: 301, width: 19, height: 19 };
equals(rect1.intersects(rect2), false);
rect1 = new Rectangle({ x: 160, y: 270, width: 20, height: 20 });
rect2 = { x: 170.5, y: 280.5, width: 19, height: 19 };
equals(rect1.intersects(rect2), true);
});
test('contains(rect)', function() {
var rect1 = new Rectangle({ x: 160, y: 270, width: 20, height: 20 });
var rect2 = { x: 195, y: 301, width: 19, height: 19 };
equals(rect1.contains(rect2), false);
rect1 = new Rectangle({ x: 160, y: 270, width: 20, height: 20 });
rect2 = new Rectangle({ x: 170.5, y: 280.5, width: 19, height: 19 });
equals(rect1.contains(rect2), false);
rect1 = new Rectangle({ x: 299, y: 161, width: 137, height: 129 });
rect2 = new Rectangle({ x: 340, y: 197, width: 61, height: 61 });
equals(rect1.contains(rect2), true);
equals(rect2.contains(rect1), false);
});
test('contains(point)', function() {
var rect = new Rectangle({ x: 160, y: 270, width: 20, height: 20 });
var point = new Point(166, 280);
equals(rect.contains(point), true);
var point = new Point(30, 30);
equals(rect.contains(point), false);
});
test('intersect(rect)', function() {
var rect1 = new Rectangle({ x: 160, y: 270, width: 20, height: 20 });
var rect2 = { x: 170.5, y: 280.5, width: 19, height: 19 };
var intersected = rect1.intersect(rect2);
equals(intersected.equals({ x: 170.5, y: 280.5, width: 9.5, height: 9.5 }), true);
});
test('unite(rect)', function() {
var rect1 = new Rectangle({ x: 160, y: 270, width: 20, height: 20 });
var rect2 = { x: 170.5, y: 280.5, width: 19, height: 19 };
var united = rect1.unite(rect2);
equals(united.equals({ x: 160, y: 270, width: 29.5, height: 29.5 }), true);
});
test('include(point)', function() {
var rect1 = new Rectangle({ x: 95, y: 151, width: 20, height: 20 });
var included = rect1.include([50, 50]);
equals(included.equals({ x: 50, y: 50, width: 65, height: 121 }), true);
});
test('toString()', function() {
var string = new Rectangle(10, 20, 30, 40).toString();
equals(string, '{ x: 10, y: 20, width: 30, height: 40 }');
});

44
test/tests_Segment.js Normal file
View file

@ -0,0 +1,44 @@
module('Segment');
test('new Segment(point)', function(){
var segment = new Segment(new Point(10, 10));
var expected = { point: { x: 10, y: 10 } };
compareSegments(segment, expected);
});
test('new Segment(x, y)', function(){
var segment = new Segment(10, 10);
var expected = { point: { x: 10, y: 10 } };
compareSegments(segment, expected);
});
test('new Segment(object)', function(){
var segment = new Segment({ point: { x: 10, y: 10 }, handleIn: { x: 5, y: 5 }, handleOut: { x: 15, y: 15 } });
var expected = { point: { x: 10, y: 10 }, handleIn: { x: 5, y: 5 }, handleOut: { x: 15, y: 15 } };
compareSegments(segment, expected);
});
test('new Segment(point, handleIn, handleOut)', function(){
var segment = new Segment(new Point(10, 10), new Point(5, 5), new Point(15, 15));
var expected = { point: { x: 10, y: 10 }, handleIn: { x: 5, y: 5 }, handleOut: { x: 15, y: 15 } };
compareSegments(segment, expected);
});
test('new Segment(x, y, inX, inY, outX, outY)', function(){
var segment = new Segment(10, 10, 5, 5, 15, 15);
var expected = { point: { x: 10, y: 10 }, handleIn: { x: 5, y: 5 }, handleOut: { x: 15, y: 15 } };
compareSegments(segment, expected);
});
test('segment.reverse()', function(){
var segment = new Segment(new Point(10, 10), new Point(5, 5), new Point(15, 15));
segment = segment.reverse();
var expected = { point: { x: 10, y: 10 }, handleIn: { x: 15, y: 15 }, handleOut: { x: 5, y: 5 } };
compareSegments(segment, expected);
});
test('segment.clone()', function(){
var segment = new Segment(new Point(10, 10), new Point(5, 5), new Point(15, 15));
var clone = segment.clone();
equals(segment == clone, false);
compareSegments(segment, clone);
});

30
test/tests_Size.js Normal file
View file

@ -0,0 +1,30 @@
module('Size');
test('new Size(10, 20)', function(){
var size = new Size(10, 20);
equals(size.width, 10);
equals(size.height, 20);
});
test('new Size([10, 20])', function(){
var size = new Size([10, 20]);
equals(size.width, 10);
equals(size.height, 20);
});
test('new Size({width: 10, height: 20})', function(){
var size = new Size({width: 10, height: 20});
equals(size.width, 10);
equals(size.height, 20);
});
test('new Size(new Point(10, 20))', function(){
var size = new Size(new Point(10, 20));
equals(size.width, 10);
equals(size.height, 20);
});
test('new Size({ x: 10, y: 20})', function(){
var size = new Size({x: 10, y: 20});
equals(size.width, 10);
equals(size.height, 20);
});