diff --git a/.gitignore b/.gitignore index 1bd72e63..31226a4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ node_modules/ -src/build/temp/ +temp/ diff --git a/docs/chapters/canonical/canonical.js b/docs/chapters/canonical/canonical.js new file mode 100644 index 00000000..30520387 --- /dev/null +++ b/docs/chapters/canonical/canonical.js @@ -0,0 +1,144 @@ +setup() { + this.unit = this.height/5; + this.labels = []; +} + +draw() { + resetTransform(); + clear(); + + if (this.canonicalMap) { + return image(this.canonicalMap); + } + + const w = this.width, + h = this.height, + unit = this.unit; + + // axes + gridlines + setStroke(`lightgrey`); + for(let x=0; x fn()); + + // Let's never draw this again: + this.cacheMap(); +} + +drawRegions(shapes, w,h,unit) { + // Plain arch region + noStroke(); + noFill(); + drawShape(shapes.loop1, shapes.loop0, [ + {x: w, y: -h}, + {x: w, y: unit}, + {x: unit, y: unit}, + ]); + + // Self-intersection region + noStroke(); + setFill(`rgba(255,0,0,0.2)`); + drawShape(shapes.cusp, shapes.loop1, shapes.loop0); + + // Double inflection region + noStroke(); + noFill(); + drawShape([{x: unit, y: unit}, {x: -w, y: unit}], shapes.cusp); + + // Single inflection region + setStroke(`green`); + setFill(`#00FF0030`); + rect(-w/2 - 1, unit, w + 2, h); + + // further labels + this.labels.push(() => { + setFontSize(18); + setFontWeight(`normal`); + setStroke(`black`); + setFill(`black`); + text(`← Single inflection →`, 0, unit*1.75, CENTER); + text(`↔ Plain curve ↕`, unit/2, -h/3); + text(`↕ Double inflection`, -w/2 + 10, 10 + unit * 0.5); + }); +} + +cuspFunction(x) { + return { x, y: (-x*x + 2*x + 3)/4 }; +} + +loopFunction0(x) { + return { x, y: (-x*x + 3*x)/3 }; +} + +loopFunction1(x) { + return { x, y: ( sqrt(3) * sqrt(4*x - x*x) - x) / 2 }; +} + + +drawBoundaries(w,h,unit) { + setWidth(1.5); + + // cusp parabola: + setStroke(`red`); + noFill(); + let cusp = plot(x => this.cuspFunction(x), -5, 1, 50, unit, unit); + this.labels.push(() => { + setFill(`red`); + text(`Curve has a cusp →`, -unit, 15, RIGHT); + }); + + // loop/arch transition boundary, elliptical section + setStroke(`magenta`); + noFill(); + let loop1 = plot(x => this.loopFunction1(x), 1, 0, 50, unit, unit); + this.labels.push(() => { + setFill(`magenta`); + text(`← Curve forms a loop at t = 1`, unit/4, unit/2 - 1); + }); + + // loop/arch transition boundary, parabolic section + setStroke(`#3300FF`); + noFill(); + let loop0 = plot(x => this.loopFunction0(x), 0, -5, 100, unit, unit); + this.labels.push(() => { + setFill(`#3300FF`); + text(`← Curve forms a loop at t = 0`, -unit+10, -unit*1.25); + }); + + setWidth(1); + return { cusp, loop0, loop1 }; +} + +drawPoints(w,h,unit) { + // the three stable points + setStroke(`black`); + setFill(`black`); + circle(0, 0, 3); + circle(0, unit, 3); + circle(unit, unit, 3); + + this.labels.push(() => { + setFill(`black`); + text(`(0,0)`, 5, 15); + text(`(0,1)`, 5, unit+15); + text(`(1,1)`, unit+5, unit+15); + }); +} + +cacheMap() { + const img = this.canonicalMap = new Image(); + img.src = toDataURL(); +} diff --git a/docs/chapters/canonical/content.en-GB.md b/docs/chapters/canonical/content.en-GB.md index 4aceefa3..427e1bcd 100644 --- a/docs/chapters/canonical/content.en-GB.md +++ b/docs/chapters/canonical/content.en-GB.md @@ -6,36 +6,37 @@ As it so happens, the answer is yes, and the solution we're going to look at was The first observation that makes things work is that if we have a cubic curve with four points, we can apply a linear transformation to these points such that three of the points end up on (0,0), (0,1) and (1,1), with the last point then being "somewhere". After applying that transformation, the location of that last point can then tell us what kind of curve we're dealing with. Specifically, we see the following breakdown: - + -This is a fairly funky image, so let's see how it breaks down. We see the three fixed points at (0,0), (0,1) and (1,1), and then the fourth point is somewhere. Depending on where it is, our curve will have certain features. Namely, if the fourth point is... +This is a fairly funky image, so let's see what the various parts of it mean... -1. anywhere on and in the red zone, the curve will either be self-intersecting (yielding a loop), or it will have a sharp discontinuity (yielding a cusp). Anywhere inside the red zone, this will be a loop. We won't know *where* that loop is (in terms of *t* values), but we are guaranteed that there is one. -2. on the left (red) edge, the curve will have a cusp. We again don't know where, just that it -has one. This edge is described by the function: +We see the three fixed points at (0,0), (0,1) and (1,1). The various regions and boundaries indicate what property the original curve will have, if the fourth point is in/on that region or boundary. Specifically, if the fourth point is... + +1. ...anywhere inside the red zone, but not on its boundaries, the curve will either be self-intersecting (yielding a loop). We won't know *where* it self-intersects (in terms of *t* values), but we are guaranteed that it does. + +2. ...on the left (red) edge of the red zone, the curve will have a cusp. We again don't know _where_, but we know there is one. This edge is described by the function: \[ y = \frac{-x^2 + 2x + 3}{4}, \{ x \leq 1 \} \] -3. on the lower right (pink) edge, the curve will have a loop at t=1, so we know the end coordinate of -the curve also lies on the curve. This edge is described by the function: +3. ...on the almost circular, lower right (pink) edge, the curve's end point touches the curve, forming a loop. This edge is described by the function: \[ y = \frac{\sqrt{3(4x - x^2)} - x}{2}, \{ 0 \leq x \leq 1 \} \] -4. on the top (blue) edge, the curve will have a loop at t=0, so we know the start coordinate of -the curve also lies on the curve. This edge is described by the function: +4. ...on the top (blue) edge, the curve's start point touches the curve, forming a loop. This edge is described by the function: \[ y = \frac{-x^2 + 3x}{3}, \{ x \leq 0 \} \] -5. inside the green zone, the curve will have a single inflection, switching concave/convex once. -6. between the red and green zones, the curve has two inflections, meaning its curvature switches between -concave/convex form twice. -7. anywhere on the right of the red zone, the curve will have no inflections. It'll just be a well-behaved arch. +5. ...inside the lower (green) zone, past `y=1`, the curve will have a single inflection (switching concave/convex once). + +6. ...between the left and lower boundaries (below the cusp line but above the single-inflection line), the curve will have two inflections (switching from concave to convex and then back again, or from convex to concave and then back again). + +7. ...anywhere on the right of self-intersection zone, the curve will have no inflections. It'll just be a simple arch. Of course, this map is fairly small, but the regions extend to infinity, with well defined boundaries. @@ -280,4 +281,4 @@ Now, I know, you're thinking "but Mathematica is super expensive!" and that's tr So, let's write up a sketch that'll show us the canonical form for any curve drawn in blue, overlaid on our canonical map, so that we can immediately tell which features our curve must have, based on where the fourth coordinate is located on the map: - + diff --git a/docs/chapters/canonical/handler.js b/docs/chapters/canonical/handler.js deleted file mode 100644 index c8b4c082..00000000 --- a/docs/chapters/canonical/handler.js +++ /dev/null @@ -1,159 +0,0 @@ -module.exports = { - setup: function(api) { - var curve = api.getDefaultCubic(); - api.setCurve(curve); - api.reset(); - api._map_loaded = false; - }, - - draw: function(api, curve) { - var w = 400, - h = w, - unit = this.unit, - center = {x:w/2, y:h/2}; - - api.setSize(w,h); - api.setPanelCount(2); - api.reset(); - - api.drawSkeleton(curve); - api.drawCurve(curve); - - api.offset.x += 400; - - if (api._map_loaded) { api.image(api._map_image); } - else { setTimeout(( - function() { - this.drawBase(api, curve); - this.draw(api, curve); - } - ).bind(this), 100); } - - api.drawLine({x:0,y:0}, {x:0, y:h}); - - var npts = [ - {x:0, y: 0}, - {x:0, y: unit}, - {x:unit, y: unit}, - this.forwardTransform(curve.points, unit) - ]; - - var canonical = new api.Bezier(npts); - api.setColor("blue"); - api.drawCurve(canonical, center); - api.drawCircle(npts[3], 3, center); - }, - - forwardTransform: function(pts, s) { - s = s || 1; - var p1 = pts[0], p2 = pts[1], p3 = pts[2], p4 = pts[3]; - - var xn = -p1.x + p4.x - (-p1.x+p2.x)*(-p1.y+p4.y)/(-p1.y+p2.y); - var xd = -p1.x + p3.x - (-p1.x+p2.x)*(-p1.y+p3.y)/(-p1.y+p2.y); - var np4x = s*xn/xd; - - var yt1 = s*(-p1.y+p4.y) / (-p1.y+p2.y); - var yt2 = s - (s*(-p1.y+p3.y)/(-p1.y+p2.y)); - var yp = yt2 * xn / xd; - var np4y = yt1 + yp; - - return {x:np4x, y:np4y}; - }, - - drawBase: function(api, curve) { - api.reset(); - - var w = 400, - h = w, - unit = this.unit = w/5, - center = {x:w/2, y:h/2}; - - api.setSize(w,h); - - // axes + gridlines - api.setColor("lightgrey"); - for(var x=0; x-10) { - pts.push({x:unit*px, y:unit*py}); - api.drawLine({x:unit*px, y:unit*py}, {x:unit*x, y:unit*y}, center); - } - px = x; - py = y; - } - pts.push({x:unit*px, y:unit*py}); - api.text("Curve form has cusp →", {x:w/2-unit*2, y: h/2+unit/2.5}); - - // loop/arch transition boundary, elliptical section - api.setColor("#FF00FF"); - api.setFill(api.getColor()); - var sqrt = Math.sqrt; - for (x=1; x>=0; x-=0.005) { - pts.push({x:unit*px, y:unit*py}); - y = 0.5 * (sqrt(3) * sqrt(4*x - x*x) - x); - api.drawLine({x:unit*px, y:unit*py}, {x:unit*x, y:unit*y}, center); - px = x; - py = y; - } - pts.push({x:unit*px, y:unit*py}); - api.text("← Curve forms a loop at t = 1", {x:w/2+unit/4, y: h/2+unit/1.5}); - - - // loop/arch transition boundary, parabolic section - api.setColor("#3300FF"); - api.setFill(api.getColor()); - for (x=0; x>-w; x-=0.01) { - pts.push({x:unit*px, y:unit*py}); - y = (-x*x + 3*x)/3; - api.drawLine({x:unit*px, y:unit*py}, {x:unit*x, y:unit*y}, center); - px = x; - py = y; - } - pts.push({x:unit*px, y:unit*py}); - api.text("← Curve forms a loop at t = 0", {x:w/2-unit+10, y: h/2-unit*1.25}); - - // shape fill - api.setColor("transparent"); - api.setFill("rgba(255,120,100,0.2)"); - api.drawPath(pts, center); - pts = [{x:-w/2,y:unit}, {x:w/2,y:unit}, {x:w/2,y:h}, {x:-w/2,y:h}]; - api.setFill("rgba(0,200,0,0.2)"); - api.drawPath(pts, center); - - // further labels - api.setColor("black"); - api.setFill(api.getColor()); - api.text("← Curve form has one inflection →", {x:w/2 - unit, y: h/2 + unit*1.75}); - api.text("← Plain curve ↕", {x:w/2 + unit/2, y: h/6}); - api.text("↕ Double inflection", {x:10, y: h/2 - 10}); - - api._map_image = api.toImage(); - api._map_loaded = true; - } -}; diff --git a/docs/chapters/canonical/interactive.js b/docs/chapters/canonical/interactive.js new file mode 100644 index 00000000..3d5b8af0 --- /dev/null +++ b/docs/chapters/canonical/interactive.js @@ -0,0 +1,230 @@ +setup() { + this.curve = Bezier.defaultCubic(this); + this.curve.points = this.curve.points.map(p => ({ x: p.x + 50, y: p.y + 50 })); + setMovable(this.curve.points); + const unit = this.unit = this.height/5; + this.labels = []; + this.canonical = new Bezier(this, + [ + {x: 0, y: 0}, + {x: 0, y: unit}, + {x: unit, y: unit}, + {x: unit, y: 0}, + ] + ); +} + +draw() { + resetTransform(); + clear(); + + const w = this.width/2, + h = this.height, + unit = this.unit; + + translate(w,0); + + setStroke(`black`); + line(0,0,0,h); + + if (!this.canonicalMap) { + // axes + gridlines + setStroke(`lightgrey`); + for(let x=0; x 1) return `single inflection`; + + if (y <= 1 && x <= 1) { + let c = this.cuspFunction(x); + + if (x <= 0) { + let l1 = this.loopFunction0(x); + let diff = abs(y - l1.y); + if (diff < 0.06) return `loop at t=0`; + if (l1.y < y && y < c.y) return `loop`; + } + + if (0 <= x && x <= 1) { + let l0 = this.loopFunction1(x); + let diff = abs(y - l0.y); + if (diff < 0.06) return `loop at t=1`; + if (l0.y < y && y < c.y) return `loop`; + } + + let diff = abs(y - c.y); + if (diff < 0.06) return `cusp`; + + if (y > c.y) return `double inflection`; + if (y < c.y) return `plain curve`; + } + + return `plain curve`; +} + +drawRegions(shapes, w,h,unit) { + // Plain arch region + noStroke(); + noFill(); // setFill(HATCH1); + drawShape(shapes.loop1, shapes.loop0, [ + {x: w, y: -h}, + {x: w, y: unit}, + {x: unit, y: unit}, + ]); + + // Self-intersection region + noStroke(); + setFill(`rgba(255,0,0,0.2)`); + drawShape(shapes.cusp, shapes.loop1, shapes.loop0); + + // Double inflection region + noStroke(); + noFill(); // setFill(HATCH2); + drawShape([{x: unit, y: unit}, {x: -w, y: unit}], shapes.cusp); + + // Single inflection region + setStroke(`green`); + setFill(`#00FF0030`); + rect(-w/2 - 1, unit, w + 2, h); + + // further labels + this.labels.push(() => { + setFontSize(18); + setFontWeight(`normal`); + setStroke(`black`); + setFill(`black`); + text(`← Single inflection →`, 0, unit*1.75, CENTER); + text(`↔ Plain curve ↕`, unit/2, -h/3); + text(`↕ Double inflection`, -w/2 + 10, 10 + unit * 0.5); + }); +} + +cuspFunction(x) { + return { x, y: (-x*x + 2*x + 3)/4 }; +} + +loopFunction0(x) { + return { x, y: (-x*x + 3*x)/3 }; +} + +loopFunction1(x) { + return { x, y: ( sqrt(3) * sqrt(4*x - x*x) - x) / 2 }; +} + + +drawBoundaries(w,h,unit) { + setWidth(1.5); + + // cusp parabola: + setStroke(`red`); + noFill(); + let cusp = plot(x => this.cuspFunction(x), -5, 1, 50, unit, unit); + this.labels.push(() => { + setFill(`red`); + text(`Curve has a cusp ⭧`, -unit, 15, RIGHT); + }); + + // loop/arch transition boundary, elliptical section + setStroke(`magenta`); + noFill(); + let loop1 = plot(x => this.loopFunction1(x), 1, 0, 50, unit, unit); + this.labels.push(() => { + setFill(`magenta`); + text(`← Curve forms a loop at t = 1`, unit/4, unit/2 - 1); + }); + + // loop/arch transition boundary, parabolic section + setStroke(`#3300FF`); + noFill(); + let loop0 = plot(x => this.loopFunction0(x), 0, -5, 100, unit, unit); + this.labels.push(() => { + setFill(`#3300FF`); + text(`⭩ Curve forms a loop at t = 0`, -unit+10, -unit*1.25); + }); + + setWidth(1); + return { cusp, loop0, loop1 }; +} + +drawPoints(w,h,unit) { + // the three stable points + setStroke(`black`); + setFill(`black`); + circle(0, 0, 3); + circle(0, unit, 3); + circle(unit, unit, 3); + + this.labels.push(() => { + setFill(`black`); + text(`(0,0)`, 5, 15); + text(`(0,1)`, 5, unit+15); + text(`(1,1)`, unit+5, unit+15); + }); +} + +cacheMap() { + const img = this.canonicalMap = new Image(); + img.src = toDataURL(); +} + +forwardTransform(points, s=1) { + var p1 = points[0], p2 = points[1], p3 = points[2], p4 = points[3]; + + var xn = -p1.x + p4.x - (-p1.x+p2.x)*(-p1.y+p4.y)/(-p1.y+p2.y); + var xd = -p1.x + p3.x - (-p1.x+p2.x)*(-p1.y+p3.y)/(-p1.y+p2.y); + var np4x = s*xn/xd; + + var yt1 = s*(-p1.y+p4.y) / (-p1.y+p2.y); + var yt2 = s - (s*(-p1.y+p3.y)/(-p1.y+p2.y)); + var yp = yt2 * xn / xd; + var np4y = yt1 + yp; + + return {x:np4x, y:np4y}; +} + +onMouseMove() { + redraw(); +} diff --git a/docs/images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg b/docs/images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg index 21926ac8..1e91c9d0 100644 --- a/docs/images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg +++ b/docs/images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg b/docs/images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg index e98760e1..c5b329cb 100644 --- a/docs/images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg +++ b/docs/images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg b/docs/images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg index e60530a5..f92f1ae5 100644 --- a/docs/images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg +++ b/docs/images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg b/docs/images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg index fa0f2001..88940f03 100644 --- a/docs/images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg +++ b/docs/images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg b/docs/images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg index 8de1835f..42e84790 100644 --- a/docs/images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg +++ b/docs/images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/abc/b4987e9b77b0df604238b88596c5f7c3.svg b/docs/images/chapters/abc/b4987e9b77b0df604238b88596c5f7c3.svg index 0095c1b5..9cfd3fb8 100644 --- a/docs/images/chapters/abc/b4987e9b77b0df604238b88596c5f7c3.svg +++ b/docs/images/chapters/abc/b4987e9b77b0df604238b88596c5f7c3.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/aligning/50679d61424222d7b6b97eb3aa663582.svg b/docs/images/chapters/aligning/50679d61424222d7b6b97eb3aa663582.svg index dba522ea..32a04a8b 100644 --- a/docs/images/chapters/aligning/50679d61424222d7b6b97eb3aa663582.svg +++ b/docs/images/chapters/aligning/50679d61424222d7b6b97eb3aa663582.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/aligning/a9af1c06a00bb3c4af816a138fb0a66d.svg b/docs/images/chapters/aligning/a9af1c06a00bb3c4af816a138fb0a66d.svg index 8f7f953d..07de4738 100644 --- a/docs/images/chapters/aligning/a9af1c06a00bb3c4af816a138fb0a66d.svg +++ b/docs/images/chapters/aligning/a9af1c06a00bb3c4af816a138fb0a66d.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/aligning/c78b203ff33e5c1606728b552505d61c.svg b/docs/images/chapters/aligning/c78b203ff33e5c1606728b552505d61c.svg index 2fbe2ac8..319c2e29 100644 --- a/docs/images/chapters/aligning/c78b203ff33e5c1606728b552505d61c.svg +++ b/docs/images/chapters/aligning/c78b203ff33e5c1606728b552505d61c.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/aligning/d480a9aa41917e5230d432cdbd6899b1.svg b/docs/images/chapters/aligning/d480a9aa41917e5230d432cdbd6899b1.svg index 31dbb1c1..92e6ab59 100644 --- a/docs/images/chapters/aligning/d480a9aa41917e5230d432cdbd6899b1.svg +++ b/docs/images/chapters/aligning/d480a9aa41917e5230d432cdbd6899b1.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/arclength/5509919419288129322cfbd4c60d0a4f.svg b/docs/images/chapters/arclength/5509919419288129322cfbd4c60d0a4f.svg index 95596b53..df7746ce 100644 --- a/docs/images/chapters/arclength/5509919419288129322cfbd4c60d0a4f.svg +++ b/docs/images/chapters/arclength/5509919419288129322cfbd4c60d0a4f.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/arclength/cb24cda7f7f4bbf3be7104c460e0ec9f.svg b/docs/images/chapters/arclength/cb24cda7f7f4bbf3be7104c460e0ec9f.svg index 2b91fd0e..01eb273d 100644 --- a/docs/images/chapters/arclength/cb24cda7f7f4bbf3be7104c460e0ec9f.svg +++ b/docs/images/chapters/arclength/cb24cda7f7f4bbf3be7104c460e0ec9f.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/arclength/d0d93f1cc26b560309dade1f1aa012f2.svg b/docs/images/chapters/arclength/d0d93f1cc26b560309dade1f1aa012f2.svg index be3f008a..f577c9c4 100644 --- a/docs/images/chapters/arclength/d0d93f1cc26b560309dade1f1aa012f2.svg +++ b/docs/images/chapters/arclength/d0d93f1cc26b560309dade1f1aa012f2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/arclength/d3003177813309f88f58a1f515f5df9f.svg b/docs/images/chapters/arclength/d3003177813309f88f58a1f515f5df9f.svg index 71c28c2e..660fe545 100644 --- a/docs/images/chapters/arclength/d3003177813309f88f58a1f515f5df9f.svg +++ b/docs/images/chapters/arclength/d3003177813309f88f58a1f515f5df9f.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/arclength/e168758d35b8f6781617eda5a32b20bf.svg b/docs/images/chapters/arclength/e168758d35b8f6781617eda5a32b20bf.svg index fdac6f72..fe5e710f 100644 --- a/docs/images/chapters/arclength/e168758d35b8f6781617eda5a32b20bf.svg +++ b/docs/images/chapters/arclength/e168758d35b8f6781617eda5a32b20bf.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/bsplined/15f9e6eea05599fe6a5eac609ca42cfa.svg b/docs/images/chapters/bsplined/15f9e6eea05599fe6a5eac609ca42cfa.svg index cebec39a..86df9bf7 100644 --- a/docs/images/chapters/bsplined/15f9e6eea05599fe6a5eac609ca42cfa.svg +++ b/docs/images/chapters/bsplined/15f9e6eea05599fe6a5eac609ca42cfa.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/bsplined/b8c1ed97fb04474733b41daf2ac1a259.svg b/docs/images/chapters/bsplined/b8c1ed97fb04474733b41daf2ac1a259.svg index d5e1f273..37593e9f 100644 --- a/docs/images/chapters/bsplined/b8c1ed97fb04474733b41daf2ac1a259.svg +++ b/docs/images/chapters/bsplined/b8c1ed97fb04474733b41daf2ac1a259.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/bsplined/c32c4cabe4193e4b4c5e1d0e46aacf72.svg b/docs/images/chapters/bsplined/c32c4cabe4193e4b4c5e1d0e46aacf72.svg index 0db88e85..3f43395f 100644 --- a/docs/images/chapters/bsplined/c32c4cabe4193e4b4c5e1d0e46aacf72.svg +++ b/docs/images/chapters/bsplined/c32c4cabe4193e4b4c5e1d0e46aacf72.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/bsplines/0f3451c711c0fe5d0b018aa4aa77d855.svg b/docs/images/chapters/bsplines/0f3451c711c0fe5d0b018aa4aa77d855.svg index a6a1b68c..4f8c574a 100644 --- a/docs/images/chapters/bsplines/0f3451c711c0fe5d0b018aa4aa77d855.svg +++ b/docs/images/chapters/bsplines/0f3451c711c0fe5d0b018aa4aa77d855.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/bsplines/763838ea6f9e6c6aa63ea5f9c6d9542f.svg b/docs/images/chapters/bsplines/763838ea6f9e6c6aa63ea5f9c6d9542f.svg index aa1bb3c9..28e5baec 100644 --- a/docs/images/chapters/bsplines/763838ea6f9e6c6aa63ea5f9c6d9542f.svg +++ b/docs/images/chapters/bsplines/763838ea6f9e6c6aa63ea5f9c6d9542f.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/bsplines/7962d6fea86da6f53a7269fba30f0138.svg b/docs/images/chapters/bsplines/7962d6fea86da6f53a7269fba30f0138.svg index 901749da..527aab36 100644 --- a/docs/images/chapters/bsplines/7962d6fea86da6f53a7269fba30f0138.svg +++ b/docs/images/chapters/bsplines/7962d6fea86da6f53a7269fba30f0138.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/bsplines/892209dad8fd1f839470dd061e870913.svg b/docs/images/chapters/bsplines/892209dad8fd1f839470dd061e870913.svg index 38a47e38..3b4f30f8 100644 --- a/docs/images/chapters/bsplines/892209dad8fd1f839470dd061e870913.svg +++ b/docs/images/chapters/bsplines/892209dad8fd1f839470dd061e870913.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/bsplines/cf45d1ea00d4866abc8a058b130299b4.svg b/docs/images/chapters/bsplines/cf45d1ea00d4866abc8a058b130299b4.svg index c3384f62..4a06c171 100644 --- a/docs/images/chapters/bsplines/cf45d1ea00d4866abc8a058b130299b4.svg +++ b/docs/images/chapters/bsplines/cf45d1ea00d4866abc8a058b130299b4.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/0430e8c7f7d4ec80e6527f96f3d56e5c.svg b/docs/images/chapters/canonical/0430e8c7f7d4ec80e6527f96f3d56e5c.svg index 198e03ad..12a16766 100644 --- a/docs/images/chapters/canonical/0430e8c7f7d4ec80e6527f96f3d56e5c.svg +++ b/docs/images/chapters/canonical/0430e8c7f7d4ec80e6527f96f3d56e5c.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/058fa85ac31eb666857a860fdedd79df.svg b/docs/images/chapters/canonical/058fa85ac31eb666857a860fdedd79df.svg index c51728b3..e70c46d8 100644 --- a/docs/images/chapters/canonical/058fa85ac31eb666857a860fdedd79df.svg +++ b/docs/images/chapters/canonical/058fa85ac31eb666857a860fdedd79df.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/10025fdab2b3fd20f5d389cbe7e3e3ce.svg b/docs/images/chapters/canonical/10025fdab2b3fd20f5d389cbe7e3e3ce.svg index 999d93cc..2dee57a9 100644 --- a/docs/images/chapters/canonical/10025fdab2b3fd20f5d389cbe7e3e3ce.svg +++ b/docs/images/chapters/canonical/10025fdab2b3fd20f5d389cbe7e3e3ce.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/20684d22b3ddc52fd6abde8ce56608a9.svg b/docs/images/chapters/canonical/20684d22b3ddc52fd6abde8ce56608a9.svg index f2e21437..991f58dd 100644 --- a/docs/images/chapters/canonical/20684d22b3ddc52fd6abde8ce56608a9.svg +++ b/docs/images/chapters/canonical/20684d22b3ddc52fd6abde8ce56608a9.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/2f85d84f0e3dd14cc25e48583aed3822.svg b/docs/images/chapters/canonical/2f85d84f0e3dd14cc25e48583aed3822.svg index 0d77a73a..437bf81f 100644 --- a/docs/images/chapters/canonical/2f85d84f0e3dd14cc25e48583aed3822.svg +++ b/docs/images/chapters/canonical/2f85d84f0e3dd14cc25e48583aed3822.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/4230e959138d8400e04abf316360009a.svg b/docs/images/chapters/canonical/4230e959138d8400e04abf316360009a.svg index f15cf4c6..664983a3 100644 --- a/docs/images/chapters/canonical/4230e959138d8400e04abf316360009a.svg +++ b/docs/images/chapters/canonical/4230e959138d8400e04abf316360009a.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/63ccae0ebe0ca70dc2afb507ab32e4bd.svg b/docs/images/chapters/canonical/63ccae0ebe0ca70dc2afb507ab32e4bd.svg index 61b0b990..514e7561 100644 --- a/docs/images/chapters/canonical/63ccae0ebe0ca70dc2afb507ab32e4bd.svg +++ b/docs/images/chapters/canonical/63ccae0ebe0ca70dc2afb507ab32e4bd.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/83262761bb7fa9b832fe483ded436973.svg b/docs/images/chapters/canonical/83262761bb7fa9b832fe483ded436973.svg index e572ce6f..768bd9c9 100644 --- a/docs/images/chapters/canonical/83262761bb7fa9b832fe483ded436973.svg +++ b/docs/images/chapters/canonical/83262761bb7fa9b832fe483ded436973.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/8cbef24b8c3b26f9daf2f89d27d36e95.svg b/docs/images/chapters/canonical/8cbef24b8c3b26f9daf2f89d27d36e95.svg index a2e334d8..49ea6868 100644 --- a/docs/images/chapters/canonical/8cbef24b8c3b26f9daf2f89d27d36e95.svg +++ b/docs/images/chapters/canonical/8cbef24b8c3b26f9daf2f89d27d36e95.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/9ccf43ffde69d452e72cb52ea0efd8ef.png b/docs/images/chapters/canonical/9ccf43ffde69d452e72cb52ea0efd8ef.png new file mode 100644 index 00000000..1d497422 Binary files /dev/null and b/docs/images/chapters/canonical/9ccf43ffde69d452e72cb52ea0efd8ef.png differ diff --git a/docs/images/chapters/canonical/add5f7fb210a306fe9ff933113f6fb91.svg b/docs/images/chapters/canonical/add5f7fb210a306fe9ff933113f6fb91.svg index 7d4e0562..2abaab00 100644 --- a/docs/images/chapters/canonical/add5f7fb210a306fe9ff933113f6fb91.svg +++ b/docs/images/chapters/canonical/add5f7fb210a306fe9ff933113f6fb91.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/c3e9b97768440f85858a10e52def2d5d.png b/docs/images/chapters/canonical/c3e9b97768440f85858a10e52def2d5d.png new file mode 100644 index 00000000..7e10de43 Binary files /dev/null and b/docs/images/chapters/canonical/c3e9b97768440f85858a10e52def2d5d.png differ diff --git a/docs/images/chapters/canonical/d089cc0687982a3302249bb82af3fc16.svg b/docs/images/chapters/canonical/d089cc0687982a3302249bb82af3fc16.svg index 96f448f5..f455d898 100644 --- a/docs/images/chapters/canonical/d089cc0687982a3302249bb82af3fc16.svg +++ b/docs/images/chapters/canonical/d089cc0687982a3302249bb82af3fc16.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/canonical/f3261ad2802d980ebe6e35b272375700.svg b/docs/images/chapters/canonical/f3261ad2802d980ebe6e35b272375700.svg index 0e984e18..a2374448 100644 --- a/docs/images/chapters/canonical/f3261ad2802d980ebe6e35b272375700.svg +++ b/docs/images/chapters/canonical/f3261ad2802d980ebe6e35b272375700.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/06ae1e3fdc660e59d618e0760e8e9ab5.svg b/docs/images/chapters/catmullconv/06ae1e3fdc660e59d618e0760e8e9ab5.svg index 4e4e3b36..8ca78170 100644 --- a/docs/images/chapters/catmullconv/06ae1e3fdc660e59d618e0760e8e9ab5.svg +++ b/docs/images/chapters/catmullconv/06ae1e3fdc660e59d618e0760e8e9ab5.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/169fd85a95e4d16fe289a75583017a11.svg b/docs/images/chapters/catmullconv/169fd85a95e4d16fe289a75583017a11.svg index b633455a..cdf87018 100644 --- a/docs/images/chapters/catmullconv/169fd85a95e4d16fe289a75583017a11.svg +++ b/docs/images/chapters/catmullconv/169fd85a95e4d16fe289a75583017a11.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/1811b59c5ab9233f08590396e5d03303.svg b/docs/images/chapters/catmullconv/1811b59c5ab9233f08590396e5d03303.svg index c2001273..05024a9b 100644 --- a/docs/images/chapters/catmullconv/1811b59c5ab9233f08590396e5d03303.svg +++ b/docs/images/chapters/catmullconv/1811b59c5ab9233f08590396e5d03303.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/1b8a782f7540503d38067317e4cd00b0.svg b/docs/images/chapters/catmullconv/1b8a782f7540503d38067317e4cd00b0.svg index bfb50afb..67089bd3 100644 --- a/docs/images/chapters/catmullconv/1b8a782f7540503d38067317e4cd00b0.svg +++ b/docs/images/chapters/catmullconv/1b8a782f7540503d38067317e4cd00b0.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/26363fc09f8cf2d41ea5b4256656bb6d.svg b/docs/images/chapters/catmullconv/26363fc09f8cf2d41ea5b4256656bb6d.svg index d881d58c..c3a8f54a 100644 --- a/docs/images/chapters/catmullconv/26363fc09f8cf2d41ea5b4256656bb6d.svg +++ b/docs/images/chapters/catmullconv/26363fc09f8cf2d41ea5b4256656bb6d.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/3ea54fe939d076f8db605c5b480e7db0.svg b/docs/images/chapters/catmullconv/3ea54fe939d076f8db605c5b480e7db0.svg index 6596a95e..5e0ad915 100644 --- a/docs/images/chapters/catmullconv/3ea54fe939d076f8db605c5b480e7db0.svg +++ b/docs/images/chapters/catmullconv/3ea54fe939d076f8db605c5b480e7db0.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/4d524810417b4caffedd13af23135f5b.svg b/docs/images/chapters/catmullconv/4d524810417b4caffedd13af23135f5b.svg index 7d5d0f78..47af7bcd 100644 --- a/docs/images/chapters/catmullconv/4d524810417b4caffedd13af23135f5b.svg +++ b/docs/images/chapters/catmullconv/4d524810417b4caffedd13af23135f5b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/5f2750de827497375d9a915f96686885.svg b/docs/images/chapters/catmullconv/5f2750de827497375d9a915f96686885.svg index 9296bda1..aba2fde4 100644 --- a/docs/images/chapters/catmullconv/5f2750de827497375d9a915f96686885.svg +++ b/docs/images/chapters/catmullconv/5f2750de827497375d9a915f96686885.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/78ac9df086ec19147414359369b563fc.svg b/docs/images/chapters/catmullconv/78ac9df086ec19147414359369b563fc.svg index fada8271..48e02771 100644 --- a/docs/images/chapters/catmullconv/78ac9df086ec19147414359369b563fc.svg +++ b/docs/images/chapters/catmullconv/78ac9df086ec19147414359369b563fc.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/841fb6a2a035c9bcf5a2d46f2a67709b.svg b/docs/images/chapters/catmullconv/841fb6a2a035c9bcf5a2d46f2a67709b.svg index 462cf9d5..29163cd8 100644 --- a/docs/images/chapters/catmullconv/841fb6a2a035c9bcf5a2d46f2a67709b.svg +++ b/docs/images/chapters/catmullconv/841fb6a2a035c9bcf5a2d46f2a67709b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/8f56909fcb62b8eef18b9b9559575c13.svg b/docs/images/chapters/catmullconv/8f56909fcb62b8eef18b9b9559575c13.svg index 1bde0d6b..6cec7f2d 100644 --- a/docs/images/chapters/catmullconv/8f56909fcb62b8eef18b9b9559575c13.svg +++ b/docs/images/chapters/catmullconv/8f56909fcb62b8eef18b9b9559575c13.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/9215d05705c8e8a7ebd718ae6f690371.svg b/docs/images/chapters/catmullconv/9215d05705c8e8a7ebd718ae6f690371.svg index 472be296..13c97bcb 100644 --- a/docs/images/chapters/catmullconv/9215d05705c8e8a7ebd718ae6f690371.svg +++ b/docs/images/chapters/catmullconv/9215d05705c8e8a7ebd718ae6f690371.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/b21386f86bef8894f108c5441dad10de.svg b/docs/images/chapters/catmullconv/b21386f86bef8894f108c5441dad10de.svg index e418fc4f..ad05befb 100644 --- a/docs/images/chapters/catmullconv/b21386f86bef8894f108c5441dad10de.svg +++ b/docs/images/chapters/catmullconv/b21386f86bef8894f108c5441dad10de.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/ba31c32eba62f1e3b15066cd5ddda597.svg b/docs/images/chapters/catmullconv/ba31c32eba62f1e3b15066cd5ddda597.svg index 0098416e..2ba18e0e 100644 --- a/docs/images/chapters/catmullconv/ba31c32eba62f1e3b15066cd5ddda597.svg +++ b/docs/images/chapters/catmullconv/ba31c32eba62f1e3b15066cd5ddda597.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/cbdd46d5e2e1a6202ef46fb03711ebe4.svg b/docs/images/chapters/catmullconv/cbdd46d5e2e1a6202ef46fb03711ebe4.svg index e6e22654..21b428cf 100644 --- a/docs/images/chapters/catmullconv/cbdd46d5e2e1a6202ef46fb03711ebe4.svg +++ b/docs/images/chapters/catmullconv/cbdd46d5e2e1a6202ef46fb03711ebe4.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/cc1e2ff43350c32f0ae9ba9a7652b8fb.svg b/docs/images/chapters/catmullconv/cc1e2ff43350c32f0ae9ba9a7652b8fb.svg index 472be296..13c97bcb 100644 --- a/docs/images/chapters/catmullconv/cc1e2ff43350c32f0ae9ba9a7652b8fb.svg +++ b/docs/images/chapters/catmullconv/cc1e2ff43350c32f0ae9ba9a7652b8fb.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/e3d30ab368dcead1411532ce3814d3f3.svg b/docs/images/chapters/catmullconv/e3d30ab368dcead1411532ce3814d3f3.svg index ededc0a7..e9f3d7e6 100644 --- a/docs/images/chapters/catmullconv/e3d30ab368dcead1411532ce3814d3f3.svg +++ b/docs/images/chapters/catmullconv/e3d30ab368dcead1411532ce3814d3f3.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/eae7f01976e511ee38b08b6edc8765d2.svg b/docs/images/chapters/catmullconv/eae7f01976e511ee38b08b6edc8765d2.svg index ec16d8ce..ac806e12 100644 --- a/docs/images/chapters/catmullconv/eae7f01976e511ee38b08b6edc8765d2.svg +++ b/docs/images/chapters/catmullconv/eae7f01976e511ee38b08b6edc8765d2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/f08e34395ce2812276fd70548f805041.svg b/docs/images/chapters/catmullconv/f08e34395ce2812276fd70548f805041.svg index cce39cd6..5814d073 100644 --- a/docs/images/chapters/catmullconv/f08e34395ce2812276fd70548f805041.svg +++ b/docs/images/chapters/catmullconv/f08e34395ce2812276fd70548f805041.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/f2b2a16a41d134ce0dfd544ab77ff25e.svg b/docs/images/chapters/catmullconv/f2b2a16a41d134ce0dfd544ab77ff25e.svg index 92471fc6..de8fc2b9 100644 --- a/docs/images/chapters/catmullconv/f2b2a16a41d134ce0dfd544ab77ff25e.svg +++ b/docs/images/chapters/catmullconv/f2b2a16a41d134ce0dfd544ab77ff25e.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/catmullconv/f41487aff3e34fafd5d4ee5979f133f1.svg b/docs/images/chapters/catmullconv/f41487aff3e34fafd5d4ee5979f133f1.svg index 1e695f22..ab576801 100644 --- a/docs/images/chapters/catmullconv/f41487aff3e34fafd5d4ee5979f133f1.svg +++ b/docs/images/chapters/catmullconv/f41487aff3e34fafd5d4ee5979f133f1.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles/7754bc3c96ae3c90162fec3bd46bedff.svg b/docs/images/chapters/circles/7754bc3c96ae3c90162fec3bd46bedff.svg index 8f16adbc..0210144f 100644 --- a/docs/images/chapters/circles/7754bc3c96ae3c90162fec3bd46bedff.svg +++ b/docs/images/chapters/circles/7754bc3c96ae3c90162fec3bd46bedff.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles/8374c4190d6213b0ac0621481afaa754.svg b/docs/images/chapters/circles/8374c4190d6213b0ac0621481afaa754.svg index 021c875f..68e2155e 100644 --- a/docs/images/chapters/circles/8374c4190d6213b0ac0621481afaa754.svg +++ b/docs/images/chapters/circles/8374c4190d6213b0ac0621481afaa754.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles/9e4d886c372f916f6511c41245ceee39.svg b/docs/images/chapters/circles/9e4d886c372f916f6511c41245ceee39.svg index 6e1bed48..3d3a0808 100644 --- a/docs/images/chapters/circles/9e4d886c372f916f6511c41245ceee39.svg +++ b/docs/images/chapters/circles/9e4d886c372f916f6511c41245ceee39.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles/adbd056f4b8fcd05b1d4f2fce27d7657.svg b/docs/images/chapters/circles/adbd056f4b8fcd05b1d4f2fce27d7657.svg index d49cdca4..a09171f6 100644 --- a/docs/images/chapters/circles/adbd056f4b8fcd05b1d4f2fce27d7657.svg +++ b/docs/images/chapters/circles/adbd056f4b8fcd05b1d4f2fce27d7657.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles/b5d864e9ed0c44c56d454fbaa4218d5e.svg b/docs/images/chapters/circles/b5d864e9ed0c44c56d454fbaa4218d5e.svg index 326df53e..ffacd873 100644 --- a/docs/images/chapters/circles/b5d864e9ed0c44c56d454fbaa4218d5e.svg +++ b/docs/images/chapters/circles/b5d864e9ed0c44c56d454fbaa4218d5e.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles/c22f6d343ee0cce7bff6a617c946ca17.svg b/docs/images/chapters/circles/c22f6d343ee0cce7bff6a617c946ca17.svg index 4412de06..dc14b685 100644 --- a/docs/images/chapters/circles/c22f6d343ee0cce7bff6a617c946ca17.svg +++ b/docs/images/chapters/circles/c22f6d343ee0cce7bff6a617c946ca17.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles/df87674db0f31fc3944aaeb6b890e196.svg b/docs/images/chapters/circles/df87674db0f31fc3944aaeb6b890e196.svg index da2a248b..d9535fe2 100644 --- a/docs/images/chapters/circles/df87674db0f31fc3944aaeb6b890e196.svg +++ b/docs/images/chapters/circles/df87674db0f31fc3944aaeb6b890e196.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles/e1059e611aa1e51db41f9ce0b4ebb95a.svg b/docs/images/chapters/circles/e1059e611aa1e51db41f9ce0b4ebb95a.svg index 76698c69..6fbc06e1 100644 --- a/docs/images/chapters/circles/e1059e611aa1e51db41f9ce0b4ebb95a.svg +++ b/docs/images/chapters/circles/e1059e611aa1e51db41f9ce0b4ebb95a.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles/ef3ab62bb896019c6157c85aae5d1ed3.svg b/docs/images/chapters/circles/ef3ab62bb896019c6157c85aae5d1ed3.svg index 6bf011b8..37b0a458 100644 --- a/docs/images/chapters/circles/ef3ab62bb896019c6157c85aae5d1ed3.svg +++ b/docs/images/chapters/circles/ef3ab62bb896019c6157c85aae5d1ed3.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/0364731626a530c8a9b30f424ada53c5.svg b/docs/images/chapters/circles_cubic/0364731626a530c8a9b30f424ada53c5.svg index 240686c6..0d5aff0a 100644 --- a/docs/images/chapters/circles_cubic/0364731626a530c8a9b30f424ada53c5.svg +++ b/docs/images/chapters/circles_cubic/0364731626a530c8a9b30f424ada53c5.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/195790bae7de813aec342ea82b5d8781.svg b/docs/images/chapters/circles_cubic/195790bae7de813aec342ea82b5d8781.svg index c28f1b22..e063afd3 100644 --- a/docs/images/chapters/circles_cubic/195790bae7de813aec342ea82b5d8781.svg +++ b/docs/images/chapters/circles_cubic/195790bae7de813aec342ea82b5d8781.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/3189cac1ddac07c1487e1e51740ecc88.svg b/docs/images/chapters/circles_cubic/3189cac1ddac07c1487e1e51740ecc88.svg index 1c2524e3..86a35ff5 100644 --- a/docs/images/chapters/circles_cubic/3189cac1ddac07c1487e1e51740ecc88.svg +++ b/docs/images/chapters/circles_cubic/3189cac1ddac07c1487e1e51740ecc88.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/49dbf244d50c787a4ab18694488d9b69.svg b/docs/images/chapters/circles_cubic/49dbf244d50c787a4ab18694488d9b69.svg index 642f2574..4143cd23 100644 --- a/docs/images/chapters/circles_cubic/49dbf244d50c787a4ab18694488d9b69.svg +++ b/docs/images/chapters/circles_cubic/49dbf244d50c787a4ab18694488d9b69.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/877f9c217c51c0087be751a7580ed459.svg b/docs/images/chapters/circles_cubic/877f9c217c51c0087be751a7580ed459.svg index a4b0b876..a8af5f84 100644 --- a/docs/images/chapters/circles_cubic/877f9c217c51c0087be751a7580ed459.svg +++ b/docs/images/chapters/circles_cubic/877f9c217c51c0087be751a7580ed459.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/a4f0dafbfe80c88723c3cc22277a9682.svg b/docs/images/chapters/circles_cubic/a4f0dafbfe80c88723c3cc22277a9682.svg index 021c875f..68e2155e 100644 --- a/docs/images/chapters/circles_cubic/a4f0dafbfe80c88723c3cc22277a9682.svg +++ b/docs/images/chapters/circles_cubic/a4f0dafbfe80c88723c3cc22277a9682.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/acbc5efb06bc34571ccc0322376e0b9b.svg b/docs/images/chapters/circles_cubic/acbc5efb06bc34571ccc0322376e0b9b.svg index 9f7c0f91..c1684190 100644 --- a/docs/images/chapters/circles_cubic/acbc5efb06bc34571ccc0322376e0b9b.svg +++ b/docs/images/chapters/circles_cubic/acbc5efb06bc34571ccc0322376e0b9b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/dfb83eec053c30e0a41b0a52aba24cd4.svg b/docs/images/chapters/circles_cubic/dfb83eec053c30e0a41b0a52aba24cd4.svg index 8fcdbbd9..68fc6890 100644 --- a/docs/images/chapters/circles_cubic/dfb83eec053c30e0a41b0a52aba24cd4.svg +++ b/docs/images/chapters/circles_cubic/dfb83eec053c30e0a41b0a52aba24cd4.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/e2258660a796dcd6189a6f5e14326dad.svg b/docs/images/chapters/circles_cubic/e2258660a796dcd6189a6f5e14326dad.svg index ad35045d..2f3b3c46 100644 --- a/docs/images/chapters/circles_cubic/e2258660a796dcd6189a6f5e14326dad.svg +++ b/docs/images/chapters/circles_cubic/e2258660a796dcd6189a6f5e14326dad.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/e75a848f5f8aead495e35175e2955e06.svg b/docs/images/chapters/circles_cubic/e75a848f5f8aead495e35175e2955e06.svg index 529a33a9..5a149c4b 100644 --- a/docs/images/chapters/circles_cubic/e75a848f5f8aead495e35175e2955e06.svg +++ b/docs/images/chapters/circles_cubic/e75a848f5f8aead495e35175e2955e06.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/circles_cubic/ee08d86b7497c7ab042ee899bf15d453.svg b/docs/images/chapters/circles_cubic/ee08d86b7497c7ab042ee899bf15d453.svg index c01d49a9..9d24dfb9 100644 --- a/docs/images/chapters/circles_cubic/ee08d86b7497c7ab042ee899bf15d453.svg +++ b/docs/images/chapters/circles_cubic/ee08d86b7497c7ab042ee899bf15d453.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/control/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg b/docs/images/chapters/control/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg index 0e030eb1..7e17c4e5 100644 --- a/docs/images/chapters/control/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg +++ b/docs/images/chapters/control/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/control/2af72ea0c3517bc05f36a08cbbed6002.svg b/docs/images/chapters/control/2af72ea0c3517bc05f36a08cbbed6002.svg index 5c2f0786..4f11ab77 100644 --- a/docs/images/chapters/control/2af72ea0c3517bc05f36a08cbbed6002.svg +++ b/docs/images/chapters/control/2af72ea0c3517bc05f36a08cbbed6002.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvature/6ed4fd2ead35c57984caddf9fe375a5f.svg b/docs/images/chapters/curvature/6ed4fd2ead35c57984caddf9fe375a5f.svg index ae274089..4d8bd968 100644 --- a/docs/images/chapters/curvature/6ed4fd2ead35c57984caddf9fe375a5f.svg +++ b/docs/images/chapters/curvature/6ed4fd2ead35c57984caddf9fe375a5f.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvature/828333034b4fed8e248683760d6bc6f4.svg b/docs/images/chapters/curvature/828333034b4fed8e248683760d6bc6f4.svg index 3ab3aa4b..91730194 100644 --- a/docs/images/chapters/curvature/828333034b4fed8e248683760d6bc6f4.svg +++ b/docs/images/chapters/curvature/828333034b4fed8e248683760d6bc6f4.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvature/d9c893051586eb8d9de51c0ae1ef8fae.svg b/docs/images/chapters/curvature/d9c893051586eb8d9de51c0ae1ef8fae.svg index f6331754..1ded0bc7 100644 --- a/docs/images/chapters/curvature/d9c893051586eb8d9de51c0ae1ef8fae.svg +++ b/docs/images/chapters/curvature/d9c893051586eb8d9de51c0ae1ef8fae.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/08f4beaebf83dca594ad125bdca7e436.svg b/docs/images/chapters/curvefitting/08f4beaebf83dca594ad125bdca7e436.svg index 697bba69..e9658b08 100644 --- a/docs/images/chapters/curvefitting/08f4beaebf83dca594ad125bdca7e436.svg +++ b/docs/images/chapters/curvefitting/08f4beaebf83dca594ad125bdca7e436.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/283bc9e8fe59a78d3c74860f62a66ecb.svg b/docs/images/chapters/curvefitting/283bc9e8fe59a78d3c74860f62a66ecb.svg index 2e54d4c3..a8172fcd 100644 --- a/docs/images/chapters/curvefitting/283bc9e8fe59a78d3c74860f62a66ecb.svg +++ b/docs/images/chapters/curvefitting/283bc9e8fe59a78d3c74860f62a66ecb.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/2bef3da3828d63d690460ce9947dbde2.svg b/docs/images/chapters/curvefitting/2bef3da3828d63d690460ce9947dbde2.svg index b72015c9..14d83cad 100644 --- a/docs/images/chapters/curvefitting/2bef3da3828d63d690460ce9947dbde2.svg +++ b/docs/images/chapters/curvefitting/2bef3da3828d63d690460ce9947dbde2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/2d42758fba3370f52191306752c2705c.svg b/docs/images/chapters/curvefitting/2d42758fba3370f52191306752c2705c.svg index f0f63bd5..17881f45 100644 --- a/docs/images/chapters/curvefitting/2d42758fba3370f52191306752c2705c.svg +++ b/docs/images/chapters/curvefitting/2d42758fba3370f52191306752c2705c.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/5f7fcb86ae1c19612b9fe02e23229e31.svg b/docs/images/chapters/curvefitting/5f7fcb86ae1c19612b9fe02e23229e31.svg index 41acd21e..bc0bf6c5 100644 --- a/docs/images/chapters/curvefitting/5f7fcb86ae1c19612b9fe02e23229e31.svg +++ b/docs/images/chapters/curvefitting/5f7fcb86ae1c19612b9fe02e23229e31.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/6202d7bd150c852b432d807c40fb1647.svg b/docs/images/chapters/curvefitting/6202d7bd150c852b432d807c40fb1647.svg index a82f0317..9da51301 100644 --- a/docs/images/chapters/curvefitting/6202d7bd150c852b432d807c40fb1647.svg +++ b/docs/images/chapters/curvefitting/6202d7bd150c852b432d807c40fb1647.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/78b8ba1aba2e4c9ad3f7890299c90152.svg b/docs/images/chapters/curvefitting/78b8ba1aba2e4c9ad3f7890299c90152.svg index c3b83fbc..cccbc329 100644 --- a/docs/images/chapters/curvefitting/78b8ba1aba2e4c9ad3f7890299c90152.svg +++ b/docs/images/chapters/curvefitting/78b8ba1aba2e4c9ad3f7890299c90152.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/7e5d59272621baf942bc722208ce70c2.svg b/docs/images/chapters/curvefitting/7e5d59272621baf942bc722208ce70c2.svg index 3191bc24..170d27e1 100644 --- a/docs/images/chapters/curvefitting/7e5d59272621baf942bc722208ce70c2.svg +++ b/docs/images/chapters/curvefitting/7e5d59272621baf942bc722208ce70c2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/7eada6f12045423de24d9a2ab8e293b1.svg b/docs/images/chapters/curvefitting/7eada6f12045423de24d9a2ab8e293b1.svg index 21af1038..25f02428 100644 --- a/docs/images/chapters/curvefitting/7eada6f12045423de24d9a2ab8e293b1.svg +++ b/docs/images/chapters/curvefitting/7eada6f12045423de24d9a2ab8e293b1.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/875ca8eea72e727ccb881b4c0b6a3224.svg b/docs/images/chapters/curvefitting/875ca8eea72e727ccb881b4c0b6a3224.svg index 4855592f..a02129e9 100644 --- a/docs/images/chapters/curvefitting/875ca8eea72e727ccb881b4c0b6a3224.svg +++ b/docs/images/chapters/curvefitting/875ca8eea72e727ccb881b4c0b6a3224.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/8d09f2be2c6db79ee966f170ffc25815.svg b/docs/images/chapters/curvefitting/8d09f2be2c6db79ee966f170ffc25815.svg index d90df626..1c31f02e 100644 --- a/docs/images/chapters/curvefitting/8d09f2be2c6db79ee966f170ffc25815.svg +++ b/docs/images/chapters/curvefitting/8d09f2be2c6db79ee966f170ffc25815.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/9151c0fdf9689ee598a2d029ab2ffe34.svg b/docs/images/chapters/curvefitting/9151c0fdf9689ee598a2d029ab2ffe34.svg index 0e9e6ab6..def94e94 100644 --- a/docs/images/chapters/curvefitting/9151c0fdf9689ee598a2d029ab2ffe34.svg +++ b/docs/images/chapters/curvefitting/9151c0fdf9689ee598a2d029ab2ffe34.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/94acb5850778dcb16c2ba3cfa676f537.svg b/docs/images/chapters/curvefitting/94acb5850778dcb16c2ba3cfa676f537.svg index dcf270ab..90d4c93e 100644 --- a/docs/images/chapters/curvefitting/94acb5850778dcb16c2ba3cfa676f537.svg +++ b/docs/images/chapters/curvefitting/94acb5850778dcb16c2ba3cfa676f537.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/ab334858d3fa309cc1a5ba535a2ca168.svg b/docs/images/chapters/curvefitting/ab334858d3fa309cc1a5ba535a2ca168.svg index 4e746e87..4d82266c 100644 --- a/docs/images/chapters/curvefitting/ab334858d3fa309cc1a5ba535a2ca168.svg +++ b/docs/images/chapters/curvefitting/ab334858d3fa309cc1a5ba535a2ca168.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/bd8e8e294eec10d2bf6ef857c7c0c2c2.svg b/docs/images/chapters/curvefitting/bd8e8e294eec10d2bf6ef857c7c0c2c2.svg index 55cfb170..0cd86b17 100644 --- a/docs/images/chapters/curvefitting/bd8e8e294eec10d2bf6ef857c7c0c2c2.svg +++ b/docs/images/chapters/curvefitting/bd8e8e294eec10d2bf6ef857c7c0c2c2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/curvefitting/d84d1c71a3ce1918f53eaf8f9fe98ac4.svg b/docs/images/chapters/curvefitting/d84d1c71a3ce1918f53eaf8f9fe98ac4.svg index cdffa229..047757ec 100644 --- a/docs/images/chapters/curvefitting/d84d1c71a3ce1918f53eaf8f9fe98ac4.svg +++ b/docs/images/chapters/curvefitting/d84d1c71a3ce1918f53eaf8f9fe98ac4.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/03967e3ecdbff78684995ca9c22a6106.svg b/docs/images/chapters/derivatives/03967e3ecdbff78684995ca9c22a6106.svg index 61231cd0..a8bb0014 100644 --- a/docs/images/chapters/derivatives/03967e3ecdbff78684995ca9c22a6106.svg +++ b/docs/images/chapters/derivatives/03967e3ecdbff78684995ca9c22a6106.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg b/docs/images/chapters/derivatives/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg index ee54ed82..cbaec892 100644 --- a/docs/images/chapters/derivatives/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg +++ b/docs/images/chapters/derivatives/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/2622790efa97f1915e7998787d8ce977.svg b/docs/images/chapters/derivatives/2622790efa97f1915e7998787d8ce977.svg index 53650d7a..ffcfd814 100644 --- a/docs/images/chapters/derivatives/2622790efa97f1915e7998787d8ce977.svg +++ b/docs/images/chapters/derivatives/2622790efa97f1915e7998787d8ce977.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/28991bba7c13698619f36b6261d91d68.svg b/docs/images/chapters/derivatives/28991bba7c13698619f36b6261d91d68.svg index 6abe0d65..ca6684fd 100644 --- a/docs/images/chapters/derivatives/28991bba7c13698619f36b6261d91d68.svg +++ b/docs/images/chapters/derivatives/28991bba7c13698619f36b6261d91d68.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/514090a0fd6c64b7d85a9dc5721a0fa6.svg b/docs/images/chapters/derivatives/514090a0fd6c64b7d85a9dc5721a0fa6.svg index 17bb0477..302b1555 100644 --- a/docs/images/chapters/derivatives/514090a0fd6c64b7d85a9dc5721a0fa6.svg +++ b/docs/images/chapters/derivatives/514090a0fd6c64b7d85a9dc5721a0fa6.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/6770214cceeb0e13e371bd908867751f.svg b/docs/images/chapters/derivatives/6770214cceeb0e13e371bd908867751f.svg index 4d2ce4c6..1062751e 100644 --- a/docs/images/chapters/derivatives/6770214cceeb0e13e371bd908867751f.svg +++ b/docs/images/chapters/derivatives/6770214cceeb0e13e371bd908867751f.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/b7815b1502029ed9d805b6ba0801a53f.svg b/docs/images/chapters/derivatives/b7815b1502029ed9d805b6ba0801a53f.svg index 9527ca8b..30538ce1 100644 --- a/docs/images/chapters/derivatives/b7815b1502029ed9d805b6ba0801a53f.svg +++ b/docs/images/chapters/derivatives/b7815b1502029ed9d805b6ba0801a53f.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/bd3c740be364071c86ccf42b99d5eba4.svg b/docs/images/chapters/derivatives/bd3c740be364071c86ccf42b99d5eba4.svg index 8be31d1c..4c0938c6 100644 --- a/docs/images/chapters/derivatives/bd3c740be364071c86ccf42b99d5eba4.svg +++ b/docs/images/chapters/derivatives/bd3c740be364071c86ccf42b99d5eba4.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/c010c0df4bb911b84da6e9d379617e4b.svg b/docs/images/chapters/derivatives/c010c0df4bb911b84da6e9d379617e4b.svg index 89a1e3fc..76d47968 100644 --- a/docs/images/chapters/derivatives/c010c0df4bb911b84da6e9d379617e4b.svg +++ b/docs/images/chapters/derivatives/c010c0df4bb911b84da6e9d379617e4b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/c7b13e6507450b3da7dc4ce3c10c370f.svg b/docs/images/chapters/derivatives/c7b13e6507450b3da7dc4ce3c10c370f.svg index c305a989..0d82b2a7 100644 --- a/docs/images/chapters/derivatives/c7b13e6507450b3da7dc4ce3c10c370f.svg +++ b/docs/images/chapters/derivatives/c7b13e6507450b3da7dc4ce3c10c370f.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/e755c2adfec5d266c50e064407ca369b.svg b/docs/images/chapters/derivatives/e755c2adfec5d266c50e064407ca369b.svg index e8e996da..5d6d6555 100644 --- a/docs/images/chapters/derivatives/e755c2adfec5d266c50e064407ca369b.svg +++ b/docs/images/chapters/derivatives/e755c2adfec5d266c50e064407ca369b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/eb4442acc5bc17f4649eb04b2953ed9b.svg b/docs/images/chapters/derivatives/eb4442acc5bc17f4649eb04b2953ed9b.svg index 16ead760..684d7ddb 100644 --- a/docs/images/chapters/derivatives/eb4442acc5bc17f4649eb04b2953ed9b.svg +++ b/docs/images/chapters/derivatives/eb4442acc5bc17f4649eb04b2953ed9b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/derivatives/fb823558e99662b24d46ae55ac93ce38.svg b/docs/images/chapters/derivatives/fb823558e99662b24d46ae55ac93ce38.svg index 699f36ad..40251733 100644 --- a/docs/images/chapters/derivatives/fb823558e99662b24d46ae55ac93ce38.svg +++ b/docs/images/chapters/derivatives/fb823558e99662b24d46ae55ac93ce38.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/0f5cffd58e864fec6739a57664eb8cbd.svg b/docs/images/chapters/explanation/0f5cffd58e864fec6739a57664eb8cbd.svg index 8c514caf..e6c14e3f 100644 --- a/docs/images/chapters/explanation/0f5cffd58e864fec6739a57664eb8cbd.svg +++ b/docs/images/chapters/explanation/0f5cffd58e864fec6739a57664eb8cbd.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/1caef9931f954e32eae5067b732c1018.svg b/docs/images/chapters/explanation/1caef9931f954e32eae5067b732c1018.svg index 20f81095..c0225a5b 100644 --- a/docs/images/chapters/explanation/1caef9931f954e32eae5067b732c1018.svg +++ b/docs/images/chapters/explanation/1caef9931f954e32eae5067b732c1018.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/2adc12d0cff01d40d9e1702014a7dc19.svg b/docs/images/chapters/explanation/2adc12d0cff01d40d9e1702014a7dc19.svg index 9f8936e7..30295b14 100644 --- a/docs/images/chapters/explanation/2adc12d0cff01d40d9e1702014a7dc19.svg +++ b/docs/images/chapters/explanation/2adc12d0cff01d40d9e1702014a7dc19.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/4cf6fb369841e2c5d36e5567a8db4306.svg b/docs/images/chapters/explanation/4cf6fb369841e2c5d36e5567a8db4306.svg index c2c79ada..2b6f20ef 100644 --- a/docs/images/chapters/explanation/4cf6fb369841e2c5d36e5567a8db4306.svg +++ b/docs/images/chapters/explanation/4cf6fb369841e2c5d36e5567a8db4306.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/6e15c433dc2340271e007742009e3532.svg b/docs/images/chapters/explanation/6e15c433dc2340271e007742009e3532.svg index c3998089..79326f58 100644 --- a/docs/images/chapters/explanation/6e15c433dc2340271e007742009e3532.svg +++ b/docs/images/chapters/explanation/6e15c433dc2340271e007742009e3532.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/741097d69c182e8742695af23980bd8f.svg b/docs/images/chapters/explanation/741097d69c182e8742695af23980bd8f.svg index 2b15abd5..8d737d51 100644 --- a/docs/images/chapters/explanation/741097d69c182e8742695af23980bd8f.svg +++ b/docs/images/chapters/explanation/741097d69c182e8742695af23980bd8f.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/9a6d17c362980775f1425d0d2ad9a36a.svg b/docs/images/chapters/explanation/9a6d17c362980775f1425d0d2ad9a36a.svg index 1cc2bbfe..965d6a79 100644 --- a/docs/images/chapters/explanation/9a6d17c362980775f1425d0d2ad9a36a.svg +++ b/docs/images/chapters/explanation/9a6d17c362980775f1425d0d2ad9a36a.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/9c18f76e76cf684ecd217ad8facc2e93.svg b/docs/images/chapters/explanation/9c18f76e76cf684ecd217ad8facc2e93.svg index 403e1d94..e3cf2d07 100644 --- a/docs/images/chapters/explanation/9c18f76e76cf684ecd217ad8facc2e93.svg +++ b/docs/images/chapters/explanation/9c18f76e76cf684ecd217ad8facc2e93.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/bb06cb82d372f822a7b35e661502bd72.svg b/docs/images/chapters/explanation/bb06cb82d372f822a7b35e661502bd72.svg index 3f8cc467..367d2825 100644 --- a/docs/images/chapters/explanation/bb06cb82d372f822a7b35e661502bd72.svg +++ b/docs/images/chapters/explanation/bb06cb82d372f822a7b35e661502bd72.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/c605597fb629b964921c6a4bca7fa4c9.svg b/docs/images/chapters/explanation/c605597fb629b964921c6a4bca7fa4c9.svg index f1ca4cf3..47dadccb 100644 --- a/docs/images/chapters/explanation/c605597fb629b964921c6a4bca7fa4c9.svg +++ b/docs/images/chapters/explanation/c605597fb629b964921c6a4bca7fa4c9.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/explanation/f24fd5e27968d96957ba706b16d8e90b.svg b/docs/images/chapters/explanation/f24fd5e27968d96957ba706b16d8e90b.svg index 58a4bfbf..f7f300ca 100644 --- a/docs/images/chapters/explanation/f24fd5e27968d96957ba706b16d8e90b.svg +++ b/docs/images/chapters/explanation/f24fd5e27968d96957ba706b16d8e90b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/extended/b80a1cac1f9ec476d6f6646ce0e154e7.svg b/docs/images/chapters/extended/b80a1cac1f9ec476d6f6646ce0e154e7.svg index 5f3caaf8..f61387f9 100644 --- a/docs/images/chapters/extended/b80a1cac1f9ec476d6f6646ce0e154e7.svg +++ b/docs/images/chapters/extended/b80a1cac1f9ec476d6f6646ce0e154e7.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/extended/d930dea961b40f4810708bd6746221a2.svg b/docs/images/chapters/extended/d930dea961b40f4810708bd6746221a2.svg index 0efc7fd7..ec0121f2 100644 --- a/docs/images/chapters/extended/d930dea961b40f4810708bd6746221a2.svg +++ b/docs/images/chapters/extended/d930dea961b40f4810708bd6746221a2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/extended/f41f553d448de8559d68fccd9c2f27d4.svg b/docs/images/chapters/extended/f41f553d448de8559d68fccd9c2f27d4.svg index 31187c02..6c96e9ef 100644 --- a/docs/images/chapters/extended/f41f553d448de8559d68fccd9c2f27d4.svg +++ b/docs/images/chapters/extended/f41f553d448de8559d68fccd9c2f27d4.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/extremities/0ec5cc72a428d75defb480530b50d720.svg b/docs/images/chapters/extremities/0ec5cc72a428d75defb480530b50d720.svg index b8120594..fcc72109 100644 --- a/docs/images/chapters/extremities/0ec5cc72a428d75defb480530b50d720.svg +++ b/docs/images/chapters/extremities/0ec5cc72a428d75defb480530b50d720.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/extremities/1c0367fad2a0d6946db1f55a8520793a.svg b/docs/images/chapters/extremities/1c0367fad2a0d6946db1f55a8520793a.svg index d2524e6c..affde4e6 100644 --- a/docs/images/chapters/extremities/1c0367fad2a0d6946db1f55a8520793a.svg +++ b/docs/images/chapters/extremities/1c0367fad2a0d6946db1f55a8520793a.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/extremities/6db78123d4b676ffdf85d53670c77468.svg b/docs/images/chapters/extremities/6db78123d4b676ffdf85d53670c77468.svg index 7ae2cd25..06834ca1 100644 --- a/docs/images/chapters/extremities/6db78123d4b676ffdf85d53670c77468.svg +++ b/docs/images/chapters/extremities/6db78123d4b676ffdf85d53670c77468.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/extremities/997a8cc704c0ab0e364cb8b532df90b0.svg b/docs/images/chapters/extremities/997a8cc704c0ab0e364cb8b532df90b0.svg index fe3ddf3d..5fb54998 100644 --- a/docs/images/chapters/extremities/997a8cc704c0ab0e364cb8b532df90b0.svg +++ b/docs/images/chapters/extremities/997a8cc704c0ab0e364cb8b532df90b0.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/extremities/c621cc41f6f22ee1beedbcb510fa5b6b.svg b/docs/images/chapters/extremities/c621cc41f6f22ee1beedbcb510fa5b6b.svg index f2a29a0c..a36c5fef 100644 --- a/docs/images/chapters/extremities/c621cc41f6f22ee1beedbcb510fa5b6b.svg +++ b/docs/images/chapters/extremities/c621cc41f6f22ee1beedbcb510fa5b6b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/extremities/ddc6f99a543afad25c55cf16b9deeed9.svg b/docs/images/chapters/extremities/ddc6f99a543afad25c55cf16b9deeed9.svg index 37076a3f..0e944e9f 100644 --- a/docs/images/chapters/extremities/ddc6f99a543afad25c55cf16b9deeed9.svg +++ b/docs/images/chapters/extremities/ddc6f99a543afad25c55cf16b9deeed9.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/extremities/e06ec558d99b53e559d24524f4201951.svg b/docs/images/chapters/extremities/e06ec558d99b53e559d24524f4201951.svg index b9b577a0..f97d11ca 100644 --- a/docs/images/chapters/extremities/e06ec558d99b53e559d24524f4201951.svg +++ b/docs/images/chapters/extremities/e06ec558d99b53e559d24524f4201951.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/inflections/1679090a942a43d27f886f236fc8d62b.svg b/docs/images/chapters/inflections/1679090a942a43d27f886f236fc8d62b.svg index eb91f0b8..fed0b796 100644 --- a/docs/images/chapters/inflections/1679090a942a43d27f886f236fc8d62b.svg +++ b/docs/images/chapters/inflections/1679090a942a43d27f886f236fc8d62b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/inflections/2029bca9f4fa15739553636af99b70a8.svg b/docs/images/chapters/inflections/2029bca9f4fa15739553636af99b70a8.svg index 0d596d81..9202cac6 100644 --- a/docs/images/chapters/inflections/2029bca9f4fa15739553636af99b70a8.svg +++ b/docs/images/chapters/inflections/2029bca9f4fa15739553636af99b70a8.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/inflections/4b5c7d0bf0fcd769db007dd98d4a024d.svg b/docs/images/chapters/inflections/4b5c7d0bf0fcd769db007dd98d4a024d.svg index 2b3ba2b2..db7ef2b3 100644 --- a/docs/images/chapters/inflections/4b5c7d0bf0fcd769db007dd98d4a024d.svg +++ b/docs/images/chapters/inflections/4b5c7d0bf0fcd769db007dd98d4a024d.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/inflections/4d78ebcf8626f777725d67d3672fa480.svg b/docs/images/chapters/inflections/4d78ebcf8626f777725d67d3672fa480.svg index cbd7521e..4d4d0eeb 100644 --- a/docs/images/chapters/inflections/4d78ebcf8626f777725d67d3672fa480.svg +++ b/docs/images/chapters/inflections/4d78ebcf8626f777725d67d3672fa480.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/inflections/7c9762c0e04693eb743905cdc0487f8b.svg b/docs/images/chapters/inflections/7c9762c0e04693eb743905cdc0487f8b.svg index 6b05bfed..09823965 100644 --- a/docs/images/chapters/inflections/7c9762c0e04693eb743905cdc0487f8b.svg +++ b/docs/images/chapters/inflections/7c9762c0e04693eb743905cdc0487f8b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/inflections/97b34ad5920612574d1b2a1a9d22d571.svg b/docs/images/chapters/inflections/97b34ad5920612574d1b2a1a9d22d571.svg index 96136043..7ba90eca 100644 --- a/docs/images/chapters/inflections/97b34ad5920612574d1b2a1a9d22d571.svg +++ b/docs/images/chapters/inflections/97b34ad5920612574d1b2a1a9d22d571.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/inflections/b2433959e1f451fa3bf238fc37e04527.svg b/docs/images/chapters/inflections/b2433959e1f451fa3bf238fc37e04527.svg index abd72ec8..887401db 100644 --- a/docs/images/chapters/inflections/b2433959e1f451fa3bf238fc37e04527.svg +++ b/docs/images/chapters/inflections/b2433959e1f451fa3bf238fc37e04527.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/inflections/bafdb6583323bda71d9a15c02d1fdec2.svg b/docs/images/chapters/inflections/bafdb6583323bda71d9a15c02d1fdec2.svg index ec06373a..ad307dec 100644 --- a/docs/images/chapters/inflections/bafdb6583323bda71d9a15c02d1fdec2.svg +++ b/docs/images/chapters/inflections/bafdb6583323bda71d9a15c02d1fdec2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrix/009c671bc526b5d75c30411c3c3a7e91.svg b/docs/images/chapters/matrix/009c671bc526b5d75c30411c3c3a7e91.svg index 8e3e62ef..d7fb358c 100644 --- a/docs/images/chapters/matrix/009c671bc526b5d75c30411c3c3a7e91.svg +++ b/docs/images/chapters/matrix/009c671bc526b5d75c30411c3c3a7e91.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrix/24bdad213879407a35b23c18394293aa.svg b/docs/images/chapters/matrix/24bdad213879407a35b23c18394293aa.svg index 4ef8cee4..fd97a72d 100644 --- a/docs/images/chapters/matrix/24bdad213879407a35b23c18394293aa.svg +++ b/docs/images/chapters/matrix/24bdad213879407a35b23c18394293aa.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrix/6da69918482a0b6b84d90a72dbeae9dd.svg b/docs/images/chapters/matrix/6da69918482a0b6b84d90a72dbeae9dd.svg index 4749be9f..acf7e3c9 100644 --- a/docs/images/chapters/matrix/6da69918482a0b6b84d90a72dbeae9dd.svg +++ b/docs/images/chapters/matrix/6da69918482a0b6b84d90a72dbeae9dd.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrix/77a11d65d7cffc4b84a85c4bec837792.svg b/docs/images/chapters/matrix/77a11d65d7cffc4b84a85c4bec837792.svg index 4215f0dd..36cae683 100644 --- a/docs/images/chapters/matrix/77a11d65d7cffc4b84a85c4bec837792.svg +++ b/docs/images/chapters/matrix/77a11d65d7cffc4b84a85c4bec837792.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrix/9bc905d79bb22580b8c1cd75a791db73.svg b/docs/images/chapters/matrix/9bc905d79bb22580b8c1cd75a791db73.svg index 254f266e..f9a8f05f 100644 --- a/docs/images/chapters/matrix/9bc905d79bb22580b8c1cd75a791db73.svg +++ b/docs/images/chapters/matrix/9bc905d79bb22580b8c1cd75a791db73.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrix/c1f815481ad5132bebc1b1f0a3edf20f.svg b/docs/images/chapters/matrix/c1f815481ad5132bebc1b1f0a3edf20f.svg index c8823a45..dd63abc7 100644 --- a/docs/images/chapters/matrix/c1f815481ad5132bebc1b1f0a3edf20f.svg +++ b/docs/images/chapters/matrix/c1f815481ad5132bebc1b1f0a3edf20f.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrix/e0d89b48cd11a726c00a2f689d48d57c.svg b/docs/images/chapters/matrix/e0d89b48cd11a726c00a2f689d48d57c.svg index da303313..1ca78db6 100644 --- a/docs/images/chapters/matrix/e0d89b48cd11a726c00a2f689d48d57c.svg +++ b/docs/images/chapters/matrix/e0d89b48cd11a726c00a2f689d48d57c.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrix/e524525c62234ce616a1e51c9848c169.svg b/docs/images/chapters/matrix/e524525c62234ce616a1e51c9848c169.svg index b9afc6a3..8f2916b0 100644 --- a/docs/images/chapters/matrix/e524525c62234ce616a1e51c9848c169.svg +++ b/docs/images/chapters/matrix/e524525c62234ce616a1e51c9848c169.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrix/e94ae04eb5732c05d38fa1c97a2a25b0.svg b/docs/images/chapters/matrix/e94ae04eb5732c05d38fa1c97a2a25b0.svg index b3b0178e..08fb7200 100644 --- a/docs/images/chapters/matrix/e94ae04eb5732c05d38fa1c97a2a25b0.svg +++ b/docs/images/chapters/matrix/e94ae04eb5732c05d38fa1c97a2a25b0.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/0d2e895e767c4cecb0fccafee1273152.svg b/docs/images/chapters/matrixsplit/0d2e895e767c4cecb0fccafee1273152.svg index fa1c269f..d8bd4ad0 100644 --- a/docs/images/chapters/matrixsplit/0d2e895e767c4cecb0fccafee1273152.svg +++ b/docs/images/chapters/matrixsplit/0d2e895e767c4cecb0fccafee1273152.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/0f84dbf6e3ea7db732ceb9d71caf9b22.svg b/docs/images/chapters/matrixsplit/0f84dbf6e3ea7db732ceb9d71caf9b22.svg index d1c9fd4b..6bb1ac97 100644 --- a/docs/images/chapters/matrixsplit/0f84dbf6e3ea7db732ceb9d71caf9b22.svg +++ b/docs/images/chapters/matrixsplit/0f84dbf6e3ea7db732ceb9d71caf9b22.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/19049f556723a4f2d985a631a91ae290.svg b/docs/images/chapters/matrixsplit/19049f556723a4f2d985a631a91ae290.svg index f9d6732f..6c5365bf 100644 --- a/docs/images/chapters/matrixsplit/19049f556723a4f2d985a631a91ae290.svg +++ b/docs/images/chapters/matrixsplit/19049f556723a4f2d985a631a91ae290.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/278b67e9b908f4abcf2e9d069a6b29a4.svg b/docs/images/chapters/matrixsplit/278b67e9b908f4abcf2e9d069a6b29a4.svg index e27e51cd..2cb2e6c8 100644 --- a/docs/images/chapters/matrixsplit/278b67e9b908f4abcf2e9d069a6b29a4.svg +++ b/docs/images/chapters/matrixsplit/278b67e9b908f4abcf2e9d069a6b29a4.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/2f2bec1e77039a40c31220f5bf83641a.svg b/docs/images/chapters/matrixsplit/2f2bec1e77039a40c31220f5bf83641a.svg index 6b10ffd4..b90e9f56 100644 --- a/docs/images/chapters/matrixsplit/2f2bec1e77039a40c31220f5bf83641a.svg +++ b/docs/images/chapters/matrixsplit/2f2bec1e77039a40c31220f5bf83641a.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/3ed7fa50bf68beef4c77d23e665063d2.svg b/docs/images/chapters/matrixsplit/3ed7fa50bf68beef4c77d23e665063d2.svg index 8e34c4aa..44d305e1 100644 --- a/docs/images/chapters/matrixsplit/3ed7fa50bf68beef4c77d23e665063d2.svg +++ b/docs/images/chapters/matrixsplit/3ed7fa50bf68beef4c77d23e665063d2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/4063d3462c179e91bb5f97c5e763560a.svg b/docs/images/chapters/matrixsplit/4063d3462c179e91bb5f97c5e763560a.svg index 73a311b0..574b4950 100644 --- a/docs/images/chapters/matrixsplit/4063d3462c179e91bb5f97c5e763560a.svg +++ b/docs/images/chapters/matrixsplit/4063d3462c179e91bb5f97c5e763560a.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/567c29ee78b49c700f54b17780682543.svg b/docs/images/chapters/matrixsplit/567c29ee78b49c700f54b17780682543.svg index 9b5c5efa..65f5ea0a 100644 --- a/docs/images/chapters/matrixsplit/567c29ee78b49c700f54b17780682543.svg +++ b/docs/images/chapters/matrixsplit/567c29ee78b49c700f54b17780682543.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/5e3fae45d325d0f0681731fb606b6fbc.svg b/docs/images/chapters/matrixsplit/5e3fae45d325d0f0681731fb606b6fbc.svg index a7d00f97..cc997f4a 100644 --- a/docs/images/chapters/matrixsplit/5e3fae45d325d0f0681731fb606b6fbc.svg +++ b/docs/images/chapters/matrixsplit/5e3fae45d325d0f0681731fb606b6fbc.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/6aeb749eb26f5a9199c1b16d7d421dc0.svg b/docs/images/chapters/matrixsplit/6aeb749eb26f5a9199c1b16d7d421dc0.svg index 44da75fe..e5025970 100644 --- a/docs/images/chapters/matrixsplit/6aeb749eb26f5a9199c1b16d7d421dc0.svg +++ b/docs/images/chapters/matrixsplit/6aeb749eb26f5a9199c1b16d7d421dc0.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/77a11d65d7cffc4b84a85c4bec837792.svg b/docs/images/chapters/matrixsplit/77a11d65d7cffc4b84a85c4bec837792.svg index 4215f0dd..36cae683 100644 --- a/docs/images/chapters/matrixsplit/77a11d65d7cffc4b84a85c4bec837792.svg +++ b/docs/images/chapters/matrixsplit/77a11d65d7cffc4b84a85c4bec837792.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/7d629178a5fb985a35770002d1912535.svg b/docs/images/chapters/matrixsplit/7d629178a5fb985a35770002d1912535.svg index 7dc9fd3e..db09f78f 100644 --- a/docs/images/chapters/matrixsplit/7d629178a5fb985a35770002d1912535.svg +++ b/docs/images/chapters/matrixsplit/7d629178a5fb985a35770002d1912535.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/859b7bc7b78e8e297ae5fddd9be40ab7.svg b/docs/images/chapters/matrixsplit/859b7bc7b78e8e297ae5fddd9be40ab7.svg index cf05e8f9..aa9992f8 100644 --- a/docs/images/chapters/matrixsplit/859b7bc7b78e8e297ae5fddd9be40ab7.svg +++ b/docs/images/chapters/matrixsplit/859b7bc7b78e8e297ae5fddd9be40ab7.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/8fb4faa046191480e89052102ecd3678.svg b/docs/images/chapters/matrixsplit/8fb4faa046191480e89052102ecd3678.svg index e9bcf845..6bd18183 100644 --- a/docs/images/chapters/matrixsplit/8fb4faa046191480e89052102ecd3678.svg +++ b/docs/images/chapters/matrixsplit/8fb4faa046191480e89052102ecd3678.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/a34473afe7a4160b45ce0f2a770fad99.svg b/docs/images/chapters/matrixsplit/a34473afe7a4160b45ce0f2a770fad99.svg index d5cfe435..93b2b311 100644 --- a/docs/images/chapters/matrixsplit/a34473afe7a4160b45ce0f2a770fad99.svg +++ b/docs/images/chapters/matrixsplit/a34473afe7a4160b45ce0f2a770fad99.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/a56f198daab08d20ef666599af14f513.svg b/docs/images/chapters/matrixsplit/a56f198daab08d20ef666599af14f513.svg index e4776d74..826425dd 100644 --- a/docs/images/chapters/matrixsplit/a56f198daab08d20ef666599af14f513.svg +++ b/docs/images/chapters/matrixsplit/a56f198daab08d20ef666599af14f513.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/abb3edce2229312f351d81092ba2145b.svg b/docs/images/chapters/matrixsplit/abb3edce2229312f351d81092ba2145b.svg index dc7e7d82..e0acfe12 100644 --- a/docs/images/chapters/matrixsplit/abb3edce2229312f351d81092ba2145b.svg +++ b/docs/images/chapters/matrixsplit/abb3edce2229312f351d81092ba2145b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/b5cf45e4b34fdd18f599b79549844d45.svg b/docs/images/chapters/matrixsplit/b5cf45e4b34fdd18f599b79549844d45.svg index ed658f6e..ae19422f 100644 --- a/docs/images/chapters/matrixsplit/b5cf45e4b34fdd18f599b79549844d45.svg +++ b/docs/images/chapters/matrixsplit/b5cf45e4b34fdd18f599b79549844d45.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/c1fcb64541c09e6d180c3d4a5511858e.svg b/docs/images/chapters/matrixsplit/c1fcb64541c09e6d180c3d4a5511858e.svg index bb9e8706..862f26a2 100644 --- a/docs/images/chapters/matrixsplit/c1fcb64541c09e6d180c3d4a5511858e.svg +++ b/docs/images/chapters/matrixsplit/c1fcb64541c09e6d180c3d4a5511858e.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/c58330e12d25c678b593ddbd4afa7c52.svg b/docs/images/chapters/matrixsplit/c58330e12d25c678b593ddbd4afa7c52.svg index 8e3e62ef..d7fb358c 100644 --- a/docs/images/chapters/matrixsplit/c58330e12d25c678b593ddbd4afa7c52.svg +++ b/docs/images/chapters/matrixsplit/c58330e12d25c678b593ddbd4afa7c52.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/c79b607a92c42789fde57c6a8c4259fd.svg b/docs/images/chapters/matrixsplit/c79b607a92c42789fde57c6a8c4259fd.svg index 2f86a288..9a0d7125 100644 --- a/docs/images/chapters/matrixsplit/c79b607a92c42789fde57c6a8c4259fd.svg +++ b/docs/images/chapters/matrixsplit/c79b607a92c42789fde57c6a8c4259fd.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/d0a2afc05a974e7e25ce0564505818be.svg b/docs/images/chapters/matrixsplit/d0a2afc05a974e7e25ce0564505818be.svg index fd797ad6..6c67a16c 100644 --- a/docs/images/chapters/matrixsplit/d0a2afc05a974e7e25ce0564505818be.svg +++ b/docs/images/chapters/matrixsplit/d0a2afc05a974e7e25ce0564505818be.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/daaae36f13bb97f2a7ac21eec6903755.svg b/docs/images/chapters/matrixsplit/daaae36f13bb97f2a7ac21eec6903755.svg index c69dd954..9b0cf1a8 100644 --- a/docs/images/chapters/matrixsplit/daaae36f13bb97f2a7ac21eec6903755.svg +++ b/docs/images/chapters/matrixsplit/daaae36f13bb97f2a7ac21eec6903755.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/dbdbbe9aed4dacb1c1c5ae29b4371870.svg b/docs/images/chapters/matrixsplit/dbdbbe9aed4dacb1c1c5ae29b4371870.svg index 89cda9e1..fec39dd4 100644 --- a/docs/images/chapters/matrixsplit/dbdbbe9aed4dacb1c1c5ae29b4371870.svg +++ b/docs/images/chapters/matrixsplit/dbdbbe9aed4dacb1c1c5ae29b4371870.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/e16eba6dfb9f0b8d1abc3e1cd3ba63a2.svg b/docs/images/chapters/matrixsplit/e16eba6dfb9f0b8d1abc3e1cd3ba63a2.svg index 1b6de205..2c470207 100644 --- a/docs/images/chapters/matrixsplit/e16eba6dfb9f0b8d1abc3e1cd3ba63a2.svg +++ b/docs/images/chapters/matrixsplit/e16eba6dfb9f0b8d1abc3e1cd3ba63a2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/e9f64464287d3d5c6a4cbe64e21746c8.svg b/docs/images/chapters/matrixsplit/e9f64464287d3d5c6a4cbe64e21746c8.svg index d2746e35..60c93baa 100644 --- a/docs/images/chapters/matrixsplit/e9f64464287d3d5c6a4cbe64e21746c8.svg +++ b/docs/images/chapters/matrixsplit/e9f64464287d3d5c6a4cbe64e21746c8.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/matrixsplit/f2695b6d6417c60343b4934dae8118f8.svg b/docs/images/chapters/matrixsplit/f2695b6d6417c60343b4934dae8118f8.svg index f03a95a3..376848fe 100644 --- a/docs/images/chapters/matrixsplit/f2695b6d6417c60343b4934dae8118f8.svg +++ b/docs/images/chapters/matrixsplit/f2695b6d6417c60343b4934dae8118f8.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/moulding/524206c49f317d27d8e07a310b24a7a3.svg b/docs/images/chapters/moulding/524206c49f317d27d8e07a310b24a7a3.svg index b06110fb..848c23d9 100644 --- a/docs/images/chapters/moulding/524206c49f317d27d8e07a310b24a7a3.svg +++ b/docs/images/chapters/moulding/524206c49f317d27d8e07a310b24a7a3.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/moulding/7bba0a4fd605e023cda922de125b3e32.svg b/docs/images/chapters/moulding/7bba0a4fd605e023cda922de125b3e32.svg index 825171d1..02e71f37 100644 --- a/docs/images/chapters/moulding/7bba0a4fd605e023cda922de125b3e32.svg +++ b/docs/images/chapters/moulding/7bba0a4fd605e023cda922de125b3e32.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/moulding/94f61d17f896aebddcf5a7c676aee7d1.svg b/docs/images/chapters/moulding/94f61d17f896aebddcf5a7c676aee7d1.svg index 719cdc93..73973d9b 100644 --- a/docs/images/chapters/moulding/94f61d17f896aebddcf5a7c676aee7d1.svg +++ b/docs/images/chapters/moulding/94f61d17f896aebddcf5a7c676aee7d1.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/offsetting/1d586b939b44ff9bdb42562a12ac2779.svg b/docs/images/chapters/offsetting/1d586b939b44ff9bdb42562a12ac2779.svg index c28fa909..ca6b1c21 100644 --- a/docs/images/chapters/offsetting/1d586b939b44ff9bdb42562a12ac2779.svg +++ b/docs/images/chapters/offsetting/1d586b939b44ff9bdb42562a12ac2779.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/offsetting/5bfee4f2ae27304475673d0596e42f9a.svg b/docs/images/chapters/offsetting/5bfee4f2ae27304475673d0596e42f9a.svg index c8b6c112..62f1e96a 100644 --- a/docs/images/chapters/offsetting/5bfee4f2ae27304475673d0596e42f9a.svg +++ b/docs/images/chapters/offsetting/5bfee4f2ae27304475673d0596e42f9a.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/offsetting/b262e50c085815421d94e120fc17f1c8.svg b/docs/images/chapters/offsetting/b262e50c085815421d94e120fc17f1c8.svg index a0f2cc18..69eb63ea 100644 --- a/docs/images/chapters/offsetting/b262e50c085815421d94e120fc17f1c8.svg +++ b/docs/images/chapters/offsetting/b262e50c085815421d94e120fc17f1c8.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/pointvectors/009715fce01e46e7c07f87a8192a8c62.svg b/docs/images/chapters/pointvectors/009715fce01e46e7c07f87a8192a8c62.svg index 50c52420..04e9edfa 100644 --- a/docs/images/chapters/pointvectors/009715fce01e46e7c07f87a8192a8c62.svg +++ b/docs/images/chapters/pointvectors/009715fce01e46e7c07f87a8192a8c62.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/pointvectors/2a55cb2d23c25408aa10cfd8db13278b.svg b/docs/images/chapters/pointvectors/2a55cb2d23c25408aa10cfd8db13278b.svg index d9ca5c01..2200181b 100644 --- a/docs/images/chapters/pointvectors/2a55cb2d23c25408aa10cfd8db13278b.svg +++ b/docs/images/chapters/pointvectors/2a55cb2d23c25408aa10cfd8db13278b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/pointvectors/2dd2f89d1c762991a86526490a3deef6.svg b/docs/images/chapters/pointvectors/2dd2f89d1c762991a86526490a3deef6.svg index 1f70946b..8abe2420 100644 --- a/docs/images/chapters/pointvectors/2dd2f89d1c762991a86526490a3deef6.svg +++ b/docs/images/chapters/pointvectors/2dd2f89d1c762991a86526490a3deef6.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/pointvectors/6101b2f8b69ebabba4a2c88456a32aa0.svg b/docs/images/chapters/pointvectors/6101b2f8b69ebabba4a2c88456a32aa0.svg index 0bf47766..7654f406 100644 --- a/docs/images/chapters/pointvectors/6101b2f8b69ebabba4a2c88456a32aa0.svg +++ b/docs/images/chapters/pointvectors/6101b2f8b69ebabba4a2c88456a32aa0.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/pointvectors/d236b7b2ad46c8ced1b43bb2a496379a.svg b/docs/images/chapters/pointvectors/d236b7b2ad46c8ced1b43bb2a496379a.svg index 142772d9..9022ac6e 100644 --- a/docs/images/chapters/pointvectors/d236b7b2ad46c8ced1b43bb2a496379a.svg +++ b/docs/images/chapters/pointvectors/d236b7b2ad46c8ced1b43bb2a496379a.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/pointvectors/deec095950fcd1f9c980be76a7093fe6.svg b/docs/images/chapters/pointvectors/deec095950fcd1f9c980be76a7093fe6.svg index f897a76d..4c60abc7 100644 --- a/docs/images/chapters/pointvectors/deec095950fcd1f9c980be76a7093fe6.svg +++ b/docs/images/chapters/pointvectors/deec095950fcd1f9c980be76a7093fe6.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/polybezier/408dd95905a5f001179c4da6051e49c5.svg b/docs/images/chapters/polybezier/408dd95905a5f001179c4da6051e49c5.svg index d3c2eedd..4f06b843 100644 --- a/docs/images/chapters/polybezier/408dd95905a5f001179c4da6051e49c5.svg +++ b/docs/images/chapters/polybezier/408dd95905a5f001179c4da6051e49c5.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/reordering/1244a85c1f9044b6f77cb709c682159c.svg b/docs/images/chapters/reordering/1244a85c1f9044b6f77cb709c682159c.svg index d46abe80..0d07fa5a 100644 --- a/docs/images/chapters/reordering/1244a85c1f9044b6f77cb709c682159c.svg +++ b/docs/images/chapters/reordering/1244a85c1f9044b6f77cb709c682159c.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png b/docs/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png index 4f847b27..d1ced7c8 100644 Binary files a/docs/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png and b/docs/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png differ diff --git a/docs/images/chapters/reordering/483c89c8726f7fd0dca0b7de339b04bd.svg b/docs/images/chapters/reordering/483c89c8726f7fd0dca0b7de339b04bd.svg index 267c833b..1f8906b2 100644 --- a/docs/images/chapters/reordering/483c89c8726f7fd0dca0b7de339b04bd.svg +++ b/docs/images/chapters/reordering/483c89c8726f7fd0dca0b7de339b04bd.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/reordering/4debbed5922d2bd84fd322c616872d20.svg b/docs/images/chapters/reordering/4debbed5922d2bd84fd322c616872d20.svg index de0769c8..cb32a047 100644 --- a/docs/images/chapters/reordering/4debbed5922d2bd84fd322c616872d20.svg +++ b/docs/images/chapters/reordering/4debbed5922d2bd84fd322c616872d20.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/reordering/773fdc86b686647c823b4f499aca3a35.svg b/docs/images/chapters/reordering/773fdc86b686647c823b4f499aca3a35.svg index 27cb2257..aeaa5a49 100644 --- a/docs/images/chapters/reordering/773fdc86b686647c823b4f499aca3a35.svg +++ b/docs/images/chapters/reordering/773fdc86b686647c823b4f499aca3a35.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/reordering/b2fda1dcce5bb13317aa42ebf5e7ea6c.svg b/docs/images/chapters/reordering/b2fda1dcce5bb13317aa42ebf5e7ea6c.svg index 30e4f66f..31314e46 100644 --- a/docs/images/chapters/reordering/b2fda1dcce5bb13317aa42ebf5e7ea6c.svg +++ b/docs/images/chapters/reordering/b2fda1dcce5bb13317aa42ebf5e7ea6c.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/reordering/d52f60b331c1b8d6733eb5217adfbc4d.svg b/docs/images/chapters/reordering/d52f60b331c1b8d6733eb5217adfbc4d.svg index 62f5c177..b95d616a 100644 --- a/docs/images/chapters/reordering/d52f60b331c1b8d6733eb5217adfbc4d.svg +++ b/docs/images/chapters/reordering/d52f60b331c1b8d6733eb5217adfbc4d.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/reordering/dd8d8d98f66ce9f51b95cbf48225e97b.svg b/docs/images/chapters/reordering/dd8d8d98f66ce9f51b95cbf48225e97b.svg index f01ff76a..4b2f9ea8 100644 --- a/docs/images/chapters/reordering/dd8d8d98f66ce9f51b95cbf48225e97b.svg +++ b/docs/images/chapters/reordering/dd8d8d98f66ce9f51b95cbf48225e97b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/reordering/faf29599c9307f930ec28065c96fde2a.svg b/docs/images/chapters/reordering/faf29599c9307f930ec28065c96fde2a.svg index 4656d758..2375a041 100644 --- a/docs/images/chapters/reordering/faf29599c9307f930ec28065c96fde2a.svg +++ b/docs/images/chapters/reordering/faf29599c9307f930ec28065c96fde2a.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/weightcontrol/02457b19087540dfb144978419524a85.svg b/docs/images/chapters/weightcontrol/02457b19087540dfb144978419524a85.svg index a508fd75..415b342a 100644 --- a/docs/images/chapters/weightcontrol/02457b19087540dfb144978419524a85.svg +++ b/docs/images/chapters/weightcontrol/02457b19087540dfb144978419524a85.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/weightcontrol/3fd61ab3fe88f694e70f61e4f8ea056b.svg b/docs/images/chapters/weightcontrol/3fd61ab3fe88f694e70f61e4f8ea056b.svg index 6b83cf66..fff9cf18 100644 --- a/docs/images/chapters/weightcontrol/3fd61ab3fe88f694e70f61e4f8ea056b.svg +++ b/docs/images/chapters/weightcontrol/3fd61ab3fe88f694e70f61e4f8ea056b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/whatis/4df088f01d0fd4de84a50bbc2e25f8a7.svg b/docs/images/chapters/whatis/4df088f01d0fd4de84a50bbc2e25f8a7.svg index 96f12448..2c10a4b8 100644 --- a/docs/images/chapters/whatis/4df088f01d0fd4de84a50bbc2e25f8a7.svg +++ b/docs/images/chapters/whatis/4df088f01d0fd4de84a50bbc2e25f8a7.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/whatis/b5aa26284ba3df74970a95cb047a841d.svg b/docs/images/chapters/whatis/b5aa26284ba3df74970a95cb047a841d.svg index aebaa95a..a6c781c5 100644 --- a/docs/images/chapters/whatis/b5aa26284ba3df74970a95cb047a841d.svg +++ b/docs/images/chapters/whatis/b5aa26284ba3df74970a95cb047a841d.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/whatis/d39b17854b29fbb3c70bec7a12820aa1.png b/docs/images/chapters/whatis/d39b17854b29fbb3c70bec7a12820aa1.png index 8c8cf36d..f316f4aa 100644 Binary files a/docs/images/chapters/whatis/d39b17854b29fbb3c70bec7a12820aa1.png and b/docs/images/chapters/whatis/d39b17854b29fbb3c70bec7a12820aa1.png differ diff --git a/docs/images/chapters/yforx/9ab2b830fe7fb73350c19bde04e9441b.svg b/docs/images/chapters/yforx/9ab2b830fe7fb73350c19bde04e9441b.svg index 94c535f2..dedd40b2 100644 --- a/docs/images/chapters/yforx/9ab2b830fe7fb73350c19bde04e9441b.svg +++ b/docs/images/chapters/yforx/9ab2b830fe7fb73350c19bde04e9441b.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/yforx/9df91c28af38c1ba2e2d38d2714c9446.svg b/docs/images/chapters/yforx/9df91c28af38c1ba2e2d38d2714c9446.svg index 87df6dea..8a3f66aa 100644 --- a/docs/images/chapters/yforx/9df91c28af38c1ba2e2d38d2714c9446.svg +++ b/docs/images/chapters/yforx/9df91c28af38c1ba2e2d38d2714c9446.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/yforx/de3bd3e271d72194c730d0ae44f031a8.svg b/docs/images/chapters/yforx/de3bd3e271d72194c730d0ae44f031a8.svg index 9433159d..3adeaef7 100644 --- a/docs/images/chapters/yforx/de3bd3e271d72194c730d0ae44f031a8.svg +++ b/docs/images/chapters/yforx/de3bd3e271d72194c730d0ae44f031a8.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 69abaf46..8937d982 100644 --- a/docs/index.html +++ b/docs/index.html @@ -64,6 +64,14 @@ + + + @@ -567,6 +575,7 @@ src="./images/chapters/whatis/b5aa26284ba3df74970a95cb047a841d.svg" width="555px" height="101px" + loading="lazy" />

So let's look at that in action: the following graphic is @@ -636,6 +645,7 @@ src="./images/chapters/explanation/1caef9931f954e32eae5067b732c1018.svg" width="93px" height="17px" + loading="lazy" />

The notation f(x) is the standard way to show that it's a @@ -652,6 +662,7 @@ src="./images/chapters/explanation/0f5cffd58e864fec6739a57664eb8cbd.svg" width="92px" height="37px" + loading="lazy" />

There's nothing really remarkable about them, they're just a sine @@ -667,6 +678,7 @@ src="./images/chapters/explanation/066a910ae6aba69c40a338320759cdd1.svg" width="104px" height="40px" + loading="lazy" />

Multiple functions, but only one variable. If we change the value @@ -682,6 +694,7 @@ src="./images/chapters/explanation/4cf6fb369841e2c5d36e5567a8db4306.svg" width="79px" height="40px" + loading="lazy" />

There we go. x/y coordinates, linked through some @@ -735,6 +748,7 @@ src="./images/chapters/explanation/bb06cb82d372f822a7b35e661502bd72.svg" width="219px" height="19px" + loading="lazy" />

If the highest order term they have is , they're called @@ -754,6 +768,7 @@ src="./images/chapters/explanation/2adc12d0cff01d40d9e1702014a7dc19.svg" width="371px" height="64px" + loading="lazy" />

I know what you're thinking: that doesn't look too simple! But if we @@ -765,6 +780,7 @@ src="./images/chapters/explanation/9c18f76e76cf684ecd217ad8facc2e93.svg" width="189px" height="85px" + loading="lazy" />

Notice that 2 is the same as 1+1, and 3 is 2+1 and 1+2, and 6 is @@ -786,6 +802,7 @@ src="./images/chapters/explanation/e107caca1577e44293cd207388ac939c.svg" width="312px" height="60px" + loading="lazy" />

It's basically just a sum of "every combination of a and @@ -799,6 +816,7 @@ src="./images/chapters/explanation/9a6d17c362980775f1425d0d2ad9a36a.svg" width="323px" height="56px" + loading="lazy" />

And that's the full description for Bézier curves. Σ in this @@ -980,6 +998,7 @@ function Bezier(3,t): src="./images/chapters/control/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg" width="379px" height="56px" + loading="lazy" />

That looks complicated, but as it so happens, the "weights" are @@ -995,6 +1014,7 @@ function Bezier(3,t): src="./images/chapters/control/c0d4dbc07b8ec7c0a18ea43c8a386935.svg" width="476px" height="40px" + loading="lazy" />

Which gives us the curve we saw at the top of the article:

The function for rational Bézier curves has two more terms:

In this, the first new term represents an additional weight for each @@ -1205,6 +1227,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/extended/b80a1cac1f9ec476d6f6646ce0e154e7.svg" width="227px" height="17px" + loading="lazy" />

The obvious start and end values here need to be @@ -1224,6 +1247,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/extended/d930dea961b40f4810708bd6746221a2.svg" width="223px" height="17px" + loading="lazy" />

With this we can guarantee that we never sum above 100%. By @@ -1314,6 +1338,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/5aea6d4d5855135051715fb1cc0ec531.svg" width="468px" height="20px" + loading="lazy" />

Disregarding our actual coordinates for a moment, we have:

We can write this as a sum of four expressions:

And we can expand these expressions:

Furthermore, we can make all the 1 and 0 factors explicit:

And that, we can view as a series of four matrix @@ -1352,6 +1381,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/6da69918482a0b6b84d90a72dbeae9dd.svg" width="607px" height="72px" + loading="lazy" />

If we compact this into a single matrix operation, we get:

This kind of polynomial basis representation is generally written @@ -1371,6 +1402,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/24bdad213879407a35b23c18394293aa.svg" width="227px" height="72px" + loading="lazy" />

And then finally, we can add in our original coordinates as a single @@ -1381,6 +1413,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/009c671bc526b5d75c30411c3c3a7e91.svg" width="323px" height="73px" + loading="lazy" />

We can perform the same trick for the quadratic curve, in which case @@ -1391,6 +1424,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/77a11d65d7cffc4b84a85c4bec837792.svg" width="263px" height="55px" + loading="lazy" />

If we plug in a t value, and then multiply the @@ -1702,6 +1736,7 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/77a11d65d7cffc4b84a85c4bec837792.svg" width="263px" height="55px" + loading="lazy" />

and

Let's say we want to split the curve at some point @@ -1723,6 +1759,7 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/278b67e9b908f4abcf2e9d069a6b29a4.svg" width="648px" height="55px" + loading="lazy" />

and

If we could compact these matrices back to the form @@ -1753,24 +1791,28 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/c79b607a92c42789fde57c6a8c4259fd.svg" width="348px" height="55px" + loading="lazy" />

We can do this because [M · M-1

Excellent! Now we can form our new quadratic curve:

Brilliant

If we want the interval [z,1], we will be evaluating this @@ -1857,12 +1905,14 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/0f84dbf6e3ea7db732ceb9d71caf9b22.svg" width="461px" height="55px" + loading="lazy" />

We're going to do the same trick of multiplying by the identity @@ -1874,6 +1924,7 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/567c29ee78b49c700f54b17780682543.svg" width="729px" height="57px" + loading="lazy" />

So, our final second curve looks like:

Nice

and

We can do the same for cubic curves. However, I'll spare you the @@ -1936,6 +1992,7 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/5e3fae45d325d0f0681731fb606b6fbc.svg" width="841px" height="75px" + loading="lazy" />

and

So, looking at our matrices, did we really need to compute the @@ -1991,6 +2049,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/faf29599c9307f930ec28065c96fde2a.svg" width="805px" height="61px" + loading="lazy" />

However, this rule also has as direct consequence that you @@ -2024,6 +2083,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/1244a85c1f9044b6f77cb709c682159c.svg" width="429px" height="41px" + loading="lazy" />

Then, we apply one of those silly (actually, super useful) calculus @@ -2037,6 +2097,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/b2fda1dcce5bb13317aa42ebf5e7ea6c.svg" width="384px" height="17px" + loading="lazy" />

So, with that seemingly trivial observation, we rewrite that Bézier @@ -2048,6 +2109,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/41e184228d85023abdadd6ce2acb54c7.svg" width="332px" height="68px" + loading="lazy" />

So far so good. Now, to see why we did this, let's write out the @@ -2060,6 +2122,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/4debbed5922d2bd84fd322c616872d20.svg" width="400px" height="163px" + loading="lazy" />

So by using this seemingly silly trick, we can suddenly express part @@ -2075,6 +2138,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/483c89c8726f7fd0dca0b7de339b04bd.svg" width="479px" height="161px" + loading="lazy" />

So, with both of those changed from an order @@ -2097,6 +2161,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/dd8d8d98f66ce9f51b95cbf48225e97b.svg" width="481px" height="257px" + loading="lazy" />

And this is where we switch over from calculus to linear algebra, @@ -2108,6 +2173,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/773fdc86b686647c823b4f499aca3a35.svg" width="77px" height="17px" + loading="lazy" />

where the matrix M is an n+1 by @@ -2118,6 +2184,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/7a9120997e4a4855ecda435553a7bbdf.svg" width="340px" height="172px" + loading="lazy" />

That might look unwieldy, but it's really just a mostly-zeroes @@ -2146,6 +2213,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/d52f60b331c1b8d6733eb5217adfbc4d.svg" width="288px" height="109px" + loading="lazy" />

The steps taken here are:

    @@ -2221,6 +2289,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/eb4442acc5bc17f4649eb04b2953ed9b.svg" width="333px" height="44px" + loading="lazy" />

    which we can also write (observing that b in this formula is @@ -2233,6 +2302,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/2622790efa97f1915e7998787d8ce977.svg" width="343px" height="44px" + loading="lazy" />

    Or, in plain text: the derivative of an nth degree Bézier @@ -2259,6 +2329,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/e755c2adfec5d266c50e064407ca369b.svg" width="209px" height="36px" + loading="lazy" />

    Applying the @@ -2272,6 +2343,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/95a0cd4cc919a3fd5b192ffeb00c231e.svg" width="412px" height="28px" + loading="lazy" />

    Which is hard to work with, so let's expand that properly:

    Now, the trick is to turn this expression into something that has @@ -2292,6 +2365,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/6770214cceeb0e13e371bd908867751f.svg" width="545px" height="76px" + loading="lazy" />

    And that's the first part done: the two components inside the @@ -2302,6 +2376,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/fb823558e99662b24d46ae55ac93ce38.svg" width="533px" height="48px" + loading="lazy" />

    Now to apply this to our weighted Bézier curves. We'll write out @@ -2313,6 +2388,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/28991bba7c13698619f36b6261d91d68.svg" width="527px" height="112px" + loading="lazy" />

    If we expand this (with some color to show how terms line up), and @@ -2324,6 +2400,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/b7815b1502029ed9d805b6ba0801a53f.svg" width="300px" height="109px" + loading="lazy" />

    Two of these terms fall way: the first term falls away because @@ -2342,6 +2419,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/c7b13e6507450b3da7dc4ce3c10c370f.svg" width="295px" height="71px" + loading="lazy" />

    And that's just a summation of lower order curves:

    We can rewrite this as a normal summation, and we're done:

    @@ -2369,12 +2449,14 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg" width="352px" height="55px" + loading="lazy" />

    What are the differences? In terms of the actual Bézier curve, @@ -2390,6 +2472,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/03967e3ecdbff78684995ca9c22a6106.svg" width="523px" height="73px" + loading="lazy" />

    We can keep performing this trick for as long as we have more than @@ -2417,6 +2500,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/d236b7b2ad46c8ced1b43bb2a496379a.svg" width="151px" height="40px" + loading="lazy" />

    This gives us the directional vector we want. We can normalize it to @@ -2429,12 +2513,14 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/6101b2f8b69ebabba4a2c88456a32aa0.svg" width="268px" height="29px" + loading="lazy" />

    The tangent is very useful for moving along a line, but what if we @@ -2450,6 +2536,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/2dd2f89d1c762991a86526490a3deef6.svg" width="341px" height="57px" + loading="lazy" />

    @@ -2473,6 +2560,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/deec095950fcd1f9c980be76a7093fe6.svg" width="179px" height="37px" + loading="lazy" />

    Which is the "long" version of the following matrix @@ -2483,6 +2571,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/2a55cb2d23c25408aa10cfd8db13278b.svg" width="205px" height="40px" + loading="lazy" />

    And that's all we need to rotate any coordinate. Note that for @@ -2929,6 +3018,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/6db78123d4b676ffdf85d53670c77468.svg" width="189px" height="64px" + loading="lazy" />

    And then we turn this into our solution for t using @@ -2939,6 +3029,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/1c0367fad2a0d6946db1f55a8520793a.svg" width="139px" height="77px" + loading="lazy" />

    Done.

    @@ -2964,6 +3055,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/0ec5cc72a428d75defb480530b50d720.svg" width="433px" height="37px" + loading="lazy" />

    So, if we can rewrite the Bézier component function as a plain @@ -2985,6 +3077,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/e06ec558d99b53e559d24524f4201951.svg" width="553px" height="37px" + loading="lazy" />

    And then, using these v values, we can find out what our @@ -2995,6 +3088,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/ddc6f99a543afad25c55cf16b9deeed9.svg" width="317px" height="112px" + loading="lazy" />

    This gives us three coefficients {a, b, c} that are expressed in @@ -3007,6 +3101,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/d9e66caeb45b6643112ce3d971b17e5b.svg" width="308px" height="64px" + loading="lazy" />

    Easy-peasy. We can now almost trivially find the roots by plugging @@ -3041,6 +3136,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/997a8cc704c0ab0e364cb8b532df90b0.svg" width="264px" height="41px" + loading="lazy" />

    We can see that the easier formula only has two constants, rather @@ -3253,6 +3349,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/extremities/c621cc41f6f22ee1beedbcb510fa5b6b.svg" width="139px" height="43px" + loading="lazy" />

    (The Wikipedia article has a decent animation for this process, so I @@ -3428,6 +3525,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/aligning/d480a9aa41917e5230d432cdbd6899b1.svg" width="487px" height="40px" + loading="lazy" />

    Then translating it so that the first coordinate lies on (0,0), @@ -3439,6 +3537,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/aligning/50679d61424222d7b6b97eb3aa663582.svg" width="471px" height="40px" + loading="lazy" />

    If we then rotate the curve so that its end point lies on the @@ -3450,6 +3549,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/aligning/a9af1c06a00bb3c4af816a138fb0a66d.svg" width="463px" height="40px" + loading="lazy" />

    If we drop all the zero-terms, this gives us:

    We can see that our original curve definition has been simplified @@ -3580,6 +3681,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/bafdb6583323bda71d9a15c02d1fdec2.svg" width="59px" height="17px" + loading="lazy" />

    What we're saying here is that given the curvature function @@ -3595,6 +3697,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/2029bca9f4fa15739553636af99b70a8.svg" width="399px" height="19px" + loading="lazy" />

    The function C(t) is the cross product between the first @@ -3626,6 +3729,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/4d78ebcf8626f777725d67d3672fa480.svg" width="613px" height="71px" + loading="lazy" />

    And of course the same functions for y:

    Asking a computer to now compose the C(t) function for us @@ -3644,6 +3749,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/b2433959e1f451fa3bf238fc37e04527.svg" width="557px" height="96px" + loading="lazy" />

    That is... unwieldy. So, we note that there are a lot of terms @@ -3664,6 +3770,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/1679090a942a43d27f886f236fc8d62b.svg" width="533px" height="19px" + loading="lazy" />

    That's a lot easier to work with: we see a fair number of terms that @@ -3675,6 +3782,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/4b5c7d0bf0fcd769db007dd98d4a024d.svg" width="480px" height="73px" + loading="lazy" />

    This is a plain quadratic curve, and we know how to solve @@ -3685,6 +3793,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/7c9762c0e04693eb743905cdc0487f8b.svg" width="428px" height="53px" + loading="lazy" />

    We can easily compute this value if the discriminator isn't @@ -3748,86 +3857,102 @@ function getCubicRoots(pa, pb, pc, pd) { then tell us what kind of curve we're dealing with. Specifically, we see the following breakdown:

    - + width="400" + height="400" + src="./chapters/canonical/canonical.js" + > + + + Scripts are disabled. Showing fallback image. +

    - This is a fairly funky image, so let's see how it breaks down. We - see the three fixed points at (0,0), (0,1) and (1,1), and then the - fourth point is somewhere. Depending on where it is, our curve will - have certain features. Namely, if the fourth point is... + This is a fairly funky image, so let's see what the various parts of + it mean... +

    +

    + We see the three fixed points at (0,0), (0,1) and (1,1). The various + regions and boundaries indicate what property the original curve + will have, if the fourth point is in/on that region or boundary. + Specifically, if the fourth point is...

    1. - anywhere on and in the red zone, the curve will either be - self-intersecting (yielding a loop), or it will have a sharp - discontinuity (yielding a cusp). Anywhere inside the red zone, - this will be a loop. We won't know where that loop is - (in terms of t values), but we are guaranteed that - there is one. + ...anywhere inside the red zone, but not on its boundaries, the + curve will either be self-intersecting (yielding a loop). We + won't know where it self-intersects (in terms of + t values), but we are guaranteed that it does.

    2. - on the left (red) edge, the curve will have a cusp. We again - don't know where, just that it has one. This edge is - described by the function: + ...on the left (red) edge of the red zone, the curve will have a + cusp. We again don't know where, but we know there is + one. This edge is described by the function:

    3. - on the lower right (pink) edge, the curve will have a loop at - t=1, so we know the end coordinate of the curve also lies - on the curve. This edge is described by the function: + ...on the almost circular, lower right (pink) edge, the curve's + end point touches the curve, forming a loop. This edge is + described by the function:

    4. - on the top (blue) edge, the curve will have a loop at t=0, so we - know the start coordinate of the curve also lies on the - curve. This edge is described by the function: + ...on the top (blue) edge, the curve's start point touches the + curve, forming a loop. This edge is described by the function:

    5. - inside the green zone, the curve will have a single inflection, - switching concave/convex once. + ...inside the lower (green) zone, past y=1, the + curve will have a single inflection (switching concave/convex + once).

    6. - between the red and green zones, the curve has two inflections, - meaning its curvature switches between concave/convex form - twice. + ...between the left and lower boundaries (below the cusp line + but above the single-inflection line), the curve will have two + inflections (switching from concave to convex and then back + again, or from convex to concave and then back again).

    7. - anywhere on the right of the red zone, the curve will have no - inflections. It'll just be a well-behaved arch. + ...anywhere on the right of self-intersection zone, the curve + will have no inflections. It'll just be a simple arch.

    @@ -3902,8 +4027,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Sweet! z stays 1, so we can effectively ignore it entirely, @@ -3913,8 +4039,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Running all our coordinates through this transformation gives a new @@ -3931,8 +4058,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    So we want some shearing value that, when multiplied by y, @@ -3942,8 +4070,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Now, running this on all our points generates a new set of @@ -3962,8 +4091,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Then, finally, this generates a new set of coordinates, let's call @@ -3982,7 +4112,8 @@ function getCubicRoots(pa, pb, pc, pd) { class="LaTeX SVG" src="./images/chapters/canonical/0430e8c7f7d4ec80e6527f96f3d56e5c.svg" width="140px" - height="65px" + height="63px" + loading="lazy" />

    And this generates our final set of four coordinates. Of these, we @@ -3994,8 +4125,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    That looks very complex, but notice that every coordinate value is @@ -4011,8 +4143,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Suddenly things look a lot simpler: the mapped x is fairly straight @@ -4024,8 +4157,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    That's kind of super-simple to write out in code, I think you'll @@ -4073,11 +4207,22 @@ function getCubicRoots(pa, pb, pc, pd) { can immediately tell which features our curve must have, based on where the fourth coordinate is located on the map:

    - + width="800" + height="400" + src="./chapters/canonical/interactive.js" + > + + + Scripts are disabled. Showing fallback image. +

    Finding Y, given X

    @@ -4129,6 +4274,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/yforx/9df91c28af38c1ba2e2d38d2714c9446.svg" width="335px" height="19px" + loading="lazy" />

    We can rewrite this to a plain polynomial form, by just fully @@ -4140,6 +4286,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/yforx/9ab2b830fe7fb73350c19bde04e9441b.svg" width="445px" height="19px" + loading="lazy" />

    Nothing special here: that's a standard cubic polynomial in "power" @@ -4154,6 +4301,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/yforx/de3bd3e271d72194c730d0ae44f031a8.svg" width="465px" height="19px" + loading="lazy" />

    You might be wondering "where did all the other 'minus x' for all @@ -4218,6 +4366,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/cb24cda7f7f4bbf3be7104c460e0ec9f.svg" width="140px" height="33px" + loading="lazy" />

    or, more commonly written using Leibnitz notation as:

    This formula says that the length of a parametric curve is in fact @@ -4275,6 +4425,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/e168758d35b8f6781617eda5a32b20bf.svg" width="636px" height="71px" + loading="lazy" />

    In plain text: an integral function can always be treated as the sum @@ -4350,6 +4501,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/5509919419288129322cfbd4c60d0a4f.svg" width="341px" height="72px" + loading="lazy" />

    That may look a bit more complicated, but the fraction involving @@ -4374,6 +4526,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/d0d93f1cc26b560309dade1f1aa012f2.svg" width="63px" height="93px" + loading="lazy" />

    Which means that in order for us to approximate the integral, we @@ -4385,6 +4538,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/e96dd431f6ef9433ccf25909dddd5bca.svg" width="476px" height="44px" + loading="lazy" />

    We can program that pretty easily, provided we have that @@ -4558,6 +4712,7 @@ if (roots.length > 0) { src="./images/chapters/curvature/d9c893051586eb8d9de51c0ae1ef8fae.svg" width="113px" height="47px" + loading="lazy" />

    Which is really just a "short form" that glosses over the fact that @@ -4568,6 +4723,7 @@ if (roots.length > 0) { src="./images/chapters/curvature/828333034b4fed8e248683760d6bc6f4.svg" width="239px" height="55px" + loading="lazy" />

    And while that's a litte more verbose, it's still just as simple to @@ -4608,6 +4764,7 @@ if (roots.length > 0) { src="./images/chapters/curvature/6ed4fd2ead35c57984caddf9fe375a5f.svg" width="81px" height="37px" + loading="lazy" />

    So that's a rather convenient fact to know, too.

    @@ -5008,6 +5165,7 @@ lli = function(line1, line2): src="./images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg" width="197px" height="16px" + loading="lazy" />

    So that just leaves finding A.

    @@ -5020,6 +5178,7 @@ lli = function(line1, line2): src="./images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg" width="197px" height="87px" + loading="lazy" />

    So, if we know the start and end coordinates, and we know the @@ -5058,6 +5217,7 @@ lli = function(line1, line2): src="./images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg" width="251px" height="39px" + loading="lazy" />

    This leads to a pretty powerful bit of knowledge: merely by knowing @@ -5070,6 +5230,7 @@ lli = function(line1, line2): src="./images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg" width="217px" height="37px" + loading="lazy" />

    And that's it, all values found.

    @@ -5092,12 +5253,14 @@ lli = function(line1, line2): src="./images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg" width="245px" height="41px" + loading="lazy" />

    Unfortunately, this trick only works for quadratic and cubic @@ -5159,6 +5322,7 @@ lli = function(line1, line2): src="./images/chapters/moulding/7bba0a4fd605e023cda922de125b3e32.svg" width="221px" height="36px" + loading="lazy" />

    For quadratic curves, this means we're done, since the new point A' @@ -5194,6 +5358,7 @@ lli = function(line1, line2): src="./images/chapters/moulding/524206c49f317d27d8e07a310b24a7a3.svg" width="132px" height="75px" + loading="lazy" />

    And then we can compute the new control points:

    And that's cubic curve manipulation.

    @@ -5349,6 +5515,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/bd8e8e294eec10d2bf6ef857c7c0c2c2.svg" width="295px" height="43px" + loading="lazy" />

    And then we (trivially) rearrange the terms across multiple lines: @@ -5358,6 +5525,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/2b8334727d3b004c6e87263fec6b32b7.svg" width="216px" height="64px" + loading="lazy" />

    This rearrangement has "factors of t" at each row (the first row @@ -5375,6 +5543,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/94acb5850778dcb16c2ba3cfa676f537.svg" width="572px" height="53px" + loading="lazy" />

    We can do the same for the cubic curve, of course. We know the @@ -5385,6 +5554,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/7eada6f12045423de24d9a2ab8e293b1.svg" width="355px" height="19px" + loading="lazy" />

    So we write out the expansion and rearrange:

    Which we can then decompose:

    And, of course, we can do this for quartic curves too (skipping @@ -5409,6 +5581,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/9151c0fdf9689ee598a2d029ab2ffe34.svg" width="491px" height="92px" + loading="lazy" />

    And so and on so on. Now, let's see how to use these @@ -5429,6 +5602,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/2bef3da3828d63d690460ce9947dbde2.svg" width="63px" height="73px" + loading="lazy" />

    Next, we need to figure out appropriate t values for @@ -5473,6 +5647,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/78b8ba1aba2e4c9ad3f7890299c90152.svg" width="395px" height="40px" + loading="lazy" />

    Where length() is literally just that: the length of @@ -5487,6 +5662,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/08f4beaebf83dca594ad125bdca7e436.svg" width="272px" height="55px" + loading="lazy" />

    And now we can move on to the actual "curve fitting" part: what we @@ -5507,6 +5683,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/7e5d59272621baf942bc722208ce70c2.svg" width="177px" height="23px" + loading="lazy" />

    Since this function only deals with individual coordinates, we'll @@ -5519,6 +5696,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/ab334858d3fa309cc1a5ba535a2ca168.svg" width="195px" height="41px" + loading="lazy" />

    And here's the trick that justifies using matrices: while we can @@ -5534,6 +5712,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/2d42758fba3370f52191306752c2705c.svg" width="141px" height="21px" + loading="lazy" />

    In which we can replace the rather cumbersome "squaring" operation @@ -5544,6 +5723,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/5f7fcb86ae1c19612b9fe02e23229e31.svg" width="225px" height="21px" + loading="lazy" />

    Here, the letter T is used instead of the number 2, to @@ -5567,6 +5747,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/6202d7bd150c852b432d807c40fb1647.svg" width="201px" height="96px" + loading="lazy" />

    Which, because of the first and last values in S, @@ -5577,6 +5758,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/4ffad56e281ee79d0688e93033429f0a.svg" width="212px" height="92px" + loading="lazy" />

    Now we can properly write out the error function as matrix @@ -5587,6 +5769,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/8d09f2be2c6db79ee966f170ffc25815.svg" width="231px" height="21px" + loading="lazy" />

    So, we have our error function: we now need to figure out the @@ -5601,6 +5784,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/283bc9e8fe59a78d3c74860f62a66ecb.svg" width="197px" height="36px" + loading="lazy" />

    Where did this derivative come from?

    @@ -5642,6 +5826,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/d84d1c71a3ce1918f53eaf8f9fe98ac4.svg" width="168px" height="27px" + loading="lazy" />

    Here, the "to the power negative one" is the notation for the @@ -5752,6 +5937,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/9215d05705c8e8a7ebd718ae6f690371.svg" width="409px" height="75px" + loading="lazy" />

    However, there's something funny going on here: the coordinate @@ -5791,6 +5977,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/1811b59c5ab9233f08590396e5d03303.svg" width="187px" height="83px" + loading="lazy" />

    This mapping says that in order to map a Catmull-Rom "point + @@ -5806,6 +5993,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/4d524810417b4caffedd13af23135f5b.svg" width="591px" height="83px" + loading="lazy" />

    Thus:

    However, we're not quite done, because Catmull-Rom curves @@ -5829,6 +6018,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/06ae1e3fdc660e59d618e0760e8e9ab5.svg" width="285px" height="84px" + loading="lazy" />

    With the mapping matrix properly done, let's rewrite the "point + @@ -5840,6 +6030,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/cc1e2ff43350c32f0ae9ba9a7652b8fb.svg" width="409px" height="75px" + loading="lazy" />

    Replace point/tangent vector with the expression for @@ -5850,6 +6041,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/f08e34395ce2812276fd70548f805041.svg" width="549px" height="81px" + loading="lazy" />

    and merge the matrices:

    This looks a lot like the Bézier matrix form, which as we saw in @@ -5867,6 +6060,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/8f56909fcb62b8eef18b9b9559575c13.svg" width="353px" height="73px" + loading="lazy" />

    So, if we want to express a Catmull-Rom curve using a Bézier @@ -5877,6 +6071,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/b21386f86bef8894f108c5441dad10de.svg" width="227px" height="84px" + loading="lazy" />

    Into something that looks like this:

    And the way we do that is with a fairly straight forward bit of @@ -5894,6 +6090,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/a47b072a325812ac4f0ff52c22792588.svg" width="440px" height="84px" + loading="lazy" />

    Then we remove the coordinate vector from both sides without @@ -5904,6 +6101,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/841fb6a2a035c9bcf5a2d46f2a67709b.svg" width="353px" height="84px" + loading="lazy" />

    Then we can "get rid of" the Bézier matrix on the right by @@ -5914,6 +6112,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/cbdd46d5e2e1a6202ef46fb03711ebe4.svg" width="657px" height="88px" + loading="lazy" />

    A matrix times its inverse is the matrix equivalent of 1, and @@ -5925,6 +6124,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/3ea54fe939d076f8db605c5b480e7db0.svg" width="369px" height="88px" + loading="lazy" />

    And now we're basically done. We just multiply those two @@ -5935,6 +6135,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/169fd85a95e4d16fe289a75583017a11.svg" width="161px" height="77px" + loading="lazy" />

    We now have the final piece of our function puzzle. Let's run @@ -5948,6 +6149,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/cc1e2ff43350c32f0ae9ba9a7652b8fb.svg" width="409px" height="75px" + loading="lazy" />

    1. rewrite to pure coordinate form:
    2. @@ -5957,6 +6159,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/f814bb8d627f9c8f33b347c1cf13d4c7.svg" width="324px" height="84px" + loading="lazy" />
      1. rewrite for "normal" coordinate vector:
      2. @@ -5966,6 +6169,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/5f2750de827497375d9a915f96686885.svg" width="441px" height="81px" + loading="lazy" />
        1. merge the inner matrices:
        2. @@ -5975,6 +6179,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/79e333cd0c569657eea033b04fb5e61b.svg" width="348px" height="84px" + loading="lazy" />
          1. rewrite for Bézier matrix form:
          2. @@ -5984,6 +6189,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/1b8a782f7540503d38067317e4cd00b0.svg" width="431px" height="77px" + loading="lazy" />
            1. @@ -5996,6 +6202,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/e3d30ab368dcead1411532ce3814d3f3.svg" width="348px" height="81px" + loading="lazy" />

              And we're done: we finally know how to convert these two curves! @@ -6014,6 +6221,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/ba31c32eba62f1e3b15066cd5ddda597.svg" width="249px" height="85px" + loading="lazy" />

              Similarly, if we have a Bézier curve defined by four coordinates @@ -6026,6 +6234,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/eae7f01976e511ee38b08b6edc8765d2.svg" width="284px" height="77px" + loading="lazy" />

              or, if your API requires specifying Catmull-Rom curves using "point @@ -6036,6 +6245,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/26363fc09f8cf2d41ea5b4256656bb6d.svg" width="284px" height="77px" + loading="lazy" />

              @@ -6146,6 +6356,7 @@ lli = function(line1, line2): src="./images/chapters/polybezier/408dd95905a5f001179c4da6051e49c5.svg" width="124px" height="17px" + loading="lazy" />

              We can effect this quite easily, because we know that the vector @@ -6160,6 +6371,7 @@ lli = function(line1, line2): src="./images/chapters/polybezier/8c1b570b3efdfbbc39ddedb4adcaaff6.svg" width="304px" height="40px" + loading="lazy" />

              So let's implement that and see what it gets us. The following two @@ -6518,6 +6730,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/1d4be24e5896dce3c16c8e71f9cc8881.svg" width="108px" height="16px" + loading="lazy" />

              However, we're working in 2D, and d is a single @@ -6534,6 +6747,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/5bfee4f2ae27304475673d0596e42f9a.svg" width="151px" height="16px" + loading="lazy" />

              Now this still isn't very useful unless we know what the formula @@ -6553,6 +6767,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/fa6c243de2aa78b7451e0086848dfdfc.svg" width="120px" height="40px" + loading="lazy" />

              Determining the length requires computing an arc length, and this @@ -6566,6 +6781,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/b262e50c085815421d94e120fc17f1c8.svg" width="169px" height="36px" + loading="lazy" />

              So if we want the length of the tangent, we plug in @@ -6577,6 +6793,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/1d586b939b44ff9bdb42562a12ac2779.svg" width="209px" height="36px" + loading="lazy" />

              And that's where things go wrong. It doesn't even really matter @@ -6806,6 +7023,7 @@ lli = function(line1, line2): src="./images/chapters/circles/8374c4190d6213b0ac0621481afaa754.svg" width="175px" height="40px" + loading="lazy" />

              What we want to find is the intersection of the tangents, so we want @@ -6816,6 +7034,7 @@ lli = function(line1, line2): src="./images/chapters/circles/a127f926eced2751a09c54bf7c361b4a.svg" width="284px" height="40px" + loading="lazy" />

              i.e. we want a point that lies on the vertical line through S (at @@ -6828,6 +7047,7 @@ lli = function(line1, line2): src="./images/chapters/circles/b5d864e9ed0c44c56d454fbaa4218d5e.svg" width="219px" height="40px" + loading="lazy" />

              First we solve for b:

              which yields:

              which we can then substitute in the expression for a:

              A quick check shows that plugging these values for a and @@ -6865,6 +7088,7 @@ lli = function(line1, line2): src="./images/chapters/circles/fe32474b4616ee9478e1308308f1b6bf.svg" width="188px" height="32px" + loading="lazy" />

              We compute T, observing that if t=0.5, the polynomial @@ -6875,6 +7099,7 @@ lli = function(line1, line2): src="./images/chapters/circles/e1059e611aa1e51db41f9ce0b4ebb95a.svg" width="252px" height="35px" + loading="lazy" />

              Which, worked out for the x and y components, gives:

              And the distance between these two is the standard Euclidean @@ -6892,6 +7118,7 @@ lli = function(line1, line2): src="./images/chapters/circles/adbd056f4b8fcd05b1d4f2fce27d7657.svg" width="399px" height="153px" + loading="lazy" />

              So, what does this distance function look like when we plot it for a @@ -6947,6 +7174,7 @@ lli = function(line1, line2): src="./images/chapters/circles/df87674db0f31fc3944aaeb6b890e196.svg" width="247px" height="53px" + loading="lazy" />

              And frankly, things are starting to look a bit ridiculous at this @@ -7090,6 +7318,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/a4f0dafbfe80c88723c3cc22277a9682.svg" width="175px" height="40px" + loading="lazy" />

              But we now need to find two control points, rather than one. If we @@ -7103,6 +7332,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/dfb83eec053c30e0a41b0a52aba24cd4.svg" width="113px" height="40px" + loading="lazy" />

              where "a" is some scaling factor, and:

              where "b" is also some scaling factor.

              @@ -7167,6 +7398,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/3189cac1ddac07c1487e1e51740ecc88.svg" width="397px" height="40px" + loading="lazy" />

              So that just leaves us to find the distance from t=0.5 to @@ -7181,6 +7413,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/fe32474b4616ee9478e1308308f1b6bf.svg" width="188px" height="32px" + loading="lazy" />

              And the distance from the origin to the line start/end is another @@ -7194,6 +7427,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/0364731626a530c8a9b30f424ada53c5.svg" width="261px" height="67px" + loading="lazy" />

              With the coordinate C, and knowledge of coordinate B, we can @@ -7205,12 +7439,14 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/ee08d86b7497c7ab042ee899bf15d453.svg" width="397px" height="48px" + loading="lazy" />

              Which means we can now determine the distance {start,guessed}, @@ -7222,6 +7458,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/178a838274748439778e2a29f5a27d0b.svg" width="252px" height="56px" + loading="lazy" />

              And after this tedious detour to find the coordinate for @@ -7233,6 +7470,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/49dbf244d50c787a4ab18694488d9b69.svg" width="524px" height="79px" + loading="lazy" />

              And that's it, we have all four points now for an approximation of @@ -7248,6 +7486,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/e2258660a796dcd6189a6f5e14326dad.svg" width="205px" height="40px" + loading="lazy" />

              and

              And, because the "quarter curve" special case comes up so incredibly @@ -7266,6 +7506,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/877f9c217c51c0087be751a7580ed459.svg" width="412px" height="33px" + loading="lazy" />

              Which, in decimal values, rounded to six significant digits, is: @@ -7275,6 +7516,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/05d36e051a38905dcb81e65db8261f24.svg" width="412px" height="16px" + loading="lazy" />

              Of course, this is for a circle with radius 1, so if you have a @@ -7561,6 +7803,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/0f3451c711c0fe5d0b018aa4aa77d855.svg" width="169px" height="41px" + loading="lazy" />

              Which, honestly, doesn't tell us all that much. All we can see is @@ -7590,6 +7833,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/cf45d1ea00d4866abc8a058b130299b4.svg" width="559px" height="43px" + loading="lazy" />

              So this is where we see the interpolation: N(t) for an (i,k) pair @@ -7606,6 +7850,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/adac18ea69cc58e01c8d5e15498e4aa6.svg" width="240px" height="40px" + loading="lazy" />

              And this function finally has a straight up evaluation: if a @@ -7640,6 +7885,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/763838ea6f9e6c6aa63ea5f9c6d9542f.svg" width="281px" height="21px" + loading="lazy" />

              This is another recursive function, with k values @@ -7651,6 +7897,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/892209dad8fd1f839470dd061e870913.svg" width="255px" height="39px" + loading="lazy" />

              That looks complicated, but it's not. Computing alpha is just a @@ -7667,6 +7914,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/4c8f9814c50c708757eeb5a68afabb7f.svg" width="368px" height="40px" + loading="lazy" />

              So, we see two stopping conditions: either i becomes 0, @@ -7685,6 +7933,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/7962d6fea86da6f53a7269fba30f0138.svg" width="417px" height="231px" + loading="lazy" />

              That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2): d(3,3) diff --git a/docs/ja-JP/index.html b/docs/ja-JP/index.html index 44897297..39320446 100644 --- a/docs/ja-JP/index.html +++ b/docs/ja-JP/index.html @@ -66,6 +66,14 @@ + + + @@ -548,6 +556,7 @@ src="./images/chapters/whatis/4df088f01d0fd4de84a50bbc2e25f8a7.svg" width="433px" height="108px" + loading="lazy" />

              では、実際に見てみましょう。下の図はインタラクティブになっています。上下キーで補間の比率が増減しますので、どうなるか確かめてみましょう。最初に3点があり、それを結んで2本の直線が引かれています。この直線の上でそれぞれ線形補間を行うと、2つの点が得られます。この2点の間でさらに線形補間を行うと、1つの点を得ることができます。そして、あらゆる比率に対して同様に点を求め、それをすべて集めると、このようにベジエ曲線ができるのです。 @@ -587,6 +596,7 @@ src="./images/chapters/explanation/1caef9931f954e32eae5067b732c1018.svg" width="93px" height="17px" + loading="lazy" />

              f(x)

              注目すべき箇所は特に何もありません。ただの正弦関数と余弦関数です。ただし、入力が別々の名前になっていることに気づくでしょう。仮にaの値を変えたとしても、f(b)の出力の値は変わらないはずです。なぜなら、こちらの関数にはaは使われていないからです。パラメトリック関数は、これを変えてしまうのでインチキなのです。パラメトリック関数においては、どの関数も変数を共有しています。例えば、 @@ -609,6 +620,7 @@ src="./images/chapters/explanation/066a910ae6aba69c40a338320759cdd1.svg" width="104px" height="40px" + loading="lazy" />

              複数の関数がありますが、変数は1つだけです。tの値を変えた場合、fa(t)fb(t)の両方の出力が変わります。これがどのように役に立つのか、疑問に思うかもしれません。しかし、実際には答えは至ってシンプルです。fa(t)fb(t)のラベルを、パラメトリック曲線の表示によく使われているもので置き換えてやれば、ぐっとはっきりするかと思います。 @@ -618,6 +630,7 @@ src="./images/chapters/explanation/4cf6fb369841e2c5d36e5567a8db4306.svg" width="79px" height="40px" + loading="lazy" />

              きました。x/y座標です。謎の値tを通して繫がっています。 @@ -652,6 +665,7 @@ src="./images/chapters/explanation/bb06cb82d372f822a7b35e661502bd72.svg" width="219px" height="19px" + loading="lazy" />

              最高次の項がであれば3次多項式、であれば2次多項式と呼び、xだけの場合は1次多項式――ただの直線です。(そしてxの入った項が何もなければ、多項式ではありません!) @@ -664,6 +678,7 @@ src="./images/chapters/explanation/6e15c433dc2340271e007742009e3532.svg" width="347px" height="64px" + loading="lazy" />

              「そこまでシンプルには見えないよ」と思っていることでしょう。しかし仮に、tを取り去って係数に1を掛けることにしてしまえば、急激に簡単になります。これが二項係数部分の項です。 @@ -673,6 +688,7 @@ src="./images/chapters/explanation/c605597fb629b964921c6a4bca7fa4c9.svg" width="163px" height="85px" + loading="lazy" />

              2は1+1に等しく、3は2+1や1+2に等しく、6は3+3に等しく、……ということに注目してください。見てわかるように、先頭と末尾は単に1になっていますが、中間はどれも次数が増えるたびに「上の2つの数を足し合わせた」ものになっています。これなら覚えやいですね。 @@ -685,6 +701,7 @@ src="./images/chapters/explanation/741097d69c182e8742695af23980bd8f.svg" width="288px" height="61px" + loading="lazy" />

              これは要するに、「abのすべての組み合わせ」の単なる和です。プラスが出てくるたびに、abへと1つずつ置き換えていけばよいのです。こちらも本当に単純です。さて、これで「二項係数多項式」がわかりました。完璧を期するため、この関数の一般の形を示しておきます。 @@ -694,6 +711,7 @@ src="./images/chapters/explanation/f24fd5e27968d96957ba706b16d8e90b.svg" width="321px" height="59px" + loading="lazy" />

              そして、これがベジエ曲線の完全な表現です。この関数中のΣは、加算の繰り返し(Σの下にある変数を使って、...=<値>から始めてΣの下にある値まで)を表します。 @@ -802,6 +820,7 @@ function Bezier(3,t): src="./images/chapters/control/2af72ea0c3517bc05f36a08cbbed6002.svg" width="360px" height="59px" + loading="lazy" />

              複雑そうに見えますが、運がいいことに「重み」というのは実はただの座標値です。というのはn次の曲線の場合、w0が始点の座標、wnが終点の座標となり、その間はどれも制御点の座標になります。例えば、始点が(120,160)、制御点が(35,200)と(220,260)、終点が(220,40)となる3次ベジエ曲線は、次のようになります。 @@ -811,6 +830,7 @@ function Bezier(3,t): src="./images/chapters/control/c0d4dbc07b8ec7c0a18ea43c8a386935.svg" width="476px" height="40px" + loading="lazy" />

              この式からは、記事の冒頭に出てきた曲線が得られます。

              The function for rational Bézier curves has two more terms:

              In this, the first new term represents an additional weight for each @@ -1005,6 +1027,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/extended/a75e84f0e7f92c2f3e8ef10b49744ba5.svg" width="252px" height="20px" + loading="lazy" />

              明らかに、始点ではa=1, b=0

              こうすれば、和が100%を超えることはないと保証できます。aの値を区間[0,1]に制限してしまえば、混ぜ合わさった値は常に2つの値の間のどこか(両端を含む)になり、また和は常に100%になります。 @@ -1091,6 +1115,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/5aea6d4d5855135051715fb1cc0ec531.svg" width="468px" height="20px" + loading="lazy" />

              実際の座標を一旦無視すると、次のようになります。

              これは、4つの項の和になっています。

              それぞれの項を展開します。

              その上で、係数の0や1もすべて明示的に書けば、このようになります。 @@ -1121,6 +1149,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/e0d89b48cd11a726c00a2f689d48d57c.svg" width="217px" height="75px" + loading="lazy" />

              さらに、これは4つの行列演算の和として見ることができます。 @@ -1130,6 +1159,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/6da69918482a0b6b84d90a72dbeae9dd.svg" width="607px" height="72px" + loading="lazy" />

              これを1つの行列演算にまとめると、以下のようになります。

              多項式基底をこのような形で表現する場合、通常はその基底を昇冪の順に並べます。したがって、tの行列を左右反転させ、大きな「混合」行列は上下に反転させる必要があります。 @@ -1146,6 +1177,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/24bdad213879407a35b23c18394293aa.svg" width="227px" height="72px" + loading="lazy" />

              そして最後に、もともとあった座標を3番目の行列として付け加えます。 @@ -1155,6 +1187,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/009c671bc526b5d75c30411c3c3a7e91.svg" width="323px" height="73px" + loading="lazy" />

              2次ベジエ曲線の場合も同様に変形することができ、最終的には以下のようになります。 @@ -1164,6 +1197,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/77a11d65d7cffc4b84a85c4bec837792.svg" width="263px" height="55px" + loading="lazy" />

              t

              ならびに

              曲線をある点t = z

              ならびに

              これらの行列をまとめて、仮に**[tの値たち] · [ベジエ行列] · @@ -1445,24 +1483,28 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/c79b607a92c42789fde57c6a8c4259fd.svg" width="348px" height="55px" + loading="lazy" />

              [M · M-1

              いいですね!これで、新しい2次ベジエ曲線が得られます。

              すばらしい

              区間[z,1]を求めたい場合は、かわりに次のような計算になります。 @@ -1534,12 +1582,14 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/0f84dbf6e3ea7db732ceb9d71caf9b22.svg" width="461px" height="55px" + loading="lazy" />

              先ほどと同じ手法を使い、[なにか · M]を[M · なにか

              よって、後半部分の曲線は結局のところ以下のようになります。

              おみごと

              および

              3次の曲線についても同様です。ただし、実際の導出はあなたにとっておきますので(自力で書き下してみてください)、新しい座標の組の結果を示すだけにします。 @@ -1602,6 +1658,7 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/5e3fae45d325d0f0681731fb606b6fbc.svg" width="841px" height="75px" + loading="lazy" />

              および

              さて、これらの行列を見るに、後半部分の曲線の行列は本当に計算する必要があったのでしょうか?いえ、ありませんでした。片方の行列が得られれば、実はもう一方の行列も暗に得られたことになります。まず、行列*

              However, this rule also has as direct consequence that you @@ -1686,6 +1745,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/1244a85c1f9044b6f77cb709c682159c.svg" width="429px" height="41px" + loading="lazy" />

              Then, we apply one of those silly (actually, super useful) calculus @@ -1699,6 +1759,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/b2fda1dcce5bb13317aa42ebf5e7ea6c.svg" width="384px" height="17px" + loading="lazy" />

              So, with that seemingly trivial observation, we rewrite that Bézier @@ -1710,6 +1771,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/41e184228d85023abdadd6ce2acb54c7.svg" width="332px" height="68px" + loading="lazy" />

              So far so good. Now, to see why we did this, let's write out the @@ -1722,6 +1784,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/4debbed5922d2bd84fd322c616872d20.svg" width="400px" height="163px" + loading="lazy" />

              So by using this seemingly silly trick, we can suddenly express part @@ -1737,6 +1800,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/483c89c8726f7fd0dca0b7de339b04bd.svg" width="479px" height="161px" + loading="lazy" />

              So, with both of those changed from an order @@ -1759,6 +1823,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/dd8d8d98f66ce9f51b95cbf48225e97b.svg" width="481px" height="257px" + loading="lazy" />

              And this is where we switch over from calculus to linear algebra, @@ -1770,6 +1835,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/773fdc86b686647c823b4f499aca3a35.svg" width="77px" height="17px" + loading="lazy" />

              where the matrix M is an n+1 by @@ -1780,6 +1846,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/7a9120997e4a4855ecda435553a7bbdf.svg" width="340px" height="172px" + loading="lazy" />

              That might look unwieldy, but it's really just a mostly-zeroes @@ -1808,6 +1875,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/d52f60b331c1b8d6733eb5217adfbc4d.svg" width="288px" height="109px" + loading="lazy" />

              The steps taken here are:

                @@ -1883,6 +1951,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/eb4442acc5bc17f4649eb04b2953ed9b.svg" width="333px" height="44px" + loading="lazy" />

                which we can also write (observing that b in this formula is @@ -1895,6 +1964,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/2622790efa97f1915e7998787d8ce977.svg" width="343px" height="44px" + loading="lazy" />

                Or, in plain text: the derivative of an nth degree Bézier @@ -1921,6 +1991,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/e755c2adfec5d266c50e064407ca369b.svg" width="209px" height="36px" + loading="lazy" />

                Applying the @@ -1934,6 +2005,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/95a0cd4cc919a3fd5b192ffeb00c231e.svg" width="412px" height="28px" + loading="lazy" />

                Which is hard to work with, so let's expand that properly:

                Now, the trick is to turn this expression into something that has @@ -1954,6 +2027,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/6770214cceeb0e13e371bd908867751f.svg" width="545px" height="76px" + loading="lazy" />

                And that's the first part done: the two components inside the @@ -1964,6 +2038,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/fb823558e99662b24d46ae55ac93ce38.svg" width="533px" height="48px" + loading="lazy" />

                Now to apply this to our weighted Bézier curves. We'll write out @@ -1975,6 +2050,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/28991bba7c13698619f36b6261d91d68.svg" width="527px" height="112px" + loading="lazy" />

                If we expand this (with some color to show how terms line up), and @@ -1986,6 +2062,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/b7815b1502029ed9d805b6ba0801a53f.svg" width="300px" height="109px" + loading="lazy" />

                Two of these terms fall way: the first term falls away because @@ -2004,6 +2081,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/c7b13e6507450b3da7dc4ce3c10c370f.svg" width="295px" height="71px" + loading="lazy" />

                And that's just a summation of lower order curves:

                We can rewrite this as a normal summation, and we're done:

    @@ -2031,12 +2111,14 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg" width="352px" height="55px" + loading="lazy" />

    What are the differences? In terms of the actual Bézier curve, @@ -2052,6 +2134,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/03967e3ecdbff78684995ca9c22a6106.svg" width="523px" height="73px" + loading="lazy" />

    We can keep performing this trick for as long as we have more than @@ -2081,6 +2164,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/d236b7b2ad46c8ced1b43bb2a496379a.svg" width="151px" height="40px" + loading="lazy" />

    This gives us the directional vector we want. We can normalize it to @@ -2093,12 +2177,14 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/6101b2f8b69ebabba4a2c88456a32aa0.svg" width="268px" height="29px" + loading="lazy" />

    The tangent is very useful for moving along a line, but what if we @@ -2114,6 +2200,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/2dd2f89d1c762991a86526490a3deef6.svg" width="341px" height="57px" + loading="lazy" />

    @@ -2137,6 +2224,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/deec095950fcd1f9c980be76a7093fe6.svg" width="179px" height="37px" + loading="lazy" />

    Which is the "long" version of the following matrix @@ -2147,6 +2235,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/2a55cb2d23c25408aa10cfd8db13278b.svg" width="205px" height="40px" + loading="lazy" />

    And that's all we need to rotate any coordinate. Note that for @@ -2601,6 +2690,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/6db78123d4b676ffdf85d53670c77468.svg" width="189px" height="64px" + loading="lazy" />

    And then we turn this into our solution for t using @@ -2611,6 +2701,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/1c0367fad2a0d6946db1f55a8520793a.svg" width="139px" height="77px" + loading="lazy" />

    Done.

    @@ -2636,6 +2727,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/0ec5cc72a428d75defb480530b50d720.svg" width="433px" height="37px" + loading="lazy" />

    So, if we can rewrite the Bézier component function as a plain @@ -2657,6 +2749,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/e06ec558d99b53e559d24524f4201951.svg" width="553px" height="37px" + loading="lazy" />

    And then, using these v values, we can find out what our @@ -2667,6 +2760,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/ddc6f99a543afad25c55cf16b9deeed9.svg" width="317px" height="112px" + loading="lazy" />

    This gives us three coefficients {a, b, c} that are expressed in @@ -2679,6 +2773,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/d9e66caeb45b6643112ce3d971b17e5b.svg" width="308px" height="64px" + loading="lazy" />

    Easy-peasy. We can now almost trivially find the roots by plugging @@ -2713,6 +2808,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/997a8cc704c0ab0e364cb8b532df90b0.svg" width="264px" height="41px" + loading="lazy" />

    We can see that the easier formula only has two constants, rather @@ -2925,6 +3021,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/extremities/c621cc41f6f22ee1beedbcb510fa5b6b.svg" width="139px" height="43px" + loading="lazy" />

    (The Wikipedia article has a decent animation for this process, so I @@ -3100,6 +3197,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/aligning/d480a9aa41917e5230d432cdbd6899b1.svg" width="487px" height="40px" + loading="lazy" />

    Then translating it so that the first coordinate lies on (0,0), @@ -3111,6 +3209,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/aligning/50679d61424222d7b6b97eb3aa663582.svg" width="471px" height="40px" + loading="lazy" />

    If we then rotate the curve so that its end point lies on the @@ -3122,6 +3221,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/aligning/a9af1c06a00bb3c4af816a138fb0a66d.svg" width="463px" height="40px" + loading="lazy" />

    If we drop all the zero-terms, this gives us:

    We can see that our original curve definition has been simplified @@ -3254,6 +3355,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/bafdb6583323bda71d9a15c02d1fdec2.svg" width="59px" height="17px" + loading="lazy" />

    What we're saying here is that given the curvature function @@ -3269,6 +3371,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/2029bca9f4fa15739553636af99b70a8.svg" width="399px" height="19px" + loading="lazy" />

    The function C(t) is the cross product between the first @@ -3300,6 +3403,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/4d78ebcf8626f777725d67d3672fa480.svg" width="613px" height="71px" + loading="lazy" />

    And of course the same functions for y:

    Asking a computer to now compose the C(t) function for us @@ -3318,6 +3423,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/b2433959e1f451fa3bf238fc37e04527.svg" width="557px" height="96px" + loading="lazy" />

    That is... unwieldy. So, we note that there are a lot of terms @@ -3338,6 +3444,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/1679090a942a43d27f886f236fc8d62b.svg" width="533px" height="19px" + loading="lazy" />

    That's a lot easier to work with: we see a fair number of terms that @@ -3349,6 +3456,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/4b5c7d0bf0fcd769db007dd98d4a024d.svg" width="480px" height="73px" + loading="lazy" />

    This is a plain quadratic curve, and we know how to solve @@ -3359,6 +3467,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/7c9762c0e04693eb743905cdc0487f8b.svg" width="428px" height="53px" + loading="lazy" />

    We can easily compute this value if the discriminator isn't @@ -3426,86 +3535,102 @@ function getCubicRoots(pa, pb, pc, pd) { then tell us what kind of curve we're dealing with. Specifically, we see the following breakdown:

    - + width="400" + height="400" + src="./chapters/canonical/canonical.js" + > + + + Scripts are disabled. Showing fallback image. +

    - This is a fairly funky image, so let's see how it breaks down. We - see the three fixed points at (0,0), (0,1) and (1,1), and then the - fourth point is somewhere. Depending on where it is, our curve will - have certain features. Namely, if the fourth point is... + This is a fairly funky image, so let's see what the various parts of + it mean... +

    +

    + We see the three fixed points at (0,0), (0,1) and (1,1). The various + regions and boundaries indicate what property the original curve + will have, if the fourth point is in/on that region or boundary. + Specifically, if the fourth point is...

    1. - anywhere on and in the red zone, the curve will either be - self-intersecting (yielding a loop), or it will have a sharp - discontinuity (yielding a cusp). Anywhere inside the red zone, - this will be a loop. We won't know where that loop is - (in terms of t values), but we are guaranteed that - there is one. + ...anywhere inside the red zone, but not on its boundaries, the + curve will either be self-intersecting (yielding a loop). We + won't know where it self-intersects (in terms of + t values), but we are guaranteed that it does.

    2. - on the left (red) edge, the curve will have a cusp. We again - don't know where, just that it has one. This edge is - described by the function: + ...on the left (red) edge of the red zone, the curve will have a + cusp. We again don't know where, but we know there is + one. This edge is described by the function:

    3. - on the lower right (pink) edge, the curve will have a loop at - t=1, so we know the end coordinate of the curve also lies - on the curve. This edge is described by the function: + ...on the almost circular, lower right (pink) edge, the curve's + end point touches the curve, forming a loop. This edge is + described by the function:

    4. - on the top (blue) edge, the curve will have a loop at t=0, so we - know the start coordinate of the curve also lies on the - curve. This edge is described by the function: + ...on the top (blue) edge, the curve's start point touches the + curve, forming a loop. This edge is described by the function:

    5. - inside the green zone, the curve will have a single inflection, - switching concave/convex once. + ...inside the lower (green) zone, past y=1, the + curve will have a single inflection (switching concave/convex + once).

    6. - between the red and green zones, the curve has two inflections, - meaning its curvature switches between concave/convex form - twice. + ...between the left and lower boundaries (below the cusp line + but above the single-inflection line), the curve will have two + inflections (switching from concave to convex and then back + again, or from convex to concave and then back again).

    7. - anywhere on the right of the red zone, the curve will have no - inflections. It'll just be a well-behaved arch. + ...anywhere on the right of self-intersection zone, the curve + will have no inflections. It'll just be a simple arch.

    @@ -3580,8 +3705,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Sweet! z stays 1, so we can effectively ignore it entirely, @@ -3591,8 +3717,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Running all our coordinates through this transformation gives a new @@ -3609,8 +3736,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    So we want some shearing value that, when multiplied by y, @@ -3620,8 +3748,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Now, running this on all our points generates a new set of @@ -3640,8 +3769,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Then, finally, this generates a new set of coordinates, let's call @@ -3660,7 +3790,8 @@ function getCubicRoots(pa, pb, pc, pd) { class="LaTeX SVG" src="./images/chapters/canonical/0430e8c7f7d4ec80e6527f96f3d56e5c.svg" width="140px" - height="65px" + height="63px" + loading="lazy" />

    And this generates our final set of four coordinates. Of these, we @@ -3672,8 +3803,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    That looks very complex, but notice that every coordinate value is @@ -3689,8 +3821,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Suddenly things look a lot simpler: the mapped x is fairly straight @@ -3702,8 +3835,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    That's kind of super-simple to write out in code, I think you'll @@ -3751,11 +3885,22 @@ function getCubicRoots(pa, pb, pc, pd) { can immediately tell which features our curve must have, based on where the fourth coordinate is located on the map:

    - + width="800" + height="400" + src="./chapters/canonical/interactive.js" + > + + + Scripts are disabled. Showing fallback image. +

    Finding Y, given X

    @@ -3807,6 +3952,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/yforx/9df91c28af38c1ba2e2d38d2714c9446.svg" width="335px" height="19px" + loading="lazy" />

    We can rewrite this to a plain polynomial form, by just fully @@ -3818,6 +3964,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/yforx/9ab2b830fe7fb73350c19bde04e9441b.svg" width="445px" height="19px" + loading="lazy" />

    Nothing special here: that's a standard cubic polynomial in "power" @@ -3832,6 +3979,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/yforx/de3bd3e271d72194c730d0ae44f031a8.svg" width="465px" height="19px" + loading="lazy" />

    You might be wondering "where did all the other 'minus x' for all @@ -3896,6 +4044,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/cb24cda7f7f4bbf3be7104c460e0ec9f.svg" width="140px" height="33px" + loading="lazy" />

    or, more commonly written using Leibnitz notation as:

    This formula says that the length of a parametric curve is in fact @@ -3953,6 +4103,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/e168758d35b8f6781617eda5a32b20bf.svg" width="636px" height="71px" + loading="lazy" />

    In plain text: an integral function can always be treated as the sum @@ -4028,6 +4179,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/5509919419288129322cfbd4c60d0a4f.svg" width="341px" height="72px" + loading="lazy" />

    That may look a bit more complicated, but the fraction involving @@ -4052,6 +4204,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/d0d93f1cc26b560309dade1f1aa012f2.svg" width="63px" height="93px" + loading="lazy" />

    Which means that in order for us to approximate the integral, we @@ -4063,6 +4216,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/e96dd431f6ef9433ccf25909dddd5bca.svg" width="476px" height="44px" + loading="lazy" />

    We can program that pretty easily, provided we have that @@ -4240,6 +4394,7 @@ if (roots.length > 0) { src="./images/chapters/curvature/d9c893051586eb8d9de51c0ae1ef8fae.svg" width="113px" height="47px" + loading="lazy" />

    Which is really just a "short form" that glosses over the fact that @@ -4250,6 +4405,7 @@ if (roots.length > 0) { src="./images/chapters/curvature/828333034b4fed8e248683760d6bc6f4.svg" width="239px" height="55px" + loading="lazy" />

    And while that's a litte more verbose, it's still just as simple to @@ -4290,6 +4446,7 @@ if (roots.length > 0) { src="./images/chapters/curvature/6ed4fd2ead35c57984caddf9fe375a5f.svg" width="81px" height="37px" + loading="lazy" />

    So that's a rather convenient fact to know, too.

    @@ -4696,6 +4853,7 @@ lli = function(line1, line2): src="./images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg" width="197px" height="16px" + loading="lazy" />

    So that just leaves finding A.

    @@ -4708,6 +4866,7 @@ lli = function(line1, line2): src="./images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg" width="197px" height="87px" + loading="lazy" />

    So, if we know the start and end coordinates, and we know the @@ -4746,6 +4905,7 @@ lli = function(line1, line2): src="./images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg" width="251px" height="39px" + loading="lazy" />

    This leads to a pretty powerful bit of knowledge: merely by knowing @@ -4758,6 +4918,7 @@ lli = function(line1, line2): src="./images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg" width="217px" height="37px" + loading="lazy" />

    And that's it, all values found.

    @@ -4780,12 +4941,14 @@ lli = function(line1, line2): src="./images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg" width="245px" height="41px" + loading="lazy" />

    Unfortunately, this trick only works for quadratic and cubic @@ -4847,6 +5010,7 @@ lli = function(line1, line2): src="./images/chapters/moulding/7bba0a4fd605e023cda922de125b3e32.svg" width="221px" height="36px" + loading="lazy" />

    For quadratic curves, this means we're done, since the new point A' @@ -4882,6 +5046,7 @@ lli = function(line1, line2): src="./images/chapters/moulding/524206c49f317d27d8e07a310b24a7a3.svg" width="132px" height="75px" + loading="lazy" />

    And then we can compute the new control points:

    And that's cubic curve manipulation.

    @@ -5041,6 +5207,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/bd8e8e294eec10d2bf6ef857c7c0c2c2.svg" width="295px" height="43px" + loading="lazy" />

    And then we (trivially) rearrange the terms across multiple lines: @@ -5050,6 +5217,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/2b8334727d3b004c6e87263fec6b32b7.svg" width="216px" height="64px" + loading="lazy" />

    This rearrangement has "factors of t" at each row (the first row @@ -5067,6 +5235,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/94acb5850778dcb16c2ba3cfa676f537.svg" width="572px" height="53px" + loading="lazy" />

    We can do the same for the cubic curve, of course. We know the @@ -5077,6 +5246,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/7eada6f12045423de24d9a2ab8e293b1.svg" width="355px" height="19px" + loading="lazy" />

    So we write out the expansion and rearrange:

    Which we can then decompose:

    And, of course, we can do this for quartic curves too (skipping @@ -5101,6 +5273,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/9151c0fdf9689ee598a2d029ab2ffe34.svg" width="491px" height="92px" + loading="lazy" />

    And so and on so on. Now, let's see how to use these @@ -5121,6 +5294,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/2bef3da3828d63d690460ce9947dbde2.svg" width="63px" height="73px" + loading="lazy" />

    Next, we need to figure out appropriate t values for @@ -5165,6 +5339,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/78b8ba1aba2e4c9ad3f7890299c90152.svg" width="395px" height="40px" + loading="lazy" />

    Where length() is literally just that: the length of @@ -5179,6 +5354,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/08f4beaebf83dca594ad125bdca7e436.svg" width="272px" height="55px" + loading="lazy" />

    And now we can move on to the actual "curve fitting" part: what we @@ -5199,6 +5375,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/7e5d59272621baf942bc722208ce70c2.svg" width="177px" height="23px" + loading="lazy" />

    Since this function only deals with individual coordinates, we'll @@ -5211,6 +5388,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/ab334858d3fa309cc1a5ba535a2ca168.svg" width="195px" height="41px" + loading="lazy" />

    And here's the trick that justifies using matrices: while we can @@ -5226,6 +5404,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/2d42758fba3370f52191306752c2705c.svg" width="141px" height="21px" + loading="lazy" />

    In which we can replace the rather cumbersome "squaring" operation @@ -5236,6 +5415,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/5f7fcb86ae1c19612b9fe02e23229e31.svg" width="225px" height="21px" + loading="lazy" />

    Here, the letter T is used instead of the number 2, to @@ -5259,6 +5439,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/6202d7bd150c852b432d807c40fb1647.svg" width="201px" height="96px" + loading="lazy" />

    Which, because of the first and last values in S, @@ -5269,6 +5450,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/4ffad56e281ee79d0688e93033429f0a.svg" width="212px" height="92px" + loading="lazy" />

    Now we can properly write out the error function as matrix @@ -5279,6 +5461,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/8d09f2be2c6db79ee966f170ffc25815.svg" width="231px" height="21px" + loading="lazy" />

    So, we have our error function: we now need to figure out the @@ -5293,6 +5476,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/283bc9e8fe59a78d3c74860f62a66ecb.svg" width="197px" height="36px" + loading="lazy" />

    Where did this derivative come from?

    @@ -5334,6 +5518,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/d84d1c71a3ce1918f53eaf8f9fe98ac4.svg" width="168px" height="27px" + loading="lazy" />

    Here, the "to the power negative one" is the notation for the @@ -5446,6 +5631,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/9215d05705c8e8a7ebd718ae6f690371.svg" width="409px" height="75px" + loading="lazy" />

    However, there's something funny going on here: the coordinate @@ -5485,6 +5671,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/1811b59c5ab9233f08590396e5d03303.svg" width="187px" height="83px" + loading="lazy" />

    This mapping says that in order to map a Catmull-Rom "point + @@ -5500,6 +5687,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/4d524810417b4caffedd13af23135f5b.svg" width="591px" height="83px" + loading="lazy" />

    Thus:

    However, we're not quite done, because Catmull-Rom curves @@ -5523,6 +5712,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/06ae1e3fdc660e59d618e0760e8e9ab5.svg" width="285px" height="84px" + loading="lazy" />

    With the mapping matrix properly done, let's rewrite the "point + @@ -5534,6 +5724,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/cc1e2ff43350c32f0ae9ba9a7652b8fb.svg" width="409px" height="75px" + loading="lazy" />

    Replace point/tangent vector with the expression for @@ -5544,6 +5735,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/f08e34395ce2812276fd70548f805041.svg" width="549px" height="81px" + loading="lazy" />

    and merge the matrices:

    This looks a lot like the Bézier matrix form, which as we saw in @@ -5561,6 +5754,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/8f56909fcb62b8eef18b9b9559575c13.svg" width="353px" height="73px" + loading="lazy" />

    So, if we want to express a Catmull-Rom curve using a Bézier @@ -5571,6 +5765,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/b21386f86bef8894f108c5441dad10de.svg" width="227px" height="84px" + loading="lazy" />

    Into something that looks like this:

    And the way we do that is with a fairly straight forward bit of @@ -5588,6 +5784,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/a47b072a325812ac4f0ff52c22792588.svg" width="440px" height="84px" + loading="lazy" />

    Then we remove the coordinate vector from both sides without @@ -5598,6 +5795,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/841fb6a2a035c9bcf5a2d46f2a67709b.svg" width="353px" height="84px" + loading="lazy" />

    Then we can "get rid of" the Bézier matrix on the right by @@ -5608,6 +5806,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/cbdd46d5e2e1a6202ef46fb03711ebe4.svg" width="657px" height="88px" + loading="lazy" />

    A matrix times its inverse is the matrix equivalent of 1, and @@ -5619,6 +5818,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/3ea54fe939d076f8db605c5b480e7db0.svg" width="369px" height="88px" + loading="lazy" />

    And now we're basically done. We just multiply those two @@ -5629,6 +5829,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/169fd85a95e4d16fe289a75583017a11.svg" width="161px" height="77px" + loading="lazy" />

    We now have the final piece of our function puzzle. Let's run @@ -5642,6 +5843,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/cc1e2ff43350c32f0ae9ba9a7652b8fb.svg" width="409px" height="75px" + loading="lazy" />

    1. rewrite to pure coordinate form:
    2. @@ -5651,6 +5853,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/f814bb8d627f9c8f33b347c1cf13d4c7.svg" width="324px" height="84px" + loading="lazy" />
      1. rewrite for "normal" coordinate vector:
      2. @@ -5660,6 +5863,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/5f2750de827497375d9a915f96686885.svg" width="441px" height="81px" + loading="lazy" />
        1. merge the inner matrices:
        2. @@ -5669,6 +5873,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/79e333cd0c569657eea033b04fb5e61b.svg" width="348px" height="84px" + loading="lazy" />
          1. rewrite for Bézier matrix form:
          2. @@ -5678,6 +5883,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/1b8a782f7540503d38067317e4cd00b0.svg" width="431px" height="77px" + loading="lazy" />
            1. @@ -5690,6 +5896,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/e3d30ab368dcead1411532ce3814d3f3.svg" width="348px" height="81px" + loading="lazy" />

              And we're done: we finally know how to convert these two curves! @@ -5708,6 +5915,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/ba31c32eba62f1e3b15066cd5ddda597.svg" width="249px" height="85px" + loading="lazy" />

              Similarly, if we have a Bézier curve defined by four coordinates @@ -5720,6 +5928,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/eae7f01976e511ee38b08b6edc8765d2.svg" width="284px" height="77px" + loading="lazy" />

              or, if your API requires specifying Catmull-Rom curves using "point @@ -5730,6 +5939,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/26363fc09f8cf2d41ea5b4256656bb6d.svg" width="284px" height="77px" + loading="lazy" />

              @@ -5842,6 +6052,7 @@ lli = function(line1, line2): src="./images/chapters/polybezier/408dd95905a5f001179c4da6051e49c5.svg" width="124px" height="17px" + loading="lazy" />

              We can effect this quite easily, because we know that the vector @@ -5856,6 +6067,7 @@ lli = function(line1, line2): src="./images/chapters/polybezier/8c1b570b3efdfbbc39ddedb4adcaaff6.svg" width="304px" height="40px" + loading="lazy" />

              So let's implement that and see what it gets us. The following two @@ -6218,6 +6430,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/1d4be24e5896dce3c16c8e71f9cc8881.svg" width="108px" height="16px" + loading="lazy" />

              However, we're working in 2D, and d is a single @@ -6234,6 +6447,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/5bfee4f2ae27304475673d0596e42f9a.svg" width="151px" height="16px" + loading="lazy" />

              Now this still isn't very useful unless we know what the formula @@ -6253,6 +6467,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/fa6c243de2aa78b7451e0086848dfdfc.svg" width="120px" height="40px" + loading="lazy" />

              Determining the length requires computing an arc length, and this @@ -6266,6 +6481,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/b262e50c085815421d94e120fc17f1c8.svg" width="169px" height="36px" + loading="lazy" />

              So if we want the length of the tangent, we plug in @@ -6277,6 +6493,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/1d586b939b44ff9bdb42562a12ac2779.svg" width="209px" height="36px" + loading="lazy" />

              And that's where things go wrong. It doesn't even really matter @@ -6514,6 +6731,7 @@ lli = function(line1, line2): src="./images/chapters/circles/8374c4190d6213b0ac0621481afaa754.svg" width="175px" height="40px" + loading="lazy" />

              What we want to find is the intersection of the tangents, so we want @@ -6524,6 +6742,7 @@ lli = function(line1, line2): src="./images/chapters/circles/a127f926eced2751a09c54bf7c361b4a.svg" width="284px" height="40px" + loading="lazy" />

              i.e. we want a point that lies on the vertical line through S (at @@ -6536,6 +6755,7 @@ lli = function(line1, line2): src="./images/chapters/circles/b5d864e9ed0c44c56d454fbaa4218d5e.svg" width="219px" height="40px" + loading="lazy" />

              First we solve for b:

              which yields:

              which we can then substitute in the expression for a:

              A quick check shows that plugging these values for a and @@ -6573,6 +6796,7 @@ lli = function(line1, line2): src="./images/chapters/circles/fe32474b4616ee9478e1308308f1b6bf.svg" width="188px" height="32px" + loading="lazy" />

              We compute T, observing that if t=0.5, the polynomial @@ -6583,6 +6807,7 @@ lli = function(line1, line2): src="./images/chapters/circles/e1059e611aa1e51db41f9ce0b4ebb95a.svg" width="252px" height="35px" + loading="lazy" />

              Which, worked out for the x and y components, gives:

              And the distance between these two is the standard Euclidean @@ -6600,6 +6826,7 @@ lli = function(line1, line2): src="./images/chapters/circles/adbd056f4b8fcd05b1d4f2fce27d7657.svg" width="399px" height="153px" + loading="lazy" />

              So, what does this distance function look like when we plot it for a @@ -6655,6 +6882,7 @@ lli = function(line1, line2): src="./images/chapters/circles/df87674db0f31fc3944aaeb6b890e196.svg" width="247px" height="53px" + loading="lazy" />

              And frankly, things are starting to look a bit ridiculous at this @@ -6802,6 +7030,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/a4f0dafbfe80c88723c3cc22277a9682.svg" width="175px" height="40px" + loading="lazy" />

              But we now need to find two control points, rather than one. If we @@ -6815,6 +7044,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/dfb83eec053c30e0a41b0a52aba24cd4.svg" width="113px" height="40px" + loading="lazy" />

              where "a" is some scaling factor, and:

              where "b" is also some scaling factor.

              @@ -6879,6 +7110,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/3189cac1ddac07c1487e1e51740ecc88.svg" width="397px" height="40px" + loading="lazy" />

              So that just leaves us to find the distance from t=0.5 to @@ -6893,6 +7125,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/fe32474b4616ee9478e1308308f1b6bf.svg" width="188px" height="32px" + loading="lazy" />

              And the distance from the origin to the line start/end is another @@ -6906,6 +7139,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/0364731626a530c8a9b30f424ada53c5.svg" width="261px" height="67px" + loading="lazy" />

              With the coordinate C, and knowledge of coordinate B, we can @@ -6917,12 +7151,14 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/ee08d86b7497c7ab042ee899bf15d453.svg" width="397px" height="48px" + loading="lazy" />

              Which means we can now determine the distance {start,guessed}, @@ -6934,6 +7170,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/178a838274748439778e2a29f5a27d0b.svg" width="252px" height="56px" + loading="lazy" />

              And after this tedious detour to find the coordinate for @@ -6945,6 +7182,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/49dbf244d50c787a4ab18694488d9b69.svg" width="524px" height="79px" + loading="lazy" />

              And that's it, we have all four points now for an approximation of @@ -6960,6 +7198,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/e2258660a796dcd6189a6f5e14326dad.svg" width="205px" height="40px" + loading="lazy" />

              and

              And, because the "quarter curve" special case comes up so incredibly @@ -6978,6 +7218,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/877f9c217c51c0087be751a7580ed459.svg" width="412px" height="33px" + loading="lazy" />

              Which, in decimal values, rounded to six significant digits, is: @@ -6987,6 +7228,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/05d36e051a38905dcb81e65db8261f24.svg" width="412px" height="16px" + loading="lazy" />

              Of course, this is for a circle with radius 1, so if you have a @@ -7273,6 +7515,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/0f3451c711c0fe5d0b018aa4aa77d855.svg" width="169px" height="41px" + loading="lazy" />

              Which, honestly, doesn't tell us all that much. All we can see is @@ -7302,6 +7545,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/cf45d1ea00d4866abc8a058b130299b4.svg" width="559px" height="43px" + loading="lazy" />

              So this is where we see the interpolation: N(t) for an (i,k) pair @@ -7318,6 +7562,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/adac18ea69cc58e01c8d5e15498e4aa6.svg" width="240px" height="40px" + loading="lazy" />

              And this function finally has a straight up evaluation: if a @@ -7352,6 +7597,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/763838ea6f9e6c6aa63ea5f9c6d9542f.svg" width="281px" height="21px" + loading="lazy" />

              This is another recursive function, with k values @@ -7363,6 +7609,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/892209dad8fd1f839470dd061e870913.svg" width="255px" height="39px" + loading="lazy" />

              That looks complicated, but it's not. Computing alpha is just a @@ -7379,6 +7626,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/4c8f9814c50c708757eeb5a68afabb7f.svg" width="368px" height="40px" + loading="lazy" />

              So, we see two stopping conditions: either i becomes 0, @@ -7397,6 +7645,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/7962d6fea86da6f53a7269fba30f0138.svg" width="417px" height="231px" + loading="lazy" />

              That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2): d(3,3) diff --git a/docs/js/custom-element/api/base-api.js b/docs/js/custom-element/api/base-api.js index db0185b9..83cbc7f6 100644 --- a/docs/js/custom-element/api/base-api.js +++ b/docs/js/custom-element/api/base-api.js @@ -1,3 +1,5 @@ +import { hatch } from "./util/hatchery.js"; + /** * The base API class, responsible for such things as setting up canvas event * handling, method accumulation, custom element binding, etc. etc. @@ -6,7 +8,12 @@ */ class BaseAPI { static get privateMethods() { - return [`constructor`, `addListeners`, `getCursorCoords`] + return [ + `constructor`, + `createHatchPatterns`, + `addListeners`, + `getCursorCoords`, + ] .concat(this.superCallers) .concat(this.eventHandlers); } @@ -44,6 +51,7 @@ class BaseAPI { } else { this.canvas = document.createElement(`canvas`); } + this.HATCHING = hatch(canvasBuildFunction); this.addListeners(); this.setSize(width, height); this.setup(); @@ -201,6 +209,11 @@ class BaseAPI { setup() { // console.log(`setup`); this.movable = []; + this.font = { + size: 10, + weight: 400, + family: `arial`, + }; } /** @@ -233,6 +246,9 @@ function enhanceContext(ctx) { lineWidth: ctx.lineWidth, textAlign: ctx.textAlign, transform: [m.a, m.b, m.c, m.d, m.e, m.f], + font: ctx.font, + shadowColor: ctx.shadowColor, + shadowBlur: ctx.shadowColor, }; styles.push(e); }; diff --git a/docs/js/custom-element/api/graphics-api.js b/docs/js/custom-element/api/graphics-api.js index b48a8603..2bc5b6fd 100644 --- a/docs/js/custom-element/api/graphics-api.js +++ b/docs/js/custom-element/api/graphics-api.js @@ -26,6 +26,12 @@ class GraphicsAPI extends BaseAPI { `CENTER`, `LEFT`, `RIGHT`, + `HATCH1`, + `HATCH2`, + `HATCH3`, + `HATCH4`, + `HATCH5`, + `HATCH6`, ]; } @@ -64,6 +70,25 @@ class GraphicsAPI extends BaseAPI { get RIGHT() { return `right`; } + // hatching patterns + get HATCH1() { + return this.HATCHING[0]; + } + get HATCH2() { + return this.HATCHING[1]; + } + get HATCH3() { + return this.HATCHING[2]; + } + get HATCH4() { + return this.HATCHING[3]; + } + get HATCH5() { + return this.HATCHING[4]; + } + get HATCH6() { + return this.HATCHING[5]; + } onMouseDown(evt) { super.onMouseDown(evt); @@ -113,6 +138,20 @@ class GraphicsAPI extends BaseAPI { points.forEach((p) => this.movable.push(p)); } + /** + * Convert the canvas to an image + */ + toDataURL() { + return this.canvas.toDataURL(); + } + + /** + * Draw an image onto the canvas + */ + image(img, x = 0, y = 0, w, h) { + this.ctx.drawImage(img, x, y, w || img.width, h || img.height); + } + /** * transforms: translate */ @@ -251,6 +290,22 @@ class GraphicsAPI extends BaseAPI { this.setStroke(false); } + /** + * Set a text stroke/color + */ + setTextStroke(color, weight) { + this.textStroke = color; + this.strokeWeight = weight; + } + + /** + * Do not use text stroking. + */ + noTextStroke() { + this.textStroke = false; + this.strokeWeight = false; + } + /** * Set the context lineWidth */ @@ -258,6 +313,52 @@ class GraphicsAPI extends BaseAPI { this.ctx.lineWidth = `${width}px`; } + /** + * Set the font size + */ + setFontSize(px) { + this.font.size = px; + this.setFont(); + } + + /** + * Set the font weight (CSS name/number) + */ + setFontWeight(val) { + this.font.weight = val; + this.setFont(); + } + + /** + * Set the font family by name + */ + setFontFamily(name) { + this.font.family = name; + this.setFont(); + } + + setFont(font) { + font = + font || `${this.font.weight} ${this.font.size}px ${this.font.family}`; + this.ctx.font = font; + } + + /** + * Set text shadow + */ + setShadow(color, px) { + this.ctx.shadowColor = color; + this.ctx.shadowBlur = px; + } + + /** + * Disable text shadow + */ + noShadow() { + this.ctx.shadowColor = `transparent`; + this.ctx.shadowBlur = 0; + } + /** * Cache all styling values */ @@ -319,12 +420,17 @@ class GraphicsAPI extends BaseAPI { x = x.x; } const ctx = this.ctx; + ctx.cacheStyle(); if (alignment) { - ctx.cacheStyle(); ctx.textAlign = alignment; } + if (this.textStroke) { + this.ctx.lineWidth = this.strokeWeight; + this.setStroke(this.textStroke); + this.ctx.strokeText(str, x, y); + } this.ctx.fillText(str, x, y); - if (alignment) ctx.restoreStyle(); + ctx.restoreStyle(); } /** @@ -339,18 +445,14 @@ class GraphicsAPI extends BaseAPI { * Draw a function plot from [start] to [end] in [steps] steps. * Returns the plot shape so that it can be cached for redrawing. */ - plot(fn, start = 0, end = 1, steps = 24) { - const ctx = this.ctx; - ctx.cacheStyle(); - ctx.fillStyle = `transparent`; + plot(fn, start = 0, end = 1, steps = 24, xscale = 1, yscale = 1) { const interval = end - start; this.start(); for (let i = 0, e = steps - 1, v; i < steps; i++) { v = fn(start + (interval * i) / e); - this.vertex(v.x, v.y); + this.vertex(v.x * xscale, v.y * yscale); } this.end(); - ctx.restoreStyle(); return this.currentShape; } @@ -378,9 +480,17 @@ class GraphicsAPI extends BaseAPI { /** * Draw a previously created shape */ - drawShape(shape) { - this.currentShape = shape; + drawShape(...shapes) { + shapes = shapes.map((s) => { + if (s instanceof Shape) return s; + return new Shape(this.POLYGON, undefined, s); + }); + this.currentShape = shapes[0].copy(); + for (let i = 1; i < shapes.length; i++) { + this.currentShape.merge(shapes[i]); + } this.end(); + this.STARTREPORTING = false; } /** @@ -516,6 +626,10 @@ class GraphicsAPI extends BaseAPI { return Math.max(...v); } + approx(v1, v2, epsilon = 0.001) { + return Math.abs(v1 - v2) < epsilon; + } + sin(v) { return Math.sin(v); } @@ -554,4 +668,4 @@ class GraphicsAPI extends BaseAPI { } } -export { GraphicsAPI, Bezier, Vector, Matrix }; +export { GraphicsAPI, Bezier, Vector, Matrix, Shape }; diff --git a/docs/js/custom-element/api/util/hatchery.js b/docs/js/custom-element/api/util/hatchery.js new file mode 100644 index 00000000..a5a01006 --- /dev/null +++ b/docs/js/custom-element/api/util/hatchery.js @@ -0,0 +1,111 @@ +const HATCHING = []; + +/** + * Build hatching patterns. These are built fully unrolled, + * mostly because they're small and there's no actual benefit + * to abstracting the drawing for only six patterns. + */ +function hatch(canvasBuildFunction) { + if (HATCHING.length > 0) { + return HATCHING; + } + + let cvs, + ctx, + w = 9, + h = 9; + + if (canvasBuildFunction) { + let b = canvasBuildFunction(w, h); + cvs = b.canvas; + ctx = b.ctx; + } else { + cvs = document.createElement("canvas"); + cvs.width = w; + cvs.height = h; + ctx = cvs.getContext(`2d`); + } + + ctx.fillStyle = `#0000FF30`; + ctx.lineWidth = 1; + + const paint = (x, y) => ctx.fillRect(x, y, 1, 1); + + // pattern: \ + ctx.clearRect(0, 0, w, h); + paint(0, 0); + paint(1, 1); + paint(2, 2); + paint(3, 3); + paint(4, 4); + paint(5, 5); + paint(6, 6); + paint(7, 7); + paint(8, 8); + HATCHING.push(ctx.createPattern(cvs, "repeat")); + + // pattern: / + ctx.clearRect(0, 0, w, h); + paint(0, 8); + paint(1, 7); + paint(2, 6); + paint(3, 5); + paint(4, 4); + paint(5, 3); + paint(6, 2); + paint(7, 1); + paint(8, 0); + HATCHING.push(ctx.createPattern(cvs, "repeat")); + + // pattern: x (without clearing, because we can overlay) + paint(0, 0); + paint(1, 1); + paint(2, 2); + paint(3, 3); + paint(5, 5); + paint(6, 6); + paint(7, 7); + paint(8, 8); + HATCHING.push(ctx.createPattern(cvs, "repeat")); + + // pattern: | + ctx.clearRect(0, 0, w, h); + paint(4, 0); + paint(4, 1); + paint(4, 2); + paint(4, 3); + paint(4, 4); + paint(4, 5); + paint(4, 6); + paint(4, 7); + paint(4, 8); + HATCHING.push(ctx.createPattern(cvs, "repeat")); + + // pattern: - + ctx.clearRect(0, 0, w, h); + paint(0, 4); + paint(1, 4); + paint(2, 4); + paint(3, 4); + paint(4, 4); + paint(5, 4); + paint(6, 4); + paint(7, 4); + paint(8, 4); + HATCHING.push(ctx.createPattern(cvs, "repeat")); + + // pattern: + (without clearing, because we can overlap) + paint(4, 0); + paint(4, 1); + paint(4, 2); + paint(4, 3); + paint(4, 5); + paint(4, 6); + paint(4, 7); + paint(4, 8); + HATCHING.push(ctx.createPattern(cvs, "repeat")); + + return HATCHING; +} + +export { hatch }; diff --git a/docs/js/custom-element/api/util/shape.js b/docs/js/custom-element/api/util/shape.js index fd9c64d2..57a0626d 100644 --- a/docs/js/custom-element/api/util/shape.js +++ b/docs/js/custom-element/api/util/shape.js @@ -4,10 +4,23 @@ * cubic Bezier curves. */ class Shape { - constructor(type, factor) { + constructor(type, factor, points = []) { this.first = false; this.segments = []; this.addSegment(type, factor); + points.forEach((p) => this.vertex(p)); + } + merge(other) { + if (!other.segments) { + other = { segments: [new Segment(Shape.POLYGON, undefined, other)] }; + } + other.segments.forEach((s) => this.segments.push(s)); + } + copy() { + const copy = new Shape(this.type, this.factor); + copy.first = this.first; + copy.segments = this.segments.map((s) => s.copy()); + return copy; } addSegment(type, factor) { this.currentSegment = new Segment(type, factor); @@ -30,10 +43,15 @@ Shape.BEZIER = `Bezier`; * A shape subpath */ class Segment { - constructor(type, factor) { + constructor(type, factor, points = []) { this.type = type; this.factor = factor; - this.points = []; + this.points = points; + } + copy() { + const copy = new Segment(this.type, this.factor); + copy.points = JSON.parse(JSON.stringify(this.points)); + return copy; } add(p) { this.points.push(p); diff --git a/docs/js/custom-element/graphics-element.js b/docs/js/custom-element/graphics-element.js index eebba39f..93a7645a 100644 --- a/docs/js/custom-element/graphics-element.js +++ b/docs/js/custom-element/graphics-element.js @@ -40,8 +40,8 @@ class GraphicsElement extends CustomElement { return ` :host([hidden]) { display: none; } :host style { display: none; } - :host canvas { position: relative; z-index: 1; display: block; margin: auto; border-radius: 0; box-sizing: content-box!important; padding: 1px; } - :host canvas:focus { border: 1px solid red; padding: 0px; } + :host canvas { position: relative; z-index: 1; display: block; margin: auto; border-radius: 0; box-sizing: content-box!important; border: 1px solid lightgrey; } + :host canvas:focus { border: 1px solid red; } :host a.view-source { display: block; position:relative; top: -0.6em; margin-bottom: -0.2em; font-size: 60%; text-decoration: none; } :host label { display: block; font-style:italic; font-size: 0.9em; text-align: right; } `; @@ -171,7 +171,7 @@ class GraphicsElement extends CustomElement { const height = this.getAttribute(`height`, 200); this.code = ` - import { GraphicsAPI, Bezier, Vector, Matrix } from "${MODULE_PATH}/api/graphics-api.js"; + import { GraphicsAPI, Bezier, Vector, Matrix, Shape } from "${MODULE_PATH}/api/graphics-api.js"; ${globalCode} diff --git a/docs/js/site/faster-lazy-loading.js b/docs/js/site/faster-lazy-loading.js new file mode 100644 index 00000000..e7223f7b --- /dev/null +++ b/docs/js/site/faster-lazy-loading.js @@ -0,0 +1,76 @@ +/** + * This file takes any with loading="lazy" and rewrites it + * so that it starts to load much earlier than browsers would load + * them, so that they're already done by the time the user gets to + * them. This prevents the ridiculous "don't start loading the img + * until we're already looking at the empty bounding box". + */ + +const images = Array.from( + document.querySelectorAll(`img[loading=lazy]`) +).filter((img) => img.parentNode.nodeName !== `FALLBACK-IMAGE`); + +// First, make images inert. As this happens before the document +// becomes active, this prevents images from loading anything. +// +// Also, by having JS deactivate the images, anyone running with +// JS disabled will still see images load in properly. In fact, +// the "lazy" attribute gets ignored when JS is disabled and all +// images will just load in immediately. + +images.forEach((img) => { + // non-negotiable order of operations: + img.dataset.src = img.src; + img.src = ``; + img.removeAttribute(`loading`); +}); + +// Then tack on the functionality that reactivates them based on viewport distance. + +let lock = false; +let retry = false; + +/** + * Test all images during scroll, in a way that doesn't hang the browser. + */ +function testImages() { + if (lock) { + if (retry) retry = clearTimeout(retry); + retry = setTimeout(testImages, 200); + } + + lock = true; + + let top = window.scrollY; + let height = document.documentElement.clientHeight; + let bottom = top + height; + + for (let pos = images.length - 1; pos >= 0; pos--) { + test(images[pos], pos, top, bottom, height); + } + + if (images.length === 0) { + window.removeEventListener("scroll", testImages); + if (retry) clearTimeout(retry); + } + + lock = false; +} + +/** + * Test individual images for whether or not they should load. + */ +function test(img, pos, top, bottom, threshold) { + top = Math.abs(img.offsetTop - top) < threshold; + bottom = Math.abs(img.offsetTop + img.offsetHeight - bottom) < threshold; + + if (top || bottom) { + img.src = img.dataset.src; + images.splice(pos, 1); + } +} + +/** + * Remember to listen for scroll passively. If you don't, bad things happen. + */ +window.addEventListener("scroll", testImages, { passive: true }); diff --git a/docs/zh-CN/index.html b/docs/zh-CN/index.html index 242d32ae..012a9038 100644 --- a/docs/zh-CN/index.html +++ b/docs/zh-CN/index.html @@ -66,6 +66,14 @@ + + + @@ -527,6 +535,7 @@ src="./images/chapters/whatis/b5aa26284ba3df74970a95cb047a841d.svg" width="555px" height="101px" + loading="lazy" />

              让我们来通过实际操作看一下:下面的图形都是可交互的,因此你可以通过上下键来增加或减少插值距离,来观察图形的变化。我们从三个点构成的两条线段开始。通过对各条线段进行线性插值得到两个点,对点之间的线段再进行线性插值,产生一个新的点。最终这些点——所有的点都可以通过选取不同的距离插值产生——构成了贝塞尔曲线 @@ -568,6 +577,7 @@ src="./images/chapters/explanation/1caef9931f954e32eae5067b732c1018.svg" width="93px" height="17px" + loading="lazy" />

              记号f(x)是表示函数的标准方式(为了方便起见,如果只有一个的话,我们称函数为f),函数的输出根据一个变量(本例中是x)变化。改变xf(x)的输出值也会变。 @@ -580,6 +590,7 @@ src="./images/chapters/explanation/0f5cffd58e864fec6739a57664eb8cbd.svg" width="92px" height="37px" + loading="lazy" />

              这俩方程没什么让人印象深刻的,只不过是正弦函数和余弦函数,但正如你所见,输入变量有两个不同的名字。如果我们改变了a的值,f(b)的输出不会有变化,因为这个方程没有用到a。参数方程通过改变这点来作弊。在参数方程中,所有不同的方程共用一个变量,如下所示: @@ -589,6 +600,7 @@ src="./images/chapters/explanation/066a910ae6aba69c40a338320759cdd1.svg" width="104px" height="40px" + loading="lazy" />

              多个方程,但只有一个变量。如果我们改变了t的值,fa(t)fb(t)的输出都会发生变化。你可能会好奇这有什么用,答案其实很简单:对于参数曲线,如果我们用常用的标记来替代fa(t)fb(t),看起来就有些明朗了: @@ -598,6 +610,7 @@ src="./images/chapters/explanation/4cf6fb369841e2c5d36e5567a8db4306.svg" width="79px" height="40px" + loading="lazy" />

              好了,通过一些神秘的t值将x/y坐标系联系起来。 @@ -631,6 +644,7 @@ src="./images/chapters/explanation/bb06cb82d372f822a7b35e661502bd72.svg" width="219px" height="19px" + loading="lazy" />

              如果它的最高次项是就称为“三次”多项式,如果最高次项是,称为“二次”多项式,如果只含有x的项,它就是一条线(不过不含任何x的项它就不是一个多项式!) @@ -643,6 +657,7 @@ src="./images/chapters/explanation/2adc12d0cff01d40d9e1702014a7dc19.svg" width="371px" height="64px" + loading="lazy" />

              我明白你在想什么:这看起来并不简单,但如果我们拿掉t并让系数乘以1,事情就会立马简单很多,看看这些二次项: @@ -652,6 +667,7 @@ src="./images/chapters/explanation/9c18f76e76cf684ecd217ad8facc2e93.svg" width="189px" height="85px" + loading="lazy" />

              需要注意的是,2与1+1相同,3相当于2+1或1+2,6相当于3+3...如你所见,每次我们增加一个维度,只要简单地将头尾置为1,中间的操作都是“将上面的两个数字相加”。现在就能很容易地记住了。 @@ -664,6 +680,7 @@ src="./images/chapters/explanation/e107caca1577e44293cd207388ac939c.svg" width="312px" height="60px" + loading="lazy" />

              基本上它就是“每个ab结合项”的和,在每个加号后面逐步的将a换成b。因此这也很简单。现在你已经知道了二次多项式,为了叙述的完整性,我将给出一般方程: @@ -673,6 +690,7 @@ src="./images/chapters/explanation/9a6d17c362980775f1425d0d2ad9a36a.svg" width="323px" height="56px" + loading="lazy" />

              这就是贝塞尔曲线完整的描述。在这个函数中的Σ表示了这是一系列的加法(用Σ下面的变量,从...=<值>开始,直到Σ上面的数字结束)。 @@ -779,6 +797,7 @@ function Bezier(3,t): src="./images/chapters/control/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg" width="379px" height="56px" + loading="lazy" />

              看起来很复杂,但实际上“权重”只是我们想让曲线所拥有的坐标值:对于一条nth阶曲线,w0是起始坐标,wn是终点坐标,中间的所有点都是控制点坐标。假设说一条曲线的起点为(120,160),终点为(220,40),并受点(35,200)和点(220,260)的控制,贝塞尔曲线方程就为: @@ -788,6 +807,7 @@ function Bezier(3,t): src="./images/chapters/control/c0d4dbc07b8ec7c0a18ea43c8a386935.svg" width="476px" height="40px" + loading="lazy" />

              这就是我们在文章开头看到的曲线:

              The function for rational Bézier curves has two more terms:

              In this, the first new term represents an additional weight for each @@ -980,6 +1002,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/extended/b80a1cac1f9ec476d6f6646ce0e154e7.svg" width="227px" height="17px" + loading="lazy" />

              很显然,起始值需要a=1, b=0,混合值就为100%的value @@ -992,6 +1015,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/extended/d930dea961b40f4810708bd6746221a2.svg" width="223px" height="17px" + loading="lazy" />

              用这个式子我们可以保证相加的值永远不会超过100%。通过将a限制在区间[0,1],我们将会一直处于这两个值之间(包括这两个端点),并且相加为100%。 @@ -1064,6 +1088,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/5aea6d4d5855135051715fb1cc0ec531.svg" width="468px" height="20px" + loading="lazy" />

              暂时不用管我们具体的坐标,现在有:

              可以将它写成四个表达式之和:

              我们可以扩展这些表达式:

              更进一步,我们可以加上所有的1和0系数,以便看得更清楚:

              现在,我们可以将它看作四个矩阵运算:

              如果我们将它压缩到一个矩阵操作里,就能得到:

              这种多项式表达式一般是以递增的顺序来写的,所以我们应该将t矩阵水平翻转,并将大的那个“混合”矩阵上下颠倒: @@ -1115,6 +1146,7 @@ function RationalBezier(3,t,w[],r[]): src="./images/chapters/matrix/24bdad213879407a35b23c18394293aa.svg" width="227px" height="72px" + loading="lazy" />

              最终,我们可以加入原始的坐标,作为第三个单独矩阵:

              我们可以对二次曲线运用相同的技巧,可以得到:

              如果我们代入t值并乘以矩阵来计算,得到的值与解原始多项式方程或用逐步线性插值计算的结果一样。 @@ -1370,6 +1404,7 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/77a11d65d7cffc4b84a85c4bec837792.svg" width="263px" height="55px" + loading="lazy" />

              and

              Let's say we want to split the curve at some point @@ -1391,6 +1427,7 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/278b67e9b908f4abcf2e9d069a6b29a4.svg" width="648px" height="55px" + loading="lazy" />

              and

              If we could compact these matrices back to the form @@ -1421,24 +1459,28 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/c79b607a92c42789fde57c6a8c4259fd.svg" width="348px" height="55px" + loading="lazy" />

              We can do this because [M · M-1

              Excellent! Now we can form our new quadratic curve:

              Brilliant

              If we want the interval [z,1], we will be evaluating this @@ -1525,12 +1573,14 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/0f84dbf6e3ea7db732ceb9d71caf9b22.svg" width="461px" height="55px" + loading="lazy" />

              We're going to do the same trick of multiplying by the identity @@ -1542,6 +1592,7 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/567c29ee78b49c700f54b17780682543.svg" width="729px" height="57px" + loading="lazy" />

              So, our final second curve looks like:

              Nice

              and

              We can do the same for cubic curves. However, I'll spare you the @@ -1604,6 +1660,7 @@ function drawCurve(points[], t): src="./images/chapters/matrixsplit/5e3fae45d325d0f0681731fb606b6fbc.svg" width="841px" height="75px" + loading="lazy" />

              and

              So, looking at our matrices, did we really need to compute the @@ -1663,6 +1721,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/faf29599c9307f930ec28065c96fde2a.svg" width="805px" height="61px" + loading="lazy" />

              However, this rule also has as direct consequence that you @@ -1696,6 +1755,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/1244a85c1f9044b6f77cb709c682159c.svg" width="429px" height="41px" + loading="lazy" />

              Then, we apply one of those silly (actually, super useful) calculus @@ -1709,6 +1769,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/b2fda1dcce5bb13317aa42ebf5e7ea6c.svg" width="384px" height="17px" + loading="lazy" />

              So, with that seemingly trivial observation, we rewrite that Bézier @@ -1720,6 +1781,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/41e184228d85023abdadd6ce2acb54c7.svg" width="332px" height="68px" + loading="lazy" />

              So far so good. Now, to see why we did this, let's write out the @@ -1732,6 +1794,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/4debbed5922d2bd84fd322c616872d20.svg" width="400px" height="163px" + loading="lazy" />

              So by using this seemingly silly trick, we can suddenly express part @@ -1747,6 +1810,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/483c89c8726f7fd0dca0b7de339b04bd.svg" width="479px" height="161px" + loading="lazy" />

              So, with both of those changed from an order @@ -1769,6 +1833,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/dd8d8d98f66ce9f51b95cbf48225e97b.svg" width="481px" height="257px" + loading="lazy" />

              And this is where we switch over from calculus to linear algebra, @@ -1780,6 +1845,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/773fdc86b686647c823b4f499aca3a35.svg" width="77px" height="17px" + loading="lazy" />

              where the matrix M is an n+1 by @@ -1790,6 +1856,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/7a9120997e4a4855ecda435553a7bbdf.svg" width="340px" height="172px" + loading="lazy" />

              That might look unwieldy, but it's really just a mostly-zeroes @@ -1818,6 +1885,7 @@ function drawCurve(points[], t): src="./images/chapters/reordering/d52f60b331c1b8d6733eb5217adfbc4d.svg" width="288px" height="109px" + loading="lazy" />

              The steps taken here are:

                @@ -1893,6 +1961,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/eb4442acc5bc17f4649eb04b2953ed9b.svg" width="333px" height="44px" + loading="lazy" />

                which we can also write (observing that b in this formula is @@ -1905,6 +1974,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/2622790efa97f1915e7998787d8ce977.svg" width="343px" height="44px" + loading="lazy" />

                Or, in plain text: the derivative of an nth degree Bézier @@ -1931,6 +2001,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/e755c2adfec5d266c50e064407ca369b.svg" width="209px" height="36px" + loading="lazy" />

                Applying the @@ -1944,6 +2015,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/95a0cd4cc919a3fd5b192ffeb00c231e.svg" width="412px" height="28px" + loading="lazy" />

                Which is hard to work with, so let's expand that properly:

                Now, the trick is to turn this expression into something that has @@ -1964,6 +2037,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/6770214cceeb0e13e371bd908867751f.svg" width="545px" height="76px" + loading="lazy" />

                And that's the first part done: the two components inside the @@ -1974,6 +2048,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/fb823558e99662b24d46ae55ac93ce38.svg" width="533px" height="48px" + loading="lazy" />

                Now to apply this to our weighted Bézier curves. We'll write out @@ -1985,6 +2060,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/28991bba7c13698619f36b6261d91d68.svg" width="527px" height="112px" + loading="lazy" />

                If we expand this (with some color to show how terms line up), and @@ -1996,6 +2072,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/b7815b1502029ed9d805b6ba0801a53f.svg" width="300px" height="109px" + loading="lazy" />

                Two of these terms fall way: the first term falls away because @@ -2014,6 +2091,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/c7b13e6507450b3da7dc4ce3c10c370f.svg" width="295px" height="71px" + loading="lazy" />

                And that's just a summation of lower order curves:

                We can rewrite this as a normal summation, and we're done:

    @@ -2041,12 +2121,14 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg" width="352px" height="55px" + loading="lazy" />

    What are the differences? In terms of the actual Bézier curve, @@ -2062,6 +2144,7 @@ function drawCurve(points[], t): src="./images/chapters/derivatives/03967e3ecdbff78684995ca9c22a6106.svg" width="523px" height="73px" + loading="lazy" />

    We can keep performing this trick for as long as we have more than @@ -2091,6 +2174,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/d236b7b2ad46c8ced1b43bb2a496379a.svg" width="151px" height="40px" + loading="lazy" />

    This gives us the directional vector we want. We can normalize it to @@ -2103,12 +2187,14 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/6101b2f8b69ebabba4a2c88456a32aa0.svg" width="268px" height="29px" + loading="lazy" />

    The tangent is very useful for moving along a line, but what if we @@ -2124,6 +2210,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/2dd2f89d1c762991a86526490a3deef6.svg" width="341px" height="57px" + loading="lazy" />

    @@ -2147,6 +2234,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/deec095950fcd1f9c980be76a7093fe6.svg" width="179px" height="37px" + loading="lazy" />

    Which is the "long" version of the following matrix @@ -2157,6 +2245,7 @@ function drawCurve(points[], t): src="./images/chapters/pointvectors/2a55cb2d23c25408aa10cfd8db13278b.svg" width="205px" height="40px" + loading="lazy" />

    And that's all we need to rotate any coordinate. Note that for @@ -2611,6 +2700,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/6db78123d4b676ffdf85d53670c77468.svg" width="189px" height="64px" + loading="lazy" />

    And then we turn this into our solution for t using @@ -2621,6 +2711,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/1c0367fad2a0d6946db1f55a8520793a.svg" width="139px" height="77px" + loading="lazy" />

    Done.

    @@ -2646,6 +2737,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/0ec5cc72a428d75defb480530b50d720.svg" width="433px" height="37px" + loading="lazy" />

    So, if we can rewrite the Bézier component function as a plain @@ -2667,6 +2759,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/e06ec558d99b53e559d24524f4201951.svg" width="553px" height="37px" + loading="lazy" />

    And then, using these v values, we can find out what our @@ -2677,6 +2770,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/ddc6f99a543afad25c55cf16b9deeed9.svg" width="317px" height="112px" + loading="lazy" />

    This gives us three coefficients {a, b, c} that are expressed in @@ -2689,6 +2783,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/d9e66caeb45b6643112ce3d971b17e5b.svg" width="308px" height="64px" + loading="lazy" />

    Easy-peasy. We can now almost trivially find the roots by plugging @@ -2723,6 +2818,7 @@ function drawCurve(points[], t): src="./images/chapters/extremities/997a8cc704c0ab0e364cb8b532df90b0.svg" width="264px" height="41px" + loading="lazy" />

    We can see that the easier formula only has two constants, rather @@ -2935,6 +3031,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/extremities/c621cc41f6f22ee1beedbcb510fa5b6b.svg" width="139px" height="43px" + loading="lazy" />

    (The Wikipedia article has a decent animation for this process, so I @@ -3110,6 +3207,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/aligning/d480a9aa41917e5230d432cdbd6899b1.svg" width="487px" height="40px" + loading="lazy" />

    Then translating it so that the first coordinate lies on (0,0), @@ -3121,6 +3219,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/aligning/50679d61424222d7b6b97eb3aa663582.svg" width="471px" height="40px" + loading="lazy" />

    If we then rotate the curve so that its end point lies on the @@ -3132,6 +3231,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/aligning/a9af1c06a00bb3c4af816a138fb0a66d.svg" width="463px" height="40px" + loading="lazy" />

    If we drop all the zero-terms, this gives us:

    We can see that our original curve definition has been simplified @@ -3264,6 +3365,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/bafdb6583323bda71d9a15c02d1fdec2.svg" width="59px" height="17px" + loading="lazy" />

    What we're saying here is that given the curvature function @@ -3279,6 +3381,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/2029bca9f4fa15739553636af99b70a8.svg" width="399px" height="19px" + loading="lazy" />

    The function C(t) is the cross product between the first @@ -3310,6 +3413,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/4d78ebcf8626f777725d67d3672fa480.svg" width="613px" height="71px" + loading="lazy" />

    And of course the same functions for y:

    Asking a computer to now compose the C(t) function for us @@ -3328,6 +3433,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/b2433959e1f451fa3bf238fc37e04527.svg" width="557px" height="96px" + loading="lazy" />

    That is... unwieldy. So, we note that there are a lot of terms @@ -3348,6 +3454,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/1679090a942a43d27f886f236fc8d62b.svg" width="533px" height="19px" + loading="lazy" />

    That's a lot easier to work with: we see a fair number of terms that @@ -3359,6 +3466,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/4b5c7d0bf0fcd769db007dd98d4a024d.svg" width="480px" height="73px" + loading="lazy" />

    This is a plain quadratic curve, and we know how to solve @@ -3369,6 +3477,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/inflections/7c9762c0e04693eb743905cdc0487f8b.svg" width="428px" height="53px" + loading="lazy" />

    We can easily compute this value if the discriminator isn't @@ -3436,86 +3545,102 @@ function getCubicRoots(pa, pb, pc, pd) { then tell us what kind of curve we're dealing with. Specifically, we see the following breakdown:

    - + width="400" + height="400" + src="./chapters/canonical/canonical.js" + > + + + Scripts are disabled. Showing fallback image. +

    - This is a fairly funky image, so let's see how it breaks down. We - see the three fixed points at (0,0), (0,1) and (1,1), and then the - fourth point is somewhere. Depending on where it is, our curve will - have certain features. Namely, if the fourth point is... + This is a fairly funky image, so let's see what the various parts of + it mean... +

    +

    + We see the three fixed points at (0,0), (0,1) and (1,1). The various + regions and boundaries indicate what property the original curve + will have, if the fourth point is in/on that region or boundary. + Specifically, if the fourth point is...

    1. - anywhere on and in the red zone, the curve will either be - self-intersecting (yielding a loop), or it will have a sharp - discontinuity (yielding a cusp). Anywhere inside the red zone, - this will be a loop. We won't know where that loop is - (in terms of t values), but we are guaranteed that - there is one. + ...anywhere inside the red zone, but not on its boundaries, the + curve will either be self-intersecting (yielding a loop). We + won't know where it self-intersects (in terms of + t values), but we are guaranteed that it does.

    2. - on the left (red) edge, the curve will have a cusp. We again - don't know where, just that it has one. This edge is - described by the function: + ...on the left (red) edge of the red zone, the curve will have a + cusp. We again don't know where, but we know there is + one. This edge is described by the function:

    3. - on the lower right (pink) edge, the curve will have a loop at - t=1, so we know the end coordinate of the curve also lies - on the curve. This edge is described by the function: + ...on the almost circular, lower right (pink) edge, the curve's + end point touches the curve, forming a loop. This edge is + described by the function:

    4. - on the top (blue) edge, the curve will have a loop at t=0, so we - know the start coordinate of the curve also lies on the - curve. This edge is described by the function: + ...on the top (blue) edge, the curve's start point touches the + curve, forming a loop. This edge is described by the function:

    5. - inside the green zone, the curve will have a single inflection, - switching concave/convex once. + ...inside the lower (green) zone, past y=1, the + curve will have a single inflection (switching concave/convex + once).

    6. - between the red and green zones, the curve has two inflections, - meaning its curvature switches between concave/convex form - twice. + ...between the left and lower boundaries (below the cusp line + but above the single-inflection line), the curve will have two + inflections (switching from concave to convex and then back + again, or from convex to concave and then back again).

    7. - anywhere on the right of the red zone, the curve will have no - inflections. It'll just be a well-behaved arch. + ...anywhere on the right of self-intersection zone, the curve + will have no inflections. It'll just be a simple arch.

    @@ -3590,8 +3715,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Sweet! z stays 1, so we can effectively ignore it entirely, @@ -3601,8 +3727,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Running all our coordinates through this transformation gives a new @@ -3619,8 +3746,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    So we want some shearing value that, when multiplied by y, @@ -3630,8 +3758,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Now, running this on all our points generates a new set of @@ -3650,8 +3779,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Then, finally, this generates a new set of coordinates, let's call @@ -3670,7 +3800,8 @@ function getCubicRoots(pa, pb, pc, pd) { class="LaTeX SVG" src="./images/chapters/canonical/0430e8c7f7d4ec80e6527f96f3d56e5c.svg" width="140px" - height="65px" + height="63px" + loading="lazy" />

    And this generates our final set of four coordinates. Of these, we @@ -3682,8 +3813,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    That looks very complex, but notice that every coordinate value is @@ -3699,8 +3831,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    Suddenly things look a lot simpler: the mapped x is fairly straight @@ -3712,8 +3845,9 @@ function getCubicRoots(pa, pb, pc, pd) {

    That's kind of super-simple to write out in code, I think you'll @@ -3761,11 +3895,22 @@ function getCubicRoots(pa, pb, pc, pd) { can immediately tell which features our curve must have, based on where the fourth coordinate is located on the map:

    - + width="800" + height="400" + src="./chapters/canonical/interactive.js" + > + + + Scripts are disabled. Showing fallback image. +

    Finding Y, given X

    @@ -3817,6 +3962,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/yforx/9df91c28af38c1ba2e2d38d2714c9446.svg" width="335px" height="19px" + loading="lazy" />

    We can rewrite this to a plain polynomial form, by just fully @@ -3828,6 +3974,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/yforx/9ab2b830fe7fb73350c19bde04e9441b.svg" width="445px" height="19px" + loading="lazy" />

    Nothing special here: that's a standard cubic polynomial in "power" @@ -3842,6 +3989,7 @@ function getCubicRoots(pa, pb, pc, pd) { src="./images/chapters/yforx/de3bd3e271d72194c730d0ae44f031a8.svg" width="465px" height="19px" + loading="lazy" />

    You might be wondering "where did all the other 'minus x' for all @@ -3906,6 +4054,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/cb24cda7f7f4bbf3be7104c460e0ec9f.svg" width="140px" height="33px" + loading="lazy" />

    or, more commonly written using Leibnitz notation as:

    This formula says that the length of a parametric curve is in fact @@ -3963,6 +4113,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/e168758d35b8f6781617eda5a32b20bf.svg" width="636px" height="71px" + loading="lazy" />

    In plain text: an integral function can always be treated as the sum @@ -4038,6 +4189,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/5509919419288129322cfbd4c60d0a4f.svg" width="341px" height="72px" + loading="lazy" />

    That may look a bit more complicated, but the fraction involving @@ -4062,6 +4214,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/d0d93f1cc26b560309dade1f1aa012f2.svg" width="63px" height="93px" + loading="lazy" />

    Which means that in order for us to approximate the integral, we @@ -4073,6 +4226,7 @@ if (roots.length > 0) { src="./images/chapters/arclength/e96dd431f6ef9433ccf25909dddd5bca.svg" width="476px" height="44px" + loading="lazy" />

    We can program that pretty easily, provided we have that @@ -4250,6 +4404,7 @@ if (roots.length > 0) { src="./images/chapters/curvature/d9c893051586eb8d9de51c0ae1ef8fae.svg" width="113px" height="47px" + loading="lazy" />

    Which is really just a "short form" that glosses over the fact that @@ -4260,6 +4415,7 @@ if (roots.length > 0) { src="./images/chapters/curvature/828333034b4fed8e248683760d6bc6f4.svg" width="239px" height="55px" + loading="lazy" />

    And while that's a litte more verbose, it's still just as simple to @@ -4300,6 +4456,7 @@ if (roots.length > 0) { src="./images/chapters/curvature/6ed4fd2ead35c57984caddf9fe375a5f.svg" width="81px" height="37px" + loading="lazy" />

    So that's a rather convenient fact to know, too.

    @@ -4706,6 +4863,7 @@ lli = function(line1, line2): src="./images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg" width="197px" height="16px" + loading="lazy" />

    So that just leaves finding A.

    @@ -4718,6 +4876,7 @@ lli = function(line1, line2): src="./images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg" width="197px" height="87px" + loading="lazy" />

    So, if we know the start and end coordinates, and we know the @@ -4756,6 +4915,7 @@ lli = function(line1, line2): src="./images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg" width="251px" height="39px" + loading="lazy" />

    This leads to a pretty powerful bit of knowledge: merely by knowing @@ -4768,6 +4928,7 @@ lli = function(line1, line2): src="./images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg" width="217px" height="37px" + loading="lazy" />

    And that's it, all values found.

    @@ -4790,12 +4951,14 @@ lli = function(line1, line2): src="./images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg" width="245px" height="41px" + loading="lazy" />

    Unfortunately, this trick only works for quadratic and cubic @@ -4857,6 +5020,7 @@ lli = function(line1, line2): src="./images/chapters/moulding/7bba0a4fd605e023cda922de125b3e32.svg" width="221px" height="36px" + loading="lazy" />

    For quadratic curves, this means we're done, since the new point A' @@ -4892,6 +5056,7 @@ lli = function(line1, line2): src="./images/chapters/moulding/524206c49f317d27d8e07a310b24a7a3.svg" width="132px" height="75px" + loading="lazy" />

    And then we can compute the new control points:

    And that's cubic curve manipulation.

    @@ -5051,6 +5217,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/bd8e8e294eec10d2bf6ef857c7c0c2c2.svg" width="295px" height="43px" + loading="lazy" />

    And then we (trivially) rearrange the terms across multiple lines: @@ -5060,6 +5227,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/2b8334727d3b004c6e87263fec6b32b7.svg" width="216px" height="64px" + loading="lazy" />

    This rearrangement has "factors of t" at each row (the first row @@ -5077,6 +5245,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/94acb5850778dcb16c2ba3cfa676f537.svg" width="572px" height="53px" + loading="lazy" />

    We can do the same for the cubic curve, of course. We know the @@ -5087,6 +5256,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/7eada6f12045423de24d9a2ab8e293b1.svg" width="355px" height="19px" + loading="lazy" />

    So we write out the expansion and rearrange:

    Which we can then decompose:

    And, of course, we can do this for quartic curves too (skipping @@ -5111,6 +5283,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/9151c0fdf9689ee598a2d029ab2ffe34.svg" width="491px" height="92px" + loading="lazy" />

    And so and on so on. Now, let's see how to use these @@ -5131,6 +5304,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/2bef3da3828d63d690460ce9947dbde2.svg" width="63px" height="73px" + loading="lazy" />

    Next, we need to figure out appropriate t values for @@ -5175,6 +5349,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/78b8ba1aba2e4c9ad3f7890299c90152.svg" width="395px" height="40px" + loading="lazy" />

    Where length() is literally just that: the length of @@ -5189,6 +5364,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/08f4beaebf83dca594ad125bdca7e436.svg" width="272px" height="55px" + loading="lazy" />

    And now we can move on to the actual "curve fitting" part: what we @@ -5209,6 +5385,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/7e5d59272621baf942bc722208ce70c2.svg" width="177px" height="23px" + loading="lazy" />

    Since this function only deals with individual coordinates, we'll @@ -5221,6 +5398,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/ab334858d3fa309cc1a5ba535a2ca168.svg" width="195px" height="41px" + loading="lazy" />

    And here's the trick that justifies using matrices: while we can @@ -5236,6 +5414,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/2d42758fba3370f52191306752c2705c.svg" width="141px" height="21px" + loading="lazy" />

    In which we can replace the rather cumbersome "squaring" operation @@ -5246,6 +5425,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/5f7fcb86ae1c19612b9fe02e23229e31.svg" width="225px" height="21px" + loading="lazy" />

    Here, the letter T is used instead of the number 2, to @@ -5269,6 +5449,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/6202d7bd150c852b432d807c40fb1647.svg" width="201px" height="96px" + loading="lazy" />

    Which, because of the first and last values in S, @@ -5279,6 +5460,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/4ffad56e281ee79d0688e93033429f0a.svg" width="212px" height="92px" + loading="lazy" />

    Now we can properly write out the error function as matrix @@ -5289,6 +5471,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/8d09f2be2c6db79ee966f170ffc25815.svg" width="231px" height="21px" + loading="lazy" />

    So, we have our error function: we now need to figure out the @@ -5303,6 +5486,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/283bc9e8fe59a78d3c74860f62a66ecb.svg" width="197px" height="36px" + loading="lazy" />

    Where did this derivative come from?

    @@ -5344,6 +5528,7 @@ lli = function(line1, line2): src="./images/chapters/curvefitting/d84d1c71a3ce1918f53eaf8f9fe98ac4.svg" width="168px" height="27px" + loading="lazy" />

    Here, the "to the power negative one" is the notation for the @@ -5456,6 +5641,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/9215d05705c8e8a7ebd718ae6f690371.svg" width="409px" height="75px" + loading="lazy" />

    However, there's something funny going on here: the coordinate @@ -5495,6 +5681,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/1811b59c5ab9233f08590396e5d03303.svg" width="187px" height="83px" + loading="lazy" />

    This mapping says that in order to map a Catmull-Rom "point + @@ -5510,6 +5697,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/4d524810417b4caffedd13af23135f5b.svg" width="591px" height="83px" + loading="lazy" />

    Thus:

    However, we're not quite done, because Catmull-Rom curves @@ -5533,6 +5722,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/06ae1e3fdc660e59d618e0760e8e9ab5.svg" width="285px" height="84px" + loading="lazy" />

    With the mapping matrix properly done, let's rewrite the "point + @@ -5544,6 +5734,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/cc1e2ff43350c32f0ae9ba9a7652b8fb.svg" width="409px" height="75px" + loading="lazy" />

    Replace point/tangent vector with the expression for @@ -5554,6 +5745,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/f08e34395ce2812276fd70548f805041.svg" width="549px" height="81px" + loading="lazy" />

    and merge the matrices:

    This looks a lot like the Bézier matrix form, which as we saw in @@ -5571,6 +5764,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/8f56909fcb62b8eef18b9b9559575c13.svg" width="353px" height="73px" + loading="lazy" />

    So, if we want to express a Catmull-Rom curve using a Bézier @@ -5581,6 +5775,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/b21386f86bef8894f108c5441dad10de.svg" width="227px" height="84px" + loading="lazy" />

    Into something that looks like this:

    And the way we do that is with a fairly straight forward bit of @@ -5598,6 +5794,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/a47b072a325812ac4f0ff52c22792588.svg" width="440px" height="84px" + loading="lazy" />

    Then we remove the coordinate vector from both sides without @@ -5608,6 +5805,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/841fb6a2a035c9bcf5a2d46f2a67709b.svg" width="353px" height="84px" + loading="lazy" />

    Then we can "get rid of" the Bézier matrix on the right by @@ -5618,6 +5816,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/cbdd46d5e2e1a6202ef46fb03711ebe4.svg" width="657px" height="88px" + loading="lazy" />

    A matrix times its inverse is the matrix equivalent of 1, and @@ -5629,6 +5828,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/3ea54fe939d076f8db605c5b480e7db0.svg" width="369px" height="88px" + loading="lazy" />

    And now we're basically done. We just multiply those two @@ -5639,6 +5839,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/169fd85a95e4d16fe289a75583017a11.svg" width="161px" height="77px" + loading="lazy" />

    We now have the final piece of our function puzzle. Let's run @@ -5652,6 +5853,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/cc1e2ff43350c32f0ae9ba9a7652b8fb.svg" width="409px" height="75px" + loading="lazy" />

    1. rewrite to pure coordinate form:
    2. @@ -5661,6 +5863,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/f814bb8d627f9c8f33b347c1cf13d4c7.svg" width="324px" height="84px" + loading="lazy" />
      1. rewrite for "normal" coordinate vector:
      2. @@ -5670,6 +5873,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/5f2750de827497375d9a915f96686885.svg" width="441px" height="81px" + loading="lazy" />
        1. merge the inner matrices:
        2. @@ -5679,6 +5883,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/79e333cd0c569657eea033b04fb5e61b.svg" width="348px" height="84px" + loading="lazy" />
          1. rewrite for Bézier matrix form:
          2. @@ -5688,6 +5893,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/1b8a782f7540503d38067317e4cd00b0.svg" width="431px" height="77px" + loading="lazy" />
            1. @@ -5700,6 +5906,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/e3d30ab368dcead1411532ce3814d3f3.svg" width="348px" height="81px" + loading="lazy" />

              And we're done: we finally know how to convert these two curves! @@ -5718,6 +5925,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/ba31c32eba62f1e3b15066cd5ddda597.svg" width="249px" height="85px" + loading="lazy" />

              Similarly, if we have a Bézier curve defined by four coordinates @@ -5730,6 +5938,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/eae7f01976e511ee38b08b6edc8765d2.svg" width="284px" height="77px" + loading="lazy" />

              or, if your API requires specifying Catmull-Rom curves using "point @@ -5740,6 +5949,7 @@ lli = function(line1, line2): src="./images/chapters/catmullconv/26363fc09f8cf2d41ea5b4256656bb6d.svg" width="284px" height="77px" + loading="lazy" />

              @@ -5852,6 +6062,7 @@ lli = function(line1, line2): src="./images/chapters/polybezier/408dd95905a5f001179c4da6051e49c5.svg" width="124px" height="17px" + loading="lazy" />

              We can effect this quite easily, because we know that the vector @@ -5866,6 +6077,7 @@ lli = function(line1, line2): src="./images/chapters/polybezier/8c1b570b3efdfbbc39ddedb4adcaaff6.svg" width="304px" height="40px" + loading="lazy" />

              So let's implement that and see what it gets us. The following two @@ -6228,6 +6440,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/1d4be24e5896dce3c16c8e71f9cc8881.svg" width="108px" height="16px" + loading="lazy" />

              However, we're working in 2D, and d is a single @@ -6244,6 +6457,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/5bfee4f2ae27304475673d0596e42f9a.svg" width="151px" height="16px" + loading="lazy" />

              Now this still isn't very useful unless we know what the formula @@ -6263,6 +6477,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/fa6c243de2aa78b7451e0086848dfdfc.svg" width="120px" height="40px" + loading="lazy" />

              Determining the length requires computing an arc length, and this @@ -6276,6 +6491,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/b262e50c085815421d94e120fc17f1c8.svg" width="169px" height="36px" + loading="lazy" />

              So if we want the length of the tangent, we plug in @@ -6287,6 +6503,7 @@ lli = function(line1, line2): src="./images/chapters/offsetting/1d586b939b44ff9bdb42562a12ac2779.svg" width="209px" height="36px" + loading="lazy" />

              And that's where things go wrong. It doesn't even really matter @@ -6524,6 +6741,7 @@ lli = function(line1, line2): src="./images/chapters/circles/8374c4190d6213b0ac0621481afaa754.svg" width="175px" height="40px" + loading="lazy" />

              What we want to find is the intersection of the tangents, so we want @@ -6534,6 +6752,7 @@ lli = function(line1, line2): src="./images/chapters/circles/a127f926eced2751a09c54bf7c361b4a.svg" width="284px" height="40px" + loading="lazy" />

              i.e. we want a point that lies on the vertical line through S (at @@ -6546,6 +6765,7 @@ lli = function(line1, line2): src="./images/chapters/circles/b5d864e9ed0c44c56d454fbaa4218d5e.svg" width="219px" height="40px" + loading="lazy" />

              First we solve for b:

              which yields:

              which we can then substitute in the expression for a:

              A quick check shows that plugging these values for a and @@ -6583,6 +6806,7 @@ lli = function(line1, line2): src="./images/chapters/circles/fe32474b4616ee9478e1308308f1b6bf.svg" width="188px" height="32px" + loading="lazy" />

              We compute T, observing that if t=0.5, the polynomial @@ -6593,6 +6817,7 @@ lli = function(line1, line2): src="./images/chapters/circles/e1059e611aa1e51db41f9ce0b4ebb95a.svg" width="252px" height="35px" + loading="lazy" />

              Which, worked out for the x and y components, gives:

              And the distance between these two is the standard Euclidean @@ -6610,6 +6836,7 @@ lli = function(line1, line2): src="./images/chapters/circles/adbd056f4b8fcd05b1d4f2fce27d7657.svg" width="399px" height="153px" + loading="lazy" />

              So, what does this distance function look like when we plot it for a @@ -6665,6 +6892,7 @@ lli = function(line1, line2): src="./images/chapters/circles/df87674db0f31fc3944aaeb6b890e196.svg" width="247px" height="53px" + loading="lazy" />

              And frankly, things are starting to look a bit ridiculous at this @@ -6812,6 +7040,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/a4f0dafbfe80c88723c3cc22277a9682.svg" width="175px" height="40px" + loading="lazy" />

              But we now need to find two control points, rather than one. If we @@ -6825,6 +7054,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/dfb83eec053c30e0a41b0a52aba24cd4.svg" width="113px" height="40px" + loading="lazy" />

              where "a" is some scaling factor, and:

              where "b" is also some scaling factor.

              @@ -6889,6 +7120,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/3189cac1ddac07c1487e1e51740ecc88.svg" width="397px" height="40px" + loading="lazy" />

              So that just leaves us to find the distance from t=0.5 to @@ -6903,6 +7135,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/fe32474b4616ee9478e1308308f1b6bf.svg" width="188px" height="32px" + loading="lazy" />

              And the distance from the origin to the line start/end is another @@ -6916,6 +7149,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/0364731626a530c8a9b30f424ada53c5.svg" width="261px" height="67px" + loading="lazy" />

              With the coordinate C, and knowledge of coordinate B, we can @@ -6927,12 +7161,14 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/ee08d86b7497c7ab042ee899bf15d453.svg" width="397px" height="48px" + loading="lazy" />

              Which means we can now determine the distance {start,guessed}, @@ -6944,6 +7180,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/178a838274748439778e2a29f5a27d0b.svg" width="252px" height="56px" + loading="lazy" />

              And after this tedious detour to find the coordinate for @@ -6955,6 +7192,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/49dbf244d50c787a4ab18694488d9b69.svg" width="524px" height="79px" + loading="lazy" />

              And that's it, we have all four points now for an approximation of @@ -6970,6 +7208,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/e2258660a796dcd6189a6f5e14326dad.svg" width="205px" height="40px" + loading="lazy" />

              and

              And, because the "quarter curve" special case comes up so incredibly @@ -6988,6 +7228,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/877f9c217c51c0087be751a7580ed459.svg" width="412px" height="33px" + loading="lazy" />

              Which, in decimal values, rounded to six significant digits, is: @@ -6997,6 +7238,7 @@ lli = function(line1, line2): src="./images/chapters/circles_cubic/05d36e051a38905dcb81e65db8261f24.svg" width="412px" height="16px" + loading="lazy" />

              Of course, this is for a circle with radius 1, so if you have a @@ -7283,6 +7525,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/0f3451c711c0fe5d0b018aa4aa77d855.svg" width="169px" height="41px" + loading="lazy" />

              Which, honestly, doesn't tell us all that much. All we can see is @@ -7312,6 +7555,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/cf45d1ea00d4866abc8a058b130299b4.svg" width="559px" height="43px" + loading="lazy" />

              So this is where we see the interpolation: N(t) for an (i,k) pair @@ -7328,6 +7572,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/adac18ea69cc58e01c8d5e15498e4aa6.svg" width="240px" height="40px" + loading="lazy" />

              And this function finally has a straight up evaluation: if a @@ -7362,6 +7607,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/763838ea6f9e6c6aa63ea5f9c6d9542f.svg" width="281px" height="21px" + loading="lazy" />

              This is another recursive function, with k values @@ -7373,6 +7619,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/892209dad8fd1f839470dd061e870913.svg" width="255px" height="39px" + loading="lazy" />

              That looks complicated, but it's not. Computing alpha is just a @@ -7389,6 +7636,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/4c8f9814c50c708757eeb5a68afabb7f.svg" width="368px" height="40px" + loading="lazy" />

              So, we see two stopping conditions: either i becomes 0, @@ -7407,6 +7655,7 @@ lli = function(line1, line2): src="./images/chapters/bsplines/7962d6fea86da6f53a7269fba30f0138.svg" width="417px" height="231px" + loading="lazy" />

              That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2): d(3,3) diff --git a/package.json b/package.json index 4997cc55..e8fdc0ce 100644 --- a/package.json +++ b/package.json @@ -16,24 +16,28 @@ "scripts": { "start": "cls && run-s clean time lint:* build time && rm -f .timing", "test": "run-s start && run-p watch server browser", - "---": "---note that the public dir is called 'docs', not 'public'---", + "------": "--- note that due to github's naming policy, the public dir is called 'docs' rather than 'public' ---", "browser": "open-cli http://localhost:8000", "build": "node ./src/build.js", - "clean": "rm -f .timing && rm -rf ./src/build/temp", + "clean": "rm -f .timing && rm -rf ./temp", "lint:tools": "prettier ./src/**/*.js --write", "lint:lib": "prettier ./docs/js/**/*.js --write", "server": "cd docs && http-server -p 8000 --cors", "watch": "run-p watch:*", - "watch:markdown": "chokidar \"./chapters/**/*.md\" -c \"npm start\"", - "watch:sketches": "chokidar \"./chapters/**/*.js\" -c \"npm start\"", + "watch:chapters": "chokidar \"./docs/chapters/**/*.*\" -c \"npm run build\"", + "watch:customelement": "chokidar \"./docs/js/custom-element/**/*.js\" -c \"npm run build\"", + "watch:src": "chokidar \"./src/**/*.*\" -c \"npm run build\"", "time": "node ./src/mark.js" }, "keywords": [ "Bezier", "curves", + "math", + "maths", "tutorial", "article", "primer", + "book", "ebook" ], "devDependencies": { diff --git a/src/build/latex/latex-to-svg.js b/src/build/latex/latex-to-svg.js index 58611961..86cc7b78 100644 --- a/src/build/latex/latex-to-svg.js +++ b/src/build/latex/latex-to-svg.js @@ -133,9 +133,12 @@ export default async function latexToSVG(latex, chapter, localeStrings, block) { var w = Math.round(((parseFloat(vb[2]) - parseFloat(vb[0])) * 4) / 3); var h = Math.round(((parseFloat(vb[3]) - parseFloat(vb[1])) * 4) / 3); - return ``; + // Update the SVG's presentation size to use pixels + svg = svg.replace(`width="${vb[2]}pt"`, `width="${w}px"`); + svg = svg.replace(`height="${vb[3]}pt"`, `height="${h}px"`); + fs.writeFileSync(SVGfilename, svg, `utf8`); + + return ``; } // This function really needs better stdio capture, diff --git a/src/build/markdown/generate-graphics-module.js b/src/build/markdown/generate-graphics-module.js index 1926c1ee..88c02334 100644 --- a/src/build/markdown/generate-graphics-module.js +++ b/src/build/markdown/generate-graphics-module.js @@ -4,13 +4,9 @@ import prettier from "prettier"; import splitCodeSections from "../../../docs/js/custom-element/lib/split-code-sections.js"; import performCodeSurgery from "../../../docs/js/custom-element/lib/perform-code-surgery.js"; -// Note that this location is relative to the temp dir, from which sketch modules get loaded. -const thisModuleURL = new URL(import.meta.url); -const thisModuleDir = path.dirname(thisModuleURL.href.replace(`file:///`, ``)); - const GRAPHICS_API_LOCATION = path .join( - path.relative(thisModuleDir, paths.public), + path.relative(paths.temp, paths.public), `js`, `custom-element`, `api`, @@ -20,7 +16,7 @@ const GRAPHICS_API_LOCATION = path .join(path.posix.sep); const RELATIVE_IMPORT_LOCATION = path - .relative(thisModuleDir, paths.chapters) + .relative(paths.temp, paths.chapters) .split(path.sep) .join(path.posix.sep); @@ -41,11 +37,12 @@ function generateGraphicsModule(chapter, code, width, height) { return prettier.format( ` import CanvasBuilder from 'canvas'; - import { GraphicsAPI, Bezier, Vector, Matrix } from "${GRAPHICS_API_LOCATION}"; + import { GraphicsAPI, Bezier, Vector, Matrix, Shape } from "${GRAPHICS_API_LOCATION}"; ${globalCode} const noop = (()=>{}); + const Image = CanvasBuilder.Image; class Example extends GraphicsAPI { ${classCode} } diff --git a/src/index.template.html b/src/index.template.html index e948bfc5..e0d82aa4 100644 --- a/src/index.template.html +++ b/src/index.template.html @@ -47,6 +47,9 @@ + + + diff --git a/src/locale-strings.js b/src/locale-strings.js index 6548b3e1..81754df6 100644 --- a/src/locale-strings.js +++ b/src/locale-strings.js @@ -1,51 +1,55 @@ const defaultLocale = "en-GB"; +// ============================= +// Locale strings start here +// ============================= + const localeStringData = { title: { - "en-GB": "A Primer on Bézier Curves", - "ja-JP": "ベジェ曲線入門", - "zh-CN": "贝塞尔曲线底漆", + "en-GB": `A Primer on Bézier Curves`, + "ja-JP": `ベジェ曲線入門`, + "zh-CN": `贝塞尔曲线底漆`, }, subtitle: { - "en-GB": - "A free, online book for when you really need to know how to do Bézier things.", + "en-GB": `A free, online book for when you really need to know how to do Bézier things.`, }, description: { - "en-GB": - "A detailed explanation of Bézier curves, and how to do the many things that we commonly want to do with them.", + "en-GB": `A detailed explanation of Bézier curves, and how to do the many things that we commonly want to do with them.`, }, tocLabel: { - "en-GB": "Table of Contents", - "ja-JP": "目次", - "zh-CN": "目录", + "en-GB": `Table of Contents`, + "ja-JP": `目次`, + "zh-CN": `目录`, }, localeName: { - "en-GB": "English", - "zh-CN": "中文", - "ja-JP": "日本語", + "en-GB": `English`, + "zh-CN": `中文`, + "ja-JP": `日本語`, }, langSwitchLabel: { - "en-GB": "Read this in your own language:", + "en-GB": `Read this in your own language:`, }, langHelpLabel: { - "en-GB": - 'Don\'t see your language listed? Help translate this content!', + "en-GB": `Don't see your language listed? Help translate this content!`, }, disabledMessage: { - "en-GB": "Scripts are disabled. Showing fallback image.", + "en-GB": `Scripts are disabled. Showing fallback image.`, }, changelogTitle: { - "en-GB": "What's new?", + "en-GB": `What's new?`, }, toggleLabel: { - "en-GB": "Toggle changes", + "en-GB": `Toggle changes`, }, changelogDescription: { - "en-GB": - "This primer is a living document, and so depending on when you last look at it, there may be new content. Click the following link to expand this section to have a look at what got added, when.", + "en-GB": `This primer is a living document, and so depending on when you last look at it, there may be new content. Click the following link to expand this section to have a look at what got added, when.`, }, }; +// ============================= +// Locale strings end here +// ============================= + class LocaleStrings { constructor(locale) { this.currentLocale = locale; diff --git a/src/project-paths.js b/src/project-paths.js index 7508336d..8d1e6ecd 100644 --- a/src/project-paths.js +++ b/src/project-paths.js @@ -16,7 +16,7 @@ const publicDir = path.join(project, `docs`); // yeah... "docs". Because Github const images = path.join(publicDir, `images`); const build = path.join(src, `build`); const chapters = path.join(publicDir, `chapters`); -const temp = path.join(build, `temp`); +const temp = path.join(project, `temp`); const paths = { project,