mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-01-17 13:38:33 +01:00
grammar checking
This commit is contained in:
parent
d77d3649b6
commit
0ad4207ac5
@ -8,7 +8,7 @@ What do they look like? They look like this! Tap on the graphic to add more poin
|
||||
|
||||
<graphics-element title="A B-Spline example" width="600" height="300" src="./basic.js"></graphics-element>
|
||||
|
||||
The important part to notice here is that we are **not** doing the same thing with B-Splines that we do for poly-Béziers or Catmull-Rom curves: both of the latter simply define new sections as literally "new sections based on new points", so a 12 point cubic poly-Bézier curve is actually impossible, because we start with a four point curve, and then add three more points for each section that follows, so we can only have 4, 7, 10, 13, 16, etc point Poly-Béziers. Similarly, while Catmull-Rom curves can grow by adding single points, this addition of a single point introduces three implicit Bézier points. Cubic B-Splines, on the other hand, are smooth interpolations of *each possible curve involving four consecutive points*, such that at any point along the curve except for our start and end points, our on-curve coordinate is defined by four control points.
|
||||
The important part to notice here is that we are **not** doing the same thing with B-Splines that we do for poly-Béziers or Catmull-Rom curves: both of the latter simply define new sections as literally "new sections based on new points", so a 12 point cubic poly-Bézier curve is actually impossible, because we start with a four point curve, and then add three more points for each section that follows, so we can only have 4, 7, 10, 13, 16, etc. point Poly-Béziers. Similarly, while Catmull-Rom curves can grow by adding single points, this addition of a single point introduces three implicit Bézier points. Cubic B-Splines, on the other hand, are smooth interpolations of *each possible curve involving four consecutive points*, such that at any point along the curve except for our start and end points, our on-curve coordinate is defined by four control points.
|
||||
|
||||
Consider the difference to be this:
|
||||
|
||||
@ -34,7 +34,7 @@ Given a B-Spline of degree `d` and thus order `k=d+1` (so a quadratic B-Spline i
|
||||
|
||||
Which, honestly, doesn't tell us all that much. All we can see is that a point on a B-Spline curve is defined as "a mix of all the control points, weighted somehow", where the weighting is achieved through the *N(...)* function, subscripted with an obvious parameter `i`, which comes from our summation, and some magical parameter `k`. So we need to know two things: 1. what does N(t) do, and 2. what is that `k`? Let's cover both, in reverse order.
|
||||
|
||||
The parameter `k` represents the "knot interval" over which a section of curve is defined. As we learned earlier, a B-Spline curve is itself an interpoliation of curves, and we can treat each transition where a control point starts or stops influencing the total curvature as a "knot on the curve".
|
||||
The parameter `k` represents the "knot interval" over which a section of curve is defined. As we learned earlier, a B-Spline curve is itself an interpolation of curves, and we can treat each transition where a control point starts or stops influencing the total curvature as a "knot on the curve".
|
||||
Doing so for a degree `d` B-Spline with `n` control point gives us `d + n + 1` knots, defining `d + n` intervals along the curve, and it is these intervals that the above `k` subscript to the N() function applies to.
|
||||
|
||||
Then the N() function itself. What does it look like?
|
||||
@ -43,7 +43,7 @@ Then the N() function itself. What does it look like?
|
||||
N_{i,k}(t) = \left ( \frac{t-knot_i}{knot_{(i+k-1)} - knot_i}\right ) \cdot N_{i,k-1}(t) + \left ( \frac{knot_{(i+k)}-t}{knot_{(i+k)} - knot_{(i+1)}} \right ) \cdot N_{i+1,k-1}(t)
|
||||
\]
|
||||
|
||||
So this is where we see the interpolation: N(t) for an (i,k) pair (that is, for a step in the above summation, on a specific knot interval) is a mix between N(t) for (i,k-1) and N(t) for (i+1,k-1), so we see that this is a recursive iteration where `i` goes up, and `k` goes down, so it seem reasonable to expect that this recursion has to stop at some point; obviously, it does, and specifically it does so for the following `i`/`k` values:
|
||||
So this is where we see the interpolation: N(t) for an `(i,k)` pair (that is, for a step in the above summation, on a specific knot interval) is a mix between N(t) for `(i,k-1)` and N(t) for `(i+1,k-1)`, so we see that this is a recursive iteration where `i` goes up, and `k` goes down, so it seem reasonable to expect that this recursion has to stop at some point; obviously, it does, and specifically it does so for the following `i`/`k` values:
|
||||
|
||||
\[
|
||||
N_{i,1}(t) = \left\{\begin{matrix}
|
||||
@ -59,7 +59,7 @@ And this function finally has a straight up evaluation: if a `t` value lies with
|
||||
|
||||
We can, yes.
|
||||
|
||||
People far smarter than us have looked at this work, and two in particular — [Maurice Cox](https://www.npl.co.uk/people/maurice-cox) and [Carl de Boor](https://en.wikipedia.org/wiki/Carl_R._de_Boor) — came to a mathematically pleasing solution: to compute a point P(t), we can compute this point by evaluating *d(t)* on a curve section between knots *i* and *i+1*:
|
||||
People far smarter than us have looked at this work, and two in particular — [Maurice Cox](https://www.npl.co.uk/people/maurice-cox) and [Carl de Boor](https://en.wikipedia.org/wiki/Carl_R._de_Boor) — came to a mathematically pleasing solution: to compute a point P(t), we can compute this point by evaluating *d(t)* on a curve section between knots `i` and `i+1`:
|
||||
|
||||
\[
|
||||
d^k_i(t) = \alpha_{i,k} \cdot d^{k-1}_i(t) + (1-\alpha_{i,k}) \cdot d^{k-1}_{i-1}(t)
|
||||
@ -139,7 +139,7 @@ One thing we need to keep in mind is that we're working with a spline that is co
|
||||
|
||||
Unlike the de Casteljau algorithm, where the `t` value stays the same at every iteration, for B-Splines that is not the case, and so we end having to (for each point we evaluate) run a fairly involving bit of recursive computation. The algorithm is discussed on [this Michigan Tech](https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/de-Boor.html) page, but an easier to read version is implemented by [b-spline.js](https://github.com/thibauts/b-spline/blob/master/index.js#L59-L71), so we'll look at its code.
|
||||
|
||||
Given an input value `t`, we first map the input to a value from the domain [0,1] to the domain [knots[degree], knots[knots.length - 1 - degree]. Then, we find the section number `s` that this mapped `t` value lies on:
|
||||
Given an input value `t`, we first map the input to a value from the domain `[0,1]` to the domain `[knots[degree], knots[knots.length - 1 - degree]`. Then, we find the section number `s` that this mapped `t` value lies on:
|
||||
|
||||
```
|
||||
for(s=domain[0]; s < domain[1]; s++) {
|
||||
@ -233,7 +233,7 @@ While a true non-uniform rational B-Spline would be hard to work with, when we t
|
||||
|
||||
## Extending our implementation to cover rational splines
|
||||
|
||||
The algorithm for working with Rational B-Splines is virtually identical to the regular algorithm, and the extension to work in the control point weights is fairly simple: we extend each control point from a point in its original number of dimensions (2D, 3D, etc) to one dimension higher, scaling the original dimensions by the control point's weight, and then assigning that weight as its value for the extended dimension.
|
||||
The algorithm for working with Rational B-Splines is virtually identical to the regular algorithm, and the extension to work in the control point weights is fairly simple: we extend each control point from a point in its original number of dimensions (2D, 3D, etc.) to one dimension higher, scaling the original dimensions by the control point's weight, and then assigning that weight as its value for the extended dimension.
|
||||
|
||||
For example, a 2D point `(x,y)` with weight `w` becomes a 3D point `(w * x, w * y, w)`.
|
||||
|
||||
|
@ -67,7 +67,7 @@ drawComponentY(dim, degree) {
|
||||
}
|
||||
|
||||
plotDimension(dim, dimension) {
|
||||
cacheStyle();
|
||||
save();
|
||||
dimension.drawCurve();
|
||||
|
||||
setFill(`red`);
|
||||
@ -118,7 +118,7 @@ plotQuadraticDimension(t1, y1, t2, y2, dim, dimension, reverse) {
|
||||
text(`t = ${t1.toFixed(2)}`, map(t1, 0,1, 15,dim-15), y1 + 25);
|
||||
circle(t2 * dim, y2, 3);
|
||||
text(`t = ${t2.toFixed(2)}`, map(t2, 0,1, 15,dim-15), y2 + 25);
|
||||
restoreStyle();
|
||||
restore();
|
||||
}
|
||||
|
||||
|
||||
@ -159,7 +159,7 @@ plotCubicDimension(t1, y1, t2, y2, dim, dimension, reverse) {
|
||||
}
|
||||
});
|
||||
|
||||
restoreStyle();
|
||||
restore();
|
||||
}
|
||||
|
||||
getRoots(v1, v2, v3) {
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@ -31,7 +31,7 @@
|
||||
<meta property="og:locale" content="en-GB" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:published_time" content="2013-06-13T12:00:00+00:00" />
|
||||
<meta property="og:updated_time" content="2020-09-20T05:43:12+00:00" />
|
||||
<meta property="og:updated_time" content="2020-09-20T21:20:31+00:00" />
|
||||
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
|
||||
<meta property="og:section" content="Bézier Curves" />
|
||||
<meta property="og:tag" content="Bézier Curves" />
|
||||
@ -2490,7 +2490,7 @@ function getCubicRoots(pa, pb, pc, pd) {
|
||||
>
|
||||
<fallback-image>
|
||||
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\1b70524bfb7d159a48f0b370d81ee2f6.png" loading="lazy" />
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\fd68347a917c9b703ff8005287ac6ca4.png" loading="lazy" />
|
||||
<label></label> </fallback-image
|
||||
></graphics-element>
|
||||
|
||||
@ -2504,7 +2504,7 @@ function getCubicRoots(pa, pb, pc, pd) {
|
||||
>
|
||||
<fallback-image>
|
||||
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\a77025630019ef3d90ffa279879a205a.png" loading="lazy" />
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\fbfe9464c9653f5efcd04411e683faf9.png" loading="lazy" />
|
||||
<label></label> </fallback-image
|
||||
></graphics-element>
|
||||
</section>
|
||||
@ -5721,7 +5721,7 @@ for p = 1 to points.length-3 (inclusive):
|
||||
The important part to notice here is that we are <strong>not</strong> doing the same thing with B-Splines that we do for poly-Béziers or
|
||||
Catmull-Rom curves: both of the latter simply define new sections as literally "new sections based on new points", so a 12 point cubic
|
||||
poly-Bézier curve is actually impossible, because we start with a four point curve, and then add three more points for each section that
|
||||
follows, so we can only have 4, 7, 10, 13, 16, etc point Poly-Béziers. Similarly, while Catmull-Rom curves can grow by adding single
|
||||
follows, so we can only have 4, 7, 10, 13, 16, etc. point Poly-Béziers. Similarly, while Catmull-Rom curves can grow by adding single
|
||||
points, this addition of a single point introduces three implicit Bézier points. Cubic B-Splines, on the other hand, are smooth
|
||||
interpolations of <em>each possible curve involving four consecutive points</em>, such that at any point along the curve except for our
|
||||
start and end points, our on-curve coordinate is defined by four control points.
|
||||
@ -5761,7 +5761,7 @@ for p = 1 to points.length-3 (inclusive):
|
||||
</p>
|
||||
<p>
|
||||
The parameter <code>k</code> represents the "knot interval" over which a section of curve is defined. As we learned earlier, a B-Spline
|
||||
curve is itself an interpoliation of curves, and we can treat each transition where a control point starts or stops influencing the total
|
||||
curve is itself an interpolation of curves, and we can treat each transition where a control point starts or stops influencing the total
|
||||
curvature as a "knot on the curve". Doing so for a degree <code>d</code> B-Spline with <code>n</code> control point gives us
|
||||
<code>d + n + 1</code> knots, defining <code>d + n</code> intervals along the curve, and it is these intervals that the above
|
||||
<code>k</code> subscript to the N() function applies to.
|
||||
@ -5769,10 +5769,10 @@ for p = 1 to points.length-3 (inclusive):
|
||||
<p>Then the N() function itself. What does it look like?</p>
|
||||
<img class="LaTeX SVG" src="./images/chapters/bsplines/cf45d1ea00d4866abc8a058b130299b4.svg" width="584px" height="49px" loading="lazy" />
|
||||
<p>
|
||||
So this is where we see the interpolation: N(t) for an (i,k) pair (that is, for a step in the above summation, on a specific knot
|
||||
interval) is a mix between N(t) for (i,k-1) and N(t) for (i+1,k-1), so we see that this is a recursive iteration where <code>i</code> goes
|
||||
up, and <code>k</code> goes down, so it seem reasonable to expect that this recursion has to stop at some point; obviously, it does, and
|
||||
specifically it does so for the following <code>i</code>/<code>k</code> values:
|
||||
So this is where we see the interpolation: N(t) for an <code>(i,k)</code> pair (that is, for a step in the above summation, on a specific
|
||||
knot interval) is a mix between N(t) for <code>(i,k-1)</code> and N(t) for <code>(i+1,k-1)</code>, so we see that this is a recursive
|
||||
iteration where <code>i</code> goes up, and <code>k</code> goes down, so it seem reasonable to expect that this recursion has to stop at
|
||||
some point; obviously, it does, and specifically it does so for the following <code>i</code>/<code>k</code> values:
|
||||
</p>
|
||||
<img class="LaTeX SVG" src="./images/chapters/bsplines/adac18ea69cc58e01c8d5e15498e4aa6.svg" width="245px" height="40px" loading="lazy" />
|
||||
<p>
|
||||
@ -5789,7 +5789,7 @@ for p = 1 to points.length-3 (inclusive):
|
||||
People far smarter than us have looked at this work, and two in particular —
|
||||
<a href="https://www.npl.co.uk/people/maurice-cox">Maurice Cox</a> and
|
||||
<a href="https://en.wikipedia.org/wiki/Carl_R._de_Boor">Carl de Boor</a> — came to a mathematically pleasing solution: to compute a point
|
||||
P(t), we can compute this point by evaluating <em>d(t)</em> on a curve section between knots <em>i</em> and <em>i+1</em>:
|
||||
P(t), we can compute this point by evaluating <em>d(t)</em> on a curve section between knots <code>i</code> and <code>i+1</code>:
|
||||
</p>
|
||||
<img class="LaTeX SVG" src="./images/chapters/bsplines/763838ea6f9e6c6aa63ea5f9c6d9542f.svg" width="287px" height="21px" loading="lazy" />
|
||||
<p>
|
||||
@ -5836,8 +5836,9 @@ for p = 1 to points.length-3 (inclusive):
|
||||
is implemented by <a href="https://github.com/thibauts/b-spline/blob/master/index.js#L59-L71">b-spline.js</a>, so we'll look at its code.
|
||||
</p>
|
||||
<p>
|
||||
Given an input value <code>t</code>, we first map the input to a value from the domain [0,1] to the domain [knots[degree],
|
||||
knots[knots.length - 1 - degree]. Then, we find the section number <code>s</code> that this mapped <code>t</code> value lies on:
|
||||
Given an input value <code>t</code>, we first map the input to a value from the domain <code>[0,1]</code> to the domain
|
||||
<code>[knots[degree], knots[knots.length - 1 - degree]</code>. Then, we find the section number <code>s</code> that this mapped
|
||||
<code>t</code> value lies on:
|
||||
</p>
|
||||
<pre><code>for(s=domain[0]; s < domain[1]; s++) {
|
||||
if(knots[s] <= t && t <= knots[s+1]) break;
|
||||
@ -5996,7 +5997,7 @@ for(let L = 1; L <= order; L++) {
|
||||
<h2>Extending our implementation to cover rational splines</h2>
|
||||
<p>
|
||||
The algorithm for working with Rational B-Splines is virtually identical to the regular algorithm, and the extension to work in the
|
||||
control point weights is fairly simple: we extend each control point from a point in its original number of dimensions (2D, 3D, etc) to
|
||||
control point weights is fairly simple: we extend each control point from a point in its original number of dimensions (2D, 3D, etc.) to
|
||||
one dimension higher, scaling the original dimensions by the control point's weight, and then assigning that weight as its value for the
|
||||
extended dimension.
|
||||
</p>
|
||||
|
@ -33,7 +33,7 @@
|
||||
<meta property="og:locale" content="ja-JP" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:published_time" content="2013-06-13T12:00:00+00:00" />
|
||||
<meta property="og:updated_time" content="2020-09-20T05:43:12+00:00" />
|
||||
<meta property="og:updated_time" content="2020-09-20T21:20:31+00:00" />
|
||||
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
|
||||
<meta property="og:section" content="Bézier Curves" />
|
||||
<meta property="og:tag" content="Bézier Curves" />
|
||||
@ -2326,7 +2326,7 @@ function getCubicRoots(pa, pb, pc, pd) {
|
||||
>
|
||||
<fallback-image>
|
||||
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\1b70524bfb7d159a48f0b370d81ee2f6.png" loading="lazy" />
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\fd68347a917c9b703ff8005287ac6ca4.png" loading="lazy" />
|
||||
<label></label> </fallback-image
|
||||
></graphics-element>
|
||||
|
||||
@ -2340,7 +2340,7 @@ function getCubicRoots(pa, pb, pc, pd) {
|
||||
>
|
||||
<fallback-image>
|
||||
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\a77025630019ef3d90ffa279879a205a.png" loading="lazy" />
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\fbfe9464c9653f5efcd04411e683faf9.png" loading="lazy" />
|
||||
<label></label> </fallback-image
|
||||
></graphics-element>
|
||||
</section>
|
||||
@ -5557,7 +5557,7 @@ for p = 1 to points.length-3 (inclusive):
|
||||
The important part to notice here is that we are <strong>not</strong> doing the same thing with B-Splines that we do for poly-Béziers or
|
||||
Catmull-Rom curves: both of the latter simply define new sections as literally "new sections based on new points", so a 12 point cubic
|
||||
poly-Bézier curve is actually impossible, because we start with a four point curve, and then add three more points for each section that
|
||||
follows, so we can only have 4, 7, 10, 13, 16, etc point Poly-Béziers. Similarly, while Catmull-Rom curves can grow by adding single
|
||||
follows, so we can only have 4, 7, 10, 13, 16, etc. point Poly-Béziers. Similarly, while Catmull-Rom curves can grow by adding single
|
||||
points, this addition of a single point introduces three implicit Bézier points. Cubic B-Splines, on the other hand, are smooth
|
||||
interpolations of <em>each possible curve involving four consecutive points</em>, such that at any point along the curve except for our
|
||||
start and end points, our on-curve coordinate is defined by four control points.
|
||||
@ -5597,7 +5597,7 @@ for p = 1 to points.length-3 (inclusive):
|
||||
</p>
|
||||
<p>
|
||||
The parameter <code>k</code> represents the "knot interval" over which a section of curve is defined. As we learned earlier, a B-Spline
|
||||
curve is itself an interpoliation of curves, and we can treat each transition where a control point starts or stops influencing the total
|
||||
curve is itself an interpolation of curves, and we can treat each transition where a control point starts or stops influencing the total
|
||||
curvature as a "knot on the curve". Doing so for a degree <code>d</code> B-Spline with <code>n</code> control point gives us
|
||||
<code>d + n + 1</code> knots, defining <code>d + n</code> intervals along the curve, and it is these intervals that the above
|
||||
<code>k</code> subscript to the N() function applies to.
|
||||
@ -5605,10 +5605,10 @@ for p = 1 to points.length-3 (inclusive):
|
||||
<p>Then the N() function itself. What does it look like?</p>
|
||||
<img class="LaTeX SVG" src="./images/chapters/bsplines/cf45d1ea00d4866abc8a058b130299b4.svg" width="584px" height="49px" loading="lazy" />
|
||||
<p>
|
||||
So this is where we see the interpolation: N(t) for an (i,k) pair (that is, for a step in the above summation, on a specific knot
|
||||
interval) is a mix between N(t) for (i,k-1) and N(t) for (i+1,k-1), so we see that this is a recursive iteration where <code>i</code> goes
|
||||
up, and <code>k</code> goes down, so it seem reasonable to expect that this recursion has to stop at some point; obviously, it does, and
|
||||
specifically it does so for the following <code>i</code>/<code>k</code> values:
|
||||
So this is where we see the interpolation: N(t) for an <code>(i,k)</code> pair (that is, for a step in the above summation, on a specific
|
||||
knot interval) is a mix between N(t) for <code>(i,k-1)</code> and N(t) for <code>(i+1,k-1)</code>, so we see that this is a recursive
|
||||
iteration where <code>i</code> goes up, and <code>k</code> goes down, so it seem reasonable to expect that this recursion has to stop at
|
||||
some point; obviously, it does, and specifically it does so for the following <code>i</code>/<code>k</code> values:
|
||||
</p>
|
||||
<img class="LaTeX SVG" src="./images/chapters/bsplines/adac18ea69cc58e01c8d5e15498e4aa6.svg" width="245px" height="40px" loading="lazy" />
|
||||
<p>
|
||||
@ -5625,7 +5625,7 @@ for p = 1 to points.length-3 (inclusive):
|
||||
People far smarter than us have looked at this work, and two in particular —
|
||||
<a href="https://www.npl.co.uk/people/maurice-cox">Maurice Cox</a> and
|
||||
<a href="https://en.wikipedia.org/wiki/Carl_R._de_Boor">Carl de Boor</a> — came to a mathematically pleasing solution: to compute a point
|
||||
P(t), we can compute this point by evaluating <em>d(t)</em> on a curve section between knots <em>i</em> and <em>i+1</em>:
|
||||
P(t), we can compute this point by evaluating <em>d(t)</em> on a curve section between knots <code>i</code> and <code>i+1</code>:
|
||||
</p>
|
||||
<img class="LaTeX SVG" src="./images/chapters/bsplines/763838ea6f9e6c6aa63ea5f9c6d9542f.svg" width="287px" height="21px" loading="lazy" />
|
||||
<p>
|
||||
@ -5672,8 +5672,9 @@ for p = 1 to points.length-3 (inclusive):
|
||||
is implemented by <a href="https://github.com/thibauts/b-spline/blob/master/index.js#L59-L71">b-spline.js</a>, so we'll look at its code.
|
||||
</p>
|
||||
<p>
|
||||
Given an input value <code>t</code>, we first map the input to a value from the domain [0,1] to the domain [knots[degree],
|
||||
knots[knots.length - 1 - degree]. Then, we find the section number <code>s</code> that this mapped <code>t</code> value lies on:
|
||||
Given an input value <code>t</code>, we first map the input to a value from the domain <code>[0,1]</code> to the domain
|
||||
<code>[knots[degree], knots[knots.length - 1 - degree]</code>. Then, we find the section number <code>s</code> that this mapped
|
||||
<code>t</code> value lies on:
|
||||
</p>
|
||||
<pre><code>for(s=domain[0]; s < domain[1]; s++) {
|
||||
if(knots[s] <= t && t <= knots[s+1]) break;
|
||||
@ -5832,7 +5833,7 @@ for(let L = 1; L <= order; L++) {
|
||||
<h2>Extending our implementation to cover rational splines</h2>
|
||||
<p>
|
||||
The algorithm for working with Rational B-Splines is virtually identical to the regular algorithm, and the extension to work in the
|
||||
control point weights is fairly simple: we extend each control point from a point in its original number of dimensions (2D, 3D, etc) to
|
||||
control point weights is fairly simple: we extend each control point from a point in its original number of dimensions (2D, 3D, etc.) to
|
||||
one dimension higher, scaling the original dimensions by the control point's weight, and then assigning that weight as its value for the
|
||||
extended dimension.
|
||||
</p>
|
||||
|
@ -303,8 +303,8 @@ For coloring purposes, there is also the `randomColor` function:
|
||||
|
||||
For temporary work, where you might want to change some properties and then revert to the previous state, there are two functions available:
|
||||
|
||||
- `cacheStyle()` cache the current collection of properties. This uses a stack, with each call adding a new "snapshot" on the stack.
|
||||
- `restoreStyle()` restore the most recently cached state from the stack.
|
||||
- `save()` cache the current collection of properties. This uses a stack, with each call adding a new "snapshot" on the stack.
|
||||
- `restore()` restore the most recently cached state from the stack.
|
||||
|
||||
### Coordinate transform function
|
||||
|
||||
|
@ -41,7 +41,7 @@ class BaseAPI {
|
||||
if (canvasBuildFunction) {
|
||||
const { canvas, ctx } = canvasBuildFunction(width, height);
|
||||
this.canvas = canvas;
|
||||
this.ctx = enhanceContext(ctx);
|
||||
this.ctx = ctx;
|
||||
this.preSized = true;
|
||||
} else {
|
||||
this.canvas = document.createElement(`canvas`);
|
||||
@ -223,7 +223,7 @@ class BaseAPI {
|
||||
this.canvas.width = this.width;
|
||||
this.canvas.style.width = `${this.width}px`;
|
||||
this.canvas.height = this.height;
|
||||
this.ctx = enhanceContext(this.canvas.getContext(`2d`));
|
||||
this.ctx = this.canvas.getContext(`2d`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,34 +259,4 @@ class BaseAPI {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure there are cacheStyle/restoreStyle functions
|
||||
// on the Canvas context, so that it's trivial to make
|
||||
// temporary changes.
|
||||
function enhanceContext(ctx) {
|
||||
const styles = [];
|
||||
ctx.cacheStyle = () => {
|
||||
let m = ctx.currentTransform || ctx.getTransform();
|
||||
let e = {
|
||||
strokeStyle: ctx.strokeStyle,
|
||||
fillStyle: ctx.fillStyle,
|
||||
lineWidth: ctx.lineWidth,
|
||||
textAlign: ctx.textAlign,
|
||||
transform: [m.a, m.b, m.c, m.d, m.e, m.f],
|
||||
font: ctx.font,
|
||||
shadowColor: ctx.shadowColor,
|
||||
shadowBlur: ctx.shadowColor,
|
||||
};
|
||||
styles.push(e);
|
||||
};
|
||||
ctx.restoreStyle = () => {
|
||||
const v = styles.pop();
|
||||
Object.keys(v).forEach((k) => {
|
||||
let val = v[k];
|
||||
if (k !== `transform`) ctx[k] = val;
|
||||
else ctx.setTransform(val[0], val[1], val[2], val[3], val[4], val[5]);
|
||||
});
|
||||
};
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export { BaseAPI };
|
||||
|
@ -416,17 +416,17 @@ class GraphicsAPI extends BaseAPI {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache all styling values
|
||||
* Save the current state/properties on a stack
|
||||
*/
|
||||
cacheStyle() {
|
||||
this.ctx.cacheStyle();
|
||||
save() {
|
||||
this.ctx.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* restore all previous styling values
|
||||
* Restore the most recently saved state/properties from the stack
|
||||
*/
|
||||
restoreStyle() {
|
||||
this.ctx.restoreStyle();
|
||||
restore() {
|
||||
this.ctx.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -449,19 +449,19 @@ class GraphicsAPI extends BaseAPI {
|
||||
* Reset the canvas bitmap to a uniform color.
|
||||
*/
|
||||
clear(color = `white`, preserveTransforms = false) {
|
||||
this.ctx.cacheStyle();
|
||||
this.save();
|
||||
this.resetTransform();
|
||||
this.ctx.fillStyle = color;
|
||||
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.ctx.restoreStyle();
|
||||
this.restore();
|
||||
if (!preserveTransforms) this.resetTransform();
|
||||
if (this._gridParams) {
|
||||
this.ctx.cacheStyle();
|
||||
this.save();
|
||||
this.setStroke(this._gridParams.color);
|
||||
this.translate(0.5, 0.5);
|
||||
this.drawGrid(this._gridParams.size);
|
||||
this.translate(-0.5, -0.5);
|
||||
this.ctx.restoreStyle();
|
||||
this.restore();
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,7 +517,7 @@ class GraphicsAPI extends BaseAPI {
|
||||
x = x.x;
|
||||
}
|
||||
const ctx = this.ctx;
|
||||
ctx.cacheStyle();
|
||||
ctx.save();
|
||||
if (alignment) {
|
||||
ctx.textAlign = alignment;
|
||||
}
|
||||
@ -527,7 +527,7 @@ class GraphicsAPI extends BaseAPI {
|
||||
this.ctx.strokeText(str, x, y);
|
||||
}
|
||||
this.ctx.fillText(str, x, y);
|
||||
ctx.restoreStyle();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,7 +68,7 @@ class Bezier extends Original {
|
||||
|
||||
drawCurve(color = `#333`) {
|
||||
const ctx = this.ctx;
|
||||
ctx.cacheStyle();
|
||||
ctx.save();
|
||||
ctx.strokeStyle = color;
|
||||
ctx.beginPath();
|
||||
const lut = this.getLUT().slice();
|
||||
@ -79,7 +79,7 @@ class Bezier extends Original {
|
||||
ctx.lineTo(p.x, p.y);
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.restoreStyle();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
drawPoints(labels = true) {
|
||||
@ -87,7 +87,7 @@ class Bezier extends Original {
|
||||
const api = this.api;
|
||||
const ctx = this.ctx;
|
||||
|
||||
ctx.cacheStyle();
|
||||
ctx.save();
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = `#999`;
|
||||
this.points.forEach((p, i) => {
|
||||
@ -100,20 +100,20 @@ class Bezier extends Original {
|
||||
api.text(`(${x},${y})`, x + 10, y + 10);
|
||||
}
|
||||
});
|
||||
ctx.restoreStyle();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
drawSkeleton(color = `#555`) {
|
||||
const api = this.api;
|
||||
const ctx = this.ctx;
|
||||
ctx.cacheStyle();
|
||||
ctx.save();
|
||||
const p = this.points;
|
||||
api.noFill();
|
||||
api.setStroke(color);
|
||||
api.start();
|
||||
p.forEach((v) => api.vertex(v.x, v.y));
|
||||
api.end();
|
||||
ctx.restoreStyle();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
getStrutPoints(t) {
|
||||
@ -139,7 +139,7 @@ class Bezier extends Original {
|
||||
|
||||
const api = this.api;
|
||||
const ctx = api.ctx;
|
||||
ctx.cacheStyle();
|
||||
ctx.save();
|
||||
api.noFill();
|
||||
api.setStroke(color);
|
||||
|
||||
@ -155,7 +155,7 @@ class Bezier extends Original {
|
||||
api.end();
|
||||
s += n;
|
||||
}
|
||||
ctx.restoreStyle();
|
||||
ctx.restore();
|
||||
|
||||
return p;
|
||||
}
|
||||
@ -167,11 +167,11 @@ class Bezier extends Original {
|
||||
MX = bbox.x.max,
|
||||
MY = bbox.y.max,
|
||||
api = this.api;
|
||||
api.cacheStyle();
|
||||
api.save();
|
||||
api.noFill();
|
||||
api.setStroke(color);
|
||||
api.rect(mx, my, MX - mx, MY - my);
|
||||
api.restoreStyle();
|
||||
api.restore();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
<meta property="og:locale" content="en-GB" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:published_time" content="2020-09-18T12:00:00+00:00" />
|
||||
<meta property="og:updated_time" content="2020-09-20T05:43:12+00:00" />
|
||||
<meta property="og:updated_time" content="2020-09-20T21:20:31+00:00" />
|
||||
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
|
||||
<meta property="og:section" content="Bézier Curves" />
|
||||
<meta property="og:tag" content="Bézier Curves" />
|
||||
|
@ -33,7 +33,7 @@
|
||||
<meta property="og:locale" content="zh-CN" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:published_time" content="2013-06-13T12:00:00+00:00" />
|
||||
<meta property="og:updated_time" content="2020-09-20T05:43:12+00:00" />
|
||||
<meta property="og:updated_time" content="2020-09-20T21:20:31+00:00" />
|
||||
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
|
||||
<meta property="og:section" content="Bézier Curves" />
|
||||
<meta property="og:tag" content="Bézier Curves" />
|
||||
@ -2298,7 +2298,7 @@ function getCubicRoots(pa, pb, pc, pd) {
|
||||
>
|
||||
<fallback-image>
|
||||
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\1b70524bfb7d159a48f0b370d81ee2f6.png" loading="lazy" />
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\fd68347a917c9b703ff8005287ac6ca4.png" loading="lazy" />
|
||||
<label></label> </fallback-image
|
||||
></graphics-element>
|
||||
|
||||
@ -2312,7 +2312,7 @@ function getCubicRoots(pa, pb, pc, pd) {
|
||||
>
|
||||
<fallback-image>
|
||||
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\a77025630019ef3d90ffa279879a205a.png" loading="lazy" />
|
||||
<img width="825px" height="275px" src="images\chapters\extremities\fbfe9464c9653f5efcd04411e683faf9.png" loading="lazy" />
|
||||
<label></label> </fallback-image
|
||||
></graphics-element>
|
||||
</section>
|
||||
@ -5529,7 +5529,7 @@ for p = 1 to points.length-3 (inclusive):
|
||||
The important part to notice here is that we are <strong>not</strong> doing the same thing with B-Splines that we do for poly-Béziers or
|
||||
Catmull-Rom curves: both of the latter simply define new sections as literally "new sections based on new points", so a 12 point cubic
|
||||
poly-Bézier curve is actually impossible, because we start with a four point curve, and then add three more points for each section that
|
||||
follows, so we can only have 4, 7, 10, 13, 16, etc point Poly-Béziers. Similarly, while Catmull-Rom curves can grow by adding single
|
||||
follows, so we can only have 4, 7, 10, 13, 16, etc. point Poly-Béziers. Similarly, while Catmull-Rom curves can grow by adding single
|
||||
points, this addition of a single point introduces three implicit Bézier points. Cubic B-Splines, on the other hand, are smooth
|
||||
interpolations of <em>each possible curve involving four consecutive points</em>, such that at any point along the curve except for our
|
||||
start and end points, our on-curve coordinate is defined by four control points.
|
||||
@ -5569,7 +5569,7 @@ for p = 1 to points.length-3 (inclusive):
|
||||
</p>
|
||||
<p>
|
||||
The parameter <code>k</code> represents the "knot interval" over which a section of curve is defined. As we learned earlier, a B-Spline
|
||||
curve is itself an interpoliation of curves, and we can treat each transition where a control point starts or stops influencing the total
|
||||
curve is itself an interpolation of curves, and we can treat each transition where a control point starts or stops influencing the total
|
||||
curvature as a "knot on the curve". Doing so for a degree <code>d</code> B-Spline with <code>n</code> control point gives us
|
||||
<code>d + n + 1</code> knots, defining <code>d + n</code> intervals along the curve, and it is these intervals that the above
|
||||
<code>k</code> subscript to the N() function applies to.
|
||||
@ -5577,10 +5577,10 @@ for p = 1 to points.length-3 (inclusive):
|
||||
<p>Then the N() function itself. What does it look like?</p>
|
||||
<img class="LaTeX SVG" src="./images/chapters/bsplines/cf45d1ea00d4866abc8a058b130299b4.svg" width="584px" height="49px" loading="lazy" />
|
||||
<p>
|
||||
So this is where we see the interpolation: N(t) for an (i,k) pair (that is, for a step in the above summation, on a specific knot
|
||||
interval) is a mix between N(t) for (i,k-1) and N(t) for (i+1,k-1), so we see that this is a recursive iteration where <code>i</code> goes
|
||||
up, and <code>k</code> goes down, so it seem reasonable to expect that this recursion has to stop at some point; obviously, it does, and
|
||||
specifically it does so for the following <code>i</code>/<code>k</code> values:
|
||||
So this is where we see the interpolation: N(t) for an <code>(i,k)</code> pair (that is, for a step in the above summation, on a specific
|
||||
knot interval) is a mix between N(t) for <code>(i,k-1)</code> and N(t) for <code>(i+1,k-1)</code>, so we see that this is a recursive
|
||||
iteration where <code>i</code> goes up, and <code>k</code> goes down, so it seem reasonable to expect that this recursion has to stop at
|
||||
some point; obviously, it does, and specifically it does so for the following <code>i</code>/<code>k</code> values:
|
||||
</p>
|
||||
<img class="LaTeX SVG" src="./images/chapters/bsplines/adac18ea69cc58e01c8d5e15498e4aa6.svg" width="245px" height="40px" loading="lazy" />
|
||||
<p>
|
||||
@ -5597,7 +5597,7 @@ for p = 1 to points.length-3 (inclusive):
|
||||
People far smarter than us have looked at this work, and two in particular —
|
||||
<a href="https://www.npl.co.uk/people/maurice-cox">Maurice Cox</a> and
|
||||
<a href="https://en.wikipedia.org/wiki/Carl_R._de_Boor">Carl de Boor</a> — came to a mathematically pleasing solution: to compute a point
|
||||
P(t), we can compute this point by evaluating <em>d(t)</em> on a curve section between knots <em>i</em> and <em>i+1</em>:
|
||||
P(t), we can compute this point by evaluating <em>d(t)</em> on a curve section between knots <code>i</code> and <code>i+1</code>:
|
||||
</p>
|
||||
<img class="LaTeX SVG" src="./images/chapters/bsplines/763838ea6f9e6c6aa63ea5f9c6d9542f.svg" width="287px" height="21px" loading="lazy" />
|
||||
<p>
|
||||
@ -5644,8 +5644,9 @@ for p = 1 to points.length-3 (inclusive):
|
||||
is implemented by <a href="https://github.com/thibauts/b-spline/blob/master/index.js#L59-L71">b-spline.js</a>, so we'll look at its code.
|
||||
</p>
|
||||
<p>
|
||||
Given an input value <code>t</code>, we first map the input to a value from the domain [0,1] to the domain [knots[degree],
|
||||
knots[knots.length - 1 - degree]. Then, we find the section number <code>s</code> that this mapped <code>t</code> value lies on:
|
||||
Given an input value <code>t</code>, we first map the input to a value from the domain <code>[0,1]</code> to the domain
|
||||
<code>[knots[degree], knots[knots.length - 1 - degree]</code>. Then, we find the section number <code>s</code> that this mapped
|
||||
<code>t</code> value lies on:
|
||||
</p>
|
||||
<pre><code>for(s=domain[0]; s < domain[1]; s++) {
|
||||
if(knots[s] <= t && t <= knots[s+1]) break;
|
||||
@ -5804,7 +5805,7 @@ for(let L = 1; L <= order; L++) {
|
||||
<h2>Extending our implementation to cover rational splines</h2>
|
||||
<p>
|
||||
The algorithm for working with Rational B-Splines is virtually identical to the regular algorithm, and the extension to work in the
|
||||
control point weights is fairly simple: we extend each control point from a point in its original number of dimensions (2D, 3D, etc) to
|
||||
control point weights is fairly simple: we extend each control point from a point in its original number of dimensions (2D, 3D, etc.) to
|
||||
one dimension higher, scaling the original dimensions by the control point's weight, and then assigning that weight as its value for the
|
||||
extended dimension.
|
||||
</p>
|
||||
|
51
package-lock.json
generated
51
package-lock.json
generated
@ -84,6 +84,11 @@
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||
"dev": true
|
||||
},
|
||||
"adverb-where": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/adverb-where/-/adverb-where-0.2.1.tgz",
|
||||
"integrity": "sha512-IAveFBziMRMNPKFdWRcdIKaJvJG1cAfU9/tf9MzqQ84Dh4QjD9eqwnt4hNSt9cbrcEJD74BMIOaRVgVDEU7MwQ=="
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
@ -668,6 +673,11 @@
|
||||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"e-prime": {
|
||||
"version": "0.10.4",
|
||||
"resolved": "https://registry.npmjs.org/e-prime/-/e-prime-0.10.4.tgz",
|
||||
"integrity": "sha512-tzBmM2mFSnAq5BuxPSyin6qXb3yMe1wufJN7L7ZPcEWS5S+jI2dhKQEoqHVEcSMMXo/j5lcWpX5jzA6wLSmX6w=="
|
||||
},
|
||||
"ecstatic": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz",
|
||||
@ -1631,6 +1641,11 @@
|
||||
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
|
||||
"dev": true
|
||||
},
|
||||
"no-cliches": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/no-cliches/-/no-cliches-0.2.2.tgz",
|
||||
"integrity": "sha512-iEOqDAOFl6uN5jZGRj39Jdo8qALzf2HPXtpFso8+BMaDylDrUMYMwhFbfYGgxdnMlsRnxYTwv68kaXEpsHIapg=="
|
||||
},
|
||||
"node-pre-gyp": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz",
|
||||
@ -1926,6 +1941,11 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"passive-voice": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/passive-voice/-/passive-voice-0.1.0.tgz",
|
||||
"integrity": "sha1-Fv+RrkC6DpLEPmcXY/3IQqcCcLE="
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@ -2495,6 +2515,11 @@
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"too-wordy": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/too-wordy/-/too-wordy-0.2.2.tgz",
|
||||
"integrity": "sha512-ePZfjs1ajL4b8jT4MeVId+9Ci5hJCzAtNIEXIHyFYmKmQuX+eHC/RNv6tuLMUhrGrhJ+sYWW/lBF/LKILHGZEA=="
|
||||
},
|
||||
"trim-newlines": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz",
|
||||
@ -2595,6 +2620,11 @@
|
||||
"foreachasync": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"weasel-words": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/weasel-words/-/weasel-words-0.1.1.tgz",
|
||||
"integrity": "sha1-cTeUZYXHP+RIggE4U70ADF1oek4="
|
||||
},
|
||||
"which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
@ -2670,6 +2700,27 @@
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
},
|
||||
"write-good": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/write-good/-/write-good-1.0.3.tgz",
|
||||
"integrity": "sha512-8n5k4h00o91iYlzudpO8VqmeEjJ2qfMtVA+rhYUVYmibEOEoOXN+YGxB7CY7BZbN2Pv8aDK4oiLSN0a3pAwwOQ==",
|
||||
"requires": {
|
||||
"adverb-where": "^0.2.1",
|
||||
"commander": "^2.19.0",
|
||||
"e-prime": "^0.10.2",
|
||||
"no-cliches": "^0.2.2",
|
||||
"passive-voice": "^0.1.0",
|
||||
"too-wordy": "^0.2.2",
|
||||
"weasel-words": "^0.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
|
@ -65,5 +65,8 @@
|
||||
"open-cli": "^6.0.1",
|
||||
"prettier": "^2.0.5",
|
||||
"svgo": "git://github.com/Pomax/svgo#dereferenceUses-plugin"
|
||||
},
|
||||
"dependencies": {
|
||||
"write-good": "^1.0.3"
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user