From ebe69a732a451ae97c4d2ed7135efa532f7ea504 Mon Sep 17 00:00:00 2001 From: Pomax Date: Mon, 7 Sep 2020 13:10:20 -0700 Subject: [PATCH] . --- docs/chapters/bsplines/basic.js | 30 +- docs/chapters/bsplines/content.en-GB.md | 93 +- docs/chapters/bsplines/interpolation.js | 53 +- docs/chapters/bsplines/rational-uniform.js | 2 +- .../4b5d220d02b08f6c9aa19389255ef8bb.png | Bin 10668 -> 10651 bytes .../4bffba7dda2a3556cf5b2ae7392083c6.png | Bin 10293 -> 10293 bytes .../dc74a2f2da19470b8d721ece5f3ce268.png | Bin 9933 -> 9914 bytes .../3f66c97dcdd56d201c2acda3bd403bbf.png | Bin 24410 -> 0 bytes ...g => 61f28e4b071beeaf755f8826c529fe0a.png} | Bin .../7962d6fea86da6f53a7269fba30f0138.svg | 535 ---------- .../ac9e15a627ed2bc88c5e1b73147b7991.png | Bin 0 -> 23665 bytes .../bd187c361b285ef878d0bc17af8a3900.svg | 993 ++++++++++++++++++ ...g => c7fe4f9cba8d4bc06436f4f238d202ce.png} | Bin ...g => e50d628696245ef68b691e28b2162d56.png} | Bin .../81e559a69298ecc24c8edebe59e79647.png | Bin 12102 -> 11823 bytes docs/index.html | 56 +- docs/ja-JP/index.html | 56 +- docs/js/custom-element/api/graphics-api.js | 32 +- docs/js/custom-element/api/types/bezier.js | 1 - docs/js/custom-element/graphics-element.js | 20 +- docs/placeholder-style.css | 2 +- docs/zh-CN/index.html | 56 +- .../markdown/generate-graphics-module.js | 70 +- src/project-paths.js | 2 + 24 files changed, 1288 insertions(+), 713 deletions(-) delete mode 100644 docs/images/chapters/bsplines/3f66c97dcdd56d201c2acda3bd403bbf.png rename docs/images/chapters/bsplines/{d9a99344a5de4b5be77824f8f8caa707.png => 61f28e4b071beeaf755f8826c529fe0a.png} (100%) delete mode 100644 docs/images/chapters/bsplines/7962d6fea86da6f53a7269fba30f0138.svg create mode 100644 docs/images/chapters/bsplines/ac9e15a627ed2bc88c5e1b73147b7991.png create mode 100644 docs/images/chapters/bsplines/bd187c361b285ef878d0bc17af8a3900.svg rename docs/images/chapters/bsplines/{bd03680e4661ae7f4d687b2f229d2495.png => c7fe4f9cba8d4bc06436f4f238d202ce.png} (100%) rename docs/images/chapters/bsplines/{dda4911d5148032e005a02ef33b78978.png => e50d628696245ef68b691e28b2162d56.png} (100%) diff --git a/docs/chapters/bsplines/basic.js b/docs/chapters/bsplines/basic.js index 000fe549..a25606f4 100644 --- a/docs/chapters/bsplines/basic.js +++ b/docs/chapters/bsplines/basic.js @@ -11,6 +11,9 @@ setup() { {x:560,y:170} ]; setMovable(points); + if (this.parameters.showCurves) { + addSlider(`highlight`, `highlight`, 1, 4, 1, 1); + } } draw() { @@ -26,13 +29,19 @@ draw() { line(p.x, p.y, n.x, n.y); } - setColor(`black`); + this.drawSplineData(); + points.forEach((p,i) => { - circle(p.x, p.y, 3) + if (this.parameters.showCurves) { + if (this.highlight - 1 <= i && i <= this.highlight + 2) { + setColor(`red`); + circle(p.x, p.y, 5); + } + } + setColor(`black`); + circle(p.x, p.y, 3); text(`${i+1}`, p.x+5, p.y+5); }); - - this.drawSplineData(); } drawSplineData() { @@ -41,15 +50,19 @@ drawSplineData() { if (this.parameters.showCurves) { for(let i=0; i vertex(p.x, p.y)); end(); @@ -62,6 +75,9 @@ onMouseDown() { y: this.cursor.y }); resetMovable(points); + if (this.parameters.showCurves) { + updateSlider(`highlight`, 1, points.length-3, 1, 1); + } redraw(); } } diff --git a/docs/chapters/bsplines/content.en-GB.md b/docs/chapters/bsplines/content.en-GB.md index 98b099fb..7a0a5019 100644 --- a/docs/chapters/bsplines/content.en-GB.md +++ b/docs/chapters/bsplines/content.en-GB.md @@ -2,7 +2,7 @@ No discussion on Bézier curves is complete without also giving mention of that other beast in the curve design space: B-Splines. Easily confused to mean Bézier splines, that's not actually what they are; they are "basis function" splines, which makes a lot of difference, and we'll be looking at those differences in this section. We're not going to dive as deep into B-Splines as we have for Bézier curves (that would be an entire primer on its own) but we'll be looking at how B-Splines work, what kind of maths is involved in computing them, and how to draw them based on a number of parameters that you can pick for individual B-Splines. -First off: B-Splines are [piecewise polynomial interpolation curves](https://en.wikipedia.org/wiki/Piecewise), where the "single curve" is built by performing polynomial interpolation over a set of points, using a sliding window of a fixed number of points. For instance, a "cubic" B-Spline defined by twelve points will have its curve built by evaluating the polynomial interpolation of four points, and the curve can be treated as a lot of different sections, each controlled by four points at a time, such that the full curve consists of smoothly connected sections defined by points {1,2,3,4}, {2,3,4,5}, ..., {8,9,10,11}, and finally {9,10,11,12}, for eight sections. +First off: B-Splines are [piecewise](https://en.wikipedia.org/wiki/Piecewise), [polynomial interpolation curves](https://en.wikipedia.org/wiki/Spline_(mathematics)), where the "single curve" is built by performing polynomial interpolation over a set of points, using a sliding window of a fixed number of points. For instance, a "cubic" B-Spline defined by twelve points will have its curve built by evaluating the polynomial interpolation of four points, and the curve can be treated as a lot of different sections, each controlled by four points at a time, such that the full curve consists of smoothly connected sections defined by points {1,2,3,4}, {2,3,4,5}, ..., {8,9,10,11}, and finally {9,10,11,12}, for eight sections. What do they look like? They look like this! Tap on the graphic to add more points, and move points around to see how they map to the spline curve drawn. @@ -15,9 +15,11 @@ Consider the difference to be this: - for Bézier curves, the curve is defined as an interpolation of points, but: - for B-Splines, the curve is defined as an interpolation of *curves*. -In fact, let's look at that again, but this time with the base curves shown, too. Each consecutive four points defined one curve: +In fact, let's look at that again, but this time with the base curves shown, too. Each consecutive four points define one curve: - + + + In order to make this interpolation of curves work, the maths is necessarily more complex than the maths for Bézier curves, so let's have a look at how things work. @@ -69,7 +71,9 @@ This is another recursive function, with *k* values decreasing from the curve or \alpha_{i,k} = \frac{t - knots[i]}{knots[i+1+n-k] - knots[i]} \] -That looks complicated, but it's not. Computing alpha is just a fraction involving known, plain numbers and once we have our alpha value, computing (1-alpha) is literally just "computing one minus alpha". Computing this d() function is thus simply a matter of "computing simple arithmetics but with recursion", which might be computationally expensive because we're doing "a lot of" steps, but is also computationally cheap because each step only involves very simple maths. Of course as before the recursion has to stop: +That looks complicated, but it's not. Computing alpha is just a fraction involving known, plain numbers. And, once we have our alpha value, we also have `(1-alpha)` because it's a trivial subtraction. Computing the `d()` function is thus mostly a matter of computing pretty simple arithmetical statements, with some caching of results so we can refer to them as we recurve. While the recursion might see computationally expensive, the total algorithm is cheap, as each step only involves very simple maths. + +Of course, the recursion does need a stop condition: \[ d^k_0(t) = 0, \ d^0_i(t) = N_{i,1}(t) = @@ -79,46 +83,69 @@ That looks complicated, but it's not. Computing alpha is just a fraction involvi \end{matrix}\right. \] -So, we see two stopping conditions: either `i` becomes 0, in which case d() is zero, or `k` becomes zero, in which case we get the same "either 1 or 0" that we saw in the N() function above. +So, we actually see two stopping conditions: either `i` becomes 0, in which case `d()` is zero, or `k` becomes zero, in which case we get the same "either 1 or 0" that we saw in the N() function above. -Thanks to Cox and de Boor, we can compute points on a B-Spline pretty easily: we just need to compute a triangle of interconnected values. For instance, d() for i=3, k=3 yields the following triangle: +Thanks to Cox and de Boor, we can compute points on a B-Spline pretty easily using the same kind of linear interpolation we saw in de Casteljau's algorithm. For instance, if we write out `d()` for `i=3` and `k=3`, we get the following recursion diagram: \[ - \begin{array}{ccccccc} - d^3_3 &→& d^2_3 &→& d^1_3 &→& d^0_3 (= 0 \text{ or } 1) \\ - &+^{α^3_3 \times …}_{(1-{α^3_3}) \times …}& &+^{α^2_3 \times …}_{(1-{α^2_3}) \times …}& &+^{α^1_3 \times …}_{(1-{α^1_3}) \times …}&\\ - &↘& &↘& &↘& \\ - & & d^2_2 &→& d^1_2 &→& d^0_2 (= 0 \text{ or } 1) \\ - & & &+^{α^2_2 \times …}_{(1-{α^2_2}) \times …}& &+^{α^1_2 \times …}_{(1-{α^1_2}) \times …}&\\ - & & &↘& &↘& \\ - & & & & d^1_1 &→& d^0_1 (= 0 \text{ or } 1) \\ - & & & & &+^{α^1_1 \times …}_{(1-{α^1_1}) \times …}&\\ - & & & & &↘& \\ - & & & & & & d^0_0 (= 0) - \end{array} + d^3_3 = \left \{ + \begin{aligned} + \alpha^3_3 \times d^2_3, & \ \textit{ with } d^2_3 = \left \{ + \begin{aligned} + \alpha^2_3 \times d^1_3, & \ \textit{ with } d^1_3 = + \left \{ + \begin{aligned} + \alpha^1_3 \times d^0_3, & \ \textit{ with } d^0_3 \textit{ either 0 or 1} \\ + + & \\ + \left ( 1 - \alpha^1_3 \right ) \times d^0_2, & \ \textit{ with } d^0_2 \textit{ either 0 or 1} \\ + \end{aligned} + \right . \\ + + & \\ + \left ( 1 - \alpha^2_3 \right ) \times d^1_2, & \ \textit{ with } d^1_2 = + \left \{ + \begin{aligned} + \alpha^1_2 \times d^0_2 & \\ + + & \\ + \left ( 1 - \alpha^1_2 \right ) \times d^0_1, & \ \textit{ with } d^0_1 \textit{ either 0 or 1} \\ + \end{aligned} + \right . \\ + \end{aligned} + \right . \\ + + & \\ + \left ( 1 - \alpha^3_3 \right ) \times d^2_2, & \ \textit{ with } d^2_2 = \left \{ + \begin{aligned} + \alpha^2_2 \times d^1_2 & \\ + & \\ + + & \\ + \left ( 1 - \alpha^2_2 \right ) \times d^1_1, & \ \textit{ with } d^1_1 = + \left \{ + \begin{aligned} + \alpha^1_1 \times d^0_1 \\ + + & \\ + \left ( 1 - \alpha^1_1 \right ) \times d^0_0, & \ \textit{ with } d^0_0 \textit{ either 0 or 1} \\ + \end{aligned} + \right . \\ + \end{aligned} + \right . + \end{aligned} + \right . \] -That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2): d(3,3) = a(3,3) x d(2,3) + (1-a(3,3)) x d(2,2)... and we simply keep expanding our triangle until we reach the terminating function parameters. Done deal! - -One thing we need to keep in mind is that we're working with a spline that is constrained by its control points, so even though the `d(..., k)` values are zero or one at the lowest level, they are really "zero or one, times their respective control point", so in the next section you'll see the algorithm for running through the computation in a way that starts with a copy of the control point vector and then works its way up to that single point: that's pretty essential! - -If we run this computation "down", starting at d(3,3), then without special code in place we would be computing quite a few terms multiple times at each step. On the other hand, we can also start with that last "column", we can generate the terminating d() values first, then compute the a() constants, perform our multiplications, generate the previous step's d() values, compute their a() constants, do the multiplications, etc. until we end up all the way back at the top. If we run our computation this way, we don't need any explicit caching, we can just "recycle" the list of numbers we start with and simply update them as we move up the triangle. So, let's implement that! +That is, we compute `d(3,3)` as a mixture of `d(2,3)` and `d(2,2)`, where those two are themselves a mixture of `d(1,3)` and `d(1,2)`, and `d(1,2)` and `d(1,1)`, respectively, which are themselves a mixture of etc. etc. We simply keep expanding our terms until we reach the stop conditions, and then sum everything back up. It's really quite elegant. +One thing we need to keep in mind is that we're working with a spline that is constrained by its control points, so even though the `d(..., k)` values are zero or one at the lowest level, they are really "zero or one, times their respective control point", so in the next section you'll see the algorithm for running through the computation in a way that starts with a copy of the control point vector and then works its way up to that single point, rather than first starting "on the left", working our way "to the right" and then summing back up "to the left". We can just start on the right and work our way left immediately. + - - - - + Changing the values in the knot vector changes how much each point influences the total curvature (with some clever knot value manipulation, we can even make the influence of certain points disappear entirely!), so we can see that while the control points define the hull inside of which we're going to be drawing a curve, it is actually the knot vector that determines the actual *shape* of the curve inside that hull. After reading the rest of this section you may want to come back here to try some specific knot vectors, and see if the resulting interpolation landscape makes sense given what you will now think should happen! - +--> ## Running the computation @@ -172,7 +199,7 @@ The most important thing to understand when it comes to B-Splines is that they w The most straightforward type of B-Spline is the uniform spline. In a uniform spline, the knots are distributed uniformly over the entire curve interval. For instance, if we have a knot vector of length twelve, then a uniform knot vector would be [0,1,2,3,...,9,10,11]. Or [4,5,6,...,13,14,15], which defines *the same intervals*, or even [0,2,3,...,18,20,22], which also defines *the same intervals*, just scaled by a constant factor, which becomes normalised during interpolation and so does not contribute to the curvature. - + This is an important point: the intervals that the knot vector defines are *relative* intervals, so it doesn't matter if every interval is size 1, or size 100 - the relative differences between the intervals is what shapes any particular curve. @@ -185,7 +212,7 @@ The problem with uniform knot vectors is that, as we need `order` control points Collapsing knot intervals, by making two or more consecutive knots have the same value, allows us to reduce the curve complexity in the sections that are affected by the knots involved. This can have drastic effects: for every interval collapse, the curve order goes down, and curve continuity goes down, to the point where collapsing `order` knots creates a situation where all continuity is lost and the curve "kinks". - + @@ -196,7 +223,7 @@ By combining knot interval collapsing at the start and end of the curve, with un For any curve of degree `D` with control points `N`, we can define a knot vector of length `N+D+1` in which the values `0 ... D+1` are the same, the values `D+1 ... N+1` follow the "uniform" pattern, and the values `N+1 ... N+D+1` are the same again. For example, a cubic B-Spline with 7 control points can have a knot vector [0,0,0,0,1,2,3,4,4,4,4], or it might have the "identical" knot vector [0,0,0,0,2,4,6,8,8,8,8], etc. Again, it is the relative differences that determine the curve shape. - + @@ -209,7 +236,7 @@ This is essentially the "free form" version of a B-Spline, and also the least in While it is true that this section on B-Splines is running quite long already, there is one more thing we need to talk about, and that's "Rational" splines, where the rationality applies to the "ratio", or relative weights, of the control points themselves. By introducing a ratio vector with weights to apply to each control point, we greatly increase our influence over the final curve shape: the more weight a control point carries, the closer to that point the spline curve will lie, a bit like turning up the gravity of a control pointl, just like for rational Bézier curves. - + Of course this brings us to the final topic that any text on B-Splines must touch on before calling it a day: the [NURBS](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline), or Non-Uniform Rational B-Spline (NURBS is not a plural, the capital S actually just stands for "spline", but a lot of people mistakenly treat it as if it is, so now you know better). NURBS is an important type of curve in computer-facilitated design, used a lot in 3D modelling (typically as NURBS surfaces) as well as in arbitrary-precision 2D design due to the level of control a NURBS curve offers designers. diff --git a/docs/chapters/bsplines/interpolation.js b/docs/chapters/bsplines/interpolation.js index 5954d465..84a0ce60 100644 --- a/docs/chapters/bsplines/interpolation.js +++ b/docs/chapters/bsplines/interpolation.js @@ -1,22 +1,10 @@ let cache = { N: [] }, points, + degree = 3, spline, pad=20, knots, - colors = [ - '#C00', - '#CC0', - '#0C0', - '#0CC', - '#00C', - '#C0C', - '#600', - '#660', - '#060', - '#066', - '#006', - '#606' - ]; + colors = ['#C00','#CC0','#0C0','#0CC','#00C','#C0C','#600','#660','#060','#066','#006','#606']; setup() { points = [ @@ -44,6 +32,7 @@ setKnotValue(i, v) { draw() { clear(); + setWidth(1); setStroke(`lightgrey`); drawGrid(pad); @@ -52,7 +41,6 @@ draw() { var y = this.height - pad; this.line(0,y,this.width,y); - var degree = 3; var n = points.length || 4; for (let i=0, e=n+degree+1; i { - addSlider(`slide-control`, `!weight ${i+1}`, 0, 10, 0.1, i%2===1? 2 : 6, v => this.setWeight(i, v)); + addSlider(`slide-control`, `!weight ${i+1}`, 0, 10, 0.1, i%2===1? 2 : 8, v => this.setWeight(i, v)); }); points = points.concat(points.slice(0,3)); diff --git a/docs/images/chapters/arclength/4b5d220d02b08f6c9aa19389255ef8bb.png b/docs/images/chapters/arclength/4b5d220d02b08f6c9aa19389255ef8bb.png index 48b1078dfc665724900e18ec1908dfcce64c288c..628498fdb99b976c3d7059c895605b4bbe8daced 100644 GIT binary patch literal 10651 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 Xwzop6HGAN;C4!WgylAPgf&c#if}=L) literal 10668 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 B?#loG diff --git a/docs/images/chapters/arclength/4bffba7dda2a3556cf5b2ae7392083c6.png b/docs/images/chapters/arclength/4bffba7dda2a3556cf5b2ae7392083c6.png index a672359daca2f8ee542e0896356ef9643a260c90..0ff1ea584666ebfea12b05cc73bd83e7c1f5d7a3 100644 GIT binary patch literal 10293 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 literal 10293 zcmdsdWmHvNwDti(4oIgUAt4|j`AU~`N_R;&Qin#m;Q#^xQc@y_ba!`mNq3iYe2a1a ze?RY!i-Un<@4afS8P9y?3{(6liGfCf27y2@q@~1^ArJ(-r(fuE@Pr_LwFdloZX_!y z26=q?_oFc{4gz@%krw-)>Xv%&*Hc&S`Vr-LT27oRR^61VY=X*_vhDCIz9UPS<7~J| zC37u}UT#}oS_#8%#l0^&T3SN&JybYU9epr8&P3;`YH*1iFHpfJzxiq)Hozt^a@OVL{knR1y-B$ni4jWp21J znLqZF7}avSfrye4&cof=e-{Vy5m#5n^z`(;weDQb+am}WJ`czwTvI{9uUmd=JW_IS z;3=f@OUTHe6&W-KGwap0b#)Qq(aHrjH3>q5Uqb?6%kLpT$dK@G--(IKnI-{5KG!0V zYzcW*`(;h9+XLuJ;!50<=@!VyByy1+9tAZE3knf)YRk^}t;5AWj`NWq#JGE??dpV3 zQ6q7Mr5&1pntyhBc2;Wl_jzDoV3s77PUI^tB3fE9#nv^#&h)%whGOXXgR{s|Hs1YUr-W&HdGUjvkviC`n%Z|k>H76nN7T~EUW$q_(ed$v znoayKjl1FEii+6I&dzR9%s#s$r{pcI#B6TkD@L;3*<5j{NrR+n4-qB@>CWC5ED6 zVq&kUs1V10$_M1-(SbANHtA|*u~W*((lC56t~`IouuFta%8eaCcx`mH`RA8`0iL)x zg-~zobG6A;CMq1(Pc6c_4M~A~u7_cSX|=XYkqK!r&$om`LB*2shA9! zAaox|Ti|3qnWp_yS6dm;XPnlwjiiVO$E(Q44w5-+Q^i8QmDY(ZZCONFS@0ifzo#G& z`N36%a3sW?jaWhkysQeK7ytHFxjuM%rycK3a)^oR`NHAM53UPZsFvSQAtvU}czAO2 zpv(LFFTC%++1pMq`VcRr%^q zu6FX$k_6_-^Asoy$+qHvA?7Beq(sy@Nw&4LKr=H-6;*zB^C|~?nVa3 zF(S*G`Xv{t+}GRNx;@%%_-O=PP*8^wzn}!Jnu|&1@u@vnG>L1NAOwriEc0nj6-`Yj z3JS`9RufS0;cL|xmM-Ug3%}6N0DA`q2@w&*J?{5EUs@-}kl4GrN*WqcW@Tk^XXggqs_8FeX_z z3WeajI^=wmRf%N2%2rfP4t*#?2+P*imi`ps=f%Z^+to4U{{FuG;R4Tp79+l}9IMfE zR3$W@hikgc;f!d}5X=t>3ehl9{eg6WR|1|F9W@SXimIv|ydNvUncF6tc5olsA%i!lS@RAM9r297}^RzgMvE8 z2~O;tA-@bB*IdJaiT(;n<8#f{C_daC%Y7A$@rshOSrPiHr`K};oYF8i$^+F2|vpbI_nhrr^| zo)%D1j0tHGmgMGR2@aE{n{qJO4WVd@jmM8Z+rlb?D`O_0Dsk&n-?WU3ylf9qFeVe< ztf(L{I$W?Q)Y}WzC{8bdQwR42(#OjtKD715l_m-KFz(S0Bqjgr0&8hj?t1o&UoYna z+4uDIwY3GsvH3ix|7UYWyvHpU^=d03HPv;d%oJ~r{>{_>KL-lEm4=ph;!8p7^vHSYkTKWy~A zCA71%o2h$`tF3+d7{sOJ=#+v9Un{Z1yG`VK`oi7a-SBw1b$hy4Xpi1ek{;h^Z|V;e z9bHU9;`um}GZIs*by!#!xAQi(-9k+(Z}`H^?(bq&^C42XG+u&}lam3*k#y8p9b{zW zBu-lr^&;IcaQ|g+>gmma z_Z8S|1Y$4n$+5DRhkx7q5*SO}&&;Nj@FlA4{<7N4urle^#Pps5x#^xBANv9~7a19; zDXBzHNlE#%FI+r4(n)a#Q&}&NY^COmc-?G&B zJT|FzZq^#_Vrluxko-I_MX*?_}EWr6@ zeevcMjzT6PB3fwnp`cSpUx(=e(W``DQFNtneO_K)@8Fe5%LS_XM)upMmA0S;eSIL? z9ytJQ8X6mMaB&ecW5_+2c(}QJ*SPm*%R{Q!EF*A}XDh9VoMA9SbMtO6o(>y|4Zig8 z@xdn`IANy+>xThDMnXoWq!IH#k&pdeT2&Pcz|QByZh$P07l zq!qEz(?k9I{CZft7rV7VbZ=OzS;Z6gscH1d2mm{K{rXesj5ANoluO!JiJ?Pgy#8aO z=Veg-jz(x`=-s-l=$7xBhE?w66XWIC^5}7weP~F&r7JY(ui}m7TNvzzt;HP1^skr2$rPgXM1`7!1|6T0AJj4>%GdV6d@Unfk=%?)oaHj;SX`@-;* zG4SHx+hZHn`%BIFQ^r6Q^1~u@A`buFws+(_+oKmlWM+NHTIi4F{e|Or)#JcBIl;Xn#?!Ud2Kgj1P^_L3t73$I59p$cvVbU~X>E(N!eja#6o) z&K%Gec+AYW_s+<8x}~!-1gtOmG`{iHSNk9hEWRi%{wE+P=)HD@MLQTAbkuzL!Dip8 z!TYvK;u6?-gU92802TFgTW4n?7%U5nRs8bsS;?3C8 z-$dWvTL&BX+*gjd|B{pX)7jbC+R_5KzxiiO7Sz*{B_09HLwQFHk`G>nictYbKB6y8 zHrDLpv%S5&+~W6);32gyP%3b(PLQuZ=BAm^@uy?!Jpb&B&|P*4B-mPNaIf z{!-gh?@oMfZ$nxJZIxZ$3cn9 zaF6c1IpDCqwUz1fh_jKN7V7xc(~udC*D^bcf`ik#!8{{wDq^+2l_AOoE8s$UUn_#A zL~?%<)wMUx5*zOurF5JuYquS3Ieo1p?vuLn~CR#wX( z3=9l~+1SC)vC@4bBj*7g6Y2dAsCFr%2Cl>9AqF~yU@Q#3EvcY@gjV4~LQG8Ye_sFk zMetH+@mWEE6&TI)$(#$-(lXKEXb}SfBDl!TPJWHoHA^J%S{Jwoynnm-#tSvGGga^K zLDCdEvN_ml@Aw^6Ufy;}DU`IdIs^WK2p50|2~&{**T)C%yMNPc+`M(=aFO-Mx;om; z;U(Xjf0H{GO+pxXsY2AtDM+>hLL;$e4@ZwQuj*YtFJIUK=d-(YzxrbNcd>89f4yCH zV3m+jbYJ%=d&{$>zg2ZR@g3W$Pe3vAWX9 z6`z-$4W^_lZ;!tG<_0~ll)`)LO%nRp%e`+B|AIP(2%n(YCaDo1R6x~StXCTZCHrK zPq-yao@h)1G2!Fvf#=OR!bEW~f^5HE#b4>D{M|hw3fzYe^)3O}I4cp6kxQ&5J=iY2 zp%eC`BRSG12W$UfGBdYA(M~_)ecUg@>3o9^^&R8NRP+^(AT-<=Q;feSR>J(ei2qo9 zUKJHB6nuB*6-mnDU&WW7faYo>8Trz0y4YYXK4)o{8jXdU`{m-I^EH~XC&9NcMf$*M z*Ar7n*y-(Bw9xTt=Zi>U(Zu4{l^Q$q*1$n^2B2WYBqh=J^9TAZFMk4vadd;Rhc3TD zPE}QPd1FJ|lTL=2jSXjKXD5l(5E1yYR<-NarH(K>hx;2C4ILd2OKj;=0B`k2R#eIE z*N9P2F-`uJpKrL>;0{GYE3o3s`oV0_6lpzO)H*wx48zYtDiEOQ?d(MMdA#Q;(r;vl z;1>(__xA^@1>OAH1GW+`TxptBRD`LZpfJa$#I8}K+ZyzO$f7v#SRyMRH%~q#*lBBs z^8I_YIrq>=CM{ef@ZqoDzn?}1ikW8R=ZEn)ZN8=n6r5Fh?s~W&Y-B|BQ$7V4uo4xn zPgMWkzhtPWs69P3ZwC3ro^#qjyHa`5M&l ziV6)8H;rtTBu_WD*Kc=_4$|mL4(0<`Eytoq#>PP4p-(1B2!q2hC8EBEB_+KX>l4k{ zZVgD0l96fY2;RI@Yw5||94~6ghtT!)xC+-S;VRn~fsOY}7FL{} z!1DRp31Pv(oL%s=CqQ{UB%1SDKmb{z=#Jtb0Xx6o-;RF>W?C>Hgs?7;4AwZXV*PFB z5tp~Qv7Yj^Z0tAcd+hmuzA)|@5Lb<-$Z&@akBt{oz5mA_jFG)26YA_e7K9#*36s>G z6VJ)jTV=#r(K5*qA|>1UpqB=Bc{7%)9hZ!ZxSaA?MqyM~xP^+8e>);uGCCoJiz6;1 zP>c!@;gcby;K5jKgxm3J)b8l+tnxkVO;UFda=I1)%A7j^mJl0e;}RtP--iHW6P_nZ2($O zyVYK;1)|~|@6U43(9j&Sk*>711-)3=+CovABx(HmCTGg+Egk^@0h4|MFp@<;sYO%v z(s6rTu>x%ka9HaC7-1KCky+OE^JgrO$(aw6HwDvFg3KQ2^XJbXb_z>rkkPSs%xBBL z#{RT3mV111@&Par2JK%-!o$M+ii(&4YXGhrhF+|or>?Hf%E<{r4N^usF6zd{M(ffN zg=`|TDOu3OhlV4Rkl6UDkYWmu-NDn(wQK9`8iyGQL$sy}DmM z6eKeMp=ggj2QyAPd3$%)s4Fr^v%E`IJK{CO?ypQ&ty8n{V#D>*5+4gB-^-_N!#yvR zLAcib9+5vTA}YY@?eV&za;HNw@Lm45FhIQxq}HM{st*K9(R|&^+RX_JZ0s2v>v2?#lf;BcWImgAdX24#@~Q{`gvADK#C9RE$UpYI4>?a)PkwWP57 zw~uCM`) z)vUH7+oKOu4edSCvaq1XBP(2xSeL=vAXbJN|a?35BTA3KiS60pkOnDx-i z&CLN0EeZXhxVpM}e05?BD6AA1z6e9-e!|7o$(o^sMX1_u_0sSk*!s<1Fo18hS!^J- zva%{%!&ZK4W=e$(EivsU1gLohsB587j{wN`0JtB5|dqY#{qw-rWsf z@)1G~{!5$qaJ5DQIF;D1UxD^gP8vN-?;RdKu_f+K+<+W;0}=>by|gEG0^rgTxTsWR zQX&XnVkRau{sBa5y>ZS{4(k#Y+x#2oJzrAlMmf0hp3`Ig5 zNP87TEy+pw{vYWc_W%+~ecRLppYz@nNM}T>#O(TzAm`_T-2=%fr8cujCg#niWRjZ~ zhZx;}XXvP~gn1rC($m&5;6VWV#dJFJ)2yFLgW)IFIc+gi*_=q)+cN>4pcI@cs#g!} zIb~kv?ra$2?qZ&BO38$Z@0N^^nKXdMY3hgjS+Qm}WsZ6hd4>9SVa1yTIuhuMc?_b& zrtP^(j=9P`O3q>`pTGJ#^;bDcxyVRzez~SXk;G>wq%6*D)L9nmKji{NKw@ia$^0R<#hPW~x z{)-Fh*iC z!Ho^*CtzcQO5kn3Cni+o()o!U92{8L*^S6TViagEO#xTy53;0{g<2=gMvwP<+zoDk zMGVHGfN%6CW&_s-w5#n!t$VTx0T~$?selJQaO)D~W`nF=R~A#TRtF&Cd&z0@l2$Is zw>OTi94-p|jGravyE;w);OWa}D42oYzG0$c{LzvuD1E|5dbl_lDrt9A;|k= zvBdBMC-?dDrIwC${4aN-m`97b?UR!zh<-mz1T8ZJ)zk~+D6M71#t$%qrhpwL9$>!jze3r}Y(d_M+QfhD!fT@oLlSz9g2ll+mWr)MF zv9YmV>%y?GvdRM7z)Xz;85-KDyTM0~8;r=8Z1_Nl+5m5gO-w93T|)``ru+?6@yDQ1IN`JC@fQ*hZW{Meh1;Z>kq^8J&4 z=LJ&c7grmJYEp7?E!9hNhAqBI+mqIiiOEg{%c%mjlaxwR12Taxsqp3pUf(!6Fx6UJt$}ubApZOvdLTc6&i~+(~30tf4{V zyXu1gSE+PuY;8_15LGR3m`-40selIM0w!yf z$H&9t08patr%$ixaKmb5%gvphB>0`vw$I(e!;vaBwEoLIuer5#|Hk^dJr5h*hkpya_zo7-EbqM{<*r6%v%VZqzT^78Vvva&MnSgDS|B({L@>FKS3 z!NI3(Ct4pwz2b5NXa-1qfosXD-TQ2i>*@a!`6{*l_NFyv7S-OBGkpB`QP;+XsoQ~+ z2oJ9vJdZv*ImtgxUO)U>uM0{Y+_ts{j{PiYnddHV8wDBW;HKS7^}5pOD+Ud2txuoN z*SoS*8+T(C0er4D^mw`C;{>j)GCl8{_x2MwZ0hgd*3xFu)3V~tTL6S}1BPR$rNuWz z(3@8Sq{_^qv)x8tzI>@QRei5+dbwq+CtHSLRi37+t6Ot?e7q_U_oOFubnT;~IB+<; z#&H9Uor6PmZufMff2R5~1+#9Auh7HQvu-%qZ^xU1`D%yLb!m|2tQ@bjOG!yRrLaS( zJOLo3TCHCCB3kcq0E!0aHFb4>G({fIQ;-BtI$Y8RLB6AOtyST$w1$Et-8O^8oYd)Ob z2{MIJ*Q3SNpYPfi7Dz!Z{Xtn77g$Jmcz8^F{8M53hxhFTM6qn4tiQ-2IbpWOfjS)o zR8Yp{_c(|4C$dNa69)2ONCl+qy`qXr`_6ctY&s7^pQ1;7y!xmM6Q9{EOmotc836$S z8;S%9GN>TJu{o|wnV6UWG_<)ro`(gVoZMe7fz|_s{V#y4PvUVh%iYT@aF;1A9|Lk6 z&5@4#s{3AxB{z-V9Tx?YR2cL-jE#>w++G|+9}&b~|CJRY1obZ+J7TpcwbA=)?dh*J z0dqEafIV7netcm3^2`MoBFNVL3kn!i)zqE}9b)3*2?I9P))AY7DeW^eFM-#n`Eu`( zFb?L2ii(OtT7Vg|T8zAY`RY}+Y5%v9@^VotD+b7?o)}8E<7EW>1~)%YBudax0;S&o zz~-W(qkpor3~LQQX`h(Dzqz?70hk`#=<)fEil&ZEHb_6OPWxFLwnr#)^71q;k{}Rt zGG2UIT3S|CR$;)footU%gSt)I++5xIB{8@Cr`lT>==gZNJHvXa|7mDw0EGaVlUfa^ z7Dy*gm?WESY;Cm<4r1`R?6-gphu8RMI0OVi@$vZEdwbNZtk^)78|0b*8jAFPduM2D z{EUNx19YSOZbQ$lmbE0Xm6Yx->Y26Q|c0m3fL2os}%bOxm)C53`gy{L}P zD?r24xS#!;X;2d&lx4{-E44b)Eb6T*vU>&jHRieJRr#%PLxe$-NV=eRI4Fd!0zdi2hdV)@PqBTF0(H9IWQl9s zNy*7!_ky4ryK$J8nTY_9@0|Ej9rPqDT^;xFhF7B?KG_|x= zKqv-=>kAkGNXzte^}?dWEW0JpZs43_Vq+0hR8&ZL9CO;x6AoroE65m0RB%D$1|#UT zn7bVS5(!F3AOM;uyNL%yGTM9n`ZWaLm;%TBvNP4HAsOIdg(V~;dYgaiumEv%CUck{ z1Dv!_?}~+jMUDhQ@g_ymY))fkWoD?U?T zWs=e4*bfi4M?Y9XSmcp9)pmxUmI3-jGD^$_NkEx<6^u?Os_8=~W=F!J5Dbis#o0eN zkbU@d0Gcaq&Mg5^D1DBz9YZO4{BPQzy`uxEs;Y|h{d@6%=NNs1)3r|K+}zy4Dk^y2 zzkdhvroTKb{Mr9J+;w~ycthZNK7dzq2}AsZ z6%QX01PJ>JYzq4hho|d`Y&>>Otn1Rs%FAPcHWlgBi?k&C`N=NGl#?|*Kp?O2oS2vx z%+Up4Drg#lOgZGpBv-GY0F8L+1q1CNoxI^=vmiu0Rg6Jc6s^3O-Glr3LO`8W1Q-$? z-A8=uPgymoX`oQ7s12M7^Yx?EC4hKhxQ?#e0+eEGpkDm{>#6H}eD(&r(^iAn_zLs^ OLZrn%iWP}`^8bHikF}ft diff --git a/docs/images/chapters/arclength/dc74a2f2da19470b8d721ece5f3ce268.png b/docs/images/chapters/arclength/dc74a2f2da19470b8d721ece5f3ce268.png index ec30941df59a97cf7b44ba7f2867decfc4927f0b..457e0d8ecdaf284f719632ac546259fdf56eead6 100644 GIT binary patch literal 9914 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 aV=iAU>c6LMSPzfU5cltDAd3}D-u@RQ3f<@c literal 9933 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/images/chapters/bsplines/ac9e15a627ed2bc88c5e1b73147b7991.png b/docs/images/chapters/bsplines/ac9e15a627ed2bc88c5e1b73147b7991.png new file mode 100644 index 0000000000000000000000000000000000000000..6d02a7dc9fb127870ef6d8289445bda56685f02f GIT binary patch literal 23665 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 Fe*oM&`I!I! literal 0 HcmV?d00001 diff --git a/docs/images/chapters/bsplines/bd187c361b285ef878d0bc17af8a3900.svg b/docs/images/chapters/bsplines/bd187c361b285ef878d0bc17af8a3900.svg new file mode 100644 index 00000000..61e89a88 --- /dev/null +++ b/docs/images/chapters/bsplines/bd187c361b285ef878d0bc17af8a3900.svg @@ -0,0 +1,993 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/chapters/bsplines/bd03680e4661ae7f4d687b2f229d2495.png b/docs/images/chapters/bsplines/c7fe4f9cba8d4bc06436f4f238d202ce.png similarity index 100% rename from docs/images/chapters/bsplines/bd03680e4661ae7f4d687b2f229d2495.png rename to docs/images/chapters/bsplines/c7fe4f9cba8d4bc06436f4f238d202ce.png diff --git a/docs/images/chapters/bsplines/dda4911d5148032e005a02ef33b78978.png b/docs/images/chapters/bsplines/e50d628696245ef68b691e28b2162d56.png similarity index 100% rename from docs/images/chapters/bsplines/dda4911d5148032e005a02ef33b78978.png rename to docs/images/chapters/bsplines/e50d628696245ef68b691e28b2162d56.png diff --git a/docs/images/chapters/reordering/81e559a69298ecc24c8edebe59e79647.png b/docs/images/chapters/reordering/81e559a69298ecc24c8edebe59e79647.png index 6b90550027b85e24c0e84e55a629ccf33eb9704b..d9e605df5cc04e3a4a37d155e9a734b8f5cac5e3 100644 GIT binary patch literal 11823 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 literal 12102 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 KREm{M1O5w-f7Pr2 diff --git a/docs/index.html b/docs/index.html index 1f7a90a3..a81803d2 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2331,12 +2331,12 @@ for p = 1 to points.length-3 (inclusive):

B-Splines

No discussion on Bézier curves is complete without also giving mention of that other beast in the curve design space: B-Splines. Easily confused to mean Bézier splines, that's not actually what they are; they are "basis function" splines, which makes a lot of difference, and we'll be looking at those differences in this section. We're not going to dive as deep into B-Splines as we have for Bézier curves (that would be an entire primer on its own) but we'll be looking at how B-Splines work, what kind of maths is involved in computing them, and how to draw them based on a number of parameters that you can pick for individual B-Splines.

-

First off: B-Splines are piecewise polynomial interpolation curves, where the "single curve" is built by performing polynomial interpolation over a set of points, using a sliding window of a fixed number of points. For instance, a "cubic" B-Spline defined by twelve points will have its curve built by evaluating the polynomial interpolation of four points, and the curve can be treated as a lot of different sections, each controlled by four points at a time, such that the full curve consists of smoothly connected sections defined by points {1,2,3,4}, {2,3,4,5}, ..., {8,9,10,11}, and finally {9,10,11,12}, for eight sections.

+

First off: B-Splines are piecewise, polynomial interpolation curves, where the "single curve" is built by performing polynomial interpolation over a set of points, using a sliding window of a fixed number of points. For instance, a "cubic" B-Spline defined by twelve points will have its curve built by evaluating the polynomial interpolation of four points, and the curve can be treated as a lot of different sections, each controlled by four points at a time, such that the full curve consists of smoothly connected sections defined by points {1,2,3,4}, {2,3,4,5}, ..., {8,9,10,11}, and finally {9,10,11,12}, for eight sections.

What do they look like? They look like this! Tap on the graphic to add more points, and move points around to see how they map to the spline curve drawn.

Scripts are disabled. Showing fallback image. - + @@ -2346,13 +2346,15 @@ for p = 1 to points.length-3 (inclusive):
  • for Bézier curves, the curve is defined as an interpolation of points, but:
  • for B-Splines, the curve is defined as an interpolation of curves.
  • -

    In fact, let's look at that again, but this time with the base curves shown, too. Each consecutive four points defined one curve:

    +

    In fact, let's look at that again, but this time with the base curves shown, too. Each consecutive four points define one curve:

    Scripts are disabled. Showing fallback image. - + - + + +

    In order to make this interpolation of curves work, the maths is necessarily more complex than the maths for Bézier curves, so let's have a look at how things work.

    How to compute a B-Spline curve: some maths

    @@ -2372,29 +2374,31 @@ Doing so for a degree d B-Spline with n control point

    This is another recursive function, with k values decreasing from the curve order to 1, and the value α (alpha) defined by:

    -

    That looks complicated, but it's not. Computing alpha is just a fraction involving known, plain numbers and once we have our alpha value, computing (1-alpha) is literally just "computing one minus alpha". Computing this d() function is thus simply a matter of "computing simple arithmetics but with recursion", which might be computationally expensive because we're doing "a lot of" steps, but is also computationally cheap because each step only involves very simple maths. Of course as before the recursion has to stop:

    +

    That looks complicated, but it's not. Computing alpha is just a fraction involving known, plain numbers. And, once we have our alpha value, we also have (1-alpha) because it's a trivial subtraction. Computing the d() function is thus mostly a matter of computing pretty simple arithmetical statements, with some caching of results so we can refer to them as we recurve. While the recursion might see computationally expensive, the total algorithm is cheap, as each step only involves very simple maths.

    +

    Of course, the recursion does need a stop condition:

    -

    So, we see two stopping conditions: either i becomes 0, in which case d() is zero, or k becomes zero, in which case we get the same "either 1 or 0" that we saw in the N() function above.

    -

    Thanks to Cox and de Boor, we can compute points on a B-Spline pretty easily: we just need to compute a triangle of interconnected values. For instance, d() for i=3, k=3 yields the following triangle:

    - -

    That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2): d(3,3) = a(3,3) x d(2,3) + (1-a(3,3)) x d(2,2)... and we simply keep expanding our triangle until we reach the terminating function parameters. Done deal!

    -

    One thing we need to keep in mind is that we're working with a spline that is constrained by its control points, so even though the d(..., k) values are zero or one at the lowest level, they are really "zero or one, times their respective control point", so in the next section you'll see the algorithm for running through the computation in a way that starts with a copy of the control point vector and then works its way up to that single point: that's pretty essential!

    -

    If we run this computation "down", starting at d(3,3), then without special code in place we would be computing quite a few terms multiple times at each step. On the other hand, we can also start with that last "column", we can generate the terminating d() values first, then compute the a() constants, perform our multiplications, generate the previous step's d() values, compute their a() constants, do the multiplications, etc. until we end up all the way back at the top. If we run our computation this way, we don't need any explicit caching, we can just "recycle" the list of numbers we start with and simply update them as we move up the triangle. So, let's implement that!

    -

    Cool, cool... but I don't know what to do with that information

    -

    I know, this is pretty mathy, so let's have a look at what happens when we change parameters here. We can't change the maths for the interpolation functions, so that gives us only one way to control what happens here: the knot vector itself. As such, let's look at the graph that shows the interpolation functions for a cubic B-Spline with seven points with a uniform knot vector (so we see seven identical functions), representing how much each point (represented by one function each) influences the total curvature, given our knot values. And, because exploration is the key to discovery, let's make the knot vector a thing we can actually manipulate (you will notice that knots are constrained in their value: any knot must strictly be equal to, or greater than, the previous value).

    - +

    So, we actually see two stopping conditions: either i becomes 0, in which case d() is zero, or k becomes zero, in which case we get the same "either 1 or 0" that we saw in the N() function above.

    +

    Thanks to Cox and de Boor, we can compute points on a B-Spline pretty easily using the same kind of linear interpolation we saw in de Casteljau's algorithm. For instance, if we write out d() for i=3 and k=3, we get the following recursion diagram:

    + +

    That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2), where those two are themselves a mixture of d(1,3) and d(1,2), and d(1,2) and d(1,1), respectively, which are themselves a mixture of etc. etc. We simply keep expanding our terms until we reach the stop conditions, and then sum everything back up. It's really quite elegant.

    +

    One thing we need to keep in mind is that we're working with a spline that is constrained by its control points, so even though the d(..., k) values are zero or one at the lowest level, they are really "zero or one, times their respective control point", so in the next section you'll see the algorithm for running through the computation in a way that starts with a copy of the control point vector and then works its way up to that single point, rather than first starting "on the left", working our way "to the right" and then summing back up "to the left". We can just start on the right and work our way left immediately.

    + - + + +Changing the values in the knot vector changes how much each point influences the total curvature (with some clever knot value manipulation, we can even make the influence of certain points disappear entirely!), so we can see that while the control points define the hull inside of which we're going to be drawing a curve, it is actually the knot vector that determines the actual *shape* of the curve inside that hull. + +After reading the rest of this section you may want to come back here to try some specific knot vectors, and see if the resulting interpolation landscape makes sense given what you will now think should happen! +--> -

    Changing the values in the knot vector changes how much each point influences the total curvature (with some clever knot value manipulation, we can even make the influence of certain points disappear entirely!), so we can see that while the control points define the hull inside of which we're going to be drawing a curve, it is actually the knot vector that determines the actual shape of the curve inside that hull.

    -

    After reading the rest of this section you may want to come back here to try some specific knot vectors, and see if the resulting interpolation landscape makes sense given what you will now think should happen!

    Running the computation

    Unlike the de Casteljau algorithm, where the t value stays the same at every iteration, for B-Splines that is not the case, and so we end having to (for each point we evaluate) run a fairly involving bit of recursive computation. The algorithm is discussed on this Michigan Tech page, but an easier to read version is implemented by b-spline.js, so we'll look at its code.

    Given an input value t, we first map the input to a value from the domain [0,1] to the domain [knots[degree], knots[knots.length - 1 - degree]. Then, we find the section number s that this mapped t value lies on:

    @@ -2432,7 +2436,7 @@ for(let L = 1; L <= order; L++) { - +

    This is an important point: the intervals that the knot vector defines are relative intervals, so it doesn't matter if every interval is size 1, or size 100 - the relative differences between the intervals is what shapes any particular curve.

    @@ -2445,7 +2449,7 @@ for(let L = 1; L <= order; L++) { - + @@ -2458,7 +2462,7 @@ for(let L = 1; L <= order; L++) { - + @@ -2469,10 +2473,10 @@ for(let L = 1; L <= order; L++) { Scripts are disabled. Showing fallback image. - + - +

    Of course this brings us to the final topic that any text on B-Splines must touch on before calling it a day: the NURBS, or Non-Uniform Rational B-Spline (NURBS is not a plural, the capital S actually just stands for "spline", but a lot of people mistakenly treat it as if it is, so now you know better). NURBS is an important type of curve in computer-facilitated design, used a lot in 3D modelling (typically as NURBS surfaces) as well as in arbitrary-precision 2D design due to the level of control a NURBS curve offers designers.

    diff --git a/docs/ja-JP/index.html b/docs/ja-JP/index.html index 2eaf155d..1b0fe430 100644 --- a/docs/ja-JP/index.html +++ b/docs/ja-JP/index.html @@ -2327,12 +2327,12 @@ for p = 1 to points.length-3 (inclusive):

    B-Splines

    No discussion on Bézier curves is complete without also giving mention of that other beast in the curve design space: B-Splines. Easily confused to mean Bézier splines, that's not actually what they are; they are "basis function" splines, which makes a lot of difference, and we'll be looking at those differences in this section. We're not going to dive as deep into B-Splines as we have for Bézier curves (that would be an entire primer on its own) but we'll be looking at how B-Splines work, what kind of maths is involved in computing them, and how to draw them based on a number of parameters that you can pick for individual B-Splines.

    -

    First off: B-Splines are piecewise polynomial interpolation curves, where the "single curve" is built by performing polynomial interpolation over a set of points, using a sliding window of a fixed number of points. For instance, a "cubic" B-Spline defined by twelve points will have its curve built by evaluating the polynomial interpolation of four points, and the curve can be treated as a lot of different sections, each controlled by four points at a time, such that the full curve consists of smoothly connected sections defined by points {1,2,3,4}, {2,3,4,5}, ..., {8,9,10,11}, and finally {9,10,11,12}, for eight sections.

    +

    First off: B-Splines are piecewise, polynomial interpolation curves, where the "single curve" is built by performing polynomial interpolation over a set of points, using a sliding window of a fixed number of points. For instance, a "cubic" B-Spline defined by twelve points will have its curve built by evaluating the polynomial interpolation of four points, and the curve can be treated as a lot of different sections, each controlled by four points at a time, such that the full curve consists of smoothly connected sections defined by points {1,2,3,4}, {2,3,4,5}, ..., {8,9,10,11}, and finally {9,10,11,12}, for eight sections.

    What do they look like? They look like this! Tap on the graphic to add more points, and move points around to see how they map to the spline curve drawn.

    Scripts are disabled. Showing fallback image. - + @@ -2342,13 +2342,15 @@ for p = 1 to points.length-3 (inclusive):
  • for Bézier curves, the curve is defined as an interpolation of points, but:
  • for B-Splines, the curve is defined as an interpolation of curves.
  • -

    In fact, let's look at that again, but this time with the base curves shown, too. Each consecutive four points defined one curve:

    +

    In fact, let's look at that again, but this time with the base curves shown, too. Each consecutive four points define one curve:

    Scripts are disabled. Showing fallback image. - + - + + +

    In order to make this interpolation of curves work, the maths is necessarily more complex than the maths for Bézier curves, so let's have a look at how things work.

    How to compute a B-Spline curve: some maths

    @@ -2368,29 +2370,31 @@ Doing so for a degree d B-Spline with n control point

    This is another recursive function, with k values decreasing from the curve order to 1, and the value α (alpha) defined by:

    -

    That looks complicated, but it's not. Computing alpha is just a fraction involving known, plain numbers and once we have our alpha value, computing (1-alpha) is literally just "computing one minus alpha". Computing this d() function is thus simply a matter of "computing simple arithmetics but with recursion", which might be computationally expensive because we're doing "a lot of" steps, but is also computationally cheap because each step only involves very simple maths. Of course as before the recursion has to stop:

    +

    That looks complicated, but it's not. Computing alpha is just a fraction involving known, plain numbers. And, once we have our alpha value, we also have (1-alpha) because it's a trivial subtraction. Computing the d() function is thus mostly a matter of computing pretty simple arithmetical statements, with some caching of results so we can refer to them as we recurve. While the recursion might see computationally expensive, the total algorithm is cheap, as each step only involves very simple maths.

    +

    Of course, the recursion does need a stop condition:

    -

    So, we see two stopping conditions: either i becomes 0, in which case d() is zero, or k becomes zero, in which case we get the same "either 1 or 0" that we saw in the N() function above.

    -

    Thanks to Cox and de Boor, we can compute points on a B-Spline pretty easily: we just need to compute a triangle of interconnected values. For instance, d() for i=3, k=3 yields the following triangle:

    - -

    That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2): d(3,3) = a(3,3) x d(2,3) + (1-a(3,3)) x d(2,2)... and we simply keep expanding our triangle until we reach the terminating function parameters. Done deal!

    -

    One thing we need to keep in mind is that we're working with a spline that is constrained by its control points, so even though the d(..., k) values are zero or one at the lowest level, they are really "zero or one, times their respective control point", so in the next section you'll see the algorithm for running through the computation in a way that starts with a copy of the control point vector and then works its way up to that single point: that's pretty essential!

    -

    If we run this computation "down", starting at d(3,3), then without special code in place we would be computing quite a few terms multiple times at each step. On the other hand, we can also start with that last "column", we can generate the terminating d() values first, then compute the a() constants, perform our multiplications, generate the previous step's d() values, compute their a() constants, do the multiplications, etc. until we end up all the way back at the top. If we run our computation this way, we don't need any explicit caching, we can just "recycle" the list of numbers we start with and simply update them as we move up the triangle. So, let's implement that!

    -

    Cool, cool... but I don't know what to do with that information

    -

    I know, this is pretty mathy, so let's have a look at what happens when we change parameters here. We can't change the maths for the interpolation functions, so that gives us only one way to control what happens here: the knot vector itself. As such, let's look at the graph that shows the interpolation functions for a cubic B-Spline with seven points with a uniform knot vector (so we see seven identical functions), representing how much each point (represented by one function each) influences the total curvature, given our knot values. And, because exploration is the key to discovery, let's make the knot vector a thing we can actually manipulate (you will notice that knots are constrained in their value: any knot must strictly be equal to, or greater than, the previous value).

    - +

    So, we actually see two stopping conditions: either i becomes 0, in which case d() is zero, or k becomes zero, in which case we get the same "either 1 or 0" that we saw in the N() function above.

    +

    Thanks to Cox and de Boor, we can compute points on a B-Spline pretty easily using the same kind of linear interpolation we saw in de Casteljau's algorithm. For instance, if we write out d() for i=3 and k=3, we get the following recursion diagram:

    + +

    That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2), where those two are themselves a mixture of d(1,3) and d(1,2), and d(1,2) and d(1,1), respectively, which are themselves a mixture of etc. etc. We simply keep expanding our terms until we reach the stop conditions, and then sum everything back up. It's really quite elegant.

    +

    One thing we need to keep in mind is that we're working with a spline that is constrained by its control points, so even though the d(..., k) values are zero or one at the lowest level, they are really "zero or one, times their respective control point", so in the next section you'll see the algorithm for running through the computation in a way that starts with a copy of the control point vector and then works its way up to that single point, rather than first starting "on the left", working our way "to the right" and then summing back up "to the left". We can just start on the right and work our way left immediately.

    + - + + +Changing the values in the knot vector changes how much each point influences the total curvature (with some clever knot value manipulation, we can even make the influence of certain points disappear entirely!), so we can see that while the control points define the hull inside of which we're going to be drawing a curve, it is actually the knot vector that determines the actual *shape* of the curve inside that hull. + +After reading the rest of this section you may want to come back here to try some specific knot vectors, and see if the resulting interpolation landscape makes sense given what you will now think should happen! +--> -

    Changing the values in the knot vector changes how much each point influences the total curvature (with some clever knot value manipulation, we can even make the influence of certain points disappear entirely!), so we can see that while the control points define the hull inside of which we're going to be drawing a curve, it is actually the knot vector that determines the actual shape of the curve inside that hull.

    -

    After reading the rest of this section you may want to come back here to try some specific knot vectors, and see if the resulting interpolation landscape makes sense given what you will now think should happen!

    Running the computation

    Unlike the de Casteljau algorithm, where the t value stays the same at every iteration, for B-Splines that is not the case, and so we end having to (for each point we evaluate) run a fairly involving bit of recursive computation. The algorithm is discussed on this Michigan Tech page, but an easier to read version is implemented by b-spline.js, so we'll look at its code.

    Given an input value t, we first map the input to a value from the domain [0,1] to the domain [knots[degree], knots[knots.length - 1 - degree]. Then, we find the section number s that this mapped t value lies on:

    @@ -2428,7 +2432,7 @@ for(let L = 1; L <= order; L++) { - +

    This is an important point: the intervals that the knot vector defines are relative intervals, so it doesn't matter if every interval is size 1, or size 100 - the relative differences between the intervals is what shapes any particular curve.

    @@ -2441,7 +2445,7 @@ for(let L = 1; L <= order; L++) { - + @@ -2454,7 +2458,7 @@ for(let L = 1; L <= order; L++) { - + @@ -2465,10 +2469,10 @@ for(let L = 1; L <= order; L++) { Scripts are disabled. Showing fallback image. - + - +

    Of course this brings us to the final topic that any text on B-Splines must touch on before calling it a day: the NURBS, or Non-Uniform Rational B-Spline (NURBS is not a plural, the capital S actually just stands for "spline", but a lot of people mistakenly treat it as if it is, so now you know better). NURBS is an important type of curve in computer-facilitated design, used a lot in 3D modelling (typically as NURBS surfaces) as well as in arbitrary-precision 2D design due to the level of control a NURBS curve offers designers.

    diff --git a/docs/js/custom-element/api/graphics-api.js b/docs/js/custom-element/api/graphics-api.js index 7f1d7ca9..98f2fb18 100644 --- a/docs/js/custom-element/api/graphics-api.js +++ b/docs/js/custom-element/api/graphics-api.js @@ -194,6 +194,29 @@ class GraphicsAPI extends BaseAPI { } } + /** + * Update a slider with new min/max/step parameters, and value. + * + * @param {*} propname + * @param {*} min + * @param {*} max + * @param {*} step + * @param {*} value + */ + updateSlider(propname, min, max, step, value) { + let slider = this._sliders[propname]; + + if (!slider) { + throw new Error(`this.${propname} has no associated slider.`); + } + + slider.setAttribute(`min`, min); + slider.setAttribute(`max`, max); + slider.setAttribute(`step`, step); + slider.setAttribute(`value`, value); + slider.updateProperty(value); + } + /** * Set up a slider to control a named, numerical property in the sketch. * @@ -207,6 +230,8 @@ class GraphicsAPI extends BaseAPI { throw new Error(`this.${propname} already exists: cannot bind slider.`); } + this._sliders = this._sliders || {}; + let propLabel = propname.replace(`!`, ``); propname = propLabel === propname ? propname : false; @@ -225,6 +250,7 @@ class GraphicsAPI extends BaseAPI { wrapper.append(label); slider.parentNode.replaceChild(wrapper, slider); slider.setAttribute(`class`, `slider`); + this._sliders[propname] = slider; wrapper.append(slider); let valueField = create(`label`); valueField.classList.add(`slider-value`); @@ -239,7 +265,7 @@ class GraphicsAPI extends BaseAPI { return undefined; } - const updateProperty = (evt) => { + slider.updateProperty = (evt) => { let value = parseFloat(slider.value); ui.update(value); try { @@ -258,8 +284,8 @@ class GraphicsAPI extends BaseAPI { }; slider.value = initial; - updateProperty({ target: { value: initial } }); - slider.listen(`input`, updateProperty); + slider.updateProperty({ target: { value: initial } }); + slider.listen(`input`, (evt) => slider.updateProperty(evt)); return slider; } diff --git a/docs/js/custom-element/api/types/bezier.js b/docs/js/custom-element/api/types/bezier.js index 3b6c75bb..ae731236 100644 --- a/docs/js/custom-element/api/types/bezier.js +++ b/docs/js/custom-element/api/types/bezier.js @@ -77,7 +77,6 @@ class Bezier extends Original { drawCurve(color = `#333`) { const ctx = this.ctx; ctx.cacheStyle(); - ctx.lineWidth = 1; ctx.strokeStyle = color; ctx.beginPath(); const lut = this.getLUT().slice(); diff --git a/docs/js/custom-element/graphics-element.js b/docs/js/custom-element/graphics-element.js index e952b697..95e148e3 100644 --- a/docs/js/custom-element/graphics-element.js +++ b/docs/js/custom-element/graphics-element.js @@ -6,7 +6,14 @@ import performCodeSurgery from "./lib/perform-code-surgery.js"; const MODULE_URL = import.meta.url; const MODULE_PATH = MODULE_URL.slice(0, MODULE_URL.lastIndexOf(`/`)); -// Really wish this was baked into the DOM API... +// Until global `await` gets added to JS, we need to declare this "constant" +// using the `let` keyword instead, and then boostrap its value during the +// `loadSource` call (using the standard if(undefined){assignvalue} pattern). +let IMPORT_GLOBALS_FROM_GRAPHICS_API = undefined; + +// Really wish this was baked into the DOM API. Having to use an +// IntersectionObserver with bounding box fallback is super dumb +// from an authoring perspective. function isInViewport(e) { if (typeof window === `undefined`) return true; if (typeof document === `undefined`) return true; @@ -110,6 +117,15 @@ class GraphicsElement extends CustomElement { async loadSource() { debugLog(`loading ${this.getAttribute(`src`)}`); + if (!IMPORT_GLOBALS_FROM_GRAPHICS_API) { + const importStatement = ( + await fetch(`${MODULE_PATH}/api/graphics-api.js`).then((r) => r.text()) + ) + .match(/(export { [^}]+ })/)[0] + .replace(`export`, `import`); + IMPORT_GLOBALS_FROM_GRAPHICS_API = `${importStatement} from "${MODULE_PATH}/api/graphics-api.js"`; + } + let src = false; let codeElement = this.querySelector(`program-code`); @@ -207,7 +223,7 @@ class GraphicsElement extends CustomElement { * Program source: ${src} * Data attributes: ${JSON.stringify(this.dataset)} */ - import { GraphicsAPI, Bezier, BSpline, Vector, Matrix, Shape } from "${MODULE_PATH}/api/graphics-api.js"; + ${IMPORT_GLOBALS_FROM_GRAPHICS_API}; ${globalCode} diff --git a/docs/placeholder-style.css b/docs/placeholder-style.css index 762cb841..271f973a 100644 --- a/docs/placeholder-style.css +++ b/docs/placeholder-style.css @@ -162,7 +162,7 @@ graphics-element .slider-wrapper:first-of-type { clear: both; border-top: 1px solid black; margin-top: 0.1em; - padding-top: 2px; + padding-top: 0.4em; } graphics-element .slider-wrapper:last-of-type { diff --git a/docs/zh-CN/index.html b/docs/zh-CN/index.html index 5c9b8582..1e08091c 100644 --- a/docs/zh-CN/index.html +++ b/docs/zh-CN/index.html @@ -2321,12 +2321,12 @@ for p = 1 to points.length-3 (inclusive):

    B-Splines

    No discussion on Bézier curves is complete without also giving mention of that other beast in the curve design space: B-Splines. Easily confused to mean Bézier splines, that's not actually what they are; they are "basis function" splines, which makes a lot of difference, and we'll be looking at those differences in this section. We're not going to dive as deep into B-Splines as we have for Bézier curves (that would be an entire primer on its own) but we'll be looking at how B-Splines work, what kind of maths is involved in computing them, and how to draw them based on a number of parameters that you can pick for individual B-Splines.

    -

    First off: B-Splines are piecewise polynomial interpolation curves, where the "single curve" is built by performing polynomial interpolation over a set of points, using a sliding window of a fixed number of points. For instance, a "cubic" B-Spline defined by twelve points will have its curve built by evaluating the polynomial interpolation of four points, and the curve can be treated as a lot of different sections, each controlled by four points at a time, such that the full curve consists of smoothly connected sections defined by points {1,2,3,4}, {2,3,4,5}, ..., {8,9,10,11}, and finally {9,10,11,12}, for eight sections.

    +

    First off: B-Splines are piecewise, polynomial interpolation curves, where the "single curve" is built by performing polynomial interpolation over a set of points, using a sliding window of a fixed number of points. For instance, a "cubic" B-Spline defined by twelve points will have its curve built by evaluating the polynomial interpolation of four points, and the curve can be treated as a lot of different sections, each controlled by four points at a time, such that the full curve consists of smoothly connected sections defined by points {1,2,3,4}, {2,3,4,5}, ..., {8,9,10,11}, and finally {9,10,11,12}, for eight sections.

    What do they look like? They look like this! Tap on the graphic to add more points, and move points around to see how they map to the spline curve drawn.

    Scripts are disabled. Showing fallback image. - + @@ -2336,13 +2336,15 @@ for p = 1 to points.length-3 (inclusive):
  • for Bézier curves, the curve is defined as an interpolation of points, but:
  • for B-Splines, the curve is defined as an interpolation of curves.
  • -

    In fact, let's look at that again, but this time with the base curves shown, too. Each consecutive four points defined one curve:

    +

    In fact, let's look at that again, but this time with the base curves shown, too. Each consecutive four points define one curve:

    Scripts are disabled. Showing fallback image. - + - + + +

    In order to make this interpolation of curves work, the maths is necessarily more complex than the maths for Bézier curves, so let's have a look at how things work.

    How to compute a B-Spline curve: some maths

    @@ -2362,29 +2364,31 @@ Doing so for a degree d B-Spline with n control point

    This is another recursive function, with k values decreasing from the curve order to 1, and the value α (alpha) defined by:

    -

    That looks complicated, but it's not. Computing alpha is just a fraction involving known, plain numbers and once we have our alpha value, computing (1-alpha) is literally just "computing one minus alpha". Computing this d() function is thus simply a matter of "computing simple arithmetics but with recursion", which might be computationally expensive because we're doing "a lot of" steps, but is also computationally cheap because each step only involves very simple maths. Of course as before the recursion has to stop:

    +

    That looks complicated, but it's not. Computing alpha is just a fraction involving known, plain numbers. And, once we have our alpha value, we also have (1-alpha) because it's a trivial subtraction. Computing the d() function is thus mostly a matter of computing pretty simple arithmetical statements, with some caching of results so we can refer to them as we recurve. While the recursion might see computationally expensive, the total algorithm is cheap, as each step only involves very simple maths.

    +

    Of course, the recursion does need a stop condition:

    -

    So, we see two stopping conditions: either i becomes 0, in which case d() is zero, or k becomes zero, in which case we get the same "either 1 or 0" that we saw in the N() function above.

    -

    Thanks to Cox and de Boor, we can compute points on a B-Spline pretty easily: we just need to compute a triangle of interconnected values. For instance, d() for i=3, k=3 yields the following triangle:

    - -

    That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2): d(3,3) = a(3,3) x d(2,3) + (1-a(3,3)) x d(2,2)... and we simply keep expanding our triangle until we reach the terminating function parameters. Done deal!

    -

    One thing we need to keep in mind is that we're working with a spline that is constrained by its control points, so even though the d(..., k) values are zero or one at the lowest level, they are really "zero or one, times their respective control point", so in the next section you'll see the algorithm for running through the computation in a way that starts with a copy of the control point vector and then works its way up to that single point: that's pretty essential!

    -

    If we run this computation "down", starting at d(3,3), then without special code in place we would be computing quite a few terms multiple times at each step. On the other hand, we can also start with that last "column", we can generate the terminating d() values first, then compute the a() constants, perform our multiplications, generate the previous step's d() values, compute their a() constants, do the multiplications, etc. until we end up all the way back at the top. If we run our computation this way, we don't need any explicit caching, we can just "recycle" the list of numbers we start with and simply update them as we move up the triangle. So, let's implement that!

    -

    Cool, cool... but I don't know what to do with that information

    -

    I know, this is pretty mathy, so let's have a look at what happens when we change parameters here. We can't change the maths for the interpolation functions, so that gives us only one way to control what happens here: the knot vector itself. As such, let's look at the graph that shows the interpolation functions for a cubic B-Spline with seven points with a uniform knot vector (so we see seven identical functions), representing how much each point (represented by one function each) influences the total curvature, given our knot values. And, because exploration is the key to discovery, let's make the knot vector a thing we can actually manipulate (you will notice that knots are constrained in their value: any knot must strictly be equal to, or greater than, the previous value).

    - +

    So, we actually see two stopping conditions: either i becomes 0, in which case d() is zero, or k becomes zero, in which case we get the same "either 1 or 0" that we saw in the N() function above.

    +

    Thanks to Cox and de Boor, we can compute points on a B-Spline pretty easily using the same kind of linear interpolation we saw in de Casteljau's algorithm. For instance, if we write out d() for i=3 and k=3, we get the following recursion diagram:

    + +

    That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2), where those two are themselves a mixture of d(1,3) and d(1,2), and d(1,2) and d(1,1), respectively, which are themselves a mixture of etc. etc. We simply keep expanding our terms until we reach the stop conditions, and then sum everything back up. It's really quite elegant.

    +

    One thing we need to keep in mind is that we're working with a spline that is constrained by its control points, so even though the d(..., k) values are zero or one at the lowest level, they are really "zero or one, times their respective control point", so in the next section you'll see the algorithm for running through the computation in a way that starts with a copy of the control point vector and then works its way up to that single point, rather than first starting "on the left", working our way "to the right" and then summing back up "to the left". We can just start on the right and work our way left immediately.

    + - + + +Changing the values in the knot vector changes how much each point influences the total curvature (with some clever knot value manipulation, we can even make the influence of certain points disappear entirely!), so we can see that while the control points define the hull inside of which we're going to be drawing a curve, it is actually the knot vector that determines the actual *shape* of the curve inside that hull. + +After reading the rest of this section you may want to come back here to try some specific knot vectors, and see if the resulting interpolation landscape makes sense given what you will now think should happen! +--> -

    Changing the values in the knot vector changes how much each point influences the total curvature (with some clever knot value manipulation, we can even make the influence of certain points disappear entirely!), so we can see that while the control points define the hull inside of which we're going to be drawing a curve, it is actually the knot vector that determines the actual shape of the curve inside that hull.

    -

    After reading the rest of this section you may want to come back here to try some specific knot vectors, and see if the resulting interpolation landscape makes sense given what you will now think should happen!

    Running the computation

    Unlike the de Casteljau algorithm, where the t value stays the same at every iteration, for B-Splines that is not the case, and so we end having to (for each point we evaluate) run a fairly involving bit of recursive computation. The algorithm is discussed on this Michigan Tech page, but an easier to read version is implemented by b-spline.js, so we'll look at its code.

    Given an input value t, we first map the input to a value from the domain [0,1] to the domain [knots[degree], knots[knots.length - 1 - degree]. Then, we find the section number s that this mapped t value lies on:

    @@ -2422,7 +2426,7 @@ for(let L = 1; L <= order; L++) { - +

    This is an important point: the intervals that the knot vector defines are relative intervals, so it doesn't matter if every interval is size 1, or size 100 - the relative differences between the intervals is what shapes any particular curve.

    @@ -2435,7 +2439,7 @@ for(let L = 1; L <= order; L++) { - + @@ -2448,7 +2452,7 @@ for(let L = 1; L <= order; L++) { - + @@ -2459,10 +2463,10 @@ for(let L = 1; L <= order; L++) { Scripts are disabled. Showing fallback image. - + - +

    Of course this brings us to the final topic that any text on B-Splines must touch on before calling it a day: the NURBS, or Non-Uniform Rational B-Spline (NURBS is not a plural, the capital S actually just stands for "spline", but a lot of people mistakenly treat it as if it is, so now you know better). NURBS is an important type of curve in computer-facilitated design, used a lot in 3D modelling (typically as NURBS surfaces) as well as in arbitrary-precision 2D design due to the level of control a NURBS curve offers designers.

    diff --git a/src/build/markdown/generate-graphics-module.js b/src/build/markdown/generate-graphics-module.js index eab63349..2bb94876 100644 --- a/src/build/markdown/generate-graphics-module.js +++ b/src/build/markdown/generate-graphics-module.js @@ -1,9 +1,22 @@ +import fs from "fs-extra"; import path from "path"; import paths from "../../project-paths.js"; -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"; +// Get all the values we need to ensure our generated graphics code knows +// where it lives, and where it can find all its dependencies + +const apiSource = fs + .readFileSync( + path.join(paths.sitejs, `custom-element`, `api`, `graphics-api.js`) + ) + .toString(`utf-8`); + +const API_IMPORTS = apiSource + .match(/(export { [^}]+ })/)[0] + .replace(`export`, `import`); + const GRAPHICS_API_LOCATION = path .join( path.relative(paths.temp, paths.public), @@ -15,11 +28,28 @@ const GRAPHICS_API_LOCATION = path .split(path.sep) .join(path.posix.sep); +const IMPORT_GLOBALS_FROM_GRAPHICS_API = `${API_IMPORTS} from "${GRAPHICS_API_LOCATION}"`; + const RELATIVE_IMPORT_LOCATION = path .relative(paths.temp, paths.chapters) .split(path.sep) .join(path.posix.sep); +/** + * Node does not have a native canvas available, so we need to shim a number + * of objects and functions to ensure it can generate a "first load" snapshot + * image use the node-canvas library, instead. + */ +const canvasBuilder = function canvasBuilder(w, h) { + const canvas = CanvasBuilder.createCanvas(w, h); + const ctx = canvas.getContext("2d"); + canvas.addEventListener = canvas.setAttribute = noop; + canvas.classList = { add: noop }; + canvas.style = {}; + ctx.getTransform = () => ctx.currentTransform; + return { canvas, ctx }; +}; + /** * ...docs go here... */ @@ -35,37 +65,23 @@ function generateGraphicsModule(chapter, code, width, height, dataset) { const classCode = performCodeSurgery(split.classCode); let moduleCode = ` - import CanvasBuilder from 'canvas'; - import { GraphicsAPI, Bezier, BSpline, Vector, Matrix, Shape } from "${GRAPHICS_API_LOCATION}"; + import CanvasBuilder from 'canvas'; + ${IMPORT_GLOBALS_FROM_GRAPHICS_API}; - ${globalCode} + const noop = (()=>{}); + const Image = CanvasBuilder.Image; - const noop = (()=>{}); - const Image = CanvasBuilder.Image; + ${globalCode} - class Example extends GraphicsAPI { - ${classCode} - } + class Example extends GraphicsAPI { ${classCode} } - const example = new Example(undefined, ${width}, ${height}, (w,h) => { - const canvas = CanvasBuilder.createCanvas(w,h); - const ctx = canvas.getContext('2d'); + const canvasBuilder = ${canvasBuilder} + const dataset = ${JSON.stringify(dataset)}; + const example = new Example(undefined, ${width}, ${height}, canvasBuilder, dataset); + const canvas = example.canvas; - // as this is node-canvas, we need to shim some functions: - canvas.addEventListener = canvas.setAttribute = noop; - canvas.classList = { add: noop }; - canvas.style = {}; - ctx.getTransform = () => ctx.currentTransform; - - return { canvas, ctx}; - }, ${JSON.stringify(dataset)}); - - const canvas = example.canvas; - - export { canvas }; - `; - - // return prettier.format(moduleCode, { parser: `babel` }); + export { canvas }; + `; return moduleCode; } diff --git a/src/project-paths.js b/src/project-paths.js index b737b194..313b1756 100644 --- a/src/project-paths.js +++ b/src/project-paths.js @@ -17,6 +17,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 sitejs = path.join(publicDir, `js`); const temp = path.join(project, `temp`); const paths = { @@ -25,6 +26,7 @@ const paths = { chapters, images, public: publicDir, + sitejs, src, temp, };