diff --git a/lib/toxi/geom/Ellipse.js b/lib/toxi/geom/Ellipse.js index fe67164..d843a5b 100644 --- a/lib/toxi/geom/Ellipse.js +++ b/lib/toxi/geom/Ellipse.js @@ -57,6 +57,11 @@ Ellipse = function(a,b,c,d) { extend(Ellipse,Vec2D); Ellipse.prototype.containsPoint = function(p) { + // Immediately reject points outside the containing rectangle + if (mathUtils.abs(p.x - this.x) > this.radius.x || + mathUtils.abs(p.y - this.y) > this.radius.y) { + return false; + } var foci = this.getFoci(); return p.distanceTo(foci[0]) + p.distanceTo(foci[1]) < 2 * mathUtils.max(this.radius.x, this.radius.y); }; @@ -90,15 +95,16 @@ Ellipse.prototype.getCircumference = function() { * @return the focus */ Ellipse.prototype.getFoci = function() { - var foci = []; if (this.radius.x > this.radius.y) { - foci[0] = this.sub(this.focus, 0); - foci[1] = this.add(this.focus, 0); - } else { - foci[0] = this.sub(0, this.focus); - foci[1] = this.add(0, this.focus); + return [ + this.sub(this.focus, 0), + this.add(this.focus, 0) + ]; } - return foci; + return [ + this.sub(0, this.focus), + this.add(0, this.focus) + ]; }; /** @@ -122,7 +128,13 @@ Ellipse.prototype.setRadii = function(rx,ry) { rx = rx.x; } this.radius.set(rx, ry); - this.focus = this.radius.magnitude(); + + if (this.radius.x > this.radius.y) { + this.focus = Math.sqrt(this.radius.x * this.radius.x - this.radius.y * this.radius.y); + } else { + this.focus = Math.sqrt(this.radius.y * this.radius.y - this.radius.x * this.radius.x); + } + return this; }; diff --git a/test/geom.Ellipse.js b/test/geom.Ellipse.js index bae8aa0..455af9f 100644 --- a/test/geom.Ellipse.js +++ b/test/geom.Ellipse.js @@ -14,4 +14,60 @@ describe('toxi.geom.Ellipse', function(){ }); }); }); + describe('#containsPoint', function(){ + describe('tall ellipse', function(){ + var ellipse = new toxi.geom.Ellipse(100, 100, 20, 50); + it('should return true for the center', function(){ + var p = new toxi.geom.Vec2D(100, 100); + assert.equal(ellipse.containsPoint(p), true); + }); + it('should return true for an internal point', function(){ + var p = new toxi.geom.Vec2D(110, 110); + assert.equal(ellipse.containsPoint(p), true); + }); + it('should return true for a point inside the top edge', function(){ + var p = new toxi.geom.Vec2D(100, 149.9); + assert.equal(ellipse.containsPoint(p), true); + }); + it('should return true for a point inside the right edge', function(){ + var p = new toxi.geom.Vec2D(119.9, 100); + assert.equal(ellipse.containsPoint(p), true); + }); + it('should return false for a point outside the ellipse', function(){ + var p = new toxi.geom.Vec2D(119, 149); + assert.equal(ellipse.containsPoint(p), false); + }); + it('should return false for a point outside the containing rectangle of the ellipse', function(){ + var p = new toxi.geom.Vec2D(125, 155); + assert.equal(ellipse.containsPoint(p), false); + }); + }); + describe('wide ellipse', function(){ + var ellipse = new toxi.geom.Ellipse(100, 100, 50, 20); + it('should return true for the center', function(){ + var p = new toxi.geom.Vec2D(100, 100); + assert.equal(ellipse.containsPoint(p), true); + }); + it('should return true for an internal point', function(){ + var p = new toxi.geom.Vec2D(110, 110); + assert.equal(ellipse.containsPoint(p), true); + }); + it('should return true for a point inside the top edge', function(){ + var p = new toxi.geom.Vec2D(100, 119.9); + assert.equal(ellipse.containsPoint(p), true); + }); + it('should return true for a point inside the right edge', function(){ + var p = new toxi.geom.Vec2D(149.9, 100); + assert.equal(ellipse.containsPoint(p), true); + }); + it('should return false for a point outside the ellipse', function(){ + var p = new toxi.geom.Vec2D(149, 119); + assert.equal(ellipse.containsPoint(p), false); + }); + it('should return false for a point outside the containing rectangle of the ellipse', function(){ + var p = new toxi.geom.Vec2D(155, 125); + assert.equal(ellipse.containsPoint(p), false); + }); + }); + }); });