From 99e4015614dc6cda0012f918119cd67dde96477e Mon Sep 17 00:00:00 2001 From: Pomax Date: Tue, 11 Aug 2020 22:50:51 -0700 Subject: [PATCH] improvements --- chapters/weightcontrol/content.en-GB.md | 8 +- index.html | 224 +----------------------- ja-JP/index.html | 215 +---------------------- lib/custom-element/graphics-element.js | 8 + tools/build/latex/latex-to-svg.js | 2 +- zh-CN/index.html | 224 +----------------------- 6 files changed, 25 insertions(+), 656 deletions(-) diff --git a/chapters/weightcontrol/content.en-GB.md b/chapters/weightcontrol/content.en-GB.md index aa8b3861..18c8cce6 100644 --- a/chapters/weightcontrol/content.en-GB.md +++ b/chapters/weightcontrol/content.en-GB.md @@ -23,10 +23,10 @@ This does something unexpected: it turns our polynomial into something that _isn But the best way to show what this does is to do literally that: let's look at the effect of "rationalising" our Bézier curves using an interactive graphic for a rationalised curves. The following graphic shows the Bézier curve from the previous section, "enriched" with ratio factors for each coordinate. The closer to zero we set one or more terms, the less relative influence the associated coordinate exerts on the curve (and of course the higher we set them, the more influence they have). Try to change the values and see how it affects what gets drawn: - ratio 1 1.0
- ratio 2 1.0
- ratio 3 1.0
- ratio 4 1.0 + ratio 1 1.0
+ ratio 2 1.0
+ ratio 3 1.0
+ ratio 4 1.0
You can think of the ratio values as each coordinate's "gravity": the higher the gravity, the closer to that coordinate the curve will want to be. You'll also notice that if you simply increase or decrease all the ratios by the same amount, nothing changes... much like with gravity, if the relative strengths stay the same, nothing really changes. The values define each coordinate's influence _relative to all other points_. diff --git a/index.html b/index.html index c66badc8..a8dfb64f 100644 --- a/index.html +++ b/index.html @@ -563,7 +563,6 @@ src="images/latex/b5aa26284ba3df74970a95cb047a841d.svg" width="501px" height="103px" - loading="lazy" />

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

The notation f(x) is the standard way to show that it's a @@ -650,7 +648,6 @@ src="images/latex/0f5cffd58e864fec6739a57664eb8cbd.svg" width="93px" height="36px" - loading="lazy" />

There's nothing really remarkable about them, they're just a sine @@ -666,7 +663,6 @@ src="images/latex/066a910ae6aba69c40a338320759cdd1.svg" width="100px" height="40px" - loading="lazy" />

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

There we go. x/y coordinates, linked through some @@ -736,7 +731,6 @@ src="images/latex/bb06cb82d372f822a7b35e661502bd72.svg" width="213px" height="20px" - loading="lazy" />

If the highest order term they have is , they're called @@ -756,7 +750,6 @@ src="images/latex/2adc12d0cff01d40d9e1702014a7dc19.svg" width="367px" height="64px" - loading="lazy" />

I know what you're thinking: that doesn't look too simple! But if we @@ -768,7 +761,6 @@ src="images/latex/9c18f76e76cf684ecd217ad8facc2e93.svg" width="184px" height="87px" - loading="lazy" />

Notice that 2 is the same as 1+1, and 3 is 2+1 and 1+2, and 6 is @@ -790,7 +782,6 @@ src="images/latex/e107caca1577e44293cd207388ac939c.svg" width="301px" height="60px" - loading="lazy" />

It's basically just a sum of "every combination of a and @@ -804,7 +795,6 @@ src="images/latex/9a6d17c362980775f1425d0d2ad9a36a.svg" width="300px" height="55px" - loading="lazy" />

And that's the full description for Bézier curves. Σ in this @@ -986,7 +976,6 @@ function Bezier(3,t): src="images/latex/14cb9fbbaae9e7d87ae6bef3ea7a782e.svg" width="352px" height="55px" - loading="lazy" />

That looks complicated, but as it so happens, the "weights" are @@ -1002,7 +991,6 @@ function Bezier(3,t): src="images/latex/c0d4dbc07b8ec7c0a18ea43c8a386935.svg" width="473px" 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 @@ -1137,19 +1123,19 @@ function Bezier(3,t,w[]): Scripts are disabled. Showing fallback image. ratio 1 - 1.0
ratio 2 - 1.0
ratio 3 - 1.0
ratio 4 - 1.0 @@ -1215,7 +1201,6 @@ function RationalBezier(3,t,w[],r[]): src="images/latex/b80a1cac1f9ec476d6f6646ce0e154e7.svg" width="215px" height="16px" - loading="lazy" />

The obvious start and end values here need to be @@ -1235,7 +1220,6 @@ function RationalBezier(3,t,w[],r[]): src="images/latex/d930dea961b40f4810708bd6746221a2.svg" width="215px" height="16px" - loading="lazy" />

With this we can guarantee that we never sum above 100%. By @@ -1326,7 +1310,6 @@ function RationalBezier(3,t,w[],r[]): src="images/latex/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 @@ -1369,7 +1348,6 @@ function RationalBezier(3,t,w[],r[]): src="images/latex/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 @@ -1390,7 +1367,6 @@ function RationalBezier(3,t,w[],r[]): src="images/latex/24bdad213879407a35b23c18394293aa.svg" width="227px" height="72px" - loading="lazy" />

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

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

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

and

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

and

If we could compact these matrices back to the form @@ -1779,28 +1749,24 @@ function drawCurve(points[], t): src="images/latex/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 @@ -1893,14 +1853,12 @@ function drawCurve(points[], t): src="images/latex/0f84dbf6e3ea7db732ceb9d71caf9b22.svg" width="461px" height="55px" - loading="lazy" />

