UNPKG

tuo-verb

Version:

A CAD library for the web

1,592 lines (1,109 loc) 127 kB
var should = require('should') , verb = require('../build/js/verb.js'); // necessary for multi-threading verb.exe.WorkerPool.basePath = process.cwd() + "/build/js/"; // some testing utilities function vecShouldBe( expected, test, tol ){ if (tol === undefined) tol = verb.core.Constants.TOLERANCE; test.length.should.be.equal( expected.length ); for (var i = 0; i < test.length; i++){ test[i].should.be.approximately( expected[i], tol ); } } function last(a){ return a[a.length-1]; } describe("verb.eval.Eval.knotSpanGivenN",function(){ it('returns correct result', function(){ var n = 7 , degree = 2 , knots = [0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 5]; should.equal( 4, verb.eval.Eval.knotSpanGivenN( n, degree, 2.5, knots ) ); should.equal( 3, verb.eval.Eval.knotSpanGivenN( n, degree, 1, knots ) ); should.equal( 3, verb.eval.Eval.knotSpanGivenN( n, degree, 1.5, knots ) ); should.equal( 7, verb.eval.Eval.knotSpanGivenN( n, degree, 4.9, knots ) ); should.equal( 7, verb.eval.Eval.knotSpanGivenN( n, degree, 10, knots ) ); should.equal( 7, verb.eval.Eval.knotSpanGivenN( n, degree, 5, knots ) ); should.equal( 2, verb.eval.Eval.knotSpanGivenN( n, degree, 0, knots ) ); should.equal( 2, verb.eval.Eval.knotSpanGivenN( n, degree, -1, knots ) ); }); }); describe("verb.eval.Eval.knotSpan",function(){ it('returns correct result for degree 2 curve', function(){ var degree = 2 , knots = [0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 5]; should.equal( 4, verb.eval.Eval.knotSpan( degree, 2.5, knots ) ); should.equal( 3, verb.eval.Eval.knotSpan( degree, 1, knots ) ); should.equal( 3, verb.eval.Eval.knotSpan( degree, 1.5, knots ) ); should.equal( 7, verb.eval.Eval.knotSpan( degree, 4.9, knots ) ); should.equal( 7, verb.eval.Eval.knotSpan( degree, 10, knots ) ); // above span should.equal( 7, verb.eval.Eval.knotSpan( degree, 5, knots ) ); // top of span should.equal( 2, verb.eval.Eval.knotSpan( degree, 0, knots ) ); // bottom span }); }); describe("verb.eval.Eval.basisFunctions, basisFunctionsGivenKnotSpanIndex",function(){ it('return correct results', function(){ var degree = 2 , span = 4 , knots = [0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 5]; var N1 = verb.eval.Eval.basisFunctionsGivenKnotSpanIndex( 4, 2.5, degree, knots ); should.equal( 3, N1.length ); should.equal( 0.125, N1[0] ); should.equal( 0.75, N1[1] ); should.equal( 0.125, N1[2] ); var N2 = verb.eval.Eval.basisFunctions( 2.5, degree, knots ); should.equal( 3, N2.length ); should.equal( 0.125, N2[0] ); should.equal( 0.75, N2[1] ); should.equal( 0.125, N2[2] ); }); }); describe("verb.eval.Eval.curvePoint",function(){ it('returns correct result for simple curve', function(){ var degree = 2 , n = 6 , knots = [0, 0, 0, 1, 2, 3, 4, 5, 5, 5] , controlPoints = [ [10, 0], [20, 10], [30, 20], [40, 30], [50, 40], [60, 30], [70, 80]] , crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var p = verb.eval.Eval.curvePointGivenN( n, crv, 2.5); should.equal( p[0], 40 ); should.equal( p[1], 30 ); var p_start = verb.eval.Eval.curvePointGivenN( n, crv, 0); should.equal( p_start[0], 10 ); should.equal( p_start[1], 0 ); var p_end = verb.eval.Eval.curvePointGivenN( n, crv, 5); should.equal( p_end[0], 70 ); should.equal( p_end[1], 80 ); }); }); describe("verb.eval.Eval.curvePointGivenN",function(){ it('returns correct result for simple curve', function(){ var degree = 3 , n = 4 , u = 0 , knots = [0, 0, 0, 0, 1, 1, 1, 1] , controlPoints = [ [10, 0], [20, 10], [30, 20], [50, 50] ] , crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var p = verb.eval.Eval.curvePoint( crv, u); should.equal( p[0], 10 ); should.equal( p[1], 0 ); var p2 = verb.eval.Eval.curvePoint( crv, 1.0); should.equal( p2[0], 50 ); should.equal( p2[1], 50 ); }); }); describe("verb.eval.Eval.areValidRelations",function(){ it('returns correct result for two cases', function(){ should.equal( false, verb.eval.Eval.areValidRelations( 0, 0, 0 ) ); should.equal( true, verb.eval.Eval.areValidRelations( 2, 2, 5 ) ); }); }); describe("verb.eval.Eval.derivativeBasisFunctionsGivenNI",function(){ it('returns correct results', function(){ // This needs to be tested better var degree = 2 , n = 7 , span = 4 , knots = [0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 5]; var N1 = verb.eval.Eval.derivativeBasisFunctionsGivenNI( span, 2.5, degree, n, knots ); // weights should.equal( 0.125, N1[0][0] ); should.equal( 0.75, N1[0][1] ); should.equal( 0.125, N1[0][2] ); // derivatives should.equal( -0.5, N1[1][0] ); should.equal( 1, N1[2][0] ); should.equal( 0, N1[1][1] ); should.equal( -2, N1[2][1] ); should.equal( 0.5, N1[1][2] ); should.equal( 1, N1[2][2] ); // length should.equal( n + 1, N1.length ); should.equal( degree + 1, N1[0].length ); }); }); describe("verb.eval.Eval.curveDerivativesGivenN",function(){ it('returns correct result for simple curve', function(){ var degree = 3 , n = 3 , u = 0 , knots = [0, 0, 0, 0, 1, 1, 1, 1] , controlPoints = [ [10, 0], [20, 10], [30, 20], [50, 50] ] , num_derivs = 2 , crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var p = verb.eval.Eval.curveDerivativesGivenN( n, crv, u, num_derivs ) ; should.equal( p[0][0], 10 ); should.equal( p[0][1], 0 ); should.equal( p[1][0] / p[1][1], 1 ); }); }); describe("verb.eval.Eval.curveDerivatives",function(){ it('returns correct result for simple curve', function(){ // This needs to be tested better var degree = 3 , u = 0 , knots = [0, 0, 0, 0, 1, 1, 1, 1] , controlPoints = [ [10, 0], [20, 10], [30, 20], [50, 50] ] , num_derivs = 2 , crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var p = verb.eval.Eval.curveDerivatives( crv, u, num_derivs ) ; should.equal( p[0][0], 10 ); should.equal( p[0][1], 0 ); should.equal( p[1][0] / p[1][1], 1 ); }); }); describe("verb.eval.Eval.surfacePointGivenNM",function(){ it('returns correct result for simple surface', function(){ // This needs to be tested better var degreeU = 3 , degreeV = 3 , knotsU = [0, 0, 0, 0, 1, 1, 1, 1] , knotsV = [0, 0, 0, 0, 1, 1, 1, 1] , controlPoints = [ [ [0, 0, 50], [10, 0, 0], [20, 0, 0], [30, 0, 0] ], [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] ], [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] ], [ [0, -30, 0], [10, -30, 0], [20, -30, 0], [30, -30, 0] ] ] , surface = new verb.core.NurbsSurfaceData( degreeU, degreeV, knotsU, knotsV, controlPoints ) , n = 3 , m = 3; var p = verb.eval.Eval.surfacePointGivenNM( n, m, surface, 0, 0 ); should.equal( p[0], 0 ); should.equal( p[1], 0 ); should.equal( p[2], 50 ); p = verb.eval.Eval.surfacePointGivenNM( n, m, surface, 1, 1 ); should.equal( p[0], 30 ); should.equal( p[1], -30 ); should.equal( p[2], 0 ); }); }); describe("verb.eval.Eval.surfacePoint",function(){ it('returns correct result for simple surface', function(){ // This needs to be tested better var degreeU = 3 , degreeV = 3 , knotsU = [0, 0, 0, 0, 1, 1, 1, 1] , knotsV = [0, 0, 0, 0, 1, 1, 1, 1] , controlPoints = [ [ [0, 0, 50], [10, 0, 0], [20, 0, 0], [30, 0, 0] ], [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] ], [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] ], [ [0, -30, 0], [10, -30, 0], [20, -30, 0], [30, -30, 0] ] ] , surface = new verb.core.NurbsSurfaceData( degreeU, degreeV, knotsU, knotsV, controlPoints ); var p = verb.eval.Eval.surfacePoint( surface, 0, 0 ); should.equal( p[0], 0 ); should.equal( p[1], 0 ); should.equal( p[2], 50 ); p = verb.eval.Eval.surfacePoint( surface, 1, 1 ); should.equal( p[0], 30 ); should.equal( p[1], -30 ); should.equal( p[2], 0 ); }); it('returns correct result for another simple surface', function(){ var degreeU = 1 , degreeV = 3 , knotsU = [0, 0, 1, 1 ] , knotsV = [0, 0, 0, 0, 1, 1, 1, 1] , controlPoints = [ [ [0, 0, 50], [10, 0, 0], [20, 0, 0], [30, 0, 0] ], [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] ] ] , surface = new verb.core.NurbsSurfaceData( degreeU, degreeV, knotsU, knotsV, controlPoints ); var p = verb.eval.Eval.surfacePoint( surface, 0, 0 ); should.equal( p[0], 0 ); should.equal( p[1], 0 ); should.equal( p[2], 50 ); }); }); describe("verb.eval.Eval.surfaceDerivativesGivenNM",function(){ it('returns correct derivatives for simple surface', function(){ var degreeU = 3 , degreeV = 3 , u = 0.0 , v = 0.0 , knotsU = [0, 0, 0, 0, 1, 1, 1, 1] , knotsV = [0, 0, 0, 0, 1, 1, 1, 1] , controlPoints = [ [ [0, 0, 0], [10, 10, 0], [20, 10, 0], [30, 0, 0] ], [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] ], [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] ], [ [0, -30, 0], [10, -30, 0], [20, -30, 0], [30, -30, 0] ] ] , n = 3 , m = 3 , num_derivatives = 1 , surface = new verb.core.NurbsSurfaceData( degreeU, degreeV, knotsU, knotsV, controlPoints ); var p = verb.eval.Eval.surfaceDerivativesGivenNM( n, m, surface, 0, 0, num_derivatives ); // 0th derivative with respect to u & v should.equal( p[0][0][0], 0 ); should.equal( p[0][0][1], 0 ); should.equal( p[0][0][2], 0 ); // d/du should.equal( p[0][1][0] / p[0][1][0], 1 ); should.equal( p[0][1][2], 0 ); // d/dv should.equal( p[1][0][0] , 0 ); should.equal( p[1][0][1] , -30 ); should.equal( p[1][0][2] , 0 ); // dd/dudv should.equal( p[1][1][0] , 0 ); should.equal( p[1][1][1] , 0 ); should.equal( p[1][1][2] , 0 ); }); }); describe("verb.eval.Eval.surfaceDerivatives",function(){ it('returns correct derivatives for simple surface', function(){ var degreeU = 3 , degreeV = 3 , u = 0.0 , v = 0.0 , knotsU = [0, 0, 0, 0, 1, 1, 1, 1] , knotsV = [0, 0, 0, 0, 1, 1, 1, 1] , controlPoints = [ [ [0, 0, 0], [10, 10, 0], [20, 10, 0], [30, 0, 0] ], [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] ], [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] ], [ [0, -30, 0], [10, -30, 0], [20, -30, 0], [30, -30, 0] ] ] , n = 3 , m = 3 , num_derivatives = 1 , surface = new verb.core.NurbsSurfaceData( degreeU, degreeV, knotsU, knotsV, controlPoints ); var p = verb.eval.Eval.surfaceDerivatives( surface, 0, 0, num_derivatives ); // 0th derivative with respect to u & v should.equal( p[0][0][0], 0 ); should.equal( p[0][0][1], 0 ); should.equal( p[0][0][2], 0 ); // d/du should.equal( p[0][1][0] / p[0][1][0], 1 ); should.equal( p[0][1][2], 0 ); // d/dv should.equal( p[1][0][0] , 0 ); should.equal( p[1][0][1] , -30 ); should.equal( p[1][0][2] , 0 ); // dd/dudv should.equal( p[1][1][0] , 0 ); should.equal( p[1][1][1] , 0 ); should.equal( p[1][1][2] , 0 ); }); }); describe("verb.eval.Eval.homogenize1d",function(){ it('returns correct results', function(){ var weights = [1, 2, 3, 4] , controlPoints = [ [10, 0], [20, 10], [30, 20], [50, 50] ] , homo_controlPoints = verb.eval.Eval.homogenize1d( controlPoints, weights); for (var i = 0; i < controlPoints.length; i++) { should.equal( homo_controlPoints[i][0], weights[i] * controlPoints[i][0] ); should.equal( homo_controlPoints[i][1], weights[i] * controlPoints[i][1] ); should.equal( homo_controlPoints[i][2], weights[i] ); } weights = [1, 2, 3, 4]; controlPoints = [ [10, 0, 4], [20, 10, 3], [30, 20, 0], [50, 50, 10] ]; homo_controlPoints = verb.eval.Eval.homogenize1d( controlPoints, weights); for (var i = 0; i < controlPoints.length; i++) { should.equal( homo_controlPoints[i][0], weights[i] * controlPoints[i][0] ); should.equal( homo_controlPoints[i][1], weights[i] * controlPoints[i][1] ); should.equal( homo_controlPoints[i][2], weights[i] * controlPoints[i][2] ); should.equal( homo_controlPoints[i][3], weights[i] ); } }); }); describe("verb.eval.Eval.homogenize2d",function(){ it('homogenize2d', function(){ var weights = [ [ 1, -2, 3, 5 ], [ 2, 1, 5, 2 ], [ -3, 4, 7, 2 ], [ 1, 6, -2, 12 ] ] , controlPoints = [ [ [0, 0, 0], [10, 10, 0], [20, 10, 0], [30, 0, 0] ], [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] ], [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] ], [ [0, -30, 0], [10, -30, 0], [20, -30, 0], [30, -30, 0] ] ] , homo_controlPoints = verb.eval.Eval.homogenize2d( controlPoints, weights) , j = 0; for (var i = 0; i < controlPoints.length; i++) { for (j = 0; j < controlPoints[i].length; j++) { should.equal( homo_controlPoints[i][j][0], weights[i][j] * controlPoints[i][j][0] ); should.equal( homo_controlPoints[i][j][1], weights[i][j] * controlPoints[i][j][1] ); should.equal( homo_controlPoints[i][j][2], weights[i][j] * controlPoints[i][j][2] ); should.equal( homo_controlPoints[i][j][3], weights[i][j] ); } } }); }); describe("verb.eval.Eval.dehomogenize",function(){ it('returns correct result', function(){ var weights = [ [ 1, -2, 3, 5 ], [ 2, 1, 5, 2 ], [ -3, 4, 7, 2 ], [ 1, 6, -2, 12 ] ] , controlPoints = [ [ [0, 0, 0], [10, 10, 0], [20, 10, 0], [30, 0, 0] ], [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] ], [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] ], [ [0, -30, 0], [10, -30, 0], [20, -30, 0], [30, -30, 0] ] ] , homo_controlPoints = verb.eval.Eval.homogenize2d( controlPoints, weights) , j = 0 , dehomo_pt = []; for (var i = 0; i < controlPoints.length; i++) { for (j = 0; j < controlPoints[i].length; j++) { dehomo_pt = verb.eval.Eval.dehomogenize( homo_controlPoints[i][j] ); should.equal( dehomo_pt.length, controlPoints[i][j].length ); should.equal( dehomo_pt[0], controlPoints[i][j][0] ); should.equal( dehomo_pt[1], controlPoints[i][j][1] ); should.equal( dehomo_pt[2], controlPoints[i][j][2] ); } } }); }); describe("verb.eval.Eval.rationalCurvePoint",function(){ it('returns correct result for quarter circle', function(){ // this represents a single quarter arc, using a rational bezier curve var degree = 2 , knots = [0, 0, 0, 1, 1, 1 ] , controlPoints = [ [1, 0, 1], [1,1,1], [0,2,2] ] , crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var p = verb.eval.Eval.rationalCurvePoint( crv, 0); should.equal( p[0], 1 ); should.equal( p[1], 0 ); p = verb.eval.Eval.rationalCurvePoint( crv, 0.5); should.equal( p[0], 0.6 ); should.equal( p[1], 0.8 ); p = verb.eval.Eval.rationalCurvePoint( crv, 1); should.equal( p[0], 0 ); should.equal( p[1], 1 ); }); }); describe("verb.eval.Eval.rationalSurfacePoint",function(){ it('returns correct result for cylinder patch', function(){ // quarter cylinder patch var degreeU = 1 , degreeV = 2 , knotsU = [0, 0, 1, 1 ] , knotsV = [0, 0, 0, 1, 1, 1 ] , controlPoints = [ [ [1, 1, 0, 1], [1, 1, 1, 1], [2, 0, 2, 2] ], [ [-1, 1, 0, 1], [-1, 1, 1, 1], [-2, 0, 2, 2] ] ] , surface = new verb.core.NurbsSurfaceData( degreeU, degreeV, knotsU, knotsV, controlPoints ); var p = verb.eval.Eval.rationalSurfacePoint( surface, 0, 0 ); should.equal( p[0], 1 ); should.equal( p[1], 1 ); should.equal( p[2], 0 ); p = verb.eval.Eval.rationalSurfacePoint( surface, 0.5, 0.5 ); should.equal( p[0], 0 ); should.equal( p[1], 0.6 ); should.equal( p[2], 0.8 ); p = verb.eval.Eval.rationalSurfacePoint( surface, 1, 1 ); should.equal( p[0], -1 ); should.equal( p[1], 0 ); should.equal( p[2], 1 ); }); }); describe("verb.eval.Eval.rationalCurveDerivatives",function(){ // this represents a single quarter arc, using a rational bezier curve var degree = 2 , knots = [0, 0, 0, 1, 1, 1 ] , controlPoints = [ [1,0,1], [1,1,1], [0,2,2] ] , crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); it('returns expected results with 2 derivatives', function(){ var num_derivatives = 2; var p = verb.eval.Eval.rationalCurveDerivatives( crv, 0, num_derivatives); should.equal( p[0][0], 1 ); should.equal( p[0][1], 0 ); should.equal( p[1][0], 0 ); should.equal( p[1][1], 2 ); should.equal( p[2][0], -4 ); should.equal( p[2][1], 0 ); p = verb.eval.Eval.rationalCurveDerivatives( crv, 1, num_derivatives); should.equal( p[0][0], 0 ); should.equal( p[0][1], 1 ); should.equal( p[1][0], -1 ); should.equal( p[1][1], 0 ); should.equal( p[2][0], 1 ); should.equal( p[2][1], -1 ); }); it('returns expected results with 3 derivatives', function(){ var num_derivatives = 3; var p = verb.eval.Eval.rationalCurveDerivatives( crv, 0, num_derivatives); should.equal( p[3][0], 0 ); should.equal( p[3][1], -12 ); p = verb.eval.Eval.rationalCurveDerivatives( crv, 1, num_derivatives); should.equal( p[3][0], 0 ); should.equal( p[3][1], 3 ); }); }); describe("verb.eval.Eval.rationalSurfaceDerivatives",function(){ // quarter cylinder patch, axis aligned with x axis, radius: 1 var degreeU = 1 , degreeV = 2 , knotsU = [0, 0, 1, 1 ] , knotsV = [0, 0, 0, 1, 1, 1 ] , controlPoints = [ [ [1, 1, 0, 1], [1, 1, 1, 1], [2, 0, 2, 2] ], [ [-1, 1, 0, 1], [-1, 1, 1, 1], [-2, 0, 2, 2] ] ] , surface = new verb.core.NurbsSurfaceData( degreeU, degreeV, knotsU, knotsV, controlPoints ); it('returns expected results with 1 derivative', function(){ var num_derivatives = 1; var p = verb.eval.Eval.rationalSurfaceDerivatives( surface, 0, 0, num_derivatives ); should.equal( p[0][0][0], 1 ); should.equal( p[0][0][1], 1 ); should.equal( p[0][0][2], 0 ); should.equal( p[0][1][0], 0 ); should.equal( p[0][1][1], 0 ); should.equal( p[0][1][2], 2 ); should.equal( p[1][0][0], -2 ); should.equal( p[1][0][1], 0 ); should.equal( p[1][0][2], 0 ); p = verb.eval.Eval.rationalSurfaceDerivatives( surface, 1, 1, num_derivatives); should.equal( p[0][0][0], -1 ); should.equal( p[0][0][1], 0 ); should.equal( p[0][0][2], 1 ); should.equal( p[0][1][0], 0 ); should.equal( p[0][1][1], -1 ); should.equal( p[0][1][2], 0 ); should.equal( p[1][0][0], -2 ); should.equal( p[1][0][1], 0 ); should.equal( p[1][0][2], 0 ); }); it('returns expected results with 2 derivatives', function(){ var num_derivatives = 2; var p = verb.eval.Eval.rationalSurfaceDerivatives( surface, 0, 0, num_derivatives ); should.equal( p[0][2][0], 0 ); should.equal( p[0][2][1], -4 ); should.equal( p[0][2][2], 0 ); should.equal( p[1][1][0], 0 ); should.equal( p[1][1][1], 0 ); should.equal( p[1][1][2], 0 ); should.equal( p[2][0][0], 0 ); should.equal( p[2][0][1], 0 ); should.equal( p[2][0][2], 0 ); p = verb.eval.Eval.rationalSurfaceDerivatives( surface, 1, 1, num_derivatives); should.equal( p[0][2][0], 0 ); should.equal( p[0][2][1], 1 ); should.equal( p[0][2][2], -1 ); should.equal( p[1][1][0], 0 ); should.equal( p[1][1][1], 0 ); should.equal( p[1][1][2], 0 ); should.equal( p[2][0][0], 0 ); should.equal( p[2][0][1], 0 ); should.equal( p[2][0][2], 0 ); }); }); describe("verb.eval.Eval.rationalCurvePoint",function(){ it('returns correct results for a line', function(){ var degree = 1 , knots = [0, 0, 1, 1] , controlPoints = [ [0, 0, 0, 1], [10, 0, 0, 1] ] , weights = [1, 1] , u1 = 0.0 , u2 = 0.5 , u3 = 1.0 , crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var p1 = verb.eval.Eval.rationalCurvePoint( crv, u1); var p2 = verb.eval.Eval.rationalCurvePoint( crv, u2); var p3 = verb.eval.Eval.rationalCurvePoint( crv, u3); should.equal(p1[0], 0); should.equal(p2[0], 5); should.equal(p3[0], 10); }); }); describe("verb.eval.Modify.curveKnotInsert",function(){ it('returns expected results when inserting 1 knot in the middle of a non-rational, cubic b-spline', function(){ var degree = 3 , u = 2.5 , knots = [ 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 5, 5 ] , r = 1; var controlPoints = []; for (var i = 0; i < 8; i++) controlPoints.push([i, 0, 0]); var crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var after = verb.eval.Modify.curveKnotInsert( crv, u, r ); after.controlPoints.forEach(function(cp){ should.exist(cp); }); after.knots.forEach(function(cp){ should.exist(cp); }); should.equal(knots.length + r, after.knots.length); should.equal(controlPoints.length + r, after.controlPoints.length); after.controlPoints[3][0].should.be.approximately( 2.8333333333, verb.core.Constants.TOLERANCE ); after.controlPoints[4][0].should.be.approximately( 3.5, verb.core.Constants.TOLERANCE ); after.controlPoints[5][0].should.be.approximately( 4.1666666666, verb.core.Constants.TOLERANCE ); var p0 = verb.eval.Eval.curvePoint( crv, 2.5); var p1 = verb.eval.Eval.curvePoint( after, 2.5); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); }); it('returns expected results when inserting 3 knots at the middle of a non-rational, cubic b-spline', function(){ var degree = 3 , u = 2.5 , knots = [ 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 5, 5 ] , r = 3; var controlPoints = []; for (var i = 0; i < 8; i++) controlPoints.push([i, 0, 0]); var crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var after = verb.eval.Modify.curveKnotInsert( crv, u, r ); after.controlPoints.forEach(function(cp){ should.exist(cp); }); after.knots.forEach(function(cp){ should.exist(cp); }); should.equal(knots.length + r, after.knots.length); should.equal(controlPoints.length + r, after.controlPoints.length); var p0 = verb.eval.Eval.curvePoint( crv, 2.5); var p1 = verb.eval.Eval.curvePoint( after, 2.5); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); }); it('returns expected results when inserting 1 knots at the beginning of a non-rational, cubic b-spline', function(){ var degree = 3 , u = 0.5 , knots = [ 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 5, 5 ] , r = 1; var controlPoints = []; for (var i = 0; i < 8; i++) controlPoints.push([i, 0, 0]); var crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var after = verb.eval.Modify.curveKnotInsert( crv, u, r ); after.controlPoints.forEach(function(cp){ should.exist(cp); }); after.knots.forEach(function(cp){ should.exist(cp); }); should.equal(knots.length + r, after.knots.length); should.equal(controlPoints.length + r, after.controlPoints.length); var p0 = verb.eval.Eval.curvePoint( crv, 2.5); var p1 = verb.eval.Eval.curvePoint( after, 2.5); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); }); it('returns expected results when inserting 1 knot at the middle of a non-rational, linear b-spline', function(){ var degree = 1 , u = 0.5 , knots = [ 0, 0, 1, 2, 3, 4, 5, 5 ] , r = 1; var controlPoints = []; for (var i = 0; i < 6; i++) controlPoints.push([i, 0, 0]); var crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var after = verb.eval.Modify.curveKnotInsert( crv, u, r ); after.controlPoints.forEach(function(cp){ should.exist(cp); }); after.knots.forEach(function(cp){ should.exist(cp); }); should.equal(knots.length + r, after.knots.length); should.equal(controlPoints.length + r, after.controlPoints.length); var p0 = verb.eval.Eval.curvePoint( crv, 2.5); var p1 = verb.eval.Eval.curvePoint( after, 2.5); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); }); }); describe("verb.eval.Eval.curveKnotRefine",function(){ function cubicInsert(u, r){ var degree = 3 , knots = [ 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 5, 5 ] , new_knots = []; for (var i = 0; i < r; i++){ new_knots.push(u); } var controlPoints = []; for (var i = 0; i < 8; i++) controlPoints.push([i, 0, 0]); var crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var after = verb.eval.Modify.curveKnotRefine( crv, new_knots ); after.controlPoints.forEach(function(cp){ should.exist(cp); }); after.knots.forEach(function(cp){ should.exist(cp); }); should.equal(knots.length + r, after.knots.length); should.equal(controlPoints.length + r, after.controlPoints.length); var p0 = verb.eval.Eval.curvePoint( crv, 2.5); var p1 = verb.eval.Eval.curvePoint( after, 2.5); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); } it('returns expected results when inserting multiple knots in the middle of a non-rational, cubic b-spline', function(){ cubicInsert(2.5, 1); cubicInsert(2.5, 2); cubicInsert(2.5, 3); cubicInsert(2.5, 4); cubicInsert(0.5, 1); cubicInsert(0.5, 2); cubicInsert(0.5, 3); cubicInsert(0.5, 4); cubicInsert(3, 1); cubicInsert(3, 2); }); }); describe("verb.eval.Divide.curveSplit",function(){ function cubicSplit(u){ var degree = 3 , knots = [ 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 5, 5 ]; var controlPoints = []; for (var i = 0; i < 8; i++) controlPoints.push([i, 0, 0, 1]); var crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var after = verb.eval.Divide.curveSplit( crv, u ); for (var i = 0; i < degree + 1; i++ ){ var d = after[0].knots.length - (degree+1); after[0].knots[d+i].should.be.approximately(u, verb.core.Constants.TOLERANCE); } for (var i = 0; i < degree + 1; i++){ var d = 0; after[1].knots[d+i].should.be.approximately(u, verb.core.Constants.TOLERANCE); } // a point evaluated on each curve is the same var p0 = verb.eval.Eval.curvePoint( after[0], after[0].knots[ after[0].knots.length-1] ); var p1 = verb.eval.Eval.curvePoint( after[1], after[1].knots[ 0] ); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); } it('returns expected results when splitting a non-rational, cubic b-spline', function(){ cubicSplit( 0.5 ); cubicSplit( 3.5 ); }); }); describe("verb.eval.Analyze.knotMultiplicities",function(){ it('is correct for a basic example', function(){ var res = verb.eval.Analyze.knotMultiplicities( [ 0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 3.3] ); res.length.should.be.equal( 5 ); res[0].knot.should.be.equal( 0 ); res[0].mult.should.be.equal( 4 ); res[1].knot.should.be.equal( 1 ); res[1].mult.should.be.equal( 2 ); res[2].knot.should.be.equal( 2 ); res[2].mult.should.be.equal( 3 ); res[3].knot.should.be.equal( 3 ); res[3].mult.should.be.equal( 1 ); res[4].knot.should.be.equal( 3.3 ); res[4].mult.should.be.equal( 1 ); }); }); describe("verb.eval.Modify.decomposeCurveIntoBeziers",function(){ it('is correct for a basic example', function(){ var degree = 3 , knots = [ 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 5, 5 ]; var controlPoints = []; for (var i = 0; i < 8; i++) { controlPoints.push([i, 0, 0]); } var crv = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var res = verb.eval.Modify.decomposeCurveIntoBeziers( crv ); res.length.should.be.equal( 5 ); res.forEach(function(x){ var u0 = x.knots[0]; var pt0 = verb.eval.Eval.curvePoint( x, u0); var pt1 = verb.eval.Eval.curvePoint( crv, u0); ( verb.core.Vec.norm(verb.core.Vec.sub(pt0, pt1))).should.be.approximately(0, verb.core.Constants.TOLERANCE ); }); }); }); describe("verb.core.Mat.transpose",function(){ it('is correct for a basic example', function(){ var a = [ [6,5,4], [1,2,3] ]; verb.core.Mat.transpose(a).should.eql( [[6,1], [5,2], [4,3]]) }); it('is correct for empty array', function(){ verb.core.Mat.transpose([]).should.eql( [] ); }); }); describe("verb.eval.Modify.surfaceKnotRefine",function(){ var degree = 3 , knotsV = [0, 0, 0, 0, 0.333, 0.666, 1, 1, 1, 1] , knotsU = [0, 0, 0, 0, 0.5, 1, 1, 1, 1] , controlPoints = [ [ [0, 0, -10], [10, 0, 0], [20, 0, 0], [30, 0, 0] , [40, 0, 0], [50, 0, 0] ], [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] , [40, -10, 0], [50, -10, 0] ], [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] , [40, -20, -2], [50, -20, 0] ], [ [0, -30, 0], [10, -30, 0], [20, -30, -23], [30, -30, 0] , [40, -30, 0], [50, -30, 0] ], [ [0, -40, 0], [10, -40, 0], [20, -40, 0], [30, -40, 4] , [40, -40, -20], [50, -40, 0] ] ] , surface = new verb.core.NurbsSurfaceData( degree, degree, knotsU, knotsV, controlPoints ); it('can add knots into a surface in the u direction', function(){ var r = 1; var u = 0.2; var new_knots = []; for (var i = 0; i < r; i++){ new_knots.push(u); } var res = verb.eval.Modify.surfaceKnotRefine( surface, new_knots, false ); res.controlPoints.forEach(function(cp){ should.exist(cp); }); res.knotsU.forEach(function(cp){ should.exist(cp); }); res.knotsV.forEach(function(cp){ should.exist(cp); }); should.equal(knotsU.length + r, res.knotsU.length); should.equal(controlPoints.length + r, res.controlPoints.length); var p0 = verb.eval.Eval.surfacePoint( surface, 0.5, 0.25 ); var p1 = verb.eval.Eval.surfacePoint( res, 0.5, 0.25); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); }); it('can add knots into a surface in the v direction', function(){ var r = 1; var u = 0.2; var new_knots = []; for (var i = 0; i < r; i++){ new_knots.push(u); } var res = verb.eval.Modify.surfaceKnotRefine( surface, new_knots, true ); res.controlPoints.forEach(function(cp){ should.exist(cp); }); res.knotsU.forEach(function(cp){ should.exist(cp); }); res.knotsV.forEach(function(cp){ should.exist(cp); }); should.equal(knotsV.length + r, res.knotsV.length); should.equal(controlPoints[0].length + r, res.controlPoints[0].length); var p0 = verb.eval.Eval.surfacePoint( surface, 0.5, 0.25 ); var p1 = verb.eval.Eval.surfacePoint( res, 0.5, 0.25); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); }); }); describe("verb.eval.Divide.surfaceSplit", function(){ var degree = 3 , knotsV = [0, 0, 0, 0, 0.333, 0.666, 1, 1, 1, 1] , knotsU = [0, 0, 0, 0, 0.5, 1, 1, 1, 1] , controlPoints = [ [ [0, 0, -10], [10, 0, 0], [20, 0, 0], [30, 0, 0] , [40, 0, 0], [50, 0, 0] ], [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] , [40, -10, 0], [50, -10, 0] ], [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] , [40, -20, -2], [50, -20, 0] ], [ [0, -30, 0], [10, -30, 0], [20, -30, -23], [30, -30, 0] , [40, -30, 0], [50, -30, 0] ], [ [0, -40, 0], [10, -40, 0], [20, -40, 0], [30, -40, 4] , [40, -40, -20], [50, -40, 0] ] ] , surface = new verb.core.NurbsSurfaceData( degree, degree, knotsU, knotsV, controlPoints ); it('can split a surface in the u direction', function(){ var u = 0.2; var res = verb.eval.Divide.surfaceSplit( surface, u, false ); res[0].controlPoints.forEach(function(cp){ should.exist(cp); }); res[0].knotsU.forEach(function(cp){ should.exist(cp); }); res[0].knotsV.forEach(function(cp){ should.exist(cp); }); should.exist( res[0].degreeU ); should.exist( res[0].degreeV ); res[1].controlPoints.forEach(function(cp){ should.exist(cp); }); res[1].knotsU.forEach(function(cp){ should.exist(cp); }); res[1].knotsV.forEach(function(cp){ should.exist(cp); }); should.exist( res[1].degreeU ); should.exist( res[1].degreeV ); var p0 = verb.eval.Eval.surfacePoint( surface, 0.1, 0.1 ); var p1 = verb.eval.Eval.surfacePoint( res[0], 0.1, 0.1); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); p0 = verb.eval.Eval.surfacePoint( surface, 0.8, 0.8 ); p1 = verb.eval.Eval.surfacePoint( res[1], 0.8, 0.8); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); }); it('can split a surface in the v direction', function(){ var u = 0.2; var res = verb.eval.Divide.surfaceSplit( surface, u, true ); res[0].controlPoints.forEach(function(cp){ should.exist(cp); }); res[0].knotsU.forEach(function(cp){ should.exist(cp); }); res[0].knotsV.forEach(function(cp){ should.exist(cp); }); should.exist( res[0].degreeU ); should.exist( res[0].degreeV ); res[1].controlPoints.forEach(function(cp){ should.exist(cp); }); res[1].knotsU.forEach(function(cp){ should.exist(cp); }); res[1].knotsV.forEach(function(cp){ should.exist(cp); }); should.exist( res[1].degreeU ); should.exist( res[1].degreeV ); var p0 = verb.eval.Eval.surfacePoint( surface, 0.1, 0.1 ); var p1 = verb.eval.Eval.surfacePoint( res[0], 0.1, 0.1); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); p0 = verb.eval.Eval.surfacePoint( surface, 0.8, 0.8 ); p1 = verb.eval.Eval.surfacePoint( res[1], 0.8, 0.8); p0[0].should.be.approximately(p1[0], verb.core.Constants.TOLERANCE); p0[1].should.be.approximately(p1[1], verb.core.Constants.TOLERANCE); p0[2].should.be.approximately(p1[2], verb.core.Constants.TOLERANCE); }); }); describe("verb.eval.Eval.rationalCurveRegularSample",function(){ it('should return 10 samples when asked to', function(){ var degree = 2 , knots = [0, 0, 0, 1, 1, 1 ] , controlPoints = [ [1, 0, 0, 1], [1, 1, 0, 1], [0, 2, 0, 2] ] , numSamples = 10 , curve = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var p = verb.eval.Tess.rationalCurveRegularSample( curve, numSamples); should.equal(p.length, 10); p.map( function(e){ e.length.should.be.equal(3); }); }); }); describe("verb.eval.Eval.threePointsAreFlat",function(){ it('should identify flat line by returning true', function(){ // this represents a single quarter arc, using a rational bezier curve var p1 = [0,0,0], p2 = [0,2,0], p3 = [0,4,0]; should.equal(true, verb.core.Trig.threePointsAreFlat(p1,p2,p3,1e-5)); }); }); describe("verb.eval.Tess.rationalCurveAdaptiveSample",function(){ it('returns two end points for a line', function(){ var degree = 1 , knots = [0, 0, 1, 1] , controlPoints = [ [0, 0, 0, 1], [10, 0, 0, 1] ] , curve = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var p = verb.eval.Tess.rationalCurveAdaptiveSample( curve, 1e-5); should.equal(p[0][0], 0); should.equal(p[1][0], 10); p.map( function(e){ e.length.should.be.equal(3); }); }); it('returns all the control points for a degree 1 curve', function(){ var degree = 1 , knots = [0, 0, 0.25, 0.5, 0.75, 1, 1] , controlPoints = [ [0, 0, 0, 1], [10, 10, 0, 1], [14, 20, 0, 1], [10, 32, 4, 1], [12, 16, 22, 1]] , curve = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var p = verb.eval.Tess.rationalCurveAdaptiveSample( curve, 1e-5); p.should.be.instanceof(Array).and.have.lengthOf(5); p[0].should.be.instanceof(Array).and.have.lengthOf(3); p[0].should.eql([0,0,0]); p[4].should.eql([12,16,22]); p.map( function(e){ e.length.should.be.equal(3); }); }); it('makes more points for an arc', function(){ var degree = 2 , knots = [0, 0, 0, 1, 1, 1 ] , v = Math.sqrt(2) / 2 , controlPoints = [ [1, 0, 0, 1], [v, v, 0, v], [0, 1, 0, 1] ] , curve = new verb.core.NurbsCurveData( degree, knots, controlPoints ); var p = verb.eval.Tess.rationalCurveAdaptiveSample( curve, 1e-8, true); var p2 = verb.eval.Tess.rationalCurveAdaptiveSample( curve, 1e-4, true); var prev = - 1e-8; for (var i = 0; i < p.length; i++){ p[i][0].should.be.above(prev); p[i][0].should.be.within(-1e-8, 1 + 1e-8); prev = p[i][0]; } p.should.be.instanceof(Array).and.not.have.lengthOf(0); p2.should.be.instanceof(Array).and.not.have.lengthOf(0); p.should.be.instanceof(Array).and.not.have.lengthOf(p2.length); should.equal(p[p.length-1][0], 1.0); should.equal(p2[p2.length-1][0], 1.0); p.map( function(e){ e.length.should.be.equal(4); }); p2.map( function(e){ e.length.should.be.equal(4); }); }); }); describe("verb.eval.Tess.rationalSurfaceAdaptive",function(){ function getComplexSurface(){ var degree = 3 , knots = [0, 0, 0, 0, 0.333, 0.666, 1, 1, 1, 1] , pts = [ [ [0, 0, -10], [10, 0, 0], [20, 0, 0], [30, 0, 0] , [40, 0, 0], [50, 0, 0] ], [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] , [40, -10, 0], [50, -10, 0] ], [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] , [40, -20, -2], [50, -20, 0] ], [ [0, -30, 0], [10, -30, 0], [20, -30, -23], [30, -30, 0] , [40, -30, 0], [50, -30, 0] ], [ [0, -40, 0], [10, -40, 0], [20, -40, 0], [30, -40, 4] , [40, -40, -20], [50, -40, 0] ], [ [0, -50, 12], [10, -50, 0], [20, -50, 0], [30, -50, 0] , [50, -50, 0], [50, -50, -15] ], ] , wts = [ [ 1, 1, 1, 1, 1, 1], [ 1, 1, 1, 1, 1, 1], [ 1, 1, 1, 1, 1, 1], [ 1, 1, 1, 1, 1, 1], [ 1, 1, 1, 1, 1, 1], [ 1, 1, 1, 1, 1, 1] ]; pts = verb.eval.Eval.homogenize2d(pts, wts); var srfObj = { degreeU : degree, degreeV : degree, knotsU : knots, knotsV : knots, controlPoints : pts }; return srfObj; } it('produces a mesh from a divided surface', function(){ var srf = getComplexSurface(); var mesh = verb.eval.Tess.rationalSurfaceAdaptive( srf, { minDivsU: 1, minDivsV: 4 } ); mesh.faces.length.should.be.greaterThan( 8 ); mesh.points.forEach(function(x){ x.length.should.be.equal( 3 ); }) mesh.points.length.should.be.equal( mesh.normals.length ); mesh.uvs.length.should.be.equal( mesh.normals.length ); }); }); describe("verb.eval.Make.ellipseArc",function(){ it('returns correct result for unit arc from 0 to 90 deg', function(){ var center = [0,0,0] , rx = 5 , ry = 1 , x = [rx,0,0] , y = [0,ry,0] , start = 0 , end = Math.PI/2; var ellipse = verb.eval.Make.ellipseArc(center, x, y, start, end); // the typical parametric rep of an ellipse var xmid = rx * Math.cos( Math.PI / 4 ) , ymid = ry * Math.sin( Math.PI / 4 ); var p = verb.eval.Eval.rationalCurvePoint( ellipse, 0.5); p[0].should.be.approximately( xmid, verb.core.Constants.EPSILON ); p[1].should.be.approximately( ymid, verb.core.Constants.EPSILON ); p[2].should.be.approximately( 0, verb.core.Constants.EPSILON ); p = verb.eval.Eval.rationalCurvePoint( ellipse, 1); p[0].should.be.approximately( 0, verb.core.Constants.EPSILON ); p[1].should.be.approximately( ry, verb.core.Constants.EPSILON ); p[2].should.be.approximately( 0, verb.core.Constants.EPSILON ); p = verb.eval.Eval.rationalCurvePoint( ellipse, 0); p[0].should.be.approximately( rx, verb.core.Constants.EPSILON ); p[1].should.be.approximately( 0, verb.core.Constants.EPSILON ); p[2].should.be.approximately( 0, verb.core.Constants.EPSILON ); }); it('returns correct result for unit arc from 0 to 90 deg', function(){ var center = [0,0,0] , rx = 5 , ry = 1 , x = [rx,0,0] , y = [0,ry,0] , start = 0 , end = Math.PI / 2; var arc = verb.eval.Make.ellipseArc(center, x, y, start, end); var p = verb.eval.Eval.rationalCurvePoint( arc, 1); p[0].should.be.approximately( 0, verb.core.Constants.EPSILON ); p[1].should.be.approximately( ry, verb.core.Constants.EPSILON ); p[2].should.be.approximately( 0, verb.core.Constants.EPSILON ); }); it('returns correct result for unit arc from 45 to 135 deg', function(){ var center = [0,0,0] , rx = 1 , ry = 10 , x = [rx,0,0] , y = [0,ry,0] , start = Math.PI/4 , end = 3 * Math.PI/4; var arc = verb.eval.Make.ellipseArc(center, x, y, start, end); var p = verb.eval.Eval.rationalCurvePoint( arc, 1); // the typical parametric rep of an ellipse var xmid = rx * Math.cos( 3 * Math.PI / 4 ) , ymid = ry * Math.sin( 3 * Math.PI / 4 ); p[0].should.be.approximately( xmid, verb.core.Constants.EPSILON ); p[1].should.be.approximately( ymid, verb.core.Constants.EPSILON ); p[2].should.be.approximately( 0, verb.core.Constants.EPSILON ); }); it('returns correct result for complete ellipse', function(){ var center = [0,0,0] , rx = 1 , ry = 10 , x = [rx,0,0] , y = [0,ry,0] , start = 0 , end = Math.PI * 2; var ellipse = verb.eval.Make.ellipseArc(center, x, y, start, end); // the typical parametric rep of an ellipse var xmid = rx * Math.cos( Math.PI / 4 ) , ymid = ry * Math.sin( Math.PI / 4 ); var p = verb.eval.Eval.rationalCurvePoint( ellipse, 0.125); p[0].should.be.approximately( xmid, verb.core.Constants.EPSILON ); p[1].should.be.approximately( ymid, verb.core.Constants.EPSILON ); p[2].should.be.approximately( 0, verb.core.Constants.EPSILON ); p = verb.eval.Eval.rationalCurvePoint( ellipse, 0.25); p[0].should.be.approximately( 0, verb.core.Constants.EPSILON ); p[1].should.be.approximately( ry, verb.core.Constants.EPSILON ); p[2].should.be.approximately( 0, verb.core.Constants.EPSILON ); p = verb.eval.Eval.rationalCurvePoint( ellipse, 0.5); p[0].should.be.approximately( -rx, verb.core.Constants.EPSILON ); p[1].should.be.approximately( 0, verb.core.Constants.EPSILON ); p[2].should.be.approximately( 0, verb.core.Constants.EPSILON ); p = verb.eval.Eval.rationalCurvePoint( ellipse, 0); p[0].should.be.approximately( rx, verb.core.Constants.EPSILON ); p[1].should.be.approximately( 0, verb.core.Constants.EPSILON ); p[2].should.be.approximately( 0, verb.core.Constants.EPSILON ); }); }); describe("verb.eval.Make.extrudedSurface",function(){ it('can extrude a line into a plane', function(){ var axis = [0,0,1] , length = 5 , prof_degree = 1 , prof_ctrl_pts = [[0,1,0,1], [1,0,0,1]] , prof_knots = [0,0,1,1] , profile = new verb.core.NurbsCurveData( prof_degree, prof_knots, prof_ctrl_pts ); var comps = verb.eval.Make.extrudedSurface(axis, length, profile); // the first row are the profile control pts should.equal( 0, comps.controlPoints[2][0][0] ); should.equal( 1, comps.controlPoints[2][0][1] ); should.equal( 0, comps.controlPoints[2][0][2] ); should.equal( 1, comps.controlPoints[2][1][0] ); should.equal( 0, comps.controlPoints[2][1][1] ); should.equal( 0, comps.controlPoints[2][1][2] ); // sample at the center var p = verb.eval.Eval.rationalSurfacePoint( comps, 0.5, 0.5); should.equal( Math.abs( 0.5 - p[0]) < verb.core.Constants.EPSILON, true ); should.equal( Math.abs( 0.5 - p[1]) < verb.core.Constants.EPSILON, true ); should.equal( Math.abs( 2.5 - p[2]) < verb.core.Constants.EPSILON, true ); }); it('can extrude a 90 deg quadratic arc bezier curve', function(){ var axis = [0,0,1] , length = 5 , prof_degree = 2 , prof_ctrl_pts = [[0,1,0], [1,1,0], [1,0,0]] , prof_knots = [0,0,0,1,1,1] , prof_weights = [1, Math.sqrt(2) / 2, 1] , profile = new verb.core.NurbsCurveData( prof_degree, prof_knots, verb.eval.Eval.homogenize1d(prof_ctrl_pts, prof_weights) ); var comps = verb.eval.Make.extrudedSurface(axis, length, profile); // the first row are the profile control pts should.equal( 0, comps.controlPoints[2][0][0] ); should.equal( 1, comps.controlPoints[2][0][1] ); should.equal( 0, comps.controlPoints[2][0][2] ); should.equal( 1, comps.controlPoints[2][0][3] ); should.equal( Math.sqrt(2) / 2, comps.controlPoints[2][1][0] ); should.equal( Math.sqrt(2) / 2, comps.controlPoints[2][1][1] ); should.equal( 0, comps.controlPoints[2][1][2] ); should.equal( Math.sqrt(2) / 2, comps.controlPoints[2][1][3] ); should.equal( 1, comps.controlPoints[2][2][0] ); should.equal( 0, comps.controlPoints[2][2][1] ); should.equal( 0, comps.controlPoints[2][2][2] ); should.equal( 1, comps.controlPoints[2][2][3] ); // sample at the center var p = verb.eval.Eval.rationalSurfacePoint( comps, 0.5, 0.5); should.equal( Math.abs( Math.sqrt(2)/2 - p[0]) < verb.core.Constants.EPSILON, true ); should.equal( Math.abs( Math.sqrt(2)/2 - p[1]) < verb.core.Constants.EPSILON, true ); should.equal( Math.abs( 2.5 - p[2]) < verb.core.Constants.EPSILON, true ); }); }); describe("verb.eval.Make.arc",function(){ it('returns correct result for unit arc from 0 to 90 deg', function(){ var center = [0,0,0] , x = [1,0,0] , y = [0,1,0] , r = 1 , start = 0 , end = Math.PI/2; var arc = verb.eval.Make.arc(center, x, y, 1, start, end); var p = verb.eval.Eval.rationalCurvePoint( arc, 0.5); should.equal( Math.abs( p[0] - Math.sqrt(2)/2 ) < verb.core.Constants.EPSILON, true ); should.equal( Math.abs( p[1] - Math.sqrt(2)/2 ) < verb.core.Constants.EPSILON, true ); should.equal( p[2], 0 ); }); it('returns correct result for unit arc from 0 to 45 deg', function(){ var center = [0,0,0] , x = [1,0,0] , y = [0,1,0] , r = 1 , start = 0 , end = Math.PI/4; var arc = verb.eval.Make.arc(center, x, y, 1, start, end); var p = verb.eval.Eval.rationalCurvePoint( arc, 1); should.equal( Math.abs( p[0] - Math.sqrt(2)/2 ) < verb.core.Constants.EPSILON, true ); should.equal( Math.abs( p[1] - Math.sqrt(2)/2 ) < verb.core.Constants.EPSILON, true ); should.equal( p[2], 0 ); }); it('returns correct result for unit arc from 45 to 135 deg', function(){ var center = [0,0,0] , x = [1,0,0] , y = [0,1,0] , r = 1 , start = Math.PI/4 , end = 3 * Math.PI/4; var arc = verb.eval.Make.arc(center, x, y, 1, start, end); var p = verb.eval.Eval.rationalCurvePoint( arc, 0.5); should.equal( Math.abs( p[0] ) < verb.core.Constants.EPSILON, true ); should.equal( Math.abs( p[1] - 1 ) < verb.core.Constants.EPSILON, true ); should.equal( p[2], 0 ); }); it('returns correct result for unit circle', function(){ var center = [0,0,0] , x = [1,0,0] , y = [0,1,0] , r = 5 , start = 0 , end = Math.PI; var arc = verb.eval.Make.arc(center, x, y, r, start, end); var p = verb.eval.Eval.rationalCurvePoint( arc, 0.5); p[0].should.be.approximately( 0, verb.core.Constants.EPSILON ); p[1].should.be.approximately( 5 , verb.core.Constants.EPSILON); p[2].should.be.approximately( 0, verb.core.Constants.EPSILON ); }); it('returns correct result for unit circle', function(){ var center = [0,0,0] , x = [1,0,0] , y = [0,1,0] , r = 1 , start = 0 , end = Math.PI * 2; var arc = verb.eval.Make.arc(center, x, y, 1, start, end); var p = verb.eval.Eval.rationalCurvePoint( arc, 0.5); should.equal( Math.abs( p[0] + 1) < verb.core.Constants.EPSILON, true ); should.equal( Math.abs( p[1] ) < verb.core.Constants.EPSILON, true ); should.equal( p[2], 0 ); }); });