Skip to content

Instantly share code, notes, and snippets.

@zz85
Last active May 9, 2016 00:59
Show Gist options
  • Save zz85/2a0e4a0b944ec89aa5eb to your computer and use it in GitHub Desktop.
Save zz85/2a0e4a0b944ec89aa5eb to your computer and use it in GitHub Desktop.
Easing Experiments

"Better" / Bruteforce Cubic Bezier Approximations for Robert Penner Easing Equations

node compare_curve.js

Sample Output

at 0.98 0.76 0.52 0.24
compute: 53232ms
Number of permutations: 6632550
CONGRATS DONE!
QuadIn: [ 0.26, 0, 0.6, 0.2 ] ,
QuadOut: [ 0.4, 0.8, 0.74, 1 ] ,
QuadInOut: [ 0.48, 0.04, 0.52, 0.96 ] ,
CubicIn: [ 0.4, 0, 0.68, 0.06 ] ,
CubicOut: [ 0.32, 0.94, 0.6, 1 ] ,
CubicInOut: [ 0.66, 0, 0.34, 1 ] ,
QuartIn: [ 0.52, 0, 0.74, 0 ] ,
QuartOut: [ 0.26, 1, 0.48, 1 ] ,
QuartInOut: [ 0.76, 0, 0.24, 1 ] ,
QuintIn: [ 0.64, 0, 0.78, 0 ] ,
QuintOut: [ 0.22, 1, 0.36, 1 ] ,
QuintInOut: [ 0.84, 0, 0.16, 1 ] ,
SineIn: [ 0.32, 0, 0.6, 0.36 ] ,
SineOut: [ 0.4, 0.64, 0.68, 1 ] ,
SineInOut: [ 0.36, 0, 0.64, 1 ] ,
ExpoIn: [ 0.66, 0, 0.86, 0 ] ,
ExpoOut: [ 0.14, 1, 0.34, 1 ] ,
ExpoInOut: [ 0.9, 0, 0.1, 1 ] ,
CircIn: [ 0.54, 0, 1, 0.44 ] ,
CircOut: [ 0, 0.56, 0.46, 1 ] ,
CircInOut: [ 0.88, 0.14, 0.12, 0.86 ] ,
var eases = require('./easing');
var Tween = eases.Tween;
var UnitBezier = eases.UnitBezier;
var Names = eases.Names;
var x1, y1, x2, y2;
var units = 50; // 10
var compares = 0;
var duration = 20000;
var epsilon = (1000 / 60 / duration) / 4;
epsilon = 1e-5;
var i;
var easeTypes = [];
console.log('Bruteforcing parameters... Using ' + units + ' units' );
console.time('compute');
for (var name in Names) {
easeTypes.push(new Type(name + 'In', Names[name] + 'In'));
easeTypes.push(new Type(name + 'Out', Names[name] + 'Out'));
easeTypes.push(new Type(name + 'InOut', Names[name] + 'InOut'));
}
function Type(name, type) {
this.name = name;
this.type = type;
var values = new Array(units);
this.leastSquares = Infinity;
this.bestFit = null;
var i;
for(i = 0; i < units; i++) {
values[i] = Tween[type](i / units);
}
this.values = values;
}
Type.prototype.compare = function(data, x1, y1, x2, y2) {
var i, diff;
var values = this.values;
var squaredSum = 0;
for(i = 0; i < units; i++) {
diff = values[i] - data[i];
squaredSum += diff * diff;
// squaredSum += Math.abs(diff);
}
if (squaredSum < this.leastSquares) {
this.bestFit = [x1, y1, x2, y2];
this.leastSquares = squaredSum;
}
};
var solved = new Array(units);
/*
// Unclipped version
for (x1=0; x1<units; x1++) {
for (y1=-units; y1<=units * 2; y1++) {
for (x2=units; x2>=0; x2--) {
for (y2=units * 2; y2>=-units; y2--) {
compare(x1 / units, y1 / units, x2 / units, y2 / units);
}
}
}
}
/**/
/**/
// Clipped version (values between 0..1)
for (x1=0; x1<units; x1++) {
for (y1=0; y1<=units; y1++) {
for (x2=units; x2>=0; x2--) {
for (y2=units; y2>=0; y2--) {
compare(x1 / units, y1 / units, x2 / units, y2 / units);
}
}
}
}
function compare(x1, y1, x2, y2) {
var bezier = new UnitBezier(x1, y1, x2, y2);
var k, val, diff;
for(i = 0; i < units; i++) {
k = i / units;
val = bezier.solve(k, epsilon);
solved[i] = val;
}
for (i=0, il = easeTypes.length; i<il;i++) {
var e = easeTypes[i];
e.compare(solved, x1, y1, x2, y2);
}
compares++;
if (compares % 100000 === 0) {
console.log('at', x1, y1, x2, y2);
}
}
function printResults() {
for (i=0, il = easeTypes.length; i<il;i++) {
var e = easeTypes[i];
console.log(e.name + ':', e.bestFit, ', // ' + e.leastSquares);
}
}
console.timeEnd('compute');
console.log('Number of permutations:', compares);
console.log('CONGRATS DONE!');
printResults();
// Cubic Bezier Easing Parameters Approximation
// of Robert Penner's Tween Equations
var Names = {
// Linear: 'Linear',
Quad: 'Quadratic',
Cubic: 'Cubic',
Quart: 'Quartic',
Quint: 'Quintic',
Sine: 'Sinusoidal',
Expo: 'Exponential',
Circ: 'Circular',
};
// values generated by @BlurSpline
var BSEasing = {
// compute: 467346ms
// Number of permutations: 58142550
// CONGRATS DONE!
QuadIn: [ 0.26, 0, 0.6, 0.2 ] , // 0.0000014195846674133613
QuadOut: [ 0.4, 0.8, 0.74, 1 ] , // 0.0000014195846674136283
QuadInOut: [ 0.48, 0.04, 0.52, 0.96 ] , // 0.00021443512293854952
CubicIn: [ 0.32, 0, 0.66, -0.02 ] , // 0.0000837264957522941
CubicOut: [ 0.34, 1.02, 0.68, 1 ] , // 0.00008372649575229691
CubicInOut: [ 0.62, -0.04, 0.38, 1.04 ] , // 0.00020290989758759337
QuartIn: [ 0.46, 0, 0.74, -0.04 ] , // 0.00004920119056999463
QuartOut: [ 0.26, 1.04, 0.54, 1 ] , // 0.00004920119056999559
QuartInOut: [ 0.7, -0.1, 0.3, 1.1 ] , // 0.0007318300363503209
QuintIn: [ 0.52, 0, 0.78, -0.1 ] , // 0.00015727214523005402
QuintOut: [ 0.22, 1.1, 0.48, 1 ] , // 0.0001572721452300539
QuintInOut: [ 0.76, -0.14, 0.24, 1.14 ] , // 0.002051715614688728
SineIn: [ 0.32, 0, 0.6, 0.36 ] , // 0.00003239261842406147
SineOut: [ 0.4, 0.64, 0.68, 1 ] , // 0.00003239261842406522
SineInOut: [ 0.36, 0, 0.64, 1 ] , // 0.000042771492108870344
ExpoIn: [ 0.62, 0.02, 0.84, -0.08 ] , // 0.00019519295753793093
ExpoOut: [ 0.16, 1.08, 0.38, 0.98 ] , // 0.00019519295753793044
ExpoInOut: [ 0.84, -0.12, 0.16, 1.12 ] , // 0.002911151068100008
CircIn: [ 0.54, 0, 1, 0.44 ] , // 0.000051489431433657843
CircOut: [ 0, 0.56, 0.46, 1 ] , // 0.00005148943143365869
CircInOut: [ 0.88, 0.14, 0.12, 0.86 ] , // 0.003209319580927181
};
var BSClippedEasing = {
QuadIn: [ 0.26, 0, 0.6, 0.2 ] , // 0.0000014195846674133613
QuadOut: [ 0.4, 0.8, 0.74, 1 ] , // 0.0000014195846674136283
QuadInOut: [ 0.48, 0.04, 0.52, 0.96 ] , // 0.00021443512293854952
CubicIn: [ 0.4, 0, 0.68, 0.06 ] , // 0.00011476736190927124
CubicOut: [ 0.32, 0.94, 0.6, 1 ] , // 0.00011476736190926926
CubicInOut: [ 0.66, 0, 0.34, 1 ] , // 0.0015339866265241262
QuartIn: [ 0.52, 0, 0.74, 0 ] , // 0.000312059008865297
QuartOut: [ 0.26, 1, 0.48, 1 ] , // 0.0003120590088652992
QuartInOut: [ 0.76, 0, 0.24, 1 ] , // 0.008168182272984338
QuintIn: [ 0.64, 0, 0.78, 0 ] , // 0.0011823628931826393
QuintOut: [ 0.22, 1, 0.36, 1 ] , // 0.001182362893182631
QuintInOut: [ 0.84, 0, 0.16, 1 ] , // 0.017662313468104155
SineIn: [ 0.32, 0, 0.6, 0.36 ] , // 0.00003239261842406147
SineOut: [ 0.4, 0.64, 0.68, 1 ] , // 0.00003239261842406522
SineInOut: [ 0.36, 0, 0.64, 1 ] , // 0.000042771492108870344
ExpoIn: [ 0.66, 0, 0.86, 0 ] , // 0.0005674614913573925
ExpoOut: [ 0.14, 1, 0.34, 1 ] , // 0.0005674614913573805
ExpoInOut: [ 0.9, 0, 0.1, 1 ] , // 0.0179277283566518
CircIn: [ 0.54, 0, 1, 0.44 ] , // 0.000051489431433657843
CircOut: [ 0, 0.56, 0.46, 1 ] , // 0.00005148943143365869
CircInOut: [ 0.88, 0.14, 0.12, 0.86 ] , // 0.003209319580927181
};
// values based on https://github.com/KinkumaDesign/CustomMediaTimingFunction/blob/master/src/KKCustomMediaTimingFunction.m
var KKEasing = {
Linear: [0,0,1,1],
QuadIn: [0.51,0,0.96,0.9],
QuadOut: [0,0.51,0.9,0.96],
QuadInOut: [0.43,0,0.57,1],
QuadOutIn: [0,0.43,1,0.57],
CubicIn: [0.55,0,0.7,0.19],
CubicOut: [0,0.55,0.19,0.7],
CubicInOut: [0.7,0,0.3,1],
CubicOutIn: [0,0.7,1,0.3],
SineIn: [0.44,0,0.99,0.98],
SineOut: [0,0.44,0.98,0.99],
SineInOut: [0.36,0,0.64,1],
SineOutIn: [0,0.36,1,0.64],
QuartIn: [0.74,0,0.74,0.19],
QuartOut: [0,0.74,0.19,0.74],
QuartInOut: [0.85,0,0.13,0.99],
QuartOutIn: [0,0.85,0.99,0.13],
QuintIn: [0.79,0,0.75,0.1],
QuintOut: [0,0.79,0.1,0.75],
QuintInOut: [0.9,0,0.09,1],
QuintOutIn: [0,0.9,1,0.09],
ExpoIn: [0.81,0,0.83,0.11],
ExpoOut: [0,0.81,0.11,0.83],
ExpoInOut: [0.97,0,0.02,0.99],
ExpoOutIn: [0,0.97,0.99,0.02],
CircIn: [0.67,0,0.99,0.57],
CircOut: [0,0.67,0.57,0.99],
CircInOut: [0.92,0.15,0.08,0.82],
CircOutIn: [0.15,0.92,0.82,0.08],
};
// Values http://matthewlein.com/ceaser/
// Based on https://github.com/jhardy/compass-ceaser-easing/blob/master/stylesheets/ceaser-easing/_ease-types.sass
var CeaserEasing = {
Linear: [0.250, 0.250, 0.750, 0.750],
// ease: [0.250, 0.100, 0.250, 1.000],
// ease-in: [0.420, 0.000, 1.000, 1.000],
// ease-out: [0.000, 0.000, 0.580, 1.000],
// ease-in-out: [0.420, 0.000, 0.580, 1.000],
QuadIn: [0.550, 0.085, 0.680, 0.530],
QuadOut: [0.250, 0.460, 0.450, 0.940],
QuadInOut: [0.455, 0.030, 0.515, 0.955],
CubicIn: [0.550, 0.055, 0.675, 0.190],
CubicOut: [0.215, 0.610, 0.355, 1.000],
CubicInOut: [0.645, 0.045, 0.355, 1.000],
QuartIn: [0.895, 0.030, 0.685, 0.220],
QuartOut: [0.165, 0.840, 0.440, 1.000],
QuartInOut: [0.770, 0.000, 0.175, 1.000],
QuintIn: [0.755, 0.050, 0.855, 0.060],
QuintOut: [0.230, 1.000, 0.320, 1.000],
QuintInOut: [0.860, 0.000, 0.070, 1.000],
SineIn: [0.470, 0.000, 0.745, 0.715],
SineOut: [0.390, 0.575, 0.565, 1.000],
SineInOut: [0.445, 0.050, 0.550, 0.950],
ExpoIn: [0.950, 0.050, 0.795, 0.035],
ExpoOut: [0.190, 1.000, 0.220, 1.000],
ExpoInOut: [1.000, 0.000, 0.000, 1.000],
CircIn: [0.600, 0.040, 0.980, 0.335],
CircOut: [0.075, 0.820, 0.165, 1.000],
CircInOut: [0.785, 0.135, 0.150, 0.860],
BackIn: [0.600, -0.280, 0.735, 0.045],
BackOut: [0.175, 0.885, 0.320, 1.275],
BackInOut: [0.680, -0.550, 0.265, 1.550],
};
// functions based on https://github.com/sole/tween.js/blob/master/src/Tween.js
Tween = {
LinearNone: function ( k ) {
return k;
},
QuadraticIn: function ( k ) {
return k * k;
},
QuadraticOut: function ( k ) {
return k * ( 2 - k );
},
QuadraticInOut: function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k;
return - 0.5 * ( --k * ( k - 2 ) - 1 );
},
CubicIn: function ( k ) {
return k * k * k;
},
CubicOut: function ( k ) {
return --k * k * k + 1;
},
CubicInOut: function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k;
return 0.5 * ( ( k -= 2 ) * k * k + 2 );
},
QuarticIn: function ( k ) {
return k * k * k * k;
},
QuarticOut: function ( k ) {
return 1 - ( --k * k * k * k );
},
QuarticInOut: function ( k ) {
if ( ( k *= 2 ) < 1) return 0.5 * k * k * k * k;
return - 0.5 * ( ( k -= 2 ) * k * k * k - 2 );
},
QuinticIn: function ( k ) {
return k * k * k * k * k;
},
QuinticOut: function ( k ) {
return --k * k * k * k * k + 1;
},
QuinticInOut: function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k * k * k;
return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 );
},
SinusoidalIn: function ( k ) {
return 1 - Math.cos( k * Math.PI / 2 );
},
SinusoidalOut: function ( k ) {
return Math.sin( k * Math.PI / 2 );
},
SinusoidalInOut: function ( k ) {
return 0.5 * ( 1 - Math.cos( Math.PI * k ) );
},
ExponentialIn: function ( k ) {
return k === 0 ? 0 : Math.pow( 1024, k - 1 );
},
ExponentialOut: function ( k ) {
return k === 1 ? 1 : 1 - Math.pow( 2, - 10 * k );
},
ExponentialInOut: function ( k ) {
if ( k === 0 ) return 0;
if ( k === 1 ) return 1;
if ( ( k *= 2 ) < 1 ) return 0.5 * Math.pow( 1024, k - 1 );
return 0.5 * ( - Math.pow( 2, - 10 * ( k - 1 ) ) + 2 );
},
CircularIn: function ( k ) {
return 1 - Math.sqrt( 1 - k * k );
},
CircularOut: function ( k ) {
return Math.sqrt( 1 - ( --k * k ) );
},
CircularInOut: function ( k ) {
if ( ( k *= 2 ) < 1) return - 0.5 * ( Math.sqrt( 1 - k * k) - 1);
return 0.5 * ( Math.sqrt( 1 - ( k -= 2) * k) + 1);
},
ElasticIn: function ( k ) {
var s, a = 0.1, p = 0.4;
if ( k === 0 ) return 0;
if ( k === 1 ) return 1;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI );
return - ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) );
},
ElasticOut: function ( k ) {
var s, a = 0.1, p = 0.4;
if ( k === 0 ) return 0;
if ( k === 1 ) return 1;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI );
return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 );
},
ElasticInOut: function ( k ) {
var s, a = 0.1, p = 0.4;
if ( k === 0 ) return 0;
if ( k === 1 ) return 1;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI );
if ( ( k *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) );
return a * Math.pow( 2, -10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1;
},
BackIn: function ( k ) {
var s = 1.70158;
return k * k * ( ( s + 1 ) * k - s );
},
BackOut: function ( k ) {
var s = 1.70158;
return --k * k * ( ( s + 1 ) * k + s ) + 1;
},
BackInOut: function ( k ) {
var s = 1.70158 * 1.525;
if ( ( k *= 2 ) < 1 ) return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) );
return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 );
},
BounceIn: function ( k ) {
return 1 - TWEEN.Easing.Bounce.Out( 1 - k );
},
BounceOut: function ( k ) {
if ( k < ( 1 / 2.75 ) ) {
return 7.5625 * k * k;
} else if ( k < ( 2 / 2.75 ) ) {
return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
} else if ( k < ( 2.5 / 2.75 ) ) {
return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
} else {
return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
}
},
BounceInOut: function ( k ) {
if ( k < 0.5 ) return BounceIn( k * 2 ) * 0.5;
return BounceOut( k * 2 - 1 ) * 0.5 + 0.5;
}
};
// http://codepen.io/onedayitwillmake/pen/EHDmw
/**
* Solver for cubic bezier curve with implicit control points at (0,0) and (1.0, 1.0)
*/
function UnitBezier(p1x, p1y, p2x, p2y) {
// pre-calculate the polynomial coefficients
// First and last control points are implied to be (0,0) and (1.0, 1.0)
this.cx = 3.0 * p1x;
this.bx = 3.0 * (p2x - p1x) - this.cx;
this.ax = 1.0 - this.cx -this.bx;
this.cy = 3.0 * p1y;
this.by = 3.0 * (p2y - p1y) - this.cy;
this.ay = 1.0 - this.cy - this.by;
}
UnitBezier.prototype.epsilon = 1e-6; // Precision
UnitBezier.prototype.sampleCurveX = function(t) {
return ((this.ax * t + this.bx) * t + this.cx) * t;
}
UnitBezier.prototype.sampleCurveY = function (t) {
return ((this.ay * t + this.by) * t + this.cy) * t;
}
UnitBezier.prototype.sampleCurveDerivativeX = function (t) {
return (3.0 * this.ax * t + 2.0 * this.bx) * t + this.cx;
}
UnitBezier.prototype.solveCurveX = function (x, epsilon) {
var t0;
var t1;
var t2;
var x2;
var d2;
var i;
// First try a few iterations of Newton's method -- normally very fast.
for (t2 = x, i = 0; i < 8; i++) {
x2 = this.sampleCurveX(t2) - x;
if (Math.abs (x2) < epsilon)
return t2;
d2 = this.sampleCurveDerivativeX(t2);
if (Math.abs(d2) < epsilon)
break;
t2 = t2 - x2 / d2;
}
// No solution found - use bi-section
t0 = 0.0;
t1 = 1.0;
t2 = x;
if (t2 < t0) return t0;
if (t2 > t1) return t1;
while (t0 < t1) {
x2 = this.sampleCurveX(t2);
if (Math.abs(x2 - x) < epsilon)
return t2;
if (x > x2) t0 = t2;
else t1 = t2;
t2 = (t1 - t0) * .5 + t0;
}
// Give up
return t2;
}
// Find new T as a function of Y along curve X
UnitBezier.prototype.solve = function (x, epsilon) {
return this.sampleCurveY( this.solveCurveX(x, epsilon) );
};
var exports = {
Names: Names,
Tween: Tween,
UnitBezier: UnitBezier
};
// if (typeof(window)!=='undefined') {
// for (var e in exports) {
// window[e] = exports[e];
// }
// }
if (typeof(module)!=='undefined') {
module.exports = exports;
}
/**
* FitCurves.js - Piecewise cubic fitting code
*
* original: FitCurves.c
* http://tog.acm.org/resources/GraphicsGems/gems/FitCurves.c
*
* ported by ynakajima (https://github.com/ynakajima).
*
* THIS SOURCE CODE IS PUBLIC DOMAIN, and
* is freely available to the entire computer graphics community
* for study, use, and modification. We do request that the
* comment at the top of each file, identifying the original
* author and its original publication in the book Graphics
* Gems, be retained in all programs that use these files.
*
*/
/**
An Algorithm for Automatically Fitting Digitized Curves
by Philip J. Schneider
from "Graphics Gems", Academic Press, 1990
*/
(function(glob) {
var TESTMODE = true,
drawCount = 0;
/* fit_cubic.js */
/* Piecewise cubic fitting code */
/* Forward declarations
void FitCurve();
static void FitCubic();
static double *Reparameterize();
static double NewtonRaphsonRootFind();
static Point2 BezierII();
static double B0(), B1(), B2(), B3();
static Vector2 ComputeLeftTangent();
static Vector2 ComputeRightTangent();
static Vector2 ComputeCenterTangent();
static double ComputeMaxError();
static double *ChordLengthParameterize();
static BezierCurve GenerateBezier();
static Vector2 V2AddII();
static Vector2 V2ScaleIII();
static Vector2 V2SubII();
*/
var MAXPOINTS = 1000; /* The most points you can have */
if (TESTMODE) {
main();
/**
* DrawBezierCurve
* @param {Number} n
* @param {Array.<Point2>} curve
*/
function DrawBezierCurve(n, curve)
{
/* You'll have to write this yourself. */
var d = (drawCount !== 0) ? '' : 'M ' + curve[0].x + ',' + curve[0].y + ' C';
for (var i = 1; i < 4; i++) {
d += ' ' + curve[i].x + ',' + curve[i].y;
}
drawCount++;
console.log(d);
}
/*
* main:
* Example of how to use the curve-fitting code. Given an array
* of points and a tolerance (squared error between points and
* fitted curve), the algorithm will generate a piecewise
* cubic Bezier representation that approximates the points.
* When a cubic is generated, the routine "DrawBezierCurve"
* is called, which outputs the Bezier curve just created
* (arguments are the degree and the control points, respectively).
* Users will have to implement this function themselves
* ascii output, etc.
*
*/
function main(argc, argv)
{
var d = [
// new Point2(0.0, 0.0),
// new Point2(0.0, 0.5),
// new Point2(1.1, 1.4),
// new Point2(2.1, 1.6),
// new Point2(3.2, 1.1),
// new Point2(4.0, 0.2),
// new Point2(4.0, 0.0)
new Point2(0, 0), new Point2(0.001, 1e-9), new Point2(0.002, 8e-9), new Point2(0.003, 2.7e-8), new Point2(0.004, 6.4e-8), new Point2(0.005, 1.2500000000000002e-7), new Point2(0.006, 2.16e-7), new Point2(0.007, 3.4300000000000004e-7), new Point2(0.008, 5.12e-7), new Point2(0.009, 7.289999999999998e-7), new Point2(0.01, 0.0000010000000000000002), new Point2(0.011, 0.0000013309999999999998), new Point2(0.012, 0.000001728), new Point2(0.013, 0.000002197), new Point2(0.014, 0.0000027440000000000003), new Point2(0.015, 0.000003375), new Point2(0.016, 0.000004096), new Point2(0.017, 0.000004913000000000001), new Point2(0.018, 0.0000058319999999999985), new Point2(0.019, 0.000006859), new Point2(0.02, 0.000008000000000000001), new Point2(0.021, 0.000009261000000000002), new Point2(0.022, 0.000010647999999999999), new Point2(0.023, 0.000012166999999999999), new Point2(0.024, 0.000013824), new Point2(0.025, 0.000015625000000000004), new Point2(0.026, 0.000017576), new Point2(0.027, 0.000019682999999999998), new Point2(0.028, 0.000021952000000000003), new Point2(0.029, 0.000024389000000000003), new Point2(0.03, 0.000027), new Point2(0.031, 0.000029790999999999996), new Point2(0.032, 0.000032768), new Point2(0.033, 0.000035937000000000005), new Point2(0.034, 0.00003930400000000001), new Point2(0.035, 0.00004287500000000001), new Point2(0.036, 0.00004665599999999999), new Point2(0.037, 0.00005065299999999999), new Point2(0.038, 0.000054872), new Point2(0.039, 0.000059319), new Point2(0.04, 0.00006400000000000001), new Point2(0.041, 0.00006892100000000001), new Point2(0.042, 0.00007408800000000001), new Point2(0.043, 0.00007950699999999998), new Point2(0.044, 0.00008518399999999999), new Point2(0.045, 0.00009112499999999999), new Point2(0.046, 0.00009733599999999999), new Point2(0.047, 0.000103823), new Point2(0.048, 0.000110592), new Point2(0.049, 0.00011764900000000003), new Point2(0.05, 0.00012500000000000003), new Point2(0.051, 0.00013265099999999996), new Point2(0.052, 0.000140608), new Point2(0.053, 0.00014887699999999998), new Point2(0.054, 0.00015746399999999998), new Point2(0.055, 0.000166375), new Point2(0.056, 0.00017561600000000002), new Point2(0.057, 0.00018519300000000003), new Point2(0.058, 0.00019511200000000003), new Point2(0.059, 0.00020537899999999997), new Point2(0.06, 0.000216), new Point2(0.061, 0.000226981), new Point2(0.062, 0.00023832799999999997), new Point2(0.063, 0.00025004700000000004), new Point2(0.064, 0.000262144), new Point2(0.065, 0.000274625), new Point2(0.066, 0.00028749600000000004), new Point2(0.067, 0.00030076300000000006), new Point2(0.068, 0.00031443200000000007), new Point2(0.069, 0.00032850900000000005), new Point2(0.07, 0.0003430000000000001), new Point2(0.071, 0.00035791099999999993), new Point2(0.072, 0.0003732479999999999), new Point2(0.073, 0.00038901699999999997), new Point2(0.074, 0.00040522399999999993), new Point2(0.075, 0.000421875), new Point2(0.076, 0.000438976), new Point2(0.077, 0.000456533), new Point2(0.078, 0.000474552), new Point2(0.079, 0.000493039), new Point2(0.08, 0.0005120000000000001), new Point2(0.081, 0.000531441), new Point2(0.082, 0.0005513680000000001), new Point2(0.083, 0.0005717870000000001), new Point2(0.084, 0.0005927040000000001), new Point2(0.085, 0.0006141250000000002), new Point2(0.086, 0.0006360559999999999), new Point2(0.087, 0.0006585029999999998), new Point2(0.088, 0.0006814719999999999), new Point2(0.089, 0.0007049689999999998), new Point2(0.09, 0.0007289999999999999), new Point2(0.091, 0.000753571), new Point2(0.092, 0.0007786879999999999), new Point2(0.093, 0.000804357), new Point2(0.094, 0.000830584), new Point2(0.095, 0.0008573750000000001), new Point2(0.096, 0.000884736), new Point2(0.097, 0.0009126730000000001), new Point2(0.098, 0.0009411920000000002), new Point2(0.099, 0.0009702990000000001), new Point2(0.1, 0.0010000000000000002), new Point2(0.101, 0.0010303010000000002), new Point2(0.102, 0.0010612079999999997), new Point2(0.103, 0.0010927269999999997), new Point2(0.104, 0.001124864), new Point2(0.105, 0.0011576249999999998), new Point2(0.106, 0.0011910159999999998), new Point2(0.107, 0.0012250429999999999), new Point2(0.108, 0.0012597119999999999), new Point2(0.109, 0.0012950289999999998), new Point2(0.11, 0.001331), new Point2(0.111, 0.001367631), new Point2(0.112, 0.0014049280000000002), new Point2(0.113, 0.001442897), new Point2(0.114, 0.0014815440000000002), new Point2(0.115, 0.001520875), new Point2(0.116, 0.0015608960000000002), new Point2(0.117, 0.0016016130000000002), new Point2(0.118, 0.0016430319999999998), new Point2(0.119, 0.0016851589999999997), new Point2(0.12, 0.001728), new Point2(0.121, 0.0017715609999999998), new Point2(0.122, 0.001815848), new Point2(0.123, 0.001860867), new Point2(0.124, 0.0019066239999999998), new Point2(0.125, 0.001953125), new Point2(0.126, 0.0020003760000000003), new Point2(0.127, 0.002048383), new Point2(0.128, 0.002097152), new Point2(0.129, 0.002146689), new Point2(0.13, 0.002197), new Point2(0.131, 0.0022480910000000002), new Point2(0.132, 0.0022999680000000003), new Point2(0.133, 0.0023526370000000007), new Point2(0.134, 0.0024061040000000005), new Point2(0.135, 0.0024603750000000003), new Point2(0.136, 0.0025154560000000005), new Point2(0.137, 0.0025713530000000006), new Point2(0.138, 0.0026280720000000004), new Point2(0.139, 0.002685619000000001), new Point2(0.14, 0.0027440000000000008), new Point2(0.141, 0.002803220999999999), new Point2(0.142, 0.0028632879999999994), new Point2(0.143, 0.002924206999999999), new Point2(0.144, 0.0029859839999999993), new Point2(0.145, 0.0030486249999999997), new Point2(0.146, 0.0031121359999999997), new Point2(0.147, 0.0031765229999999992), new Point2(0.148, 0.0032417919999999994), new Point2(0.149, 0.0033079489999999997), new Point2(0.15, 0.003375), new Point2(0.151, 0.0034429509999999997), new Point2(0.152, 0.003511808), new Point2(0.153, 0.003581577), new Point2(0.154, 0.003652264), new Point2(0.155, 0.003723875), new Point2(0.156, 0.003796416), new Point2(0.157, 0.003869893), new Point2(0.158, 0.003944312), new Point2(0.159, 0.004019679), new Point2(0.16, 0.004096000000000001), new Point2(0.161, 0.004173281000000001), new Point2(0.162, 0.004251528), new Point2(0.163, 0.004330747), new Point2(0.164, 0.004410944000000001), new Point2(0.165, 0.0044921250000000005), new Point2(0.166, 0.004574296000000001), new Point2(0.167, 0.004657463000000001), new Point2(0.168, 0.004741632000000001), new Point2(0.169, 0.0048268090000000005), new Point2(0.17, 0.004913000000000002), new Point2(0.171, 0.005000211000000001), new Point2(0.172, 0.005088447999999999), new Point2(0.173, 0.005177716999999999), new Point2(0.174, 0.005268023999999999), new Point2(0.175, 0.005359374999999999), new Point2(0.176, 0.005451775999999999), new Point2(0.177, 0.005545232999999999), new Point2(0.178, 0.005639751999999999), new Point2(0.179, 0.005735339), new Point2(0.18, 0.0058319999999999995), new Point2(0.181, 0.005929741), new Point2(0.182, 0.006028568), new Point2(0.183, 0.006128486999999999), new Point2(0.184, 0.006229503999999999), new Point2(0.185, 0.006331625), new Point2(0.186, 0.006434856), new Point2(0.187, 0.006539203), new Point2(0.188, 0.006644672), new Point2(0.189, 0.006751269000000001), new Point2(0.19, 0.0068590000000000005), new Point2(0.191, 0.006967871), new Point2(0.192, 0.007077888), new Point2(0.193, 0.007189057000000001), new Point2(0.194, 0.007301384000000001), new Point2(0.195, 0.0074148750000000005), new Point2(0.196, 0.007529536000000002), new Point2(0.197, 0.007645373000000001), new Point2(0.198, 0.007762392000000001), new Point2(0.199, 0.007880599000000002), new Point2(0.2, 0.008000000000000002), new Point2(0.201, 0.008120601000000002), new Point2(0.202, 0.008242408000000001), new Point2(0.203, 0.008365427000000002), new Point2(0.204, 0.008489663999999997), new Point2(0.205, 0.008615124999999998), new Point2(0.206, 0.008741815999999998), new Point2(0.207, 0.008869743), new Point2(0.208, 0.008998912), new Point2(0.209, 0.009129328999999999), new Point2(0.21, 0.009260999999999998), new Point2(0.211, 0.009393931), new Point2(0.212, 0.009528127999999999), new Point2(0.213, 0.009663597), new Point2(0.214, 0.009800343999999999), new Point2(0.215, 0.009938375), new Point2(0.216, 0.010077695999999999), new Point2(0.217, 0.010218313), new Point2(0.218, 0.010360231999999999), new Point2(0.219, 0.010503459), new Point2(0.22, 0.010648), new Point2(0.221, 0.010793861), new Point2(0.222, 0.010941048), new Point2(0.223, 0.011089567000000002), new Point2(0.224, 0.011239424000000001), new Point2(0.225, 0.011390625000000001), new Point2(0.226, 0.011543176), new Point2(0.227, 0.011697083000000002), new Point2(0.228, 0.011852352000000002), new Point2(0.229, 0.012008989000000001), new Point2(0.23, 0.012167), new Point2(0.231, 0.012326391000000003), new Point2(0.232, 0.012487168000000002), new Point2(0.233, 0.012649337000000002), new Point2(0.234, 0.012812904000000002), new Point2(0.235, 0.012977874999999998), new Point2(0.236, 0.013144255999999998), new Point2(0.237, 0.013312052999999999), new Point2(0.238, 0.013481271999999997), new Point2(0.239, 0.013651918999999998), new Point2(0.24, 0.013824), new Point2(0.241, 0.013997520999999999), new Point2(0.242, 0.014172487999999999), new Point2(0.243, 0.014348907), new Point2(0.244, 0.014526784), new Point2(0.245, 0.014706124999999999), new Point2(0.246, 0.014886936), new Point2(0.247, 0.015069223), new Point2(0.248, 0.015252991999999998), new Point2(0.249, 0.015438249), new Point2(0.25, 0.015625), new Point2(0.251, 0.015813251), new Point2(0.252, 0.016003008000000003), new Point2(0.253, 0.016194277), new Point2(0.254, 0.016387064), new Point2(0.255, 0.016581375), new Point2(0.256, 0.016777216), new Point2(0.257, 0.016974593), new Point2(0.258, 0.017173512), new Point2(0.259, 0.017373979), new Point2(0.26, 0.017576), new Point2(0.261, 0.017779581000000003), new Point2(0.262, 0.017984728000000002), new Point2(0.263, 0.018191447000000003), new Point2(0.264, 0.018399744000000003), new Point2(0.265, 0.018609625000000005), new Point2(0.266, 0.018821096000000006), new Point2(0.267, 0.019034163000000003), new Point2(0.268, 0.019248832000000004), new Point2(0.269, 0.019465109000000005), new Point2(0.27, 0.019683000000000003), new Point2(0.271, 0.019902511000000005), new Point2(0.272, 0.020123648000000004), new Point2(0.273, 0.020346417000000006), new Point2(0.274, 0.020570824000000005), new Point2(0.275, 0.020796875000000006), new Point2(0.276, 0.021024576000000003), new Point2(0.277, 0.021253933000000006), new Point2(0.278, 0.02148495200000001), new Point2(0.279, 0.021717639000000007), new Point2(0.28, 0.021952000000000006), new Point2(0.281, 0.022188041000000006), new Point2(0.282, 0.022425767999999992), new Point2(0.283, 0.022665186999999993), new Point2(0.284, 0.022906303999999995), new Point2(0.285, 0.023149124999999996), new Point2(0.286, 0.023393655999999992), new Point2(0.287, 0.023639902999999993), new Point2(0.288, 0.023887871999999994), new Point2(0.289, 0.024137568999999994), new Point2(0.29, 0.024388999999999997), new Point2(0.291, 0.024642170999999997), new Point2(0.292, 0.024897087999999998), new Point2(0.293, 0.025153756999999995), new Point2(0.294, 0.025412183999999994), new Point2(0.295, 0.025672374999999997), new Point2(0.296, 0.025934335999999995), new Point2(0.297, 0.026198073), new Point2(0.298, 0.026463591999999998), new Point2(0.299, 0.026730899), new Point2(0.3, 0.027), new Point2(0.301, 0.027270900999999997), new Point2(0.302, 0.027543607999999997), new Point2(0.303, 0.027818126999999998), new Point2(0.304, 0.028094464), new Point2(0.305, 0.028372625), new Point2(0.306, 0.028652616), new Point2(0.307, 0.028934443), new Point2(0.308, 0.029218112), new Point2(0.309, 0.029503629), new Point2(0.31, 0.029791), new Point2(0.311, 0.030080231), new Point2(0.312, 0.030371328), new Point2(0.313, 0.030664297), new Point2(0.314, 0.030959144), new Point2(0.315, 0.031255875), new Point2(0.316, 0.031554496), new Point2(0.317, 0.031855013), new Point2(0.318, 0.032157432), new Point2(0.319, 0.032461759), new Point2(0.32, 0.032768000000000005), new Point2(0.321, 0.033076161), new Point2(0.322, 0.03338624800000001), new Point2(0.323, 0.033698267000000004), new Point2(0.324, 0.034012224), new Point2(0.325, 0.03432812500000001), new Point2(0.326, 0.034645976), new Point2(0.327, 0.03496578300000001), new Point2(0.328, 0.03528755200000001), new Point2(0.329, 0.035611289000000004), new Point2(0.33, 0.035937000000000004), new Point2(0.331, 0.036264691), new Point2(0.332, 0.03659436800000001), new Point2(0.333, 0.03692603700000001), new Point2(0.334, 0.037259704000000005), new Point2(0.335, 0.03759537500000001), new Point2(0.336, 0.03793305600000001), new Point2(0.337, 0.03827275300000001), new Point2(0.338, 0.038614472000000004), new Point2(0.339, 0.03895821900000001), new Point2(0.34, 0.03930400000000001), new Point2(0.341, 0.03965182100000001), new Point2(0.342, 0.04000168800000001), new Point2(0.343, 0.04035360700000001), new Point2(0.344, 0.04070758399999999), new Point2(0.345, 0.04106362499999999), new Point2(0.346, 0.041421735999999994), new Point2(0.347, 0.04178192299999999), new Point2(0.348, 0.04214419199999999), new Point2(0.349, 0.04250854899999999), new Point2(0.35, 0.04287499999999999), new Point2(0.351, 0.04324355099999999), new Point2(0.352, 0.043614207999999995), new Point2(0.353, 0.04398697699999999), new Point2(0.354, 0.044361863999999994), new Point2(0.355, 0.044738875), new Point2(0.356, 0.04511801599999999), new Point2(0.357, 0.04549929299999999), new Point2(0.358, 0.045882712), new Point2(0.359, 0.046268278999999995), new Point2(0.36, 0.046655999999999996), new Point2(0.361, 0.047045881), new Point2(0.362, 0.047437928), new Point2(0.363, 0.047832147), new Point2(0.364, 0.048228544), new Point2(0.365, 0.04862712499999999), new Point2(0.366, 0.049027895999999994), new Point2(0.367, 0.049430863), new Point2(0.368, 0.049836031999999995), new Point2(0.369, 0.050243409), new Point2(0.37, 0.050653), new Point2(0.371, 0.051064810999999995), new Point2(0.372, 0.051478848), new Point2(0.373, 0.051895117), new Point2(0.374, 0.052313624), new Point2(0.375, 0.052734375), new Point2(0.376, 0.053157376), new Point2(0.377, 0.053582633000000005), new Point2(0.378, 0.054010152000000006), new Point2(0.379, 0.054439939), new Point2(0.38, 0.054872000000000004), new Point2(0.381, 0.05530634100000001), new Point2(0.382, 0.055742968), new Point2(0.383, 0.05618188700000001), new Point2(0.384, 0.056623104), new Point2(0.385, 0.057066625), new Point2(0.386, 0.05751245600000001), new Point2(0.387, 0.057960603000000006), new Point2(0.388, 0.05841107200000001), new Point2(0.389, 0.058863869000000006), new Point2(0.39, 0.059319000000000004), new Point2(0.391, 0.05977647100000001), new Point2(0.392, 0.06023628800000001), new Point2(0.393, 0.060698457000000004), new Point2(0.394, 0.06116298400000001), new Point2(0.395, 0.061629875000000015), new Point2(0.396, 0.062099136000000006), new Point2(0.397, 0.06257077300000001), new Point2(0.398, 0.06304479200000002), new Point2(0.399, 0.063521199), new Point2(0.4, 0.06400000000000002), new Point2(0.401, 0.06448120100000002), new Point2(0.402, 0.06496480800000001), new Point2(0.403, 0.06545082700000002), new Point2(0.404, 0.06593926400000001), new Point2(0.405, 0.06643012500000002), new Point2(0.406, 0.06692341600000001), new Point2(0.407, 0.06741914299999999), new Point2(0.408, 0.06791731199999998), new Point2(0.409, 0.06841792899999999), new Point2(0.41, 0.06892099999999998), new Point2(0.411, 0.06942653099999999), new Point2(0.412, 0.06993452799999998), new Point2(0.413, 0.07044499699999998), new Point2(0.414, 0.070957944), new Point2(0.415, 0.07147337499999999), new Point2(0.416, 0.071991296), new Point2(0.417, 0.07251171299999999), new Point2(0.418, 0.07303463199999999), new Point2(0.419, 0.073560059), new Point2(0.42, 0.07408799999999999), new Point2(0.421, 0.07461846099999998), new Point2(0.422, 0.075151448), new Point2(0.423, 0.075686967), new Point2(0.424, 0.07622502399999999), new Point2(0.425, 0.07676562499999999), new Point2(0.426, 0.077308776), new Point2(0.427, 0.07785448299999999), new Point2(0.428, 0.07840275199999999), new Point2(0.429, 0.07895358899999999), new Point2(0.43, 0.079507), new Point2(0.431, 0.08006299099999999), new Point2(0.432, 0.08062156799999999), new Point2(0.433, 0.08118273699999999), new Point2(0.434, 0.081746504), new Point2(0.435, 0.08231287500000001), new Point2(0.436, 0.08288185599999999), new Point2(0.437, 0.083453453), new Point2(0.438, 0.084027672), new Point2(0.439, 0.084604519), new Point2(0.44, 0.085184), new Point2(0.441, 0.085766121), new Point2(0.442, 0.086350888), new Point2(0.443, 0.086938307), new Point2(0.444, 0.087528384), new Point2(0.445, 0.08812112500000001), new Point2(0.446, 0.08871653600000001), new Point2(0.447, 0.08931462300000001), new Point2(0.448, 0.08991539200000001), new Point2(0.449, 0.090518849), new Point2(0.45, 0.09112500000000001), new Point2(0.451, 0.091733851), new Point2(0.452, 0.092345408), new Point2(0.453, 0.092959677), new Point2(0.454, 0.09357666400000002), new Point2(0.455, 0.09419637500000001), new Point2(0.456, 0.09481881600000001), new Point2(0.457, 0.095443993), new Point2(0.458, 0.09607191200000001), new Point2(0.459, 0.09670257900000001), new Point2(0.46, 0.097336), new Point2(0.461, 0.097972181), new Point2(0.462, 0.09861112800000002), new Point2(0.463, 0.09925284700000002), new Point2(0.464, 0.09989734400000001), new Point2(0.465, 0.10054462500000001), new Point2(0.466, 0.10119469600000001), new Point2(0.467, 0.10184756300000002), new Point2(0.468, 0.10250323200000001), new Point2(0.469, 0.10316170899999998), new Point2(0.47, 0.10382299999999998), new Point2(0.471, 0.10448711099999998), new Point2(0.472, 0.10515404799999999), new Point2(0.473, 0.10582381699999999), new Point2(0.474, 0.10649642399999999), new Point2(0.475, 0.10717187499999999), new Point2(0.476, 0.10785017599999998), new Point2(0.477, 0.10853133299999998), new Point2(0.478, 0.10921535199999999), new Point2(0.479, 0.10990223899999998), new Point2(0.48, 0.110592), new Point2(0.481, 0.11128464099999999), new Point2(0.482, 0.11198016799999999), new Point2(0.483, 0.112678587), new Point2(0.484, 0.11337990399999999), new Point2(0.485, 0.114084125), new Point2(0.486, 0.114791256), new Point2(0.487, 0.115501303), new Point2(0.488, 0.116214272), new Point2(0.489, 0.116930169), new Point2(0.49, 0.11764899999999999), new Point2(0.491, 0.118370771), new Point2(0.492, 0.119095488), new Point2(0.493, 0.11982315699999999), new Point2(0.494, 0.120553784), new Point2(0.495, 0.12128737499999999), new Point2(0.496, 0.12202393599999999), new Point2(0.497, 0.122763473), new Point2(0.498, 0.123505992), new Point2(0.499, 0.124251499), new Point2(0.5, 0.125), new Point2(0.501, 0.125751501), new Point2(0.502, 0.126506008), new Point2(0.503, 0.127263527), new Point2(0.504, 0.12802406400000002), new Point2(0.505, 0.128787625), new Point2(0.506, 0.129554216), new Point2(0.507, 0.13032384300000002), new Point2(0.508, 0.131096512), new Point2(0.509, 0.131872229), new Point2(0.51, 0.132651), new Point2(0.511, 0.133432831), new Point2(0.512, 0.134217728), new Point2(0.513, 0.135005697), new Point2(0.514, 0.135796744), new Point2(0.515, 0.136590875), new Point2(0.516, 0.137388096), new Point2(0.517, 0.138188413), new Point2(0.518, 0.138991832), new Point2(0.519, 0.139798359), new Point2(0.52, 0.140608), new Point2(0.521, 0.14142076100000003), new Point2(0.522, 0.14223664800000002), new Point2(0.523, 0.14305566700000003), new Point2(0.524, 0.14387782400000002), new Point2(0.525, 0.14470312500000002), new Point2(0.526, 0.14553157600000002), new Point2(0.527, 0.146363183), new Point2(0.528, 0.14719795200000002), new Point2(0.529, 0.148035889), new Point2(0.53, 0.14887700000000004), new Point2(0.531, 0.149721291), new Point2(0.532, 0.15056876800000005), new Point2(0.533, 0.15141943700000002), new Point2(0.534, 0.15227330400000003), new Point2(0.535, 0.153130375), new Point2(0.536, 0.15399065600000003), new Point2(0.537, 0.15485415300000002), new Point2(0.538, 0.15572087200000004), new Point2(0.539, 0.15659081900000002), new Point2(0.54, 0.15746400000000002), new Point2(0.541, 0.15834042100000004), new Point2(0.542, 0.15922008800000004), new Point2(0.543, 0.16010300700000002), new Point2(0.544, 0.16098918400000004), new Point2(0.545, 0.16187862500000003), new Point2(0.546, 0.16277133600000004), new Point2(0.547, 0.16366732300000003), new Point2(0.548, 0.16456659200000004), new Point2(0.549, 0.16546914900000004), new Point2(0.55, 0.16637500000000005), new Point2(0.551, 0.16728415100000005), new Point2(0.552, 0.16819660800000003), new Point2(0.553, 0.16911237700000004), new Point2(0.554, 0.17003146400000005), new Point2(0.555, 0.17095387500000003), new Point2(0.556, 0.17187961600000007), new Point2(0.557, 0.17280869300000004), new Point2(0.558, 0.17374111200000006), new Point2(0.559, 0.17467687900000006), new Point2(0.56, 0.17561600000000005), new Point2(0.561, 0.17655848100000007), new Point2(0.562, 0.17750432800000004), new Point2(0.563, 0.17845354699999996), new Point2(0.564, 0.17940614399999993), new Point2(0.565, 0.18036212499999993), new Point2(0.566, 0.18132149599999994), new Point2(0.567, 0.18228426299999997), new Point2(0.568, 0.18325043199999996), new Point2(0.569, 0.18422000899999996), new Point2(0.57, 0.18519299999999997), new Point2(0.571, 0.18616941099999998), new Point2(0.572, 0.18714924799999993), new Point2(0.573, 0.18813251699999994), new Point2(0.574, 0.18911922399999995), new Point2(0.575, 0.19010937499999994), new Point2(0.576, 0.19110297599999995), new Point2(0.577, 0.19210003299999998), new Point2(0.578, 0.19310055199999995), new Point2(0.579, 0.19410453899999996), new Point2(0.58, 0.19511199999999998), new Point2(0.581, 0.19612294099999997), new Point2(0.582, 0.19713736799999998), new Point2(0.583, 0.19815528699999996), new Point2(0.584, 0.19917670399999998), new Point2(0.585, 0.20020162499999997), new Point2(0.586, 0.20123005599999996), new Point2(0.587, 0.20226200299999997), new Point2(0.588, 0.20329747199999995), new Point2(0.589, 0.20433646899999997), new Point2(0.59, 0.20537899999999998), new Point2(0.591, 0.20642507099999996), new Point2(0.592, 0.20747468799999996), new Point2(0.593, 0.20852785699999998), new Point2(0.594, 0.209584584), new Point2(0.595, 0.21064487499999998), new Point2(0.596, 0.21170873599999998), new Point2(0.597, 0.21277617299999999), new Point2(0.598, 0.213847192), new Point2(0.599, 0.21492179899999997), new Point2(0.6, 0.216), new Point2(0.601, 0.217081801), new Point2(0.602, 0.21816720799999997), new Point2(0.603, 0.21925622699999997), new Point2(0.604, 0.22034886399999998), new Point2(0.605, 0.221445125), new Point2(0.606, 0.22254501599999998), new Point2(0.607, 0.22364854299999998), new Point2(0.608, 0.224755712), new Point2(0.609, 0.22586652899999998), new Point2(0.61, 0.226981), new Point2(0.611, 0.22809913099999998), new Point2(0.612, 0.229220928), new Point2(0.613, 0.23034639699999998), new Point2(0.614, 0.231475544), new Point2(0.615, 0.23260837499999998), new Point2(0.616, 0.233744896), new Point2(0.617, 0.234885113), new Point2(0.618, 0.236029032), new Point2(0.619, 0.23717665899999998), new Point2(0.62, 0.238328), new Point2(0.621, 0.239483061), new Point2(0.622, 0.240641848), new Point2(0.623, 0.241804367), new Point2(0.624, 0.242970624), new Point2(0.625, 0.244140625), new Point2(0.626, 0.245314376), new Point2(0.627, 0.246491883), new Point2(0.628, 0.247673152), new Point2(0.629, 0.248858189), new Point2(0.63, 0.250047), new Point2(0.631, 0.251239591), new Point2(0.632, 0.252435968), new Point2(0.633, 0.25363613700000004), new Point2(0.634, 0.254840104), new Point2(0.635, 0.256047875), new Point2(0.636, 0.257259456), new Point2(0.637, 0.258474853), new Point2(0.638, 0.259694072), new Point2(0.639, 0.260917119), new Point2(0.64, 0.26214400000000004), new Point2(0.641, 0.263374721), new Point2(0.642, 0.264609288), new Point2(0.643, 0.265847707), new Point2(0.644, 0.26708998400000006), new Point2(0.645, 0.26833612500000004), new Point2(0.646, 0.26958613600000003), new Point2(0.647, 0.27084002300000004), new Point2(0.648, 0.272097792), new Point2(0.649, 0.27335944900000003), new Point2(0.65, 0.27462500000000006), new Point2(0.651, 0.27589445100000004), new Point2(0.652, 0.277167808), new Point2(0.653, 0.27844507700000004), new Point2(0.654, 0.27972626400000006), new Point2(0.655, 0.28101137500000006), new Point2(0.656, 0.28230041600000005), new Point2(0.657, 0.283593393), new Point2(0.658, 0.28489031200000003), new Point2(0.659, 0.286191179), new Point2(0.66, 0.28749600000000003), new Point2(0.661, 0.28880478100000007), new Point2(0.662, 0.290117528), new Point2(0.663, 0.29143424700000004), new Point2(0.664, 0.2927549440000001), new Point2(0.665, 0.294079625), new Point2(0.666, 0.29540829600000007), new Point2(0.667, 0.296740963), new Point2(0.668, 0.29807763200000004), new Point2(0.669, 0.29941830900000005), new Point2(0.67, 0.30076300000000006), new Point2(0.671, 0.30211171100000006), new Point2(0.672, 0.30346444800000005), new Point2(0.673, 0.3048212170000001), new Point2(0.674, 0.30618202400000005), new Point2(0.675, 0.30754687500000005), new Point2(0.676, 0.30891577600000003), new Point2(0.677, 0.31028873300000004), new Point2(0.678, 0.3116657520000001), new Point2(0.679, 0.3130468390000001), new Point2(0.68, 0.3144320000000001), new Point2(0.681, 0.3158212410000001), new Point2(0.682, 0.3172145680000001), new Point2(0.683, 0.3186119870000001), new Point2(0.684, 0.32001350400000006), new Point2(0.685, 0.3214191250000001), new Point2(0.686, 0.32282885600000005), new Point2(0.687, 0.3242427030000001), new Point2(0.688, 0.32566067199999993), new Point2(0.689, 0.32708276899999994), new Point2(0.69, 0.32850899999999994), new Point2(0.691, 0.3299393709999999), new Point2(0.692, 0.33137388799999995), new Point2(0.693, 0.3328125569999999), new Point2(0.694, 0.33425538399999993), new Point2(0.695, 0.33570237499999994), new Point2(0.696, 0.3371535359999999), new Point2(0.697, 0.3386088729999999), new Point2(0.698, 0.34006839199999994), new Point2(0.699, 0.3415320989999999), new Point2(0.7, 0.3429999999999999), new Point2(0.701, 0.3444721009999999), new Point2(0.702, 0.34594840799999993), new Point2(0.703, 0.34742892699999994), new Point2(0.704, 0.34891366399999996), new Point2(0.705, 0.3504026249999999), new Point2(0.706, 0.3518958159999999), new Point2(0.707, 0.3533932429999999), new Point2(0.708, 0.35489491199999995), new Point2(0.709, 0.3564008289999999), new Point2(0.71, 0.357911), new Point2(0.711, 0.35942543099999996), new Point2(0.712, 0.3609441279999999), new Point2(0.713, 0.36246709699999996), new Point2(0.714, 0.3639943439999999), new Point2(0.715, 0.36552587499999994), new Point2(0.716, 0.367061696), new Point2(0.717, 0.36860181299999994), new Point2(0.718, 0.37014623199999996), new Point2(0.719, 0.371694959), new Point2(0.72, 0.37324799999999997), new Point2(0.721, 0.374805361), new Point2(0.722, 0.376367048), new Point2(0.723, 0.377933067), new Point2(0.724, 0.379503424), new Point2(0.725, 0.381078125), new Point2(0.726, 0.382657176), new Point2(0.727, 0.3842405829999999), new Point2(0.728, 0.385828352), new Point2(0.729, 0.3874204889999999), new Point2(0.73, 0.38901699999999995), new Point2(0.731, 0.390617891), new Point2(0.732, 0.39222316799999996), new Point2(0.733, 0.393832837), new Point2(0.734, 0.395446904), new Point2(0.735, 0.39706537499999994), new Point2(0.736, 0.39868825599999996), new Point2(0.737, 0.400315553), new Point2(0.738, 0.401947272), new Point2(0.739, 0.40358341899999994), new Point2(0.74, 0.405224), new Point2(0.741, 0.40686902100000005), new Point2(0.742, 0.40851848799999996), new Point2(0.743, 0.410172407), new Point2(0.744, 0.411830784), new Point2(0.745, 0.413493625), new Point2(0.746, 0.415160936), new Point2(0.747, 0.41683272299999996), new Point2(0.748, 0.418508992), new Point2(0.749, 0.420189749), new Point2(0.75, 0.421875), new Point2(0.751, 0.42356475099999996), new Point2(0.752, 0.425259008), new Point2(0.753, 0.426957777), new Point2(0.754, 0.42866106400000004), new Point2(0.755, 0.430368875), new Point2(0.756, 0.43208121600000005), new Point2(0.757, 0.43379809300000005), new Point2(0.758, 0.435519512), new Point2(0.759, 0.43724547900000005), new Point2(0.76, 0.43897600000000003), new Point2(0.761, 0.440711081), new Point2(0.762, 0.44245072800000007), new Point2(0.763, 0.44419494700000006), new Point2(0.764, 0.445943744), new Point2(0.765, 0.44769712500000003), new Point2(0.766, 0.44945509600000005), new Point2(0.767, 0.45121766300000005), new Point2(0.768, 0.452984832), new Point2(0.769, 0.45475660900000003), new Point2(0.77, 0.456533), new Point2(0.771, 0.458314011), new Point2(0.772, 0.4600996480000001), new Point2(0.773, 0.461889917), new Point2(0.774, 0.46368482400000005), new Point2(0.775, 0.46548437500000006), new Point2(0.776, 0.46728857600000007), new Point2(0.777, 0.46909743300000006), new Point2(0.778, 0.47091095200000005), new Point2(0.779, 0.4727291390000001), new Point2(0.78, 0.47455200000000003), new Point2(0.781, 0.4763795410000001), new Point2(0.782, 0.4782117680000001), new Point2(0.783, 0.48004868700000003), new Point2(0.784, 0.4818903040000001), new Point2(0.785, 0.48373662500000003), new Point2(0.786, 0.48558765600000003), new Point2(0.787, 0.48744340300000005), new Point2(0.788, 0.4893038720000001), new Point2(0.789, 0.4911690690000001), new Point2(0.79, 0.4930390000000001), new Point2(0.791, 0.49491367100000005), new Point2(0.792, 0.49679308800000005), new Point2(0.793, 0.4986772570000001), new Point2(0.794, 0.5005661840000001), new Point2(0.795, 0.5024598750000001), new Point2(0.796, 0.5043583360000001), new Point2(0.797, 0.5062615730000001), new Point2(0.798, 0.508169592), new Point2(0.799, 0.5100823990000001), new Point2(0.8, 0.5120000000000001), new Point2(0.801, 0.5139224010000001), new Point2(0.802, 0.5158496080000001), new Point2(0.803, 0.5177816270000001), new Point2(0.804, 0.5197184640000001), new Point2(0.805, 0.5216601250000001), new Point2(0.806, 0.5236066160000001), new Point2(0.807, 0.5255579430000001), new Point2(0.808, 0.5275141120000001), new Point2(0.809, 0.5294751290000002), new Point2(0.81, 0.5314410000000002), new Point2(0.811, 0.5334117310000002), new Point2(0.812, 0.5353873280000001), new Point2(0.813, 0.5373677969999999), new Point2(0.814, 0.5393531439999999), new Point2(0.815, 0.541343375), new Point2(0.816, 0.5433384959999998), new Point2(0.817, 0.5453385129999999), new Point2(0.818, 0.5473434319999999), new Point2(0.819, 0.5493532589999999), new Point2(0.82, 0.5513679999999999), new Point2(0.821, 0.5533876609999999), new Point2(0.822, 0.5554122479999999), new Point2(0.823, 0.5574417669999999), new Point2(0.824, 0.5594762239999999), new Point2(0.825, 0.561515625), new Point2(0.826, 0.5635599759999999), new Point2(0.827, 0.5656092829999999), new Point2(0.828, 0.567663552), new Point2(0.829, 0.5697227889999998), new Point2(0.83, 0.5717869999999999), new Point2(0.831, 0.573856191), new Point2(0.832, 0.575930368), new Point2(0.833, 0.5780095369999999), new Point2(0.834, 0.5800937039999999), new Point2(0.835, 0.582182875), new Point2(0.836, 0.5842770559999999), new Point2(0.837, 0.586376253), new Point2(0.838, 0.588480472), new Point2(0.839, 0.5905897189999999), new Point2(0.84, 0.5927039999999999), new Point2(0.841, 0.5948233209999999), new Point2(0.842, 0.5969476879999999), new Point2(0.843, 0.5990771069999999), new Point2(0.844, 0.601211584), new Point2(0.845, 0.6033511249999999), new Point2(0.846, 0.605495736), new Point2(0.847, 0.6076454229999999), new Point2(0.848, 0.6098001919999999), new Point2(0.849, 0.6119600489999999), new Point2(0.85, 0.6141249999999999), new Point2(0.851, 0.6162950509999999), new Point2(0.852, 0.618470208), new Point2(0.853, 0.620650477), new Point2(0.854, 0.6228358639999999), new Point2(0.855, 0.6250263749999999), new Point2(0.856, 0.6272220159999999), new Point2(0.857, 0.629422793), new Point2(0.858, 0.6316287119999999), new Point2(0.859, 0.633839779), new Point2(0.86, 0.636056), new Point2(0.861, 0.638277381), new Point2(0.862, 0.6405039279999999), new Point2(0.863, 0.642735647), new Point2(0.864, 0.6449725439999999), new Point2(0.865, 0.647214625), new Point2(0.866, 0.6494618959999999), new Point2(0.867, 0.651714363), new Point2(0.868, 0.653972032), new Point2(0.869, 0.6562349089999999), new Point2(0.87, 0.6585030000000001), new Point2(0.871, 0.660776311), new Point2(0.872, 0.6630548479999999), new Point2(0.873, 0.665338617), new Point2(0.874, 0.667627624), new Point2(0.875, 0.669921875), new Point2(0.876, 0.672221376), new Point2(0.877, 0.674526133), new Point2(0.878, 0.676836152), new Point2(0.879, 0.679151439), new Point2(0.88, 0.681472), new Point2(0.881, 0.683797841), new Point2(0.882, 0.686128968), new Point2(0.883, 0.6884653869999999), new Point2(0.884, 0.690807104), new Point2(0.885, 0.6931541250000001), new Point2(0.886, 0.695506456), new Point2(0.887, 0.697864103), new Point2(0.888, 0.700227072), new Point2(0.889, 0.7025953690000001), new Point2(0.89, 0.7049690000000001), new Point2(0.891, 0.707347971), new Point2(0.892, 0.7097322880000001), new Point2(0.893, 0.712121957), new Point2(0.894, 0.7145169840000001), new Point2(0.895, 0.716917375), new Point2(0.896, 0.7193231360000001), new Point2(0.897, 0.721734273), new Point2(0.898, 0.724150792), new Point2(0.899, 0.7265726990000001), new Point2(0.9, 0.7290000000000001), new Point2(0.901, 0.731432701), new Point2(0.902, 0.733870808), new Point2(0.903, 0.7363143270000001), new Point2(0.904, 0.738763264), new Point2(0.905, 0.741217625), new Point2(0.906, 0.743677416), new Point2(0.907, 0.7461426430000001), new Point2(0.908, 0.7486133120000001), new Point2(0.909, 0.7510894290000001), new Point2(0.91, 0.7535710000000001), new Point2(0.911, 0.756058031), new Point2(0.912, 0.7585505280000001), new Point2(0.913, 0.7610484970000001), new Point2(0.914, 0.763551944), new Point2(0.915, 0.7660608750000001), new Point2(0.916, 0.7685752960000001), new Point2(0.917, 0.7710952130000002), new Point2(0.918, 0.7736206320000001), new Point2(0.919, 0.7761515590000001), new Point2(0.92, 0.778688), new Point2(0.921, 0.7812299610000001), new Point2(0.922, 0.783777448), new Point2(0.923, 0.7863304670000001), new Point2(0.924, 0.7888890240000002), new Point2(0.925, 0.7914531250000001), new Point2(0.926, 0.7940227760000002), new Point2(0.927, 0.7965979830000002), new Point2(0.928, 0.7991787520000001), new Point2(0.929, 0.8017650890000001), new Point2(0.93, 0.8043570000000001), new Point2(0.931, 0.8069544910000002), new Point2(0.932, 0.8095575680000001), new Point2(0.933, 0.8121662370000001), new Point2(0.934, 0.8147805040000001), new Point2(0.935, 0.8174003750000002), new Point2(0.936, 0.8200258560000001), new Point2(0.937, 0.8226569530000002), new Point2(0.938, 0.8252936719999998), new Point2(0.939, 0.8279360189999998), new Point2(0.94, 0.8305839999999999), new Point2(0.941, 0.8332376209999998), new Point2(0.942, 0.8358968879999998), new Point2(0.943, 0.8385618069999999), new Point2(0.944, 0.8412323839999999), new Point2(0.945, 0.843908625), new Point2(0.946, 0.8465905359999999), new Point2(0.947, 0.8492781229999998), new Point2(0.948, 0.8519713919999999), new Point2(0.949, 0.8546703489999998), new Point2(0.95, 0.8573749999999999), new Point2(0.951, 0.8600853509999998), new Point2(0.952, 0.8628014079999998), new Point2(0.953, 0.8655231769999999), new Point2(0.954, 0.8682506639999998), new Point2(0.955, 0.870983875), new Point2(0.956, 0.8737228159999999), new Point2(0.957, 0.8764674929999999), new Point2(0.958, 0.8792179119999999), new Point2(0.959, 0.8819740789999999), new Point2(0.96, 0.884736), new Point2(0.961, 0.8875036809999999), new Point2(0.962, 0.8902771279999999), new Point2(0.963, 0.8930563469999998), new Point2(0.964, 0.8958413439999999), new Point2(0.965, 0.8986321249999999), new Point2(0.966, 0.901428696), new Point2(0.967, 0.9042310629999999), new Point2(0.968, 0.9070392319999999), new Point2(0.969, 0.9098532089999999), new Point2(0.97, 0.912673), new Point2(0.971, 0.915498611), new Point2(0.972, 0.918330048), new Point2(0.973, 0.9211673169999999), new Point2(0.974, 0.924010424), new Point2(0.975, 0.9268593749999999), new Point2(0.976, 0.929714176), new Point2(0.977, 0.932574833), new Point2(0.978, 0.935441352), new Point2(0.979, 0.9383137389999999), new Point2(0.98, 0.9411919999999999), new Point2(0.981, 0.944076141), new Point2(0.982, 0.946966168), new Point2(0.983, 0.949862087), new Point2(0.984, 0.952763904), new Point2(0.985, 0.955671625), new Point2(0.986, 0.9585852559999999), new Point2(0.987, 0.961504803), new Point2(0.988, 0.964430272), new Point2(0.989, 0.967361669), new Point2(0.99, 0.9702989999999999), new Point2(0.991, 0.9732422709999999), new Point2(0.992, 0.9761914879999999), new Point2(0.993, 0.9791466569999999), new Point2(0.994, 0.982107784), new Point2(0.995, 0.985074875), new Point2(0.996, 0.988047936), new Point2(0.997, 0.991026973), new Point2(0.998, 0.994011992), new Point2(0.999, 0.997002999), new Point2(1, 1),
],
error = 0.01; /* Squared error */
FitCurve(d, d.length, error); /* Fit the Bezier curves */
}
} //endif /* TESTMODE */
/**
* FitCurve :
* Fit a Bezier curve to a set of digitized points
* @param {Array.<Point2>} d Array of digitized points.
* @param {Number} nPts Number of digitized points
* @param {Number} error User-defined error squared
*/
function FitCurve(d, nPts, error)
{
var tHat1 = new Vector2(), tHat2 = new Vector2(); /* Unit tangent vectors at endpoints */
tHat1 = ComputeLeftTangent(d, 0);
tHat2 = ComputeRightTangent(d, nPts - 1);
FitCubic(d, 0, nPts - 1, tHat1, tHat2, error);
}
/**
* FitCubic :
* Fit a Bezier curve to a (sub)set of digitized points
* @param {Array.<Point2>} d Array of digitized points
* @param {Number} first Indices of first pts in region
* @param {Number} last Indices of last pts in region
* @param {Point2} tHat1 Unit tangent vectors at endpoints
* @param {Point2} tHat2 Unit tangent vectors at endpoints
* @param {Number} error User-defined error squared
*/
function FitCubic(d, first, last, tHat1, tHat2, error)
{
var bezCurve, /*Control points of fitted Bezier curve*/
u = [], /* Parameter values for point */
uPrime = [], /* Improved parameter values */
maxError, /* Maximum fitting error */
splitPoint, /* Point to split point set at */
nPts, /* Number of points in subset */
iterationError, /*Error below which you try iterating */
maxIterations = 10, /* Max times to try iterating */
tHatCenter = new Vector2(), /* Unit tangent vector at splitPoint */
i;
iterationError = error * error;
nPts = last - first + 1;
/* Use heuristic if region only has two points in it */
if (nPts == 2) {
var dist = V2DistanceBetween2Points(d[last], d[first]) / 3.0;
bezCurve = [];
bezCurve[0] = d[first];
bezCurve[3] = d[last];
tHat1 = V2Scale(tHat1, dist);
tHat2 = V2Scale(tHat2, dist);
bezCurve[1] = V2Add(bezCurve[0], tHat1);
bezCurve[2] = V2Add(bezCurve[3], tHat2);
DrawBezierCurve(3, bezCurve);
return;
}
/* Parameterize points, and attempt to fit curve */
u = ChordLengthParameterize(d, first, last);
bezCurve = GenerateBezier(d, first, last, u, tHat1, tHat2);
/* Find max deviation of points to fitted curve */
var resultMaxError = ComputeMaxError(d, first, last, bezCurve, u, splitPoint);
maxError = resultMaxError.maxError;
splitPoint = resultMaxError.splitPoint;
if (maxError < error) {
DrawBezierCurve(3, bezCurve);
return;
}
/* If error not too large, try some reparameterization */
/* and iteration */
if (maxError < iterationError) {
for (i = 0; i < maxIterations; i++) {
uPrime = Reparameterize(d, first, last, u, bezCurve);
bezCurve = GenerateBezier(d, first, last, uPrime, tHat1, tHat2);
resultMaxError = ComputeMaxError(d, first, last,
bezCurve, uPrime, splitPoint);
maxError = resultMaxError.maxError;
splitPoint = resultMaxError.splitPoint;
if (maxError < error) {
DrawBezierCurve(3, bezCurve);
return;
}
u = uPrime;
}
}
/* Fitting failed -- split at max error point and fit recursively */
tHatCenter = ComputeCenterTangent(d, splitPoint);
FitCubic(d, first, splitPoint, tHat1, tHatCenter, error);
tHatCenter = V2Negate(tHatCenter);
FitCubic(d, splitPoint, last, tHatCenter, tHat2, error);
}
/**
* GenerateBezier :
* Use least-squares method to find Bezier control points for region.
* @param {Array.<Point2>} d Array of digitized points
* @param {Number} first Indices defining region
* @param {Number} last Indices defining region
* @param {Array.<Number>} uPrime Parameter values for region
* @param {Vector2} tHat1 Unit tangents at endpoints
* @param {Vector2} tHat2 Unit tangents at endpoints
* @return {Array.<Point2> BezierCurve
*/
function GenerateBezier(d, first, last, uPrime, tHat1, tHat2)
{
var i,
A = [], /* Precomputed rhs for eqn */
nPts, /* Number of pts in sub-curve */
C = [[], []], /* Matrix C */
X = [], /* Matrix X */
det_C0_C1, /* Determinants of matrices */
det_C0_X,
det_X_C1,
alpha_l, /* Alpha values, left and right */
alpha_r,
tmp = new Vector2(), /* Utility variable */
bezCurve; /* RETURN bezier curve ctl pts */
bezCurve = [];
nPts = last - first + 1;
/* Compute the A's */
for (i = 0; i < nPts; i++) {
var v1 = new Vector2(tHat1.x, tHat1.y),
v2 = new Vector2(tHat2.x, tHat2.y);
v1 = V2Scale(v1, B1(uPrime[i]));
v2 = V2Scale(v2, B2(uPrime[i]));
A[i] = [];
A[i][0] = v1;
A[i][1] = v2;
}
/* Create the C and X matrices */
C[0][0] = 0.0;
C[0][1] = 0.0;
C[1][0] = 0.0;
C[1][1] = 0.0;
X[0] = 0.0;
X[1] = 0.0;
for (i = 0; i < nPts; i++) {
C[0][0] += V2Dot(A[i][0], A[i][0]);
C[0][1] += V2Dot(A[i][0], A[i][1]);
// C[1][0] += V2Dot(A[i][0], A[i][1]);
C[1][0] = C[0][1];
C[1][1] += V2Dot(A[i][1], A[i][1]);
tmp = V2SubII(d[first + i],
V2AddII(
V2ScaleIII(d[first], B0(uPrime[i])),
V2AddII(
V2ScaleIII(d[first], B1(uPrime[i])),
V2AddII(
V2ScaleIII(d[last], B2(uPrime[i])),
V2ScaleIII(d[last], B3(uPrime[i]))))));
X[0] += V2Dot(A[i][0], tmp);
X[1] += V2Dot(A[i][1], tmp);
}
/* Compute the determinants of C and X */
det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
det_C0_X = C[0][0] * X[1] - C[1][0] * X[0];
det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
/* Finally, derive alpha values */
alpha_l = (det_C0_C1 == 0) ? 0.0 : det_X_C1 / det_C0_C1;
alpha_r = (det_C0_C1 == 0) ? 0.0 : det_C0_X / det_C0_C1;
/* If alpha negative, use the Wu/Barsky heuristic (see text) */
/* (if alpha is 0, you get coincident control points that lead to
* divide by zero in any subsequent NewtonRaphsonRootFind() call. */
var segLength = V2DistanceBetween2Points(d[last], d[first]);
var epsilon = 1.0e-6 * segLength;
if (alpha_l < epsilon || alpha_r < epsilon)
{
/* fall back on standard (probably inaccurate) formula, and subdivide further if needed. */
var dist = segLength / 3.0;
bezCurve[0] = d[first];
bezCurve[3] = d[last];
bezCurve[1] = V2Add(bezCurve[0], V2Scale(tHat1, dist));
bezCurve[2] = V2Add(bezCurve[3], V2Scale(tHat2, dist));
return (bezCurve);
}
/* First and last control points of the Bezier curve are */
/* positioned exactly at the first and last data points */
/* Control points 1 and 2 are positioned an alpha distance out */
/* on the tangent vectors, left and right, respectively */
bezCurve[0] = d[first];
bezCurve[3] = d[last];
bezCurve[1] = V2Add(bezCurve[0], V2Scale(tHat1, alpha_l));
bezCurve[2] = V2Add(bezCurve[3], V2Scale(tHat2, alpha_r));
return (bezCurve);
}
/**
* Reparameterize:
* Given set of points and their parameterization, try to find
* a better parameterization.
* @param {Array.<Point2>} d Array of digitized points
* @param {Number} first Indices defining region
* @param {Number} last Indices defining region
* @param {Array.<Number>} u Current parameter values
* @param {Array.<Point2>} bezCurve Current fitted curve
* @return {Number}
*/
function Reparameterize(d, first, last, u, bezCurve)
{
var nPts = last-first+1,
i,
uPrime = [], /* New parameter values */
_bezCurve = [
new Point2(bezCurve[0].x, bezCurve[0].y),
new Point2(bezCurve[1].x, bezCurve[1].y),
new Point2(bezCurve[2].x, bezCurve[2].y),
new Point2(bezCurve[3].x, bezCurve[3].y)
];
for (i = first; i <= last; i++) {
uPrime[i-first] = NewtonRaphsonRootFind(_bezCurve, d[i], u[i-
first]);
}
return (uPrime);
}
/**
* NewtonRaphsonRootFind :
* Use Newton-Raphson iteration to find better root.
* @param {Array.<Point2>} _Q Current fitted curve
* @param {Point2} _P Digitized point
* @param {Number} u Parameter value for "P"
* @return {Number}
*/
function NewtonRaphsonRootFind(_Q, _P, u)
{
var numerator, denominator,
Q1 = [new Point2(), new Point2(), new Point2()], /* Q' and Q'' */
Q2 = [new Point2(), new Point2()],
Q_u = new Point2(), Q1_u = new Point2(), Q2_u = new Point2(), /*u evaluated at Q, Q', & Q'' */
uPrime, /* Improved u */
i,
Q = [
new Point2(_Q[0].x, _Q[0].y),
new Point2(_Q[1].x, _Q[1].y),
new Point2(_Q[2].x, _Q[2].y),
new Point2(_Q[3].x, _Q[3].y),
],
P = new Point2(_P.x, _P.y);
/* Compute Q(u) */
Q_u = BezierII(3, Q, u);
/* Generate control vertices for Q' */
for (i = 0; i <= 2; i++) {
Q1[i].x = (Q[i+1].x - Q[i].x) * 3.0;
Q1[i].y = (Q[i+1].y - Q[i].y) * 3.0;
}
/* Generate control vertices for Q'' */
for (i = 0; i <= 1; i++) {
Q2[i].x = (Q1[i+1].x - Q1[i].x) * 2.0;
Q2[i].y = (Q1[i+1].y - Q1[i].y) * 2.0;
}
/* Compute Q'(u) and Q''(u) */
Q1_u = BezierII(2, Q1, u);
Q2_u = BezierII(1, Q2, u);
/* Compute f(u)/f'(u) */
numerator = (Q_u.x - P.x) * (Q1_u.x) + (Q_u.y - P.y) * (Q1_u.y);
denominator = (Q1_u.x) * (Q1_u.x) + (Q1_u.y) * (Q1_u.y) +
(Q_u.x - P.x) * (Q2_u.x) + (Q_u.y - P.y) * (Q2_u.y);
if (denominator == 0.0) return u;
/* u = u - f(u)/f'(u) */
uPrime = u - (numerator/denominator);
return (uPrime);
}
/**
* Bezier :
* Evaluate a Bezier curve at a particular parameter value
* @param {Number} degree The degree of the bezier curve
* @param {Array.<Point2>} V Array of control points
* @param {Number} t Parametric value to find point for
* @return {Point2}
*/
function BezierII(degree, V, t)
{
var i, j,
Q, /* Point on curve at parameter t */
Vtemp = []; /* Local copy of control points */
/* Copy array */
for (i = 0; i <= degree; i++) {
Vtemp[i] = new Point2(V[i].x, V[i].y);
}
/* Triangle computation */
for (i = 1; i <= degree; i++) {
for (j = 0; j <= degree-i; j++) {
Vtemp[j].x = (1.0 - t) * Vtemp[j].x + t * Vtemp[j+1].x;
Vtemp[j].y = (1.0 - t) * Vtemp[j].y + t * Vtemp[j+1].y;
}
}
Q = new Point2(Vtemp[0].x, Vtemp[0].y);
return Q;
}
/*
* B0, B1, B2, B3 :
* Bezier multipliers
*/
function B0(u)
{
var tmp = 1.0 - u;
return (tmp * tmp * tmp);
}
function B1(u)
{
var tmp = 1.0 - u;
return (3 * u * (tmp * tmp));
}
function B2(u)
{
var tmp = 1.0 - u;
return (3 * u * u * tmp);
}
function B3(u)
{
return (u * u * u);
}
/**
* ComputeLeftTangent, ComputeRightTangent, ComputeCenterTangent :
* Approximate unit tangents at endpoints and "center" of digitized curve
*/
/**
* @param {Array.<Point2>} d Digitized points.
* @param {Number} end Index to "left" end of region.
* @return {Vector2}
*/
function ComputeLeftTangent(d, end)
{
var tHat1 = new Vector2();
tHat1 = V2SubII(d[end+1], d[end]);
tHat1 = V2Normalize(tHat1);
return tHat1;
}
/**
* @param {Array.<Point2>} d Digitized points.
* @param {Number} end Index to "right" end of region.
* @return {Vector2}
*/
function ComputeRightTangent(d, end)
{
var tHat2 = new Vector2();
tHat2 = V2SubII(d[end-1], d[end]);
tHat2 = V2Normalize(tHat2);
return tHat2;
}
/**
* @param {Array.<Point2>} d Digitized points.
* @param {Number} end Index to point inside region.
* @return {Vector2}
*/
function ComputeCenterTangent(d, center)
{
var V1 = new Vector2(), V2 = new Vector2(), tHatCenter = new Vector2();
V1 = V2SubII(d[center-1], d[center]);
V2 = V2SubII(d[center], d[center+1]);
tHatCenter.x = (V1.x + V2.x)/2.0;
tHatCenter.y = (V1.y + V2.y)/2.0;
tHatCenter = V2Normalize(tHatCenter);
return tHatCenter;
}
/**
* ChordLengthParameterize :
* Assign parameter values to digitized points
* using relative distances between points.
* @param {Array.<Point2>} d Array of digitized points
* @param {Number} first Indices defining region
* @param {Number} last Indices defining region
* @return {Number}
*/
function ChordLengthParameterize(d, first, last)
{
var i,
u; /* Parameterization */
u = [];
u[0] = 0.0;
for (i = first+1; i <= last; i++) {
u[i-first] = u[i-first-1] +
V2DistanceBetween2Points(d[i], d[i-1]);
}
for (i = first + 1; i <= last; i++) {
u[i-first] = u[i-first] / u[last-first];
}
return u;
}
/**
* ComputeMaxError :
* Find the maximum squared distance of digitized points
* to fitted curve.
* @param {Array.<Point2>} d Array of digitized points
* @param {Number} first Indices defining region
* @param {Number} last Indices defining region
* @param {Array.<Point2>} bezCurve Fitted Bezier curve
* @param {Array.<Number>} u Parameterization of points
* @param {Number} splitPoint Point of maximum error
*/
function ComputeMaxError(d, first, last, bezCurve, u, splitPoint)
{
var i,
maxDist, /* Maximum error */
dist, /* Current error */
P = new Point2(), /* Point on curve */
v = new Vector2(); /* Vector from point to curve */
splitPoint = (last - first + 1)/2;
maxDist = 0.0;
for (i = first + 1; i < last; i++) {
P = BezierII(3, bezCurve, u[i-first]);
v = V2SubII(P, d[i]);
dist = V2SquaredLength(v);
if (dist >= maxDist) {
maxDist = dist;
splitPoint = i;
}
}
return {maxError: maxDist, splitPoint: splitPoint};
}
function V2AddII(a, b)
{
var c = new Vector2();
c.x = a.x + b.x; c.y = a.y + b.y;
return (c);
}
function V2ScaleIII(v, s)
{
var result = new Vector2();
result.x = v.x * s; result.y = v.y * s;
return (result);
}
function V2SubII(a, b)
{
var c = new Vector2();
c.x = a.x - b.x; c.y = a.y - b.y;
return c;
}
// include "GraphicsGems.h"
/*
* GraphicsGems.h
* Version 1.0 - Andrew Glassner
* from "Graphics Gems", Academic Press, 1990
*/
/*********************/
/* 2d geometry types */
/*********************/
function Point2Struct(x, y) { /* 2d point */
this.x = typeof x === 'number' ? x : 0;
this.y = typeof y === 'number' ? y : 0;
}
function Point2(x, y) {
Point2Struct.apply(this, arguments);
}
function Vector2(x, y) {
Point2Struct.apply(this, arguments);
}
/***********************/
/* two-argument macros */
/***********************/
/* linear interpolation from l (when a=0) to h (when a=1)*/
/* (equal to (a*h)+((1-a)*l) */
function LERP(a,l,h) { return ((l)+(((h)-(l))*(a))) }
/*
2d and 3d Vector C Library
by Andrew Glassner
from "Graphics Gems", Academic Press, 1990
*/
/******************/
/* 2d Library */
/******************/
/* returns squared length of input vector */
function V2SquaredLength(a)
{
return((a.x * a.x) + (a.y * a.y));
}
/* returns length of input vector */
function V2Length(a)
{
return(Math.sqrt(V2SquaredLength(a)));
}
/* negates the input vector and returns it */
function V2Negate(v)
{
var result = new Point2();
result.x = -v.x; result.y = -v.y;
return(result);
}
/* normalizes the input vector and returns it */
function V2Normalize(v)
{
var result = new Point2(),
len = V2Length(v);
if (len != 0.0) { result.x = v.x / len; result.y = v.y / len; }
return(result);
}
/* scales the input vector to the new length and returns it */
function V2Scale(v, newlen)
{
var result = new Point2(),
len = V2Length(v);
if (len != 0.0) { result.x = v.x * newlen/len; result.y = v.y * newlen/len; }
return(result);
}
/* return vector sum c = a+b */
function V2Add(a, b)
{
var c = new Point2();
c.x = a.x + b.x; c.y = a.y + b.y;
return(c);
}
/* return vector difference c = a-b */
function V2Sub(a, b)
{
var c = new Point2();
c.x = a.x - b.x; c.y = a.y - b.y;
return(c);
}
/* return the dot product of vectors a and b */
function V2Dot(a, b)
{
return((a.x * b.x) + (a.y * b.y));
}
/* make a linear combination of two vectors and return the result. */
/* result = (a * ascl) + (b * bscl) */
function V2Combine (a, b, result, ascl, bscl)
{
result.x = (ascl * a.x) + (bscl * b.x);
result.y = (ascl * a.y) + (bscl * b.y);
return(result);
}
/* multiply two vectors together component-wise */
function V2Mul (a, b, result)
{
result.x = a.x * b.x;
result.y = a.y * b.y;
return(result);
}
/* return the distance between two points */
function V2DistanceBetween2Points(a, b)
{
var dx = a.x - b.x;
var dy = a.y - b.y;
return(Math.sqrt((dx*dx)+(dy*dy)));
}
})(this);
<html>
<body>
<div style="position: fixed; top: 10px; left: 100px;">
<h2>
Cubic Bezier Approximations for Robert Penner Easing Equations</h2>
<div style="float: right;">
experiment by <a href="https://twitter.com/blurspline">@blurspline</a>. <a href="http://www.lab4games.net/zz85/blog/2014/12/26/better-cubic-bezier-approximations-for-robert-penner-easing-equations/">Read more</a>
</div>
</div>
<script src="easing.js"></script>
<script>
var w = 1000;
var h = 1000;
var w2 = w/2;
var h2 = h/2;
var canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
document.body.appendChild(canvas);
var select = document.createElement('select');
select.style.cssText = 'position: fixed; top: 75px; left: 15px;';
document.body.appendChild(select);
var select2 = document.createElement('select');
select2.style.cssText = 'position: fixed; top: 95px; left: 15px;';
document.body.appendChild(select2);
select2.add(new Option('BSEasing'));
select2.add(new Option('CeaserEasing'));
select2.add(new Option('KKEasing'));
select2.add(new Option('BSClippedEasing'));
function addOption(type, n) {
var option = new Option();
option.textContent = type + n;
option.value = type + n + '|' + Names[type] + n;
select.appendChild(option);
}
for (var name in Names) {
addOption(name, 'In');
addOption(name, 'Out');
addOption(name, 'InOut');
}
select.onchange = change;
select2.onchange = change;
function change() {
Approx = window[select2.value];
var v = select.value.split('|');
tweenType = Tween[v[1]];
params = Approx[v[0]];
checkErrors();
}
function checkErrors() {
errors = 0;
var units = 100;
var epsilon = 1e-5;
for (var i = 0; i < units; i++) {
var k = i / units;
var bezier = new UnitBezier(params[0], params[1], params[2], params[3]);
var diff = bezier.solve(k, epsilon) - tweenType(k);
errors += diff * diff;
}
}
var ctx = canvas.getContext('2d');
var varient = 'Quart', type = 'InOut';
var Approx = BSEasing; // CeaserEasing KKEasing BSEasing
var params;
var LOOP = 4;
var errors = 0;
var tweenType = Tween[Names[varient] + type];
params = Approx[varient + type];
change();
// Manual override if needed
// var params = CeaserEasing.QuadIn;
// params = [ 0.36, 0, 0.68, 0.04 ];
/*
// Use this block to generate points for fit_curve.js
var str = '';
var size = 1000;
for (var i = 0; i <= size; i++) {
var k = i / size;
var val = tweenType(k);
str += 'new Point2(' + k * 100 + ', ' + val * 100 + '), ';
}
console.log(str);
*/
function draw() {
ctx.save();
ctx.clearRect(0, 0, w, h);
ctx.translate(0, h);
ctx.scale(1, -1);
ctx.translate(250, 250);
ctx.strokeStyle = 'grey';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(w2, 0);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, h2);
ctx.stroke();
ctx.strokeStyle = 'rgba(50, 50, 50, 0.5)';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(w2, h2);
ctx.stroke();
ctx.globalAlpha = 0.8;
ctx.lineWidth = 4;
ctx.strokeStyle = 'red';
ctx.beginPath();
ctx.moveTo(0, 0);
for (var x = 0; x <= w2; x++) {
var t = x / w2;
// var k = Tween.CircularIn(t); // Circular
var k = tweenType(t);
ctx.lineTo(x, k * h2);
}
ctx.stroke();
ctx.strokeStyle = 'blue';
ctx.fillStyle = 'pink';
ctx.lineWidth = 1;
// control handles
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(params[0] * w2, params[1] * h2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(w2, h2);
ctx.lineTo(params[2] * w2, params[3] * h2);
ctx.stroke();
// control grips
ctx.beginPath();
ctx.arc(params[0] * w2, params[1] * h2, 10, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(params[2] * w2, params[3] * h2, 10, 0, Math.PI * 2);
ctx.fill();
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.bezierCurveTo(params[0] * w2, params[1] * h2, params[2] * w2, params[3] * h2, w2, h2);
ctx.stroke();
// Manual drawing of bezier
// ctx.strokeStyle = 'green';
// ctx.beginPath();
// ctx.moveTo(0, 0);
// for (var x = 0; x <= w2; x++) {
// var x2 = x / w2;
// var rx = cubicBezier(x2, 0, params[0], params[2], 1);
// var ry = cubicBezier(x2, 0, params[1], params[3], 1);
// ctx.lineTo(rx * w2, ry * h2);
// }
// ctx.stroke();
ctx.lineWidth = 1;
var k = Date.now() / 1000 % LOOP;
k /= LOOP;
var val = tweenType(k);
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(-15, val * h2,10, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = 'grey';
ctx.beginPath();
ctx.moveTo(k * w2, 0);
ctx.lineTo(k * w2, val * h2);
ctx.stroke();
ctx.strokeStyle = 'red';
ctx.beginPath();
ctx.moveTo(0, val * h2);
ctx.lineTo(k * w2, val * h2);
ctx.stroke();
// rx = cubicBezier(rx, 0, params[0], params[2], 1);
// ctx.fillStyle = 'blue';
// ctx.beginPath();
// ctx.arc(-55, rx * w2, 10, 0, Math.PI * 2);
// ctx.fill();
var duration = 20000;
var epsilon = (1000 / 60 / duration) / 4;
var epsilon = 1e-5;
var bezier = new UnitBezier(params[0], params[1], params[2], params[3]);
var r2 = bezier.solve(k, epsilon);
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(-35, r2 * h2, 10, 0, Math.PI * 2);
ctx.fill();
// moving lines
// ctx.beginPath();
// ctx.moveTo(k * w2, 0);
// ctx.lineTo(k * w2, r2 * h2);
// ctx.stroke();
ctx.strokeStyle = 'blue';
ctx.beginPath();
ctx.moveTo(0, r2 * h2);
ctx.lineTo(k * w2, r2 * h2);
ctx.stroke();
ctx.restore();
ctx.font = '14px sans-serif';
ctx.fillText('value', 250, 250);
ctx.fillText('time', 750, 750);
ctx.font = '20px sans-serif';
ctx.fillText('Cubic bezier: (' + params[0] + ','
+ params[1] + ') (' +
+ params[2] + ',' +
+ params[3] + ')', 250, 150);
ctx.font = '9px sans-serif';
ctx.fillText('Red is Robert Penner\'s tweens, blue is cubic bezier approximations. Drag pink handles to adjust.', 250, 100);
ctx.font = '9px sans-serif';
ctx.fillText('Error: ' + errors, 250, 120);
}
function cubicBezier(t, p0, p1, p2, p3) {
var t2 = 1 - t;
var cx0 = t2 * t2 * t2;
var cx1 = 3 * t2 * t2 * t;
var cx2 = 3 * t2 * t * t;
var cx3 = t * t * t;
return cx0 * p0 + cx1 * p1 + cx2 * p2 + cx3 * p3;
}
var tx, ty, to = -1, startx, starty;
canvas.addEventListener('mousedown', function(e) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
x -= 250;
y = h - y - 250;
var sx = x - params[0] * w2;
var sy = y - params[1] * h2;
if (sx * sx + sy * sy < 10 * 10) {
tx = params[0] * w2;
ty = params[1] * h2;
to = 0;
}
sx = x - params[2] * w2;
sy = y - params[3] * h2;
if (sx * sx + sy * sy < 10 * 10) {
tx = params[2] * w2;
ty = params[3] * h2;
to = 2;
}
startx = e.clientX;
starty = e.clientY;
});
document.addEventListener('mousemove', move);
function move(e) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
x -= 250;
y = h - y - 250;
if (to>-1) {
params[to] = x / w2;
params[to+1] = y / h2;
checkErrors();
}
}
canvas.addEventListener('mouseup', function(e) {
to = -1;
})
function animate() {
requestAnimationFrame(animate);
draw();
}
animate();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment