diff --git a/lib/toxi/geom/Spline2D.js b/lib/toxi/geom/Spline2D.js index 1db7e07..6220303 100644 --- a/lib/toxi/geom/Spline2D.js +++ b/lib/toxi/geom/Spline2D.js @@ -52,6 +52,10 @@ Spline2D.prototype = { computeVertices: function(res){ this.updateCoefficients(); + if( res < 1 ){ + res = 1; + } + res++; if (this.bernstein === undefined || this.bernstein.resolution != res) { this.bernstein = new BernsteinPolynomial(res); } @@ -60,12 +64,13 @@ Spline2D.prototype = { this.findCPoints(); var deltaP = new Vec2D(); var deltaQ = new Vec2D(); + res--; for (var i = 0; i < this.numP - 1; i++) { var p = this.points[i]; var q = this.points[i + 1]; deltaP.set(this.delta[i]).addSelf(p); deltaQ.set(q).subSelf(this.delta[i + 1]); - for (var k = 0; k < bst.resolution; k++) { + for (var k = 0; k < res; k++) { var x = p.x * bst.b0[k] + deltaP.x * bst.b1[k] + deltaQ.x * bst.b2[k] + q.x * bst.b3[k]; @@ -75,6 +80,7 @@ Spline2D.prototype = { this.vertices.push(new Vec2D(x, y)); } } + this.vertices.push(this.points[this.points.length-1].copy()); return this.vertices; }, diff --git a/lib/toxi/geom/Spline3D.js b/lib/toxi/geom/Spline3D.js index 1bfba94..577fb46 100644 --- a/lib/toxi/geom/Spline3D.js +++ b/lib/toxi/geom/Spline3D.js @@ -52,6 +52,10 @@ define([ computeVertices: function(res){ this.updateCoefficients(); + if( res < 1 ){ + res = 1; + } + res++; if (this.bernstein === undefined || this.bernstein.resolution != res) { this.bernstein = new BernsteinPolynomial(res); } @@ -60,12 +64,13 @@ define([ this.findCPoints(); var deltaP = new Vec3D(); var deltaQ = new Vec3D(); + res--; for (var i = 0; i < this.numP - 1; i++) { var p = this.points[i]; var q = this.points[i + 1]; deltaP.set(this.delta[i]).addSelf(p); deltaQ.set(q).subSelf(this.delta[i + 1]); - for (var k = 0; k < bst.resolution; k++) { + for (var k = 0; k < res; k++) { var x = p.x * bst.b0[k] + deltaP.x * bst.b1[k] + deltaQ.x * bst.b2[k] + q.x * bst.b3[k]; @@ -77,6 +82,7 @@ define([ this.vertices.push(new Vec3D(x, y, z)); } } + this.vertices.push(this.points[this.points.length-1].copy()); return this.vertices; }, diff --git a/test/geom.BernsteinPolynomial.js b/test/geom.BernsteinPolynomial.js new file mode 100644 index 0000000..22a5d0f --- /dev/null +++ b/test/geom.BernsteinPolynomial.js @@ -0,0 +1,51 @@ +var toxi = require('../index'), + assert = require('assert'); + +describe('BernsteinPolynomial', function(){ + var isNear = function( v1, v2 ){ + return Math.abs(v1-v2) < 0.0001; + }; + var expected = { + 4: [ + [ 1.0, 0.29629624, 0.03703703, 0.0 ], + [ 0.0, 0.4444444, 0.2222222, 0.0 ], + [ 0.0, 0.22222224, 0.44444448, 0.0 ], + [ 0.0, 0.03703704, 0.29629633, 1.0 ] + ], + 6: [ + [ 1.0, 0.512, 0.21600002, 0.06399999, 0.0079999985, 0.0 ], + [ 0.0, 0.38400003, 0.43200004, 0.288, 0.09599999, 0.0 ], + [ 0.0, 0.09600001, 0.28800002, 0.43199998, 0.384, 0.0 ], + [ 0.0, 0.0080, 0.064, 0.21600002, 0.512, 1.0 ] + ], + 13: [ + [ 1.0, 0.7702547, 0.57870364, 0.421875, 0.29629624, 0.19849536, 0.125, 0.07233798, 0.03703705, 0.015625011, 0.004629636, 5.7870575E-4, 1.6940659E-21 ], + [ 0.0, 0.21006945, 0.3472222, 0.421875, 0.4444444, 0.4253472, 0.375, 0.30381948, 0.22222225, 0.14062504, 0.0694445, 0.019097265, 4.2632557E-14 ], + [ 0.0, 0.019097226, 0.06944445, 0.140625, 0.22222224, 0.30381945, 0.375, 0.4253472, 0.44444442, 0.42187503, 0.34722233, 0.21006964, 3.5762778E-7 ], + [ 0.0, 5.7870377E-4, 0.00462963, 0.015625, 0.03703704, 0.07233798, 0.125, 0.19849536, 0.29629624, 0.4218749, 0.5787035, 0.7702544, 0.99999964 ] + ] + }; + var eachNear = function(res,bNum){ + return function(b, i){ + assert.ok( isNear(b,expected[res][bNum][i]) ); + }; + }; + var forBers = function( bers ){ + var r = bers.resolution; + bers.b0.forEach(eachNear(r,0)); + bers.b1.forEach(eachNear(r,1)); + bers.b2.forEach(eachNear(r,2)); + bers.b3.forEach(eachNear(r,3)); + }; + it('should match the java output for resolution 4', function(){ + forBers(new toxi.geom.BernsteinPolynomial(4)); + }); + + it('should match the java output for resolution 6', function(){ + forBers(new toxi.geom.BernsteinPolynomial(6)); + }); + + it('should match the java output for resolution 13', function(){ + forBers(new toxi.geom.BernsteinPolynomial(13)); + }); +}); diff --git a/test/geom.Spline2D.js b/test/geom.Spline2D.js index 3095e3e..57cae5b 100644 --- a/test/geom.Spline2D.js +++ b/test/geom.Spline2D.js @@ -78,7 +78,7 @@ describe("Spline2D", function(){ [2,3,4,5,6,7,8,9,10,20].forEach(function( res ){ it('for resolution ' + res, function(){ var vertices = instance.computeVertices( res ); - assert.equal( vertices.length, instance.getNumPoints() * res - res ); + assert.equal( vertices.length, instance.getNumPoints() * res - (res-1) ); //ensure that all vertex positions are valid numbers vertices.forEach(function( vert ){ ['x','y'].forEach(function( a ){ diff --git a/test/geom.Spline3D.js b/test/geom.Spline3D.js index b20cd1c..8b4cbc4 100644 --- a/test/geom.Spline3D.js +++ b/test/geom.Spline3D.js @@ -1,7 +1,6 @@ /*global describe, it*/ var toxi = require('../index'), assert = require('assert'); -//tests for toxi.geom.Spline2D //TODO: test Spline3D#getDecimatedVertices() describe("Spline3D", function(){ describe('default instance', function(){ @@ -57,6 +56,42 @@ describe("Spline3D", function(){ testInstance( instance ); }); + describe('example instance with java output for comparison', function(){ + var Vec3D = toxi.geom.Vec3D; + var spline = new toxi.geom.Spline3D(); + spline.add (new Vec3D( 0.5, 0.0, 0.0 ) ); + spline.add( new Vec3D( 0.65, 0.25, 0.5 ) ); + spline.add( new Vec3D( 0.5, 0.5, 0.65 ) ); + spline.add( new Vec3D( 0.35, 0.75, 0.5 ) ); + spline.add( new Vec3D( 0.5, 1.0, 0.0 ) ); + + + var eachNear = function(res){ + return function(v, i){ + assert.ok( v.equalsWithTolerance(expected[res][i], 0.01) ); + }; + }; + var expected = { + 4: [{x:0.5, y:0.0, z:0.0}, {x:0.5204241, y:0.023995537, z:0.05527344}, {x:0.56696427, y:0.08482143, z:0.1890625}, {x:0.6175223, y:0.16573662, z:0.3533203}, {x:0.65, y:0.25, z:0.5}, {x:0.6476562, y:0.32421875, z:0.5919922}, {x:0.6151786, y:0.38839284, z:0.6359375}, {x:0.5626116, y:0.44587052, z:0.64941406}, {x:0.5, y:0.5, z:0.65}, {x:0.4373884, y:0.5541295, z:0.64941406}, {x:0.38482141, y:0.6116072, z:0.63593745}, {x:0.35234374, y:0.67578125, z:0.59199214}, {x:0.35, y:0.75, z:0.5}, {x:0.38247767, y:0.8342634, z:0.35332033}, {x:0.43303573, y:0.91517854, z:0.1890625}, {x:0.47957587, y:0.9760045, z:0.055273443}, {x:0.5, y:1.0, z:0.0}], + 8: [{x:0.5, y:0.0, z:0.0}, {x:0.5055664, y:0.0063476567, z:0.014819336}, {x:0.5204241, y:0.023995537, z:0.05527344}, {x:0.5418108, y:0.050851006, z:0.115356445}, {x:0.56696427, y:0.08482143, z:0.1890625}, {x:0.5931222, y:0.12381418, z:0.27038574}, {x:0.6175223, y:0.16573662, z:0.3533203}, {x:0.6374023, y:0.2084961, z:0.43186036}, {x:0.65, y:0.25, z:0.5}, {x:0.6532226, y:0.28857422, z:0.5531006}, {x:0.6476562, y:0.32421875, z:0.5919922}, {x:0.63455635, y:0.35735214, z:0.61887205}, {x:0.6151786, y:0.38839284, z:0.6359375}, {x:0.59077847, y:0.41775948, z:0.64538574}, {x:0.5626116, y:0.44587052, z:0.64941406}, {x:0.5319336, y:0.47314453, z:0.6502197}, {x:0.5, y:0.5, z:0.65}, {x:0.4680664, y:0.52685547, z:0.65021974}, {x:0.4373884, y:0.5541295, z:0.64941406}, {x:0.40922156, y:0.5822405, z:0.64538574}, {x:0.38482141, y:0.6116072, z:0.63593745}, {x:0.36544365, y:0.64264786, z:0.61887205}, {x:0.35234374, y:0.67578125, z:0.59199214}, {x:0.34677735, y:0.7114258, z:0.5531006}, {x:0.35, y:0.75, z:0.5}, {x:0.36259764, y:0.7915039, z:0.43186036}, {x:0.38247767, y:0.8342634, z:0.35332033}, {x:0.4068778, y:0.87618583, z:0.27038574}, {x:0.43303573, y:0.91517854, z:0.1890625}, {x:0.45818916, y:0.949149, z:0.11535645}, {x:0.47957587, y:0.9760045, z:0.055273443}, {x:0.49443358, y:0.99365234, z:0.014819337}, {x:0.5, y:1.0, z:0.0}], + 13: [{x:0.5, y:0.0, z:0.0}, {x:0.5021751, y:0.0024546464, z:0.005757853}, {x:0.50827104, y:0.0094934665, z:0.02209832}, {x:0.51764417, y:0.020628782, z:0.04762176}, {x:0.52965087, y:0.035372917, z:0.08092856}, {x:0.5436471, y:0.053238183, z:0.12061903}, {x:0.5589895, y:0.07373691, z:0.16529359}, {x:0.57503414, y:0.09638144, z:0.2135526}, {x:0.5911373, y:0.12068406, z:0.2639964}, {x:0.6066551, y:0.14615712, z:0.31522533}, {x:0.62094414, y:0.1723129, z:0.36583978}, {x:0.6333604, y:0.19866377, z:0.41444016}, {x:0.64326024, y:0.22472203, z:0.45962676}, {x:0.65, y:0.25, z:0.5}, {x:0.6530918, y:0.27410755, z:0.53447884}, {x:0.65267247, y:0.29704466, z:0.56325674}, {x:0.6490344, y:0.31890887, z:0.58684564}, {x:0.6424703, y:0.33979782, z:0.60575795}, {x:0.6332725, y:0.35980877, z:0.62050515}, {x:0.6217342, y:0.37903965, z:0.63159996}, {x:0.60814744, y:0.39758763, z:0.6395539}, {x:0.59280515, y:0.41555044, z:0.6448794}, {x:0.57599974, y:0.43302557, z:0.6480883}, {x:0.5580239, y:0.45011052, z:0.6496928}, {x:0.53917027, y:0.4669029, z:0.6502048}, {x:0.51973146, y:0.4835002, z:0.6501365}, {x:0.5, y:0.5, z:0.65}, {x:0.48026854, y:0.5164998, z:0.65013653}, {x:0.46082973, y:0.53309715, z:0.65020484}, {x:0.441976, y:0.54988945, z:0.6496927}, {x:0.42400032, y:0.5669745, z:0.64808834}, {x:0.40719482, y:0.5844495, z:0.6448793}, {x:0.3918526, y:0.60241246, z:0.63955396}, {x:0.37826583, y:0.6209605, z:0.6315999}, {x:0.3667274, y:0.6401912, z:0.6205052}, {x:0.35752976, y:0.6602022, z:0.60575783}, {x:0.3509656, y:0.68109107, z:0.58684564}, {x:0.34732753, y:0.70295537, z:0.56325674}, {x:0.3469081, y:0.7258924, z:0.5344788}, {x:0.35, y:0.75, z:0.5}, {x:0.35673967, y:0.7752779, z:0.4596268}, {x:0.36663958, y:0.8013363, z:0.41444018}, {x:0.37905583, y:0.827687, z:0.36583975}, {x:0.39334485, y:0.85384303, z:0.31522536}, {x:0.40886268, y:0.87931585, z:0.2639963}, {x:0.4249659, y:0.90361863, z:0.21355262}, {x:0.44101048, y:0.92626315, z:0.16529357}, {x:0.45635283, y:0.94676185, z:0.120619036}, {x:0.47034916, y:0.9646271, z:0.080928534}, {x:0.4823558, y:0.9793712, z:0.047621757}, {x:0.491729, y:0.99050653, z:0.022098316}, {x:0.49782497, y:0.99754536, z:0.0057578515}, {x:0.5, y:1.0, z:0.0}] + }; + var forRes = function( res ){ + var verts = spline.computeVertices(res); + assert.equal(verts.length, expected[res].length); + verts.forEach(eachNear(res)); + }; + it('should match the java output', function(){ + forRes(4); + }); + it('should match for 8 points', function(){ + forRes(8); + }); + it('should match for 13 points', function(){ + forRes(13); + }); + }); + function getNVec3D( n ){ var points = []; for(var i=0; i