diff --git a/article.js b/article.js index 17506339..5fe92852 100644 --- a/article.js +++ b/article.js @@ -25051,15 +25051,19 @@ null, "We could naively implement the basis function as a mathematical construct, using the function as our guide, like this:" ), - "
function Bezier(n,t):", - '\n', - " sum = 0", - '\n', - " for(k=0; k", + React.createElement( + "pre", + null, + "lut = [ [1], // n=0", + '\n', + " [1,1], // n=1", + '\n', + " [1,2,1], // n=2", + '\n', + " [1,3,3,1], // n=3", + '\n', + " [1,4,6,4,1], // n=4", + '\n', + " [1,5,10,10,5,1], // n=5", + '\n', + " [1,6,15,20,15,6,1]] // n=6", + '\n', + '\n', + "binomial(n,k):", + '\n', + " while(n >= lut.length):", + '\n', + " s = lut.length", + '\n', + " nextRow = new array(size=s+1)", + '\n', + " nextRow[0] = 1", + '\n', + " for(i=1, prev=s-1; i<prev; i++):", + '\n', + " nextRow[i] = lut[prev][i-1] + lut[prev][i]", + '\n', + " nextRow[s] = 1", + '\n', + " lut.add(nextRow)", + '\n', + " return lut[n][k]" + ), React.createElement( "p", null, "So what's going on here? First, we declare a lookup table with a size that's reasonably large enough to accommodate most lookups. Then, we declare a function to get us the values we need, and we make sure that if an n/k pair is requested that isn't in the LUT yet, we expand it first. Our basis function now looks like this:" ), - "", + React.createElement( + "pre", + null, + "function Bezier(n,t):", + '\n', + " sum = 0", + '\n', + " for(k=0; k lut = [ [1], // n=0", - '\n', - " [1,1], // n=1", - '\n', - " [1,2,1], // n=2", - '\n', - " [1,3,3,1], // n=3", - '\n', - " [1,4,6,4,1], // n=4", - '\n', - " [1,5,10,10,5,1], // n=5", - '\n', - " [1,6,15,20,15,6,1]] // n=6", - '\n', - '\n', - "binomial(n,k):", - '\n', - " while(n >= lut.length):", - '\n', - " s = lut.length", - '\n', - " nextRow = new array(size=s+1)", - '\n', - " nextRow[0] = 1", - '\n', - " for(i=1, prev=s-1; i<prev; i++):", - '\n', - " nextRow[i] = lut[prev][i-1] + lut[prev][i]", - '\n', - " nextRow[s] = 1", - '\n', - " lut.add(nextRow)", - '\n', - " return lut[n][k]
function Bezier(n,t):", - '\n', - " sum = 0", - '\n', - " for(k=0; k", + React.createElement( + "pre", + null, + "function Bezier(2,t):", + '\n', + " t2 = t * t", + '\n', + " mt = 1-t", + '\n', + " mt2 = mt * mt", + '\n', + " return mt2 + 2*mt*t + t2", + '\n', + '\n', + "function Bezier(3,t):", + '\n', + " t2 = t * t", + '\n', + " t3 = t2 * t", + '\n', + " mt = 1-t", + '\n', + " mt2 = mt * mt", + '\n', + " mt3 = mt2 * mt", + '\n', + " return mt3 + 3*mt2*t + 3*mt*t2 + t3" + ), React.createElement( "p", null, @@ -25498,44 +25514,52 @@ null, "Given that we already know how to implement basis function, adding in the control points is remarkably easy:" ), - "", + React.createElement( + "pre", + null, + "function Bezier(n,t):", + '\n', + " sum = 0", + '\n', + " for(k=0; k function Bezier(2,t):", - '\n', - " t2 = t * t", - '\n', - " mt = 1-t", - '\n', - " mt2 = mt * mt", - '\n', - " return mt2 + 2*mt*t + t2", - '\n', - '\n', - "function Bezier(3,t):", - '\n', - " t2 = t * t", - '\n', - " t3 = t2 * t", - '\n', - " mt = 1-t", - '\n', - " mt2 = mt * mt", - '\n', - " mt3 = mt2 * mt", - '\n', - " return mt3 + 3*mt2*t + 3*mt*t2 + t3
function Bezier(n,t,w[]):", - '\n', - " sum = 0", - '\n', - " for(k=0; k", + React.createElement( + "pre", + null, + "function Bezier(2,t,w[]):", + '\n', + " t2 = t * t", + '\n', + " mt = 1-t", + '\n', + " mt2 = mt * mt", + '\n', + " return w[0]*mt2 + w[1]*2*mt*t + w[2]*t2", + '\n', + '\n', + "function Bezier(3,t,w[]):", + '\n', + " t2 = t * t", + '\n', + " t3 = t2 * t", + '\n', + " mt = 1-t", + '\n', + " mt2 = mt * mt", + '\n', + " mt3 = mt2 * mt", + '\n', + " return w[0]*mt3 + 3*w[1]*mt2*t + 3*w[2]*mt*t2 + w[3]*t3" + ), React.createElement( "p", null, @@ -25956,21 +25980,25 @@ null, "Let's just use the algorithm we just specified, and implement that:" ), - "", + React.createElement( + "pre", + null, + "function Bezier(n,t,w[]):", + '\n', + " sum = 0", + '\n', + " for(k=0; k function Bezier(2,t,w[]):", - '\n', - " t2 = t * t", - '\n', - " mt = 1-t", - '\n', - " mt2 = mt * mt", - '\n', - " return w[0]*mt2 + w[1]*2*mt*t + w[2]*t2", - '\n', - '\n', - "function Bezier(3,t,w[]):", - '\n', - " t2 = t * t", - '\n', - " t3 = t2 * t", - '\n', - " mt = 1-t", - '\n', - " mt2 = mt * mt", - '\n', - " mt3 = mt2 * mt", - '\n', - " return w[0]*mt3 + 3*w[1]*mt2*t + 3*w[2]*mt*t2 + w[3]*t3
function drawCurve(points[], t):", - '\n', - " if(points.length==1):", - '\n', - " draw(points[0])", - '\n', - " else:", - '\n', - " newpoints=array(points.size-1)", - '\n', - " for(i=0; i", + React.createElement( + "pre", + null, + "function flattenCurve(curve, segmentCount):", + '\n', + " step = 1/segmentCount;", + '\n', + " coordinates = [curve.getXValue(0), curve.getYValue(0)]", + '\n', + " for(i=1; i <= segmentCount; i++):", + '\n', + " t = i*step;", + '\n', + " coordinates.push[curve.getXValue(t), curve.getYValue(t)]", + '\n', + " return coordinates;" + ), React.createElement( "p", null, "And done, that's the algorithm implemented. That just leaves drawing the resulting \"curve\" as a sequence of lines:" ), - "", + React.createElement( + "pre", + null, + "function drawCurve(points[], t):", + '\n', + " if(points.length==1):", + '\n', + " draw(points[0])", + '\n', + " else:", + '\n', + " newpoints=array(points.size-1)", + '\n', + " for(i=0; i function drawCurve(points[], t):", - '\n', - " if(points.length==1):", - '\n', - " draw(points[0])", - '\n', - " else:", - '\n', - " newpoints=array(points.size-1)", - '\n', - " for(i=0; i ", + React.createElement( + "pre", + null, + "function drawCurve(points[], t):", + '\n', + " if(points.length==1):", + '\n', + " draw(points[0])", + '\n', + " else:", + '\n', + " newpoints=array(points.size-1)", + '\n', + " for(i=0; i function flattenCurve(curve, segmentCount):", - '\n', - " step = 1/segmentCount;", - '\n', - " coordinates = [curve.getXValue(0), curve.getYValue(0)]", - '\n', - " for(i=1; i <= segmentCount; i++):", - '\n', - " t = i*step;", - '\n', - " coordinates.push[curve.getXValue(t), curve.getYValue(t)]", - '\n', - " return coordinates;
function drawFlattenedCurve(curve, segmentCount):", - '\n', - " coordinates = flattenCurve(curve, segmentCount)", - '\n', - " coord = coordinates[0], _coords;", - '\n', - " for(i=1; i < coordinates.length; i++):", - '\n', - " _coords = coordinates[i]", - '\n', - " line(coords, _coords)", - '\n', - " coords = _coords", + React.createElement( + "pre", + null, + "function drawFlattenedCurve(curve, segmentCount):", + '\n', + " coordinates = flattenCurve(curve, segmentCount)", + '\n', + " coord = coordinates[0], _coords;", + '\n', + " for(i=1; i < coordinates.length; i++):", + '\n', + " _coords = coordinates[i]", + '\n', + " line(coords, _coords)", + '\n', + " coords = _coords" + ), React.createElement( "p", null, @@ -26328,37 +26368,41 @@ null, "We can implement curve splitting by bolting some extra logging onto the de Casteljau function:" ), - "
left=[]", - '\n', - "right=[]", - '\n', - "function drawCurve(points[], t):", - '\n', - " if(points.length==1):", - '\n', - " left.add(points[0])", - '\n', - " right.add(points[0])", - '\n', - " draw(points[0])", - '\n', - " else:", - '\n', - " newpoints=array(points.size-1)", - '\n', - " for(i=0; i" + React.createElement( + "pre", + null, + '\n', + "// A helper function to filter for values in the [0,1] interval:", + '\n', + "function accept(t) ", + '{', + '\n', + " return 0<=t && t <=1;", + '\n', + '}', + '\n', + '\n', + "// A special cuberoot function, which we can use because we don't care about complex roots:", + '\n', + "function crt(v) ", + '{', + '\n', + " if(v<0) return -Math.pow(-v,1/3);", + '\n', + " return Math.pow(v,1/3);", + '\n', + '}', + '\n', + '\n', + "// Now then: given cubic coordinates pa, pb, pc, pd, find all roots.", + '\n', + "function getCubicRoots(pa, pb, pc, pd) ", + '{', + '\n', + " var d = (-pa + 3*pb - 3*pc + pd),", + '\n', + " a = (3*pa - 6*pb + 3*pc) / d,", + '\n', + " b = (-3*pa + 3*pb) / d,", + '\n', + " c = pa / d;", + '\n', + '\n', + " var p = (3*b - a*a)/3,", + '\n', + " p3 = p/3,", + '\n', + " q = (2*a*a*a - 9*a*b + 27*c)/27,", + '\n', + " q2 = q/2,", + '\n', + " discriminant = q2*q2 + p3*p3*p3;", + '\n', + '\n', + " // and some variables we're going to use later on:", + '\n', + " var u1,v1,root1,root2,root3;", + '\n', + '\n', + " // three possible real roots:", + '\n', + " if (discriminant < 0) ", + '{', + '\n', + " var mp3 = -p/3,", + '\n', + " mp33 = mp3*mp3*mp3,", + '\n', + " r = sqrt( mp33 ),", + '\n', + " t = -q / (2*r),", + '\n', + " cosphi = t<-1 ? -1 : t>1 ? 1 : t,", + '\n', + " phi = acos(cosphi),", + '\n', + " crtr = cuberoot(r),", + '\n', + " t1 = 2*crtr;", + '\n', + " root1 = t1 * cos(phi/3) - a/3;", + '\n', + " root2 = t1 * cos((phi+2*pi)/3) - a/3;", + '\n', + " root3 = t1 * cos((phi+4*pi)/3) - a/3;", + '\n', + " return [root1, root2, root3].filter(accept);", + '\n', + " ", + '}', + '\n', + '\n', + " // three real roots, but two of them are equal:", + '\n', + " else if(discriminant === 0) ", + '{', + '\n', + " u1 = q2 < 0 ? cuberoot(-q2) : -cuberoot(q2);", + '\n', + " root1 = 2*u1 - a/3;", + '\n', + " root2 = -u1 - a/3;", + '\n', + " return [root1, root2].filter(accept);", + '\n', + " ", + '}', + '\n', + '\n', + " // one real root, two complex roots", + '\n', + " else ", + '{', + '\n', + " var sd = sqrt(discriminant);", + '\n', + " u1 = cuberoot(sd - q2);", + '\n', + " v1 = cuberoot(sd + q2);", + '\n', + " root1 = u1 - v1 - a/3;", + '\n', + " return [root1].filter(accept);", + '\n', + " ", + '}', + '\n', + '}' + ) ), React.createElement( "p", diff --git a/lib/pre-loader.js b/lib/pre-loader.js index 89f8a8c5..00505731 100644 --- a/lib/pre-loader.js +++ b/lib/pre-loader.js @@ -1,4 +1,5 @@ var blockLoader = require("block-loader"); + var options = { start: "", + React.createElement( + "pre", + null, + "left=[]", + '\n', + "right=[]", + '\n', + "function drawCurve(points[], t):", + '\n', + " if(points.length==1):", + '\n', + " left.add(points[0])", + '\n', + " right.add(points[0])", + '\n', + " draw(points[0])", + '\n', + " else:", + '\n', + " newpoints=array(points.size-1)", + '\n', + " for(i=0; i ", - '\n', - "// A helper function to filter for values in the [0,1] interval:", - '\n', - "function accept(t) ", - '{', - '\n', - " return 0<=t && t <=1;", - '\n', - '}', - '\n', - '\n', - "// A special cuberoot function, which we can use because we don't care about complex roots:", - '\n', - "function crt(v) ", - '{', - '\n', - " if(v<0) return -Math.pow(-v,1/3);", - '\n', - " return Math.pow(v,1/3);", - '\n', - '}', - '\n', - '\n', - "// Now then: given cubic coordinates pa, pb, pc, pd, find all roots.", - '\n', - "function getCubicRoots(pa, pb, pc, pd) ", - '{', - '\n', - " var d = (-pa + 3*pb - 3*pc + pd),", - '\n', - " a = (3*pa - 6*pb + 3*pc) / d,", - '\n', - " b = (-3*pa + 3*pb) / d,", - '\n', - " c = pa / d;", - '\n', - '\n', - " var p = (3*b - a*a)/3,", - '\n', - " p3 = p/3,", - '\n', - " q = (2*a*a*a - 9*a*b + 27*c)/27,", - '\n', - " q2 = q/2,", - '\n', - " discriminant = q2*q2 + p3*p3*p3;", - '\n', - '\n', - " // and some variables we're going to use later on:", - '\n', - " var u1,v1,root1,root2,root3;", - '\n', - '\n', - " // three possible real roots:", - '\n', - " if (discriminant < 0) ", - '{', - '\n', - " var mp3 = -p/3,", - '\n', - " mp33 = mp3*mp3*mp3,", - '\n', - " r = sqrt( mp33 ),", - '\n', - " t = -q / (2*r),", - '\n', - " cosphi = t<-1 ? -1 : t>1 ? 1 : t,", - '\n', - " phi = acos(cosphi),", - '\n', - " crtr = cuberoot(r),", - '\n', - " t1 = 2*crtr;", - '\n', - " root1 = t1 * cos(phi/3) - a/3;", - '\n', - " root2 = t1 * cos((phi+2*pi)/3) - a/3;", - '\n', - " root3 = t1 * cos((phi+4*pi)/3) - a/3;", - '\n', - " return [root1, root2, root3].filter(accept);", - '\n', - " ", - '}', - '\n', - '\n', - " // three real roots, but two of them are equal:", - '\n', - " else if(discriminant === 0) ", - '{', - '\n', - " u1 = q2 < 0 ? cuberoot(-q2) : -cuberoot(q2);", - '\n', - " root1 = 2*u1 - a/3;", - '\n', - " root2 = -u1 - a/3;", - '\n', - " return [root1, root2].filter(accept);", - '\n', - " ", - '}', - '\n', - '\n', - " // one real root, two complex roots", - '\n', - " else ", - '{', - '\n', - " var sd = sqrt(discriminant);", - '\n', - " u1 = cuberoot(sd - q2);", - '\n', - " v1 = cuberoot(sd + q2);", - '\n', - " root1 = u1 - v1 - a/3;", - '\n', - " return [root1].filter(accept);", - '\n', - " ", - '}', - '\n', - '}', - "
", end: "", @@ -8,12 +9,16 @@ var options = { *
elements used in JSX... */ process: function fixPreBlocks(pre) { - return pre + var replaced = pre + .replace(options.start,'') + .replace(options.end,'') .replace(/&/g,'&') .replace(//g,'>') .replace(/([{}])/g,"{'$1'}") .replace(/\n/g,"{'\\n'}"); + return options.start + replaced + options.end; } }; + module.exports = blockLoader(options);