We're going to do the same trick of multiplying by the identity @@ -1912,7 +1870,6 @@ function drawCurve(points[], t): src="images/latex/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 @@ -1980,7 +1932,6 @@ function drawCurve(points[], t): src="images/latex/5e3fae45d325d0f0681731fb606b6fbc.svg" width="841px" height="75px" - loading="lazy" />

and

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

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

Then, we apply one of those silly (actually, super useful) calculus @@ -2085,7 +2033,6 @@ function drawCurve(points[], t): src="images/latex/b2fda1dcce5bb13317aa42ebf5e7ea6c.svg" width="379px" height="16px" - loading="lazy" />

So, with that seemingly trivial observation, we rewrite that Bézier @@ -2097,7 +2044,6 @@ function drawCurve(points[], t): src="images/latex/41e184228d85023abdadd6ce2acb54c7.svg" width="316px" height="67px" - loading="lazy" />

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

So by using this seemingly silly trick, we can suddenly express part @@ -2126,7 +2071,6 @@ function drawCurve(points[], t): src="images/latex/483c89c8726f7fd0dca0b7de339b04bd.svg" width="471px" height="159px" - loading="lazy" />

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

And this is where we switch over from calculus to linear algebra, @@ -2161,7 +2104,6 @@ function drawCurve(points[], t): src="images/latex/773fdc86b686647c823b4f499aca3a35.svg" width="71px" height="16px" - loading="lazy" />

where the matrix M is an n+1 by @@ -2172,7 +2114,6 @@ function drawCurve(points[], t): src="images/latex/7a9120997e4a4855ecda435553a7bbdf.svg" width="336px" height="187px" - loading="lazy" />

That might look unwieldy, but it's really just a mostly-zeroes @@ -2201,7 +2142,6 @@ function drawCurve(points[], t): src="images/latex/d52f60b331c1b8d6733eb5217adfbc4d.svg" width="272px" height="116px" - loading="lazy" />

