var Point = Base.extend({ beans: true, 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.length; 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.length; 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.length; 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.length * point.length; 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.angle - point.angle; 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()); } } });