The steps taken here are:

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

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

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

    Applying the @@ -2320,7 +2257,6 @@ function drawCurve(points[], t): src="images/latex/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 @@ -2342,7 +2277,6 @@ function drawCurve(points[], t): src="images/latex/6770214cceeb0e13e371bd908867751f.svg" width="545px" height="76px" - loading="lazy" />

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

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

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

    Two of these terms fall way: the first term falls away because @@ -2396,7 +2327,6 @@ function drawCurve(points[], t): src="images/latex/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:

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

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

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

    This gives us the directional vector we want. We can normalize it to @@ -2490,14 +2414,12 @@ function drawCurve(points[], t): src="images/latex/6101b2f8b69ebabba4a2c88456a32aa0.svg" width="251px" height="25px" - loading="lazy" />

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

    @@ -2537,7 +2458,6 @@ function drawCurve(points[], t): src="images/latex/deec095950fcd1f9c980be76a7093fe6.svg" width="183px" height="37px" - loading="lazy" />

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

    And that's all we need to rotate any coordinate. Note that for @@ -2925,7 +2844,6 @@ function drawCurve(points[], t): src="images/latex/ec28870926b6ed08625d942b578e6fbe.svg" width="132px" height="104px" - loading="lazy" />

    Done. And quadratic curves have no meaningful second derivative, so @@ -2945,7 +2863,6 @@ function drawCurve(points[], t): src="images/latex/0ec5cc72a428d75defb480530b50d720.svg" width="411px" height="40px" - loading="lazy" />

    So, if we can express a Bézier component function as a plain @@ -2967,7 +2884,6 @@ function drawCurve(points[], t): src="images/latex/e06ec558d99b53e559d24524f4201951.svg" width="537px" height="36px" - loading="lazy" />

    And then, using these v values, we can find out what our @@ -2978,7 +2894,6 @@ function drawCurve(points[], t): src="images/latex/ddc6f99a543afad25c55cf16b9deeed9.svg" width="315px" height="119px" - loading="lazy" />

    This gives us thee coefficients a, b, and @@ -2992,7 +2907,6 @@ function drawCurve(points[], t): src="images/latex/d9e66caeb45b6643112ce3d971b17e5b.svg" width="308px" height="63px" - loading="lazy" />

    Easy-peasy. We can now almost trivially find the roots by plugging @@ -3019,7 +2933,6 @@ function drawCurve(points[], t): src="images/latex/997a8cc704c0ab0e364cb8b532df90b0.svg" width="253px" height="44px" - loading="lazy" />

    This is easier because for the "easier formula" we can use @@ -3193,7 +3106,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/04336edba7697ca91861821e32fc14be.svg" width="124px" height="45px" - loading="lazy" />

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

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

    If we then rotate the curve so that its end point lies on the @@ -3335,7 +3245,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/a9af1c06a00bb3c4af816a138fb0a66d.svg" width="452px" 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 @@ -3425,7 +3333,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/bafdb6583323bda71d9a15c02d1fdec2.svg" width="59px" height="16px" - loading="lazy" />

    What we're saying here is that given the curvature function @@ -3441,7 +3348,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/2029bca9f4fa15739553636af99b70a8.svg" width="385px" height="21px" - loading="lazy" />

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

    And of course the same functions for y:

    Asking a computer to now compose the C(t) function for us @@ -3493,7 +3397,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/b2433959e1f451fa3bf238fc37e04527.svg" width="552px" height="97px" - loading="lazy" />

    That is... unwieldy. So, we note that there are a lot of terms @@ -3513,7 +3416,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/4dbe6398d0075b5b9ef39458ef620616.svg" width="560px" height="21px" - loading="lazy" />

    That's a lot easier to work with: we see a fair number of terms that @@ -3525,7 +3427,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/d7a657089da19f032dd3b3e1d9ed1d89.svg" width="509px" height="73px" - loading="lazy" />

    This is a plain quadratic curve, and we know how to solve @@ -3536,7 +3437,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/b94df866222bed63d123df6b839a4d14.svg" width="435px" height="55px" - loading="lazy" />

    We can easily compute this value if the discriminator isn't @@ -3624,7 +3524,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/63ccae0ebe0ca70dc2afb507ab32e4bd.svg" width="180px" height="37px" - loading="lazy" />

  1. @@ -3638,7 +3537,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/add5f7fb210a306fe9ff933113f6fb91.svg" width="231px" height="39px" - loading="lazy" />
  2. @@ -3652,7 +3550,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/4230e959138d8400e04abf316360009a.svg" width="153px" height="37px" - loading="lazy" />
  3. @@ -3748,7 +3645,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/d089cc0687982a3302249bb82af3fc16.svg" width="472px" height="55px" - loading="lazy" />

    Sweet! z stays 1, so we can effectively ignore it entirely, @@ -3760,7 +3656,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/058fa85ac31eb666857a860fdedd79df.svg" width="455px" height="57px" - loading="lazy" />

    Running all our coordinates through this transformation gives a new @@ -3779,7 +3674,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/10025fdab2b3fd20f5d389cbe7e3e3ce.svg" width="195px" height="53px" - loading="lazy" />

    So we want some shearing value that, when multiplied by y, @@ -3791,7 +3685,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/20684d22b3ddc52fd6abde8ce56608a9.svg" width="133px" height="67px" - loading="lazy" />

    Now, running this on all our points generates a new set of @@ -3812,7 +3705,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/8cbef24b8c3b26f9daf2f89d27d36e95.svg" width="137px" height="71px" - loading="lazy" />

    Then, finally, this generates a new set of coordinates, let's call @@ -3832,7 +3724,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/0430e8c7f7d4ec80e6527f96f3d56e5c.svg" width="140px" height="65px" - loading="lazy" />

    And this generates our final set of four coordinates. Of these, we @@ -3846,7 +3737,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/2f85d84f0e3dd14cc25e48583aed3822.svg" width="455px" height="91px" - loading="lazy" />

    That looks very complex, but notice that every coordinate value is @@ -3864,7 +3754,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/83262761bb7fa9b832fe483ded436973.svg" width="353px" height="59px" - loading="lazy" />

    Suddenly things look a lot simpler: the mapped x is fairly straight @@ -3878,7 +3767,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/f3261ad2802d980ebe6e35b272375700.svg" width="408px" height="40px" - loading="lazy" />

    That's kind of super-simple to write out in code, I think you'll @@ -3982,7 +3870,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/9df91c28af38c1ba2e2d38d2714c9446.svg" width="335px" height="19px" - loading="lazy" />

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

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

    You might be wondering "where did all the other 'minus x' for all @@ -4074,7 +3959,6 @@ if (roots.length > 0) { src="images/latex/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 @@ -4133,7 +4016,6 @@ if (roots.length > 0) { src="images/latex/e168758d35b8f6781617eda5a32b20bf.svg" width="636px" height="71px" - loading="lazy" />

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

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

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

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

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

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

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

  4. @@ -4873,7 +4749,6 @@ lli = function(line1, line2): src="images/latex/34fe255294faf45ab02128f7997b92ce.svg" width="197px" height="16px" - loading="lazy" />

    So that just leaves finding A.

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

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

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

    And that's it, all values found.

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

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

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

    And then we can compute the new control points:

    And that's cubic curve manipulation.

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

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

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

    We can do the same for the cubic curve, of course. We know the @@ -5262,7 +5126,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -5289,7 +5150,6 @@ lli = function(line1, line2): src="images/latex/9151c0fdf9689ee598a2d029ab2ffe34.svg" width="491px" height="92px" - loading="lazy" />

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

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

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

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

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

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

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

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

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

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

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

    Where did this derivative come from?

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

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

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

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

    Thus:

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

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

    Replace point/tangent vector with the expression for @@ -5749,7 +5591,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -5768,7 +5608,6 @@ lli = function(line1, line2): src="images/latex/8f56909fcb62b8eef18b9b9559575c13.svg" width="353px" height="73px" - loading="lazy" />

    So, if we want to express a Catmull-Rom curve using a Bézier @@ -5779,7 +5618,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -5798,7 +5635,6 @@ lli = function(line1, line2): src="images/latex/a47b072a325812ac4f0ff52c22792588.svg" width="440px" height="84px" - loading="lazy" />

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

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

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

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

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

    1. rewrite to pure coordinate form:
    2. @@ -5867,7 +5698,6 @@ lli = function(line1, line2): src="images/latex/f814bb8d627f9c8f33b347c1cf13d4c7.svg" width="324px" height="84px" - loading="lazy" />
      1. rewrite for "normal" coordinate vector:
      2. @@ -5877,7 +5707,6 @@ lli = function(line1, line2): src="images/latex/5f2750de827497375d9a915f96686885.svg" width="441px" height="81px" - loading="lazy" />
        1. merge the inner matrices:
        2. @@ -5887,7 +5716,6 @@ lli = function(line1, line2): src="images/latex/79e333cd0c569657eea033b04fb5e61b.svg" width="348px" height="84px" - loading="lazy" />
          1. rewrite for Bézier matrix form:
          2. @@ -5897,7 +5725,6 @@ lli = function(line1, line2): src="images/latex/1b8a782f7540503d38067317e4cd00b0.svg" width="431px" height="77px" - loading="lazy" />
            1. @@ -5910,7 +5737,6 @@ lli = function(line1, line2): src="images/latex/e3d30ab368dcead1411532ce3814d3f3.svg" width="348px" height="81px" - loading="lazy" />

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

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

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

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

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

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

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

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

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

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

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

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

              i.e. we want a point that lies on the vertical line through S (at @@ -6755,7 +6569,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -6796,7 +6606,6 @@ lli = function(line1, line2): src="images/latex/fe32474b4616ee9478e1308308f1b6bf.svg" width="188px" height="32px" - loading="lazy" />

              We compute T, observing that if t=0.5, the polynomial @@ -6807,7 +6616,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -6826,7 +6633,6 @@ lli = function(line1, line2): src="images/latex/adbd056f4b8fcd05b1d4f2fce27d7657.svg" width="399px" height="153px" - loading="lazy" />

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

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

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

              where "a" is some scaling factor, and:

              where "b" is also some scaling factor.

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

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

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

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

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

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

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

              and

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

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

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

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

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

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

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

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

              So, we see two stopping conditions: either i becomes 0, @@ -7641,7 +7426,6 @@ lli = function(line1, line2): src="images/latex/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/ja-JP/index.html b/ja-JP/index.html index e6e19a54..de2673ef 100644 --- a/ja-JP/index.html +++ b/ja-JP/index.html @@ -472,7 +472,6 @@ src="images/latex/4df088f01d0fd4de84a50bbc2e25f8a7.svg" width="433px" height="108px" - loading="lazy" />

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

              f(x)

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

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

              きました。x/y座標です。謎の値tを通して繫がっています。 @@ -581,7 +576,6 @@ src="images/latex/bb06cb82d372f822a7b35e661502bd72.svg" width="213px" height="20px" - loading="lazy" />

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

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

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

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

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

              複雑そうに見えますが、運がいいことに「重み」というのは実はただの座標値です。というのはn次の曲線の場合、w0が始点の座標、wnが終点の座標となり、その間はどれも制御点の座標になります。例えば、始点が(120,160)、制御点が(35,200)と(220,260)、終点が(220,40)となる3次ベジエ曲線は、次のようになります。 @@ -746,7 +735,6 @@ function Bezier(3,t): src="images/latex/c0d4dbc07b8ec7c0a18ea43c8a386935.svg" width="473px" 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 @@ -867,19 +853,19 @@ function Bezier(3,t,w[]): Scripts are disabled. Showing fallback image. ratio 1 - 1.0
              ratio 2 - 1.0
              ratio 3 - 1.0
              ratio 4 - 1.0 @@ -941,7 +927,6 @@ function RationalBezier(3,t,w[],r[]): src="images/latex/a75e84f0e7f92c2f3e8ef10b49744ba5.svg" width="252px" height="20px" - loading="lazy" />

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

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

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

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

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

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

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

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

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

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

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

              t

              ならびに

              曲線をある点t = z

              ならびに

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

              [M · M-1

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

              すばらしい

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

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

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

              おみごと

              および

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

              および

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

              しかし同時にこの規則から、n次の曲線をn-1次の曲線へと次数下げすることは、一般には不可能だという結論も得られます。なぜなら、制御点をきれいに「引き離す」ことができないからです。試してみたところで、得られる曲線は元と同じにはなりません。それどころか、まったくの別物に見えるかもしれません。 @@ -1640,7 +1589,6 @@ function drawCurve(points[], t): src="images/latex/eb4442acc5bc17f4649eb04b2953ed9b.svg" width="333px" height="44px" - loading="lazy" />

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

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

              Applying the @@ -1694,7 +1640,6 @@ function drawCurve(points[], t): src="images/latex/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 @@ -1716,7 +1660,6 @@ function drawCurve(points[], t): src="images/latex/6770214cceeb0e13e371bd908867751f.svg" width="545px" height="76px" - loading="lazy" />

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

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

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

              Two of these terms fall way: the first term falls away because @@ -1770,7 +1710,6 @@ function drawCurve(points[], t): src="images/latex/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:

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

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

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

    This gives us the directional vector we want. We can normalize it to @@ -1864,14 +1797,12 @@ function drawCurve(points[], t): src="images/latex/6101b2f8b69ebabba4a2c88456a32aa0.svg" width="251px" height="25px" - loading="lazy" />

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

    @@ -1911,7 +1841,6 @@ function drawCurve(points[], t): src="images/latex/deec095950fcd1f9c980be76a7093fe6.svg" width="183px" height="37px" - loading="lazy" />

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

    And that's all we need to rotate any coordinate. Note that for @@ -2299,7 +2227,6 @@ function drawCurve(points[], t): src="images/latex/ec28870926b6ed08625d942b578e6fbe.svg" width="132px" height="104px" - loading="lazy" />

    Done. And quadratic curves have no meaningful second derivative, so @@ -2319,7 +2246,6 @@ function drawCurve(points[], t): src="images/latex/0ec5cc72a428d75defb480530b50d720.svg" width="411px" height="40px" - loading="lazy" />

    So, if we can express a Bézier component function as a plain @@ -2341,7 +2267,6 @@ function drawCurve(points[], t): src="images/latex/e06ec558d99b53e559d24524f4201951.svg" width="537px" height="36px" - loading="lazy" />

    And then, using these v values, we can find out what our @@ -2352,7 +2277,6 @@ function drawCurve(points[], t): src="images/latex/ddc6f99a543afad25c55cf16b9deeed9.svg" width="315px" height="119px" - loading="lazy" />

    This gives us thee coefficients a, b, and @@ -2366,7 +2290,6 @@ function drawCurve(points[], t): src="images/latex/d9e66caeb45b6643112ce3d971b17e5b.svg" width="308px" height="63px" - loading="lazy" />

    Easy-peasy. We can now almost trivially find the roots by plugging @@ -2393,7 +2316,6 @@ function drawCurve(points[], t): src="images/latex/997a8cc704c0ab0e364cb8b532df90b0.svg" width="253px" height="44px" - loading="lazy" />

    This is easier because for the "easier formula" we can use @@ -2567,7 +2489,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/04336edba7697ca91861821e32fc14be.svg" width="124px" height="45px" - loading="lazy" />

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

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

    If we then rotate the curve so that its end point lies on the @@ -2709,7 +2628,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/a9af1c06a00bb3c4af816a138fb0a66d.svg" width="452px" 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 @@ -2799,7 +2716,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/bafdb6583323bda71d9a15c02d1fdec2.svg" width="59px" height="16px" - loading="lazy" />

    What we're saying here is that given the curvature function @@ -2815,7 +2731,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/2029bca9f4fa15739553636af99b70a8.svg" width="385px" height="21px" - loading="lazy" />

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

    And of course the same functions for y:

    Asking a computer to now compose the C(t) function for us @@ -2867,7 +2780,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/b2433959e1f451fa3bf238fc37e04527.svg" width="552px" height="97px" - loading="lazy" />

    That is... unwieldy. So, we note that there are a lot of terms @@ -2887,7 +2799,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/4dbe6398d0075b5b9ef39458ef620616.svg" width="560px" height="21px" - loading="lazy" />

    That's a lot easier to work with: we see a fair number of terms that @@ -2899,7 +2810,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/d7a657089da19f032dd3b3e1d9ed1d89.svg" width="509px" height="73px" - loading="lazy" />

    This is a plain quadratic curve, and we know how to solve @@ -2910,7 +2820,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/b94df866222bed63d123df6b839a4d14.svg" width="435px" height="55px" - loading="lazy" />

    We can easily compute this value if the discriminator isn't @@ -2998,7 +2907,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/63ccae0ebe0ca70dc2afb507ab32e4bd.svg" width="180px" height="37px" - loading="lazy" />

  5. @@ -3012,7 +2920,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/add5f7fb210a306fe9ff933113f6fb91.svg" width="231px" height="39px" - loading="lazy" />
  6. @@ -3026,7 +2933,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/4230e959138d8400e04abf316360009a.svg" width="153px" height="37px" - loading="lazy" />
  7. @@ -3122,7 +3028,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/d089cc0687982a3302249bb82af3fc16.svg" width="472px" height="55px" - loading="lazy" />

    Sweet! z stays 1, so we can effectively ignore it entirely, @@ -3134,7 +3039,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/058fa85ac31eb666857a860fdedd79df.svg" width="455px" height="57px" - loading="lazy" />

    Running all our coordinates through this transformation gives a new @@ -3153,7 +3057,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/10025fdab2b3fd20f5d389cbe7e3e3ce.svg" width="195px" height="53px" - loading="lazy" />

    So we want some shearing value that, when multiplied by y, @@ -3165,7 +3068,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/20684d22b3ddc52fd6abde8ce56608a9.svg" width="133px" height="67px" - loading="lazy" />

    Now, running this on all our points generates a new set of @@ -3186,7 +3088,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/8cbef24b8c3b26f9daf2f89d27d36e95.svg" width="137px" height="71px" - loading="lazy" />

    Then, finally, this generates a new set of coordinates, let's call @@ -3206,7 +3107,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/0430e8c7f7d4ec80e6527f96f3d56e5c.svg" width="140px" height="65px" - loading="lazy" />

    And this generates our final set of four coordinates. Of these, we @@ -3220,7 +3120,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/2f85d84f0e3dd14cc25e48583aed3822.svg" width="455px" height="91px" - loading="lazy" />

    That looks very complex, but notice that every coordinate value is @@ -3238,7 +3137,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/83262761bb7fa9b832fe483ded436973.svg" width="353px" height="59px" - loading="lazy" />

    Suddenly things look a lot simpler: the mapped x is fairly straight @@ -3252,7 +3150,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/f3261ad2802d980ebe6e35b272375700.svg" width="408px" height="40px" - loading="lazy" />

    That's kind of super-simple to write out in code, I think you'll @@ -3356,7 +3253,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/9df91c28af38c1ba2e2d38d2714c9446.svg" width="335px" height="19px" - loading="lazy" />

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

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

    You might be wondering "where did all the other 'minus x' for all @@ -3448,7 +3342,6 @@ if (roots.length > 0) { src="images/latex/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 @@ -3507,7 +3399,6 @@ if (roots.length > 0) { src="images/latex/e168758d35b8f6781617eda5a32b20bf.svg" width="636px" height="71px" - loading="lazy" />

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

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

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

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

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

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

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

  8. @@ -4247,7 +4132,6 @@ lli = function(line1, line2): src="images/latex/34fe255294faf45ab02128f7997b92ce.svg" width="197px" height="16px" - loading="lazy" />

    So that just leaves finding A.

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

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

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

    And that's it, all values found.

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

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

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

    And then we can compute the new control points:

    And that's cubic curve manipulation.

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

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

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

    We can do the same for the cubic curve, of course. We know the @@ -4636,7 +4509,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -4663,7 +4533,6 @@ lli = function(line1, line2): src="images/latex/9151c0fdf9689ee598a2d029ab2ffe34.svg" width="491px" height="92px" - loading="lazy" />

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

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

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

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

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

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

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

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

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

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

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

    Where did this derivative come from?

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

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

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

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

    Thus:

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

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

    Replace point/tangent vector with the expression for @@ -5123,7 +4974,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -5142,7 +4991,6 @@ lli = function(line1, line2): src="images/latex/8f56909fcb62b8eef18b9b9559575c13.svg" width="353px" height="73px" - loading="lazy" />

    So, if we want to express a Catmull-Rom curve using a Bézier @@ -5153,7 +5001,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -5172,7 +5018,6 @@ lli = function(line1, line2): src="images/latex/a47b072a325812ac4f0ff52c22792588.svg" width="440px" height="84px" - loading="lazy" />

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

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

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

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

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

    1. rewrite to pure coordinate form:
    2. @@ -5241,7 +5081,6 @@ lli = function(line1, line2): src="images/latex/f814bb8d627f9c8f33b347c1cf13d4c7.svg" width="324px" height="84px" - loading="lazy" />
      1. rewrite for "normal" coordinate vector:
      2. @@ -5251,7 +5090,6 @@ lli = function(line1, line2): src="images/latex/5f2750de827497375d9a915f96686885.svg" width="441px" height="81px" - loading="lazy" />
        1. merge the inner matrices:
        2. @@ -5261,7 +5099,6 @@ lli = function(line1, line2): src="images/latex/79e333cd0c569657eea033b04fb5e61b.svg" width="348px" height="84px" - loading="lazy" />
          1. rewrite for Bézier matrix form:
          2. @@ -5271,7 +5108,6 @@ lli = function(line1, line2): src="images/latex/1b8a782f7540503d38067317e4cd00b0.svg" width="431px" height="77px" - loading="lazy" />
            1. @@ -5284,7 +5120,6 @@ lli = function(line1, line2): src="images/latex/e3d30ab368dcead1411532ce3814d3f3.svg" width="348px" height="81px" - loading="lazy" />

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

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

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

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

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

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

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

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

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

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

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

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

              i.e. we want a point that lies on the vertical line through S (at @@ -6129,7 +5952,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -6170,7 +5989,6 @@ lli = function(line1, line2): src="images/latex/fe32474b4616ee9478e1308308f1b6bf.svg" width="188px" height="32px" - loading="lazy" />

              We compute T, observing that if t=0.5, the polynomial @@ -6181,7 +5999,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -6200,7 +6016,6 @@ lli = function(line1, line2): src="images/latex/adbd056f4b8fcd05b1d4f2fce27d7657.svg" width="399px" height="153px" - loading="lazy" />

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

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

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

              where "a" is some scaling factor, and:

              where "b" is also some scaling factor.

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

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

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

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

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

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

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

              and

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

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

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

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

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

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

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

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

              So, we see two stopping conditions: either i becomes 0, @@ -7015,7 +6809,6 @@ lli = function(line1, line2): src="images/latex/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/lib/custom-element/graphics-element.js b/lib/custom-element/graphics-element.js index 903dcb7c..44d5c63c 100644 --- a/lib/custom-element/graphics-element.js +++ b/lib/custom-element/graphics-element.js @@ -18,7 +18,15 @@ CustomElement.register(class ProgramCode extends HTMLElement {}); class GraphicsElement extends CustomElement { constructor() { super({ header: false, footer: false }); + + // Strip out fallback images: if we can get here, + // we should not be loading fallback images because + // we know we're showing live content instead. + let fallback = this.querySelector(`fallback-image`); + if (fallback) this.removeChild(fallback); + this.loadSource(); + if (this.title) { this.label = document.createElement(`label`); this.label.textContent = this.title; diff --git a/tools/build/latex/latex-to-svg.js b/tools/build/latex/latex-to-svg.js index fe919e97..c04af552 100644 --- a/tools/build/latex/latex-to-svg.js +++ b/tools/build/latex/latex-to-svg.js @@ -141,7 +141,7 @@ export default async function latexToSVG(latex, chapter, localeStrings, block) { return ``; + )}" width="${Math.round(w)}px" height="${Math.round(h)}px">`; } // This function really needs better stdio capture, diff --git a/zh-CN/index.html b/zh-CN/index.html index f3e22ea4..b9d19416 100644 --- a/zh-CN/index.html +++ b/zh-CN/index.html @@ -457,7 +457,6 @@ src="images/latex/b5aa26284ba3df74970a95cb047a841d.svg" width="501px" height="103px" - loading="lazy" />

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

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

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

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

              好了,通过一些神秘的t值将x/y坐标系联系起来。 @@ -564,7 +559,6 @@ src="images/latex/bb06cb82d372f822a7b35e661502bd72.svg" width="213px" height="20px" - loading="lazy" />

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

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

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

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

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

              看起来很复杂,但实际上“权重”只是我们想让曲线所拥有的坐标值:对于一条nth阶曲线,w0是起始坐标,wn是终点坐标,中间的所有点都是控制点坐标。假设说一条曲线的起点为(120,160),终点为(220,40),并受点(35,200)和点(220,260)的控制,贝塞尔曲线方程就为: @@ -727,7 +716,6 @@ function Bezier(3,t): src="images/latex/c0d4dbc07b8ec7c0a18ea43c8a386935.svg" width="473px" 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 @@ -848,19 +834,19 @@ function Bezier(3,t,w[]): Scripts are disabled. Showing fallback image. ratio 1 - 1.0
              ratio 2 - 1.0
              ratio 3 - 1.0
              ratio 4 - 1.0 @@ -922,7 +908,6 @@ function RationalBezier(3,t,w[],r[]): src="images/latex/b80a1cac1f9ec476d6f6646ce0e154e7.svg" width="215px" height="16px" - loading="lazy" />

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

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

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

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

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

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

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

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

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

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

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

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

              and

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

              and

              If we could compact these matrices back to the form @@ -1371,28 +1341,24 @@ function drawCurve(points[], t): src="images/latex/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 @@ -1485,14 +1445,12 @@ function drawCurve(points[], t): src="images/latex/0f84dbf6e3ea7db732ceb9d71caf9b22.svg" width="461px" height="55px" - loading="lazy" />

              We're going to do the same trick of multiplying by the identity @@ -1504,7 +1462,6 @@ function drawCurve(points[], t): src="images/latex/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 @@ -1572,7 +1524,6 @@ function drawCurve(points[], t): src="images/latex/5e3fae45d325d0f0681731fb606b6fbc.svg" width="841px" height="75px" - loading="lazy" />

              and

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

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

              Then, we apply one of those silly (actually, super useful) calculus @@ -1677,7 +1625,6 @@ function drawCurve(points[], t): src="images/latex/b2fda1dcce5bb13317aa42ebf5e7ea6c.svg" width="379px" height="16px" - loading="lazy" />

              So, with that seemingly trivial observation, we rewrite that Bézier @@ -1689,7 +1636,6 @@ function drawCurve(points[], t): src="images/latex/41e184228d85023abdadd6ce2acb54c7.svg" width="316px" height="67px" - loading="lazy" />

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

              So by using this seemingly silly trick, we can suddenly express part @@ -1718,7 +1663,6 @@ function drawCurve(points[], t): src="images/latex/483c89c8726f7fd0dca0b7de339b04bd.svg" width="471px" height="159px" - loading="lazy" />

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

              And this is where we switch over from calculus to linear algebra, @@ -1753,7 +1696,6 @@ function drawCurve(points[], t): src="images/latex/773fdc86b686647c823b4f499aca3a35.svg" width="71px" height="16px" - loading="lazy" />

              where the matrix M is an n+1 by @@ -1764,7 +1706,6 @@ function drawCurve(points[], t): src="images/latex/7a9120997e4a4855ecda435553a7bbdf.svg" width="336px" height="187px" - loading="lazy" />

              That might look unwieldy, but it's really just a mostly-zeroes @@ -1793,7 +1734,6 @@ function drawCurve(points[], t): src="images/latex/d52f60b331c1b8d6733eb5217adfbc4d.svg" width="272px" height="116px" - loading="lazy" />

              The steps taken here are:

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

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

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

                Applying the @@ -1912,7 +1849,6 @@ function drawCurve(points[], t): src="images/latex/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 @@ -1934,7 +1869,6 @@ function drawCurve(points[], t): src="images/latex/6770214cceeb0e13e371bd908867751f.svg" width="545px" height="76px" - loading="lazy" />

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

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

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

                Two of these terms fall way: the first term falls away because @@ -1988,7 +1919,6 @@ function drawCurve(points[], t): src="images/latex/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:

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

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

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

    This gives us the directional vector we want. We can normalize it to @@ -2082,14 +2006,12 @@ function drawCurve(points[], t): src="images/latex/6101b2f8b69ebabba4a2c88456a32aa0.svg" width="251px" height="25px" - loading="lazy" />

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

    @@ -2129,7 +2050,6 @@ function drawCurve(points[], t): src="images/latex/deec095950fcd1f9c980be76a7093fe6.svg" width="183px" height="37px" - loading="lazy" />

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

    And that's all we need to rotate any coordinate. Note that for @@ -2517,7 +2436,6 @@ function drawCurve(points[], t): src="images/latex/ec28870926b6ed08625d942b578e6fbe.svg" width="132px" height="104px" - loading="lazy" />

    Done. And quadratic curves have no meaningful second derivative, so @@ -2537,7 +2455,6 @@ function drawCurve(points[], t): src="images/latex/0ec5cc72a428d75defb480530b50d720.svg" width="411px" height="40px" - loading="lazy" />

    So, if we can express a Bézier component function as a plain @@ -2559,7 +2476,6 @@ function drawCurve(points[], t): src="images/latex/e06ec558d99b53e559d24524f4201951.svg" width="537px" height="36px" - loading="lazy" />

    And then, using these v values, we can find out what our @@ -2570,7 +2486,6 @@ function drawCurve(points[], t): src="images/latex/ddc6f99a543afad25c55cf16b9deeed9.svg" width="315px" height="119px" - loading="lazy" />

    This gives us thee coefficients a, b, and @@ -2584,7 +2499,6 @@ function drawCurve(points[], t): src="images/latex/d9e66caeb45b6643112ce3d971b17e5b.svg" width="308px" height="63px" - loading="lazy" />

    Easy-peasy. We can now almost trivially find the roots by plugging @@ -2611,7 +2525,6 @@ function drawCurve(points[], t): src="images/latex/997a8cc704c0ab0e364cb8b532df90b0.svg" width="253px" height="44px" - loading="lazy" />

    This is easier because for the "easier formula" we can use @@ -2785,7 +2698,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/04336edba7697ca91861821e32fc14be.svg" width="124px" height="45px" - loading="lazy" />

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

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

    If we then rotate the curve so that its end point lies on the @@ -2927,7 +2837,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/a9af1c06a00bb3c4af816a138fb0a66d.svg" width="452px" 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 @@ -3017,7 +2925,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/bafdb6583323bda71d9a15c02d1fdec2.svg" width="59px" height="16px" - loading="lazy" />

    What we're saying here is that given the curvature function @@ -3033,7 +2940,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/2029bca9f4fa15739553636af99b70a8.svg" width="385px" height="21px" - loading="lazy" />

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

    And of course the same functions for y:

    Asking a computer to now compose the C(t) function for us @@ -3085,7 +2989,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/b2433959e1f451fa3bf238fc37e04527.svg" width="552px" height="97px" - loading="lazy" />

    That is... unwieldy. So, we note that there are a lot of terms @@ -3105,7 +3008,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/4dbe6398d0075b5b9ef39458ef620616.svg" width="560px" height="21px" - loading="lazy" />

    That's a lot easier to work with: we see a fair number of terms that @@ -3117,7 +3019,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/d7a657089da19f032dd3b3e1d9ed1d89.svg" width="509px" height="73px" - loading="lazy" />

    This is a plain quadratic curve, and we know how to solve @@ -3128,7 +3029,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/b94df866222bed63d123df6b839a4d14.svg" width="435px" height="55px" - loading="lazy" />

    We can easily compute this value if the discriminator isn't @@ -3216,7 +3116,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/63ccae0ebe0ca70dc2afb507ab32e4bd.svg" width="180px" height="37px" - loading="lazy" />

  9. @@ -3230,7 +3129,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/add5f7fb210a306fe9ff933113f6fb91.svg" width="231px" height="39px" - loading="lazy" />
  10. @@ -3244,7 +3142,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/4230e959138d8400e04abf316360009a.svg" width="153px" height="37px" - loading="lazy" />
  11. @@ -3340,7 +3237,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/d089cc0687982a3302249bb82af3fc16.svg" width="472px" height="55px" - loading="lazy" />

    Sweet! z stays 1, so we can effectively ignore it entirely, @@ -3352,7 +3248,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/058fa85ac31eb666857a860fdedd79df.svg" width="455px" height="57px" - loading="lazy" />

    Running all our coordinates through this transformation gives a new @@ -3371,7 +3266,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/10025fdab2b3fd20f5d389cbe7e3e3ce.svg" width="195px" height="53px" - loading="lazy" />

    So we want some shearing value that, when multiplied by y, @@ -3383,7 +3277,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/20684d22b3ddc52fd6abde8ce56608a9.svg" width="133px" height="67px" - loading="lazy" />

    Now, running this on all our points generates a new set of @@ -3404,7 +3297,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/8cbef24b8c3b26f9daf2f89d27d36e95.svg" width="137px" height="71px" - loading="lazy" />

    Then, finally, this generates a new set of coordinates, let's call @@ -3424,7 +3316,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/0430e8c7f7d4ec80e6527f96f3d56e5c.svg" width="140px" height="65px" - loading="lazy" />

    And this generates our final set of four coordinates. Of these, we @@ -3438,7 +3329,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/2f85d84f0e3dd14cc25e48583aed3822.svg" width="455px" height="91px" - loading="lazy" />

    That looks very complex, but notice that every coordinate value is @@ -3456,7 +3346,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/83262761bb7fa9b832fe483ded436973.svg" width="353px" height="59px" - loading="lazy" />

    Suddenly things look a lot simpler: the mapped x is fairly straight @@ -3470,7 +3359,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/f3261ad2802d980ebe6e35b272375700.svg" width="408px" height="40px" - loading="lazy" />

    That's kind of super-simple to write out in code, I think you'll @@ -3574,7 +3462,6 @@ function getCubicRoots(pa, pb, pc, pd) { src="images/latex/9df91c28af38c1ba2e2d38d2714c9446.svg" width="335px" height="19px" - loading="lazy" />

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

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

    You might be wondering "where did all the other 'minus x' for all @@ -3666,7 +3551,6 @@ if (roots.length > 0) { src="images/latex/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 @@ -3725,7 +3608,6 @@ if (roots.length > 0) { src="images/latex/e168758d35b8f6781617eda5a32b20bf.svg" width="636px" height="71px" - loading="lazy" />

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

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

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

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

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

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

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

  12. @@ -4465,7 +4341,6 @@ lli = function(line1, line2): src="images/latex/34fe255294faf45ab02128f7997b92ce.svg" width="197px" height="16px" - loading="lazy" />

    So that just leaves finding A.

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

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

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

    And that's it, all values found.

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

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

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

    And then we can compute the new control points:

    And that's cubic curve manipulation.

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

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

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

    We can do the same for the cubic curve, of course. We know the @@ -4854,7 +4718,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -4881,7 +4742,6 @@ lli = function(line1, line2): src="images/latex/9151c0fdf9689ee598a2d029ab2ffe34.svg" width="491px" height="92px" - loading="lazy" />

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

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

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

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

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

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

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

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

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

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

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

    Where did this derivative come from?

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

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

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

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

    Thus:

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

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

    Replace point/tangent vector with the expression for @@ -5341,7 +5183,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -5360,7 +5200,6 @@ lli = function(line1, line2): src="images/latex/8f56909fcb62b8eef18b9b9559575c13.svg" width="353px" height="73px" - loading="lazy" />

    So, if we want to express a Catmull-Rom curve using a Bézier @@ -5371,7 +5210,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -5390,7 +5227,6 @@ lli = function(line1, line2): src="images/latex/a47b072a325812ac4f0ff52c22792588.svg" width="440px" height="84px" - loading="lazy" />

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

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

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

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

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

    1. rewrite to pure coordinate form:
    2. @@ -5459,7 +5290,6 @@ lli = function(line1, line2): src="images/latex/f814bb8d627f9c8f33b347c1cf13d4c7.svg" width="324px" height="84px" - loading="lazy" />
      1. rewrite for "normal" coordinate vector:
      2. @@ -5469,7 +5299,6 @@ lli = function(line1, line2): src="images/latex/5f2750de827497375d9a915f96686885.svg" width="441px" height="81px" - loading="lazy" />
        1. merge the inner matrices:
        2. @@ -5479,7 +5308,6 @@ lli = function(line1, line2): src="images/latex/79e333cd0c569657eea033b04fb5e61b.svg" width="348px" height="84px" - loading="lazy" />
          1. rewrite for Bézier matrix form:
          2. @@ -5489,7 +5317,6 @@ lli = function(line1, line2): src="images/latex/1b8a782f7540503d38067317e4cd00b0.svg" width="431px" height="77px" - loading="lazy" />
            1. @@ -5502,7 +5329,6 @@ lli = function(line1, line2): src="images/latex/e3d30ab368dcead1411532ce3814d3f3.svg" width="348px" height="81px" - loading="lazy" />

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

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

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

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

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

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

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

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

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

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

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

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

              i.e. we want a point that lies on the vertical line through S (at @@ -6347,7 +6161,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -6388,7 +6198,6 @@ lli = function(line1, line2): src="images/latex/fe32474b4616ee9478e1308308f1b6bf.svg" width="188px" height="32px" - loading="lazy" />

              We compute T, observing that if t=0.5, the polynomial @@ -6399,7 +6208,6 @@ lli = function(line1, line2): src="images/latex/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 @@ -6418,7 +6225,6 @@ lli = function(line1, line2): src="images/latex/adbd056f4b8fcd05b1d4f2fce27d7657.svg" width="399px" height="153px" - loading="lazy" />

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

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

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

              where "a" is some scaling factor, and:

              where "b" is also some scaling factor.

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

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

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

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

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

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

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

              and

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

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

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

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

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

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

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

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

              So, we see two stopping conditions: either i becomes 0, @@ -7233,7 +7018,6 @@ lli = function(line1, line2): src="images/latex/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)