circle refinement
@@ -5,7 +5,7 @@ let curve;
|
||||
setup() {
|
||||
curve = Bezier.defaultCubic(this);
|
||||
setMovable(curve.points);
|
||||
setSlider(`.slide-control`, `radius`, 102);
|
||||
setSlider(`.slide-control`, `radius`, 70);
|
||||
}
|
||||
|
||||
draw() {
|
||||
@@ -86,9 +86,18 @@ findClosest(x, y, LUT, pd2, pd1, distanceEpsilon = 5) {
|
||||
|
||||
drawProjection(x, y, LUT, i) {
|
||||
let B = refineBinary(curve, x, y, LUT, i, this.radius);
|
||||
setColor(`rgba(100,100,255)`);
|
||||
circle(B.x, B.y, 3);
|
||||
line(B.x, B.y, x, y);
|
||||
|
||||
// Our initial guess might actually not be close enough,
|
||||
// so it's possible that after binary refining, it turns
|
||||
// out the local minimum is actually still too far away
|
||||
// to count. In that case B will be undefined, so we need
|
||||
// to make sure to check before we draw it.
|
||||
|
||||
if (B) {
|
||||
setColor(`rgba(100,100,255)`);
|
||||
circle(B.x, B.y, 3);
|
||||
line(B.x, B.y, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
onMouseMove() {
|
||||
|
@@ -11,7 +11,7 @@ First, we observe that "finding intersections" in this case means that, given a
|
||||
Which seems simple enough. Unfortunately, when we expand that `dist` function, things get a lot more problematic:
|
||||
|
||||
\[
|
||||
\begin{array}{cl}
|
||||
\begin{aligned}
|
||||
r &= dist(B(t), c) \\
|
||||
&= \sqrt{ \left ( B_x{t} - c_x \right )^2 + \left ( B_y{t} - c_y \right )^2} \\
|
||||
&= \sqrt{ \left (
|
||||
@@ -21,7 +21,7 @@ Which seems simple enough. Unfortunately, when we expand that `dist` function, t
|
||||
\left (
|
||||
y_1 (1-t)^3 + 3 y_2 (1-t)^2 t + 2 y_3 (1-t) t^2 + y_4 t^3 - c_y
|
||||
\right )^2}
|
||||
\end{array}
|
||||
\end{aligned}
|
||||
\]
|
||||
|
||||
And now we have a problem because that's a sixth degree polynomial inside the square root. So, thanks to the [Abel-Ruffini theorem](https://en.wikipedia.org/wiki/Abel%E2%80%93Ruffini_theorem) that we saw before, we can't solve this by just going "square both sides because we don't care about signs"... we can't solve a sixth degree polynomial. So, we're going to have to actually evaluate that expression. We can "simplify" this by translating all our coordinates so that the center of the circle is (0,0) and all our coordinates are shifted accordingly, which makes the c<sub>x</sub> and c<sub>y</sub> terms fall away, but then we're still left with a monstrous function to solve.
|
||||
@@ -29,7 +29,7 @@ And now we have a problem because that's a sixth degree polynomial inside the sq
|
||||
So instead, we turn to the same kind of "LUT walking" that we saw for projecting points onto a curve, with a twist: instead of finding the on-curve point with the smallest distance to our projection point, we want to find the on-curve point that has the exact distance `r` to our projection point (namely, our circle center). Of course, there can be more than one such point, so there's also a bit more code to make sure we find all of them, but let's look at the steps involved:
|
||||
|
||||
```
|
||||
c = our circle's center point
|
||||
p = our circle's center point
|
||||
r = our circle's radius
|
||||
d = some initially huge value
|
||||
i = 0
|
||||
@@ -40,18 +40,21 @@ for (coordinate, index) in LUT:
|
||||
i = index
|
||||
```
|
||||
|
||||
This is _very_ similar to the code in the previous section, but adapted so that we home in on points with an exact distance, rather than "the smallest distance". So far so good. However, we also want to make sure we find _all_ the points, not just a single one, so we need a little more code for that:
|
||||
This is _very_ similar to the code in the previous section, with an extra input `r` for the circle radius, and a minor change in the "distance for this coordinate": rather than just `distance(coordinate, p)` we want to know the difference between that distance and the circle radius. After all, if that difference is zero, then the distance from the coordinate to the circle center is exactly the radius, so the coordinate lies on both the curve and the circle.
|
||||
|
||||
So far so good.
|
||||
|
||||
However, we also want to make sure we find _all_ the points, not just a single one, so we need a little more code for that:
|
||||
|
||||
```
|
||||
c = our circle's center point
|
||||
p = our circle's center point
|
||||
r = our circle's radius
|
||||
d = some initially huge value
|
||||
start = 0
|
||||
values = []
|
||||
run:
|
||||
i = findClosest(start, x, y, r, LUT)
|
||||
if (i < start) stop
|
||||
if (i>0 && i === start) stop
|
||||
do:
|
||||
i = findClosest(start, p, r, LUT)
|
||||
if i < start, or i>0 but the same as start: stop
|
||||
values.add(i);
|
||||
start = i + 2;
|
||||
```
|
||||
@@ -59,31 +62,32 @@ run:
|
||||
After running this code, `values` will be the list of all LUT coordinates that are closest to the distance `r`: we can use those values to run the same kind of refinement lookup we used for point projection (with the caveat that we're now _not_ checking for smallest distance, but for "distance closest to `r`"), and we'll have all our intersection points. Of course, that does require explaining what `findClosest` does: rather than looking for a global minimum, we're now interested in finding a _local_ minimum, so instead of checking a single point and looking at its distance value, we check _three_ points ("current", "previous" and "before previous") and then check whether they form a local minimum:
|
||||
|
||||
```
|
||||
findClosest(start, x, y, r, LUT):
|
||||
D = some very large number
|
||||
pd2 = LUT[start-2], if it exists. Otherwise use D
|
||||
pd1 = LUT[start-1], if it exists. Otherwise use D
|
||||
findClosest(start, p, r, LUT):
|
||||
minimizedDistance = some very large number
|
||||
pd2 = LUT[start-2], if it exists. Otherwise use minimizedDistance
|
||||
pd1 = LUT[start-1], if it exists. Otherwise use minimizedDistance
|
||||
slice = LUT.subset(start, LUT.length)
|
||||
epsilon = the largest point-to-point distance in our LUT
|
||||
i = -1;
|
||||
|
||||
for (point, index) in slice:
|
||||
q = abs(dist(point, (x,y)) - r);
|
||||
if pd1 < epsilon && pd2 > pd1 && pd1 < q:
|
||||
for (coordinate, index) in slice:
|
||||
q = abs(dist(coordinate, p) - r);
|
||||
if pd1 less than all three values epsilon, pd2, and q:
|
||||
i = index - 1
|
||||
break
|
||||
|
||||
if q < D: D = q
|
||||
|
||||
minimizedDistance = min(q, minimizedDistance)
|
||||
pd2 = pd1
|
||||
pd1 = q
|
||||
|
||||
return start + i
|
||||
```
|
||||
|
||||
In words: given a `start` index, the circles `x`, `y`, and `r` values, and our LUT, we check where, closest to out `start` index, we can find a local minimum for the difference between the distance to the circle center, and the circle's radius value. We track this over three points, and we know we've found a local minimum if the distances `{pd2, pd1, index}` show that the middle value (`pd1`) is lower than the values on either side. When we do, we can set our "best guess related to `start`" as the index that belongs to that `pd1`. Of course, since we're not checking values relative to some `start` value, we might not find another candidate value at all, in which case we return `start - 1`, so that a simple "is the result less than `start`?" lets us determine that there are no more intersections to find.
|
||||
In words: given a `start` index, the circle center and radius, and our LUT, we check where (closest to out `start` index) we can find a local minimum for the difference between "the distance from the curve to the circle center", and the circle's radius. We track this by looking at three values (associated with the indices `index-2`, `index-1`, and `index`), and we know we've found a local minimum if the three values show that the middle value (`pd1`) is less than either value beside it. When we do, we can set our "best guess, relative to `start`" as `index-1`. Of course, since we're now checking values relative to some `start` value, we might not find another candidate value at all, in which case we return `start - 1`, so that a simple "is the result less than `start`?" lets us determine that there are no more intersections to find.
|
||||
|
||||
So, the following graphic shows this off for the standard cubic curve (which you can move the coordinates around for, of course) and a circle with a controllable radius centered on the graphic's center, using the code approach described above.
|
||||
Finally, while not necessary for point projection, there is one more step we need to perform when we run the binary refinement function on our candidate LUT indices, because we've so far only been testing using distances "closest to the radius of the circle", and that's actually not good enough... we need distances that _are_ the radius of the circle. So, after running the refinement for each of these indices, we need to discard any final value that isn't the circle radius. And because we're working with floating point numbers, what this really means is that we need to discard any value that's a pixel or more "off". Or, if we want to get really fancy, "some small `epsilon` value".
|
||||
|
||||
Based on all of that, the following graphic shows this off for the standard cubic curve (which you can move the coordinates around for, of course) and a circle with a controllable radius centered on the graphic's center, using the code approach described above.
|
||||
|
||||
<graphics-element title="circle intersection" src="./circle.js">
|
||||
<input type="range" min="1" max="150" step="1" value="70" class="slide-control">
|
||||
|
@@ -9,8 +9,9 @@ p = some point to project onto the curve
|
||||
d = some initially huge value
|
||||
i = 0
|
||||
for (coordinate, index) in LUT:
|
||||
if distance(coordinate, p) < d:
|
||||
d = distance(coordinate, p)
|
||||
q = distance(coordinate, p)
|
||||
if q < d:
|
||||
d = q
|
||||
i = index
|
||||
```
|
||||
|
||||
|
@@ -1,10 +1,5 @@
|
||||
function abs(v) {
|
||||
return v<0 ? -v : v;
|
||||
}
|
||||
|
||||
function dist(x1,y1,x2,y2) {
|
||||
return ((x1-x2)**2 + (y2-y1)**2) ** 0.5;
|
||||
}
|
||||
const abs = v => v<0 ? -v : v;
|
||||
const dist = (x1,y1,x2,y2) => ((x1-x2)**2 + (y2-y1)**2) ** 0.5;
|
||||
|
||||
/*
|
||||
We already know that LUT[i1] and LUT[i2] are *not* good distances,
|
||||
@@ -13,7 +8,7 @@ function dist(x1,y1,x2,y2) {
|
||||
five points, and then check which three of those five are a new,
|
||||
better, interval to check within.
|
||||
*/
|
||||
function refineBinary(curve, x, y, LUT, i, targetDistance=0) {
|
||||
function refineBinary(curve, x, y, LUT, i, targetDistance=0, epsilon=1) {
|
||||
let q = LUT[i],
|
||||
count = 1,
|
||||
distance = Number.MAX_SAFE_INTEGER;
|
||||
@@ -49,6 +44,12 @@ function refineBinary(curve, x, y, LUT, i, targetDistance=0) {
|
||||
// always better than while(true). Never use while(true)
|
||||
} while (count++ < 25);
|
||||
|
||||
// If we're trying to hit a target, discard the result if
|
||||
// it is not close enough to the target.
|
||||
if (targetDistance && distance > epsilon) {
|
||||
q = undefined;
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,11 @@
|
||||
\setmainfont[Ligatures=TeX]TeX Gyre Pagella \setmathfontTeX Gyre Pagella Math
|
||||
|
||||
r= dist(B(t), c)
|
||||
┌─────────────────────────┐
|
||||
│ 2 2
|
||||
= │(B t - c ) + (B t - c )
|
||||
⟍│ x x y y
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│╭ 3 2 2 3 ╮2 ╭ 3 2 2 3 ╮2
|
||||
= ││ x (1-t) + 3 x (1-t) t + 2 x (1-t) t + x t - c │ + │ y (1-t) + 3 y (1-t) t + 2 y (1-t) t + y t - c │
|
||||
⟍│╰ 1 2 3 4 x ╯ ╰ 1 2 3 4 y ╯
|
117
docs/index.html
@@ -38,7 +38,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-11-21T01:19:44+00:00" />
|
||||
<meta property="og:updated_time" content="2020-11-21T19:09: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" />
|
||||
@@ -6299,14 +6299,15 @@ lli = function(line1, line2):
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="7">
|
||||
<textarea disabled rows="7" role="doc-example">
|
||||
<td rowspan="8">
|
||||
<textarea disabled rows="8" role="doc-example">
|
||||
p = some point to project onto the curve
|
||||
d = some initially huge value
|
||||
i = 0
|
||||
for (coordinate, index) in LUT:
|
||||
if distance(coordinate, p) < d:
|
||||
d = distance(coordinate, p)
|
||||
q = distance(coordinate, p)
|
||||
if q < d:
|
||||
d = q
|
||||
i = index</textarea
|
||||
>
|
||||
</td>
|
||||
@@ -6329,6 +6330,9 @@ for (coordinate, index) in LUT:
|
||||
<tr>
|
||||
<td>7</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
@@ -6400,21 +6404,21 @@ for (coordinate, index) in LUT:
|
||||
<!--
|
||||
\setmainfont[Ligatures=TeX]TeX Gyre Pagella \setmathfontTeX Gyre Pagella Math
|
||||
|
||||
r = dist(B(t), c)
|
||||
┌─────────────────────────┐
|
||||
│ 2 2
|
||||
= │(B t - c ) + (B t - c )
|
||||
⟍│ x x y y
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│╭ 3 2 2 3 ╮2 ╭ 3 2 2 3 ╮2
|
||||
= ││ x (1-t) + 3 x (1-t) t + 2 x (1-t) t + x t - c │ + │ y (1-t) + 3 y (1-t) t + 2 y (1-t) t + y t - c │
|
||||
⟍│╰ 1 2 3 4 x ╯ ╰ 1 2 3 4 y ╯
|
||||
r= dist(B(t), c)
|
||||
┌─────────────────────────┐
|
||||
│ 2 2
|
||||
= │(B t - c ) + (B t - c )
|
||||
⟍│ x x y y
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│╭ 3 2 2 3 ╮2 ╭ 3 2 2 3 ╮2
|
||||
= ││ x (1-t) + 3 x (1-t) t + 2 x (1-t) t + x t - c │ + │ y (1-t) + 3 y (1-t) t + 2 y (1-t) t + y t - c │
|
||||
⟍│╰ 1 2 3 4 x ╯ ╰ 1 2 3 4 y ╯
|
||||
-->
|
||||
<img
|
||||
class="LaTeX SVG"
|
||||
src="./images/chapters/circleintersection/699e6ea5bffbe8fe377af21a2bd28532.svg"
|
||||
width="819px"
|
||||
height="92px"
|
||||
src="./images/chapters/circleintersection/3e0594855ca99fb87dcc65a693e1ad22.svg"
|
||||
width="811px"
|
||||
height="103px"
|
||||
loading="lazy"
|
||||
/>
|
||||
<p>
|
||||
@@ -6437,7 +6441,7 @@ for (coordinate, index) in LUT:
|
||||
<td>1</td>
|
||||
<td rowspan="9">
|
||||
<textarea disabled rows="9" role="doc-example">
|
||||
c = our circle's center point
|
||||
p = our circle's center point
|
||||
r = our circle's radius
|
||||
d = some initially huge value
|
||||
i = 0
|
||||
@@ -6476,25 +6480,27 @@ for (coordinate, index) in LUT:
|
||||
</table>
|
||||
|
||||
<p>
|
||||
This is <em>very</em> similar to the code in the previous section, but adapted so that we home in on points with an exact distance, rather
|
||||
than "the smallest distance". So far so good. However, we also want to make sure we find <em>all</em> the points, not just a single one,
|
||||
so we need a little more code for that:
|
||||
This is <em>very</em> similar to the code in the previous section, with an extra input <code>r</code> for the circle radius, and a minor
|
||||
change in the "distance for this coordinate": rather than just <code>distance(coordinate, p)</code> we want to know the difference between
|
||||
that distance and the circle radius. After all, if that difference is zero, then the distance from the coordinate to the circle center is
|
||||
exactly the radius, so the coordinate lies on both the curve and the circle.
|
||||
</p>
|
||||
<p>So far so good.</p>
|
||||
<p>However, we also want to make sure we find <em>all</em> the points, not just a single one, so we need a little more code for that:</p>
|
||||
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="11">
|
||||
<textarea disabled rows="11" role="doc-example">
|
||||
c = our circle's center point
|
||||
<td rowspan="10">
|
||||
<textarea disabled rows="10" role="doc-example">
|
||||
p = our circle's center point
|
||||
r = our circle's radius
|
||||
d = some initially huge value
|
||||
start = 0
|
||||
values = []
|
||||
run:
|
||||
i = findClosest(start, x, y, r, LUT)
|
||||
if (i < start) stop
|
||||
if (i>0 && i === start) stop
|
||||
do:
|
||||
i = findClosest(start, p, r, LUT)
|
||||
if i < start, or i>0 but the same as start: stop
|
||||
values.add(i);
|
||||
start = i + 2;</textarea
|
||||
>
|
||||
@@ -6527,9 +6533,6 @@ run:
|
||||
<tr>
|
||||
<td>10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
@@ -6544,24 +6547,23 @@ run:
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="20">
|
||||
<textarea disabled rows="20" role="doc-example">
|
||||
findClosest(start, x, y, r, LUT):
|
||||
D = some very large number
|
||||
pd2 = LUT[start-2], if it exists. Otherwise use D
|
||||
pd1 = LUT[start-1], if it exists. Otherwise use D
|
||||
<td rowspan="19">
|
||||
<textarea disabled rows="19" role="doc-example">
|
||||
findClosest(start, p, r, LUT):
|
||||
minimizedDistance = some very large number
|
||||
pd2 = LUT[start-2], if it exists. Otherwise use minimizedDistance
|
||||
pd1 = LUT[start-1], if it exists. Otherwise use minimizedDistance
|
||||
slice = LUT.subset(start, LUT.length)
|
||||
epsilon = the largest point-to-point distance in our LUT
|
||||
i = -1;
|
||||
|
||||
for (point, index) in slice:
|
||||
q = abs(dist(point, (x,y)) - r);
|
||||
if pd1 < epsilon && pd2 > pd1 && pd1 < q:
|
||||
for (coordinate, index) in slice:
|
||||
q = abs(dist(coordinate, p) - r);
|
||||
if pd1 less than all three values epsilon, pd2, and q:
|
||||
i = index - 1
|
||||
break
|
||||
|
||||
if q < D: D = q
|
||||
|
||||
minimizedDistance = min(q, minimizedDistance)
|
||||
pd2 = pd1
|
||||
pd1 = q
|
||||
|
||||
@@ -6623,29 +6625,34 @@ findClosest(start, x, y, r, LUT):
|
||||
<tr>
|
||||
<td>19</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>20</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
In words: given a <code>start</code> index, the circles <code>x</code>, <code>y</code>, and <code>r</code> values, and our LUT, we check
|
||||
where, closest to out <code>start</code> index, we can find a local minimum for the difference between the distance to the circle center,
|
||||
and the circle's radius value. We track this over three points, and we know we've found a local minimum if the distances
|
||||
<code>{pd2, pd1, index}</code> show that the middle value (<code>pd1</code>) is lower than the values on either side. When we do, we can
|
||||
set our "best guess related to <code>start</code>" as the index that belongs to that <code>pd1</code>. Of course, since we're not checking
|
||||
values relative to some <code>start</code> value, we might not find another candidate value at all, in which case we return
|
||||
<code>start - 1</code>, so that a simple "is the result less than <code>start</code>?" lets us determine that there are no more
|
||||
In words: given a <code>start</code> index, the circle center and radius, and our LUT, we check where (closest to out
|
||||
<code>start</code> index) we can find a local minimum for the difference between "the distance from the curve to the circle center", and
|
||||
the circle's radius. We track this by looking at three values (associated with the indices <code>index-2</code>, <code>index-1</code>, and
|
||||
<code>index</code>), and we know we've found a local minimum if the three values show that the middle value (<code>pd1</code>) is less
|
||||
than either value beside it. When we do, we can set our "best guess, relative to <code>start</code>" as <code>index-1</code>. Of course,
|
||||
since we're now checking values relative to some <code>start</code> value, we might not find another candidate value at all, in which case
|
||||
we return <code>start - 1</code>, so that a simple "is the result less than <code>start</code>?" lets us determine that there are no more
|
||||
intersections to find.
|
||||
</p>
|
||||
<p>
|
||||
So, the following graphic shows this off for the standard cubic curve (which you can move the coordinates around for, of course) and a
|
||||
circle with a controllable radius centered on the graphic's center, using the code approach described above.
|
||||
Finally, while not necessary for point projection, there is one more step we need to perform when we run the binary refinement function on
|
||||
our candidate LUT indices, because we've so far only been testing using distances "closest to the radius of the circle", and that's
|
||||
actually not good enough... we need distances that <em>are</em> the radius of the circle. So, after running the refinement for each of
|
||||
these indices, we need to discard any final value that isn't the circle radius. And because we're working with floating point numbers,
|
||||
what this really means is that we need to discard any value that's a pixel or more "off". Or, if we want to get really fancy, "some small
|
||||
<code>epsilon</code> value".
|
||||
</p>
|
||||
<p>
|
||||
Based on all of that, the following graphic shows this off for the standard cubic curve (which you can move the coordinates around for, of
|
||||
course) and a circle with a controllable radius centered on the graphic's center, using the code approach described above.
|
||||
</p>
|
||||
<graphics-element title="circle intersection" width="275" height="275" src="./chapters/circleintersection/circle.js">
|
||||
<fallback-image>
|
||||
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
|
||||
<img width="275px" height="275px" src="./images/chapters/circleintersection/ee119fad4c48f5e17871ff31268cf8cc.png" loading="lazy" />
|
||||
<img width="275px" height="275px" src="./images/chapters/circleintersection/3a3ea30971de247da93034de614a63a4.png" loading="lazy" />
|
||||
<label>circle intersection</label>
|
||||
</fallback-image>
|
||||
<input type="range" min="1" max="150" step="1" value="70" class="slide-control" />
|
||||
|
@@ -41,7 +41,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-11-21T01:19:44+00:00" />
|
||||
<meta property="og:updated_time" content="2020-11-21T19:09: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" />
|
||||
@@ -6439,14 +6439,15 @@ lli = function(line1, line2):
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="7">
|
||||
<textarea disabled rows="7" role="doc-example">
|
||||
<td rowspan="8">
|
||||
<textarea disabled rows="8" role="doc-example">
|
||||
p = some point to project onto the curve
|
||||
d = some initially huge value
|
||||
i = 0
|
||||
for (coordinate, index) in LUT:
|
||||
if distance(coordinate, p) < d:
|
||||
d = distance(coordinate, p)
|
||||
q = distance(coordinate, p)
|
||||
if q < d:
|
||||
d = q
|
||||
i = index</textarea
|
||||
>
|
||||
</td>
|
||||
@@ -6469,6 +6470,9 @@ for (coordinate, index) in LUT:
|
||||
<tr>
|
||||
<td>7</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
@@ -6547,21 +6551,21 @@ for (coordinate, index) in LUT:
|
||||
<!--
|
||||
\setmainfont[Ligatures=TeX]TeX Gyre Pagella \setmathfontTeX Gyre Pagella Math
|
||||
|
||||
r = dist(B(t), c)
|
||||
┌─────────────────────────┐
|
||||
│ 2 2
|
||||
= │(B t - c ) + (B t - c )
|
||||
⟍│ x x y y
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│╭ 3 2 2 3 ╮2 ╭ 3 2 2 3 ╮2
|
||||
= ││ x (1-t) + 3 x (1-t) t + 2 x (1-t) t + x t - c │ + │ y (1-t) + 3 y (1-t) t + 2 y (1-t) t + y t - c │
|
||||
⟍│╰ 1 2 3 4 x ╯ ╰ 1 2 3 4 y ╯
|
||||
r= dist(B(t), c)
|
||||
┌─────────────────────────┐
|
||||
│ 2 2
|
||||
= │(B t - c ) + (B t - c )
|
||||
⟍│ x x y y
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│╭ 3 2 2 3 ╮2 ╭ 3 2 2 3 ╮2
|
||||
= ││ x (1-t) + 3 x (1-t) t + 2 x (1-t) t + x t - c │ + │ y (1-t) + 3 y (1-t) t + 2 y (1-t) t + y t - c │
|
||||
⟍│╰ 1 2 3 4 x ╯ ╰ 1 2 3 4 y ╯
|
||||
-->
|
||||
<img
|
||||
class="LaTeX SVG"
|
||||
src="./images/chapters/circleintersection/699e6ea5bffbe8fe377af21a2bd28532.svg"
|
||||
width="819px"
|
||||
height="92px"
|
||||
src="./images/chapters/circleintersection/3e0594855ca99fb87dcc65a693e1ad22.svg"
|
||||
width="811px"
|
||||
height="103px"
|
||||
loading="lazy"
|
||||
/>
|
||||
<p>
|
||||
@@ -6584,7 +6588,7 @@ for (coordinate, index) in LUT:
|
||||
<td>1</td>
|
||||
<td rowspan="9">
|
||||
<textarea disabled rows="9" role="doc-example">
|
||||
c = our circle's center point
|
||||
p = our circle's center point
|
||||
r = our circle's radius
|
||||
d = some initially huge value
|
||||
i = 0
|
||||
@@ -6623,25 +6627,27 @@ for (coordinate, index) in LUT:
|
||||
</table>
|
||||
|
||||
<p>
|
||||
This is <em>very</em> similar to the code in the previous section, but adapted so that we home in on points with an exact distance, rather
|
||||
than "the smallest distance". So far so good. However, we also want to make sure we find <em>all</em> the points, not just a single one,
|
||||
so we need a little more code for that:
|
||||
This is <em>very</em> similar to the code in the previous section, with an extra input <code>r</code> for the circle radius, and a minor
|
||||
change in the "distance for this coordinate": rather than just <code>distance(coordinate, p)</code> we want to know the difference between
|
||||
that distance and the circle radius. After all, if that difference is zero, then the distance from the coordinate to the circle center is
|
||||
exactly the radius, so the coordinate lies on both the curve and the circle.
|
||||
</p>
|
||||
<p>So far so good.</p>
|
||||
<p>However, we also want to make sure we find <em>all</em> the points, not just a single one, so we need a little more code for that:</p>
|
||||
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="11">
|
||||
<textarea disabled rows="11" role="doc-example">
|
||||
c = our circle's center point
|
||||
<td rowspan="10">
|
||||
<textarea disabled rows="10" role="doc-example">
|
||||
p = our circle's center point
|
||||
r = our circle's radius
|
||||
d = some initially huge value
|
||||
start = 0
|
||||
values = []
|
||||
run:
|
||||
i = findClosest(start, x, y, r, LUT)
|
||||
if (i < start) stop
|
||||
if (i>0 && i === start) stop
|
||||
do:
|
||||
i = findClosest(start, p, r, LUT)
|
||||
if i < start, or i>0 but the same as start: stop
|
||||
values.add(i);
|
||||
start = i + 2;</textarea
|
||||
>
|
||||
@@ -6674,9 +6680,6 @@ run:
|
||||
<tr>
|
||||
<td>10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
@@ -6691,24 +6694,23 @@ run:
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="20">
|
||||
<textarea disabled rows="20" role="doc-example">
|
||||
findClosest(start, x, y, r, LUT):
|
||||
D = some very large number
|
||||
pd2 = LUT[start-2], if it exists. Otherwise use D
|
||||
pd1 = LUT[start-1], if it exists. Otherwise use D
|
||||
<td rowspan="19">
|
||||
<textarea disabled rows="19" role="doc-example">
|
||||
findClosest(start, p, r, LUT):
|
||||
minimizedDistance = some very large number
|
||||
pd2 = LUT[start-2], if it exists. Otherwise use minimizedDistance
|
||||
pd1 = LUT[start-1], if it exists. Otherwise use minimizedDistance
|
||||
slice = LUT.subset(start, LUT.length)
|
||||
epsilon = the largest point-to-point distance in our LUT
|
||||
i = -1;
|
||||
|
||||
for (point, index) in slice:
|
||||
q = abs(dist(point, (x,y)) - r);
|
||||
if pd1 < epsilon && pd2 > pd1 && pd1 < q:
|
||||
for (coordinate, index) in slice:
|
||||
q = abs(dist(coordinate, p) - r);
|
||||
if pd1 less than all three values epsilon, pd2, and q:
|
||||
i = index - 1
|
||||
break
|
||||
|
||||
if q < D: D = q
|
||||
|
||||
minimizedDistance = min(q, minimizedDistance)
|
||||
pd2 = pd1
|
||||
pd1 = q
|
||||
|
||||
@@ -6770,24 +6772,29 @@ findClosest(start, x, y, r, LUT):
|
||||
<tr>
|
||||
<td>19</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>20</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
In words: given a <code>start</code> index, the circles <code>x</code>, <code>y</code>, and <code>r</code> values, and our LUT, we check
|
||||
where, closest to out <code>start</code> index, we can find a local minimum for the difference between the distance to the circle center,
|
||||
and the circle's radius value. We track this over three points, and we know we've found a local minimum if the distances
|
||||
<code>{pd2, pd1, index}</code> show that the middle value (<code>pd1</code>) is lower than the values on either side. When we do, we can
|
||||
set our "best guess related to <code>start</code>" as the index that belongs to that <code>pd1</code>. Of course, since we're not checking
|
||||
values relative to some <code>start</code> value, we might not find another candidate value at all, in which case we return
|
||||
<code>start - 1</code>, so that a simple "is the result less than <code>start</code>?" lets us determine that there are no more
|
||||
In words: given a <code>start</code> index, the circle center and radius, and our LUT, we check where (closest to out
|
||||
<code>start</code> index) we can find a local minimum for the difference between "the distance from the curve to the circle center", and
|
||||
the circle's radius. We track this by looking at three values (associated with the indices <code>index-2</code>, <code>index-1</code>, and
|
||||
<code>index</code>), and we know we've found a local minimum if the three values show that the middle value (<code>pd1</code>) is less
|
||||
than either value beside it. When we do, we can set our "best guess, relative to <code>start</code>" as <code>index-1</code>. Of course,
|
||||
since we're now checking values relative to some <code>start</code> value, we might not find another candidate value at all, in which case
|
||||
we return <code>start - 1</code>, so that a simple "is the result less than <code>start</code>?" lets us determine that there are no more
|
||||
intersections to find.
|
||||
</p>
|
||||
<p>
|
||||
So, the following graphic shows this off for the standard cubic curve (which you can move the coordinates around for, of course) and a
|
||||
circle with a controllable radius centered on the graphic's center, using the code approach described above.
|
||||
Finally, while not necessary for point projection, there is one more step we need to perform when we run the binary refinement function on
|
||||
our candidate LUT indices, because we've so far only been testing using distances "closest to the radius of the circle", and that's
|
||||
actually not good enough... we need distances that <em>are</em> the radius of the circle. So, after running the refinement for each of
|
||||
these indices, we need to discard any final value that isn't the circle radius. And because we're working with floating point numbers,
|
||||
what this really means is that we need to discard any value that's a pixel or more "off". Or, if we want to get really fancy, "some small
|
||||
<code>epsilon</code> value".
|
||||
</p>
|
||||
<p>
|
||||
Based on all of that, the following graphic shows this off for the standard cubic curve (which you can move the coordinates around for, of
|
||||
course) and a circle with a controllable radius centered on the graphic's center, using the code approach described above.
|
||||
</p>
|
||||
<graphics-element
|
||||
title="circle intersection"
|
||||
@@ -6799,7 +6806,7 @@ findClosest(start, x, y, r, LUT):
|
||||
>
|
||||
<fallback-image>
|
||||
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
|
||||
<img width="275px" height="275px" src="./images/chapters/circleintersection/ee119fad4c48f5e17871ff31268cf8cc.png" loading="lazy" />
|
||||
<img width="275px" height="275px" src="./images/chapters/circleintersection/3a3ea30971de247da93034de614a63a4.png" loading="lazy" />
|
||||
<label>circle intersection</label>
|
||||
</fallback-image>
|
||||
<input type="range" min="1" max="150" step="1" value="70" class="slide-control" />
|
||||
|
@@ -34,7 +34,7 @@
|
||||
<meta property="og:locale" content="en-GB" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:published_time" content="Thu Sep 17 2020 17:00:00 +00:00" />
|
||||
<meta property="og:updated_time" content="Fri Nov 20 2020 17:19:44 +00:00" />
|
||||
<meta property="og:updated_time" content="Sat Nov 21 2020 11:09:32 +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:description" content="" />
|
||||
<meta property="og:locale" content="en-GB" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:published_time" content="Fri Nov 20 2020 17:19:44 GMT-0800 (Pacific Standard Time)" />
|
||||
<meta property="og:published_time" content="Sat Nov 21 2020 11:09:32 GMT-0800 (Pacific Standard Time)" />
|
||||
<meta property="og:updated_time" content="" />
|
||||
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
|
||||
<meta property="og:section" content="Bézier Curves" />
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<atom:link href="https://pomax.github.io/bezierinfo" rel="self"></atom:link>
|
||||
<description>News updates for the <a href="https://pomax.github.io/bezierinfo">primer on Bézier Curves</a> by Pomax</description>
|
||||
<language>en-GB</language>
|
||||
<lastBuildDate>Fri Nov 20 2020 17:19:44 +00:00</lastBuildDate>
|
||||
<lastBuildDate>Sat Nov 21 2020 11:09:32 +00:00</lastBuildDate>
|
||||
<image>
|
||||
<url>https://pomax.github.io/bezierinfo/images/og-image.png</url>
|
||||
<title>A Primer on Bézier Curves</title>
|
||||
|
@@ -159,6 +159,7 @@ main {
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
border-bottom: 1px solid black;
|
||||
box-shadow: 0 0 0 0.5em white;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -39,7 +39,7 @@
|
||||
<meta property="og:locale" content="uk-UA" />
|
||||
<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-11-21T01:19:44+00:00" />
|
||||
<meta property="og:updated_time" content="2020-11-21T19:09: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" />
|
||||
@@ -6574,14 +6574,15 @@ lli = function(line1, line2):
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="7">
|
||||
<textarea disabled rows="7" role="doc-example">
|
||||
<td rowspan="8">
|
||||
<textarea disabled rows="8" role="doc-example">
|
||||
p = some point to project onto the curve
|
||||
d = some initially huge value
|
||||
i = 0
|
||||
for (coordinate, index) in LUT:
|
||||
if distance(coordinate, p) < d:
|
||||
d = distance(coordinate, p)
|
||||
q = distance(coordinate, p)
|
||||
if q < d:
|
||||
d = q
|
||||
i = index</textarea
|
||||
>
|
||||
</td>
|
||||
@@ -6604,6 +6605,9 @@ for (coordinate, index) in LUT:
|
||||
<tr>
|
||||
<td>7</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
@@ -6682,21 +6686,21 @@ for (coordinate, index) in LUT:
|
||||
<!--
|
||||
\setmainfont[Ligatures=TeX]TeX Gyre Pagella \setmathfontTeX Gyre Pagella Math
|
||||
|
||||
r = dist(B(t), c)
|
||||
┌─────────────────────────┐
|
||||
│ 2 2
|
||||
= │(B t - c ) + (B t - c )
|
||||
⟍│ x x y y
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│╭ 3 2 2 3 ╮2 ╭ 3 2 2 3 ╮2
|
||||
= ││ x (1-t) + 3 x (1-t) t + 2 x (1-t) t + x t - c │ + │ y (1-t) + 3 y (1-t) t + 2 y (1-t) t + y t - c │
|
||||
⟍│╰ 1 2 3 4 x ╯ ╰ 1 2 3 4 y ╯
|
||||
r= dist(B(t), c)
|
||||
┌─────────────────────────┐
|
||||
│ 2 2
|
||||
= │(B t - c ) + (B t - c )
|
||||
⟍│ x x y y
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│╭ 3 2 2 3 ╮2 ╭ 3 2 2 3 ╮2
|
||||
= ││ x (1-t) + 3 x (1-t) t + 2 x (1-t) t + x t - c │ + │ y (1-t) + 3 y (1-t) t + 2 y (1-t) t + y t - c │
|
||||
⟍│╰ 1 2 3 4 x ╯ ╰ 1 2 3 4 y ╯
|
||||
-->
|
||||
<img
|
||||
class="LaTeX SVG"
|
||||
src="./images/chapters/circleintersection/699e6ea5bffbe8fe377af21a2bd28532.svg"
|
||||
width="819px"
|
||||
height="92px"
|
||||
src="./images/chapters/circleintersection/3e0594855ca99fb87dcc65a693e1ad22.svg"
|
||||
width="811px"
|
||||
height="103px"
|
||||
loading="lazy"
|
||||
/>
|
||||
<p>
|
||||
@@ -6719,7 +6723,7 @@ for (coordinate, index) in LUT:
|
||||
<td>1</td>
|
||||
<td rowspan="9">
|
||||
<textarea disabled rows="9" role="doc-example">
|
||||
c = our circle's center point
|
||||
p = our circle's center point
|
||||
r = our circle's radius
|
||||
d = some initially huge value
|
||||
i = 0
|
||||
@@ -6758,25 +6762,27 @@ for (coordinate, index) in LUT:
|
||||
</table>
|
||||
|
||||
<p>
|
||||
This is <em>very</em> similar to the code in the previous section, but adapted so that we home in on points with an exact distance, rather
|
||||
than "the smallest distance". So far so good. However, we also want to make sure we find <em>all</em> the points, not just a single one,
|
||||
so we need a little more code for that:
|
||||
This is <em>very</em> similar to the code in the previous section, with an extra input <code>r</code> for the circle radius, and a minor
|
||||
change in the "distance for this coordinate": rather than just <code>distance(coordinate, p)</code> we want to know the difference between
|
||||
that distance and the circle radius. After all, if that difference is zero, then the distance from the coordinate to the circle center is
|
||||
exactly the radius, so the coordinate lies on both the curve and the circle.
|
||||
</p>
|
||||
<p>So far so good.</p>
|
||||
<p>However, we also want to make sure we find <em>all</em> the points, not just a single one, so we need a little more code for that:</p>
|
||||
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="11">
|
||||
<textarea disabled rows="11" role="doc-example">
|
||||
c = our circle's center point
|
||||
<td rowspan="10">
|
||||
<textarea disabled rows="10" role="doc-example">
|
||||
p = our circle's center point
|
||||
r = our circle's radius
|
||||
d = some initially huge value
|
||||
start = 0
|
||||
values = []
|
||||
run:
|
||||
i = findClosest(start, x, y, r, LUT)
|
||||
if (i < start) stop
|
||||
if (i>0 && i === start) stop
|
||||
do:
|
||||
i = findClosest(start, p, r, LUT)
|
||||
if i < start, or i>0 but the same as start: stop
|
||||
values.add(i);
|
||||
start = i + 2;</textarea
|
||||
>
|
||||
@@ -6809,9 +6815,6 @@ run:
|
||||
<tr>
|
||||
<td>10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
@@ -6826,24 +6829,23 @@ run:
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="20">
|
||||
<textarea disabled rows="20" role="doc-example">
|
||||
findClosest(start, x, y, r, LUT):
|
||||
D = some very large number
|
||||
pd2 = LUT[start-2], if it exists. Otherwise use D
|
||||
pd1 = LUT[start-1], if it exists. Otherwise use D
|
||||
<td rowspan="19">
|
||||
<textarea disabled rows="19" role="doc-example">
|
||||
findClosest(start, p, r, LUT):
|
||||
minimizedDistance = some very large number
|
||||
pd2 = LUT[start-2], if it exists. Otherwise use minimizedDistance
|
||||
pd1 = LUT[start-1], if it exists. Otherwise use minimizedDistance
|
||||
slice = LUT.subset(start, LUT.length)
|
||||
epsilon = the largest point-to-point distance in our LUT
|
||||
i = -1;
|
||||
|
||||
for (point, index) in slice:
|
||||
q = abs(dist(point, (x,y)) - r);
|
||||
if pd1 < epsilon && pd2 > pd1 && pd1 < q:
|
||||
for (coordinate, index) in slice:
|
||||
q = abs(dist(coordinate, p) - r);
|
||||
if pd1 less than all three values epsilon, pd2, and q:
|
||||
i = index - 1
|
||||
break
|
||||
|
||||
if q < D: D = q
|
||||
|
||||
minimizedDistance = min(q, minimizedDistance)
|
||||
pd2 = pd1
|
||||
pd1 = q
|
||||
|
||||
@@ -6905,24 +6907,29 @@ findClosest(start, x, y, r, LUT):
|
||||
<tr>
|
||||
<td>19</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>20</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
In words: given a <code>start</code> index, the circles <code>x</code>, <code>y</code>, and <code>r</code> values, and our LUT, we check
|
||||
where, closest to out <code>start</code> index, we can find a local minimum for the difference between the distance to the circle center,
|
||||
and the circle's radius value. We track this over three points, and we know we've found a local minimum if the distances
|
||||
<code>{pd2, pd1, index}</code> show that the middle value (<code>pd1</code>) is lower than the values on either side. When we do, we can
|
||||
set our "best guess related to <code>start</code>" as the index that belongs to that <code>pd1</code>. Of course, since we're not checking
|
||||
values relative to some <code>start</code> value, we might not find another candidate value at all, in which case we return
|
||||
<code>start - 1</code>, so that a simple "is the result less than <code>start</code>?" lets us determine that there are no more
|
||||
In words: given a <code>start</code> index, the circle center and radius, and our LUT, we check where (closest to out
|
||||
<code>start</code> index) we can find a local minimum for the difference between "the distance from the curve to the circle center", and
|
||||
the circle's radius. We track this by looking at three values (associated with the indices <code>index-2</code>, <code>index-1</code>, and
|
||||
<code>index</code>), and we know we've found a local minimum if the three values show that the middle value (<code>pd1</code>) is less
|
||||
than either value beside it. When we do, we can set our "best guess, relative to <code>start</code>" as <code>index-1</code>. Of course,
|
||||
since we're now checking values relative to some <code>start</code> value, we might not find another candidate value at all, in which case
|
||||
we return <code>start - 1</code>, so that a simple "is the result less than <code>start</code>?" lets us determine that there are no more
|
||||
intersections to find.
|
||||
</p>
|
||||
<p>
|
||||
So, the following graphic shows this off for the standard cubic curve (which you can move the coordinates around for, of course) and a
|
||||
circle with a controllable radius centered on the graphic's center, using the code approach described above.
|
||||
Finally, while not necessary for point projection, there is one more step we need to perform when we run the binary refinement function on
|
||||
our candidate LUT indices, because we've so far only been testing using distances "closest to the radius of the circle", and that's
|
||||
actually not good enough... we need distances that <em>are</em> the radius of the circle. So, after running the refinement for each of
|
||||
these indices, we need to discard any final value that isn't the circle radius. And because we're working with floating point numbers,
|
||||
what this really means is that we need to discard any value that's a pixel or more "off". Or, if we want to get really fancy, "some small
|
||||
<code>epsilon</code> value".
|
||||
</p>
|
||||
<p>
|
||||
Based on all of that, the following graphic shows this off for the standard cubic curve (which you can move the coordinates around for, of
|
||||
course) and a circle with a controllable radius centered on the graphic's center, using the code approach described above.
|
||||
</p>
|
||||
<graphics-element
|
||||
title="circle intersection"
|
||||
@@ -6934,7 +6941,7 @@ findClosest(start, x, y, r, LUT):
|
||||
>
|
||||
<fallback-image>
|
||||
<span class="view-source">Скрипти вимкнено. показує резервний.</span>
|
||||
<img width="275px" height="275px" src="./images/chapters/circleintersection/ee119fad4c48f5e17871ff31268cf8cc.png" loading="lazy" />
|
||||
<img width="275px" height="275px" src="./images/chapters/circleintersection/3a3ea30971de247da93034de614a63a4.png" loading="lazy" />
|
||||
<label>circle intersection</label>
|
||||
</fallback-image>
|
||||
<input type="range" min="1" max="150" step="1" value="70" class="slide-control" />
|
||||
|
@@ -41,7 +41,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-11-21T01:19:44+00:00" />
|
||||
<meta property="og:updated_time" content="2020-11-21T19:09: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" />
|
||||
@@ -6415,14 +6415,15 @@ lli = function(line1, line2):
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="7">
|
||||
<textarea disabled rows="7" role="doc-example">
|
||||
<td rowspan="8">
|
||||
<textarea disabled rows="8" role="doc-example">
|
||||
p = some point to project onto the curve
|
||||
d = some initially huge value
|
||||
i = 0
|
||||
for (coordinate, index) in LUT:
|
||||
if distance(coordinate, p) < d:
|
||||
d = distance(coordinate, p)
|
||||
q = distance(coordinate, p)
|
||||
if q < d:
|
||||
d = q
|
||||
i = index</textarea
|
||||
>
|
||||
</td>
|
||||
@@ -6445,6 +6446,9 @@ for (coordinate, index) in LUT:
|
||||
<tr>
|
||||
<td>7</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
@@ -6523,21 +6527,21 @@ for (coordinate, index) in LUT:
|
||||
<!--
|
||||
\setmainfont[Ligatures=TeX]TeX Gyre Pagella \setmathfontTeX Gyre Pagella Math
|
||||
|
||||
r = dist(B(t), c)
|
||||
┌─────────────────────────┐
|
||||
│ 2 2
|
||||
= │(B t - c ) + (B t - c )
|
||||
⟍│ x x y y
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│╭ 3 2 2 3 ╮2 ╭ 3 2 2 3 ╮2
|
||||
= ││ x (1-t) + 3 x (1-t) t + 2 x (1-t) t + x t - c │ + │ y (1-t) + 3 y (1-t) t + 2 y (1-t) t + y t - c │
|
||||
⟍│╰ 1 2 3 4 x ╯ ╰ 1 2 3 4 y ╯
|
||||
r= dist(B(t), c)
|
||||
┌─────────────────────────┐
|
||||
│ 2 2
|
||||
= │(B t - c ) + (B t - c )
|
||||
⟍│ x x y y
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│╭ 3 2 2 3 ╮2 ╭ 3 2 2 3 ╮2
|
||||
= ││ x (1-t) + 3 x (1-t) t + 2 x (1-t) t + x t - c │ + │ y (1-t) + 3 y (1-t) t + 2 y (1-t) t + y t - c │
|
||||
⟍│╰ 1 2 3 4 x ╯ ╰ 1 2 3 4 y ╯
|
||||
-->
|
||||
<img
|
||||
class="LaTeX SVG"
|
||||
src="./images/chapters/circleintersection/699e6ea5bffbe8fe377af21a2bd28532.svg"
|
||||
width="819px"
|
||||
height="92px"
|
||||
src="./images/chapters/circleintersection/3e0594855ca99fb87dcc65a693e1ad22.svg"
|
||||
width="811px"
|
||||
height="103px"
|
||||
loading="lazy"
|
||||
/>
|
||||
<p>
|
||||
@@ -6560,7 +6564,7 @@ for (coordinate, index) in LUT:
|
||||
<td>1</td>
|
||||
<td rowspan="9">
|
||||
<textarea disabled rows="9" role="doc-example">
|
||||
c = our circle's center point
|
||||
p = our circle's center point
|
||||
r = our circle's radius
|
||||
d = some initially huge value
|
||||
i = 0
|
||||
@@ -6599,25 +6603,27 @@ for (coordinate, index) in LUT:
|
||||
</table>
|
||||
|
||||
<p>
|
||||
This is <em>very</em> similar to the code in the previous section, but adapted so that we home in on points with an exact distance, rather
|
||||
than "the smallest distance". So far so good. However, we also want to make sure we find <em>all</em> the points, not just a single one,
|
||||
so we need a little more code for that:
|
||||
This is <em>very</em> similar to the code in the previous section, with an extra input <code>r</code> for the circle radius, and a minor
|
||||
change in the "distance for this coordinate": rather than just <code>distance(coordinate, p)</code> we want to know the difference between
|
||||
that distance and the circle radius. After all, if that difference is zero, then the distance from the coordinate to the circle center is
|
||||
exactly the radius, so the coordinate lies on both the curve and the circle.
|
||||
</p>
|
||||
<p>So far so good.</p>
|
||||
<p>However, we also want to make sure we find <em>all</em> the points, not just a single one, so we need a little more code for that:</p>
|
||||
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="11">
|
||||
<textarea disabled rows="11" role="doc-example">
|
||||
c = our circle's center point
|
||||
<td rowspan="10">
|
||||
<textarea disabled rows="10" role="doc-example">
|
||||
p = our circle's center point
|
||||
r = our circle's radius
|
||||
d = some initially huge value
|
||||
start = 0
|
||||
values = []
|
||||
run:
|
||||
i = findClosest(start, x, y, r, LUT)
|
||||
if (i < start) stop
|
||||
if (i>0 && i === start) stop
|
||||
do:
|
||||
i = findClosest(start, p, r, LUT)
|
||||
if i < start, or i>0 but the same as start: stop
|
||||
values.add(i);
|
||||
start = i + 2;</textarea
|
||||
>
|
||||
@@ -6650,9 +6656,6 @@ run:
|
||||
<tr>
|
||||
<td>10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
@@ -6667,24 +6670,23 @@ run:
|
||||
<table class="code">
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="20">
|
||||
<textarea disabled rows="20" role="doc-example">
|
||||
findClosest(start, x, y, r, LUT):
|
||||
D = some very large number
|
||||
pd2 = LUT[start-2], if it exists. Otherwise use D
|
||||
pd1 = LUT[start-1], if it exists. Otherwise use D
|
||||
<td rowspan="19">
|
||||
<textarea disabled rows="19" role="doc-example">
|
||||
findClosest(start, p, r, LUT):
|
||||
minimizedDistance = some very large number
|
||||
pd2 = LUT[start-2], if it exists. Otherwise use minimizedDistance
|
||||
pd1 = LUT[start-1], if it exists. Otherwise use minimizedDistance
|
||||
slice = LUT.subset(start, LUT.length)
|
||||
epsilon = the largest point-to-point distance in our LUT
|
||||
i = -1;
|
||||
|
||||
for (point, index) in slice:
|
||||
q = abs(dist(point, (x,y)) - r);
|
||||
if pd1 < epsilon && pd2 > pd1 && pd1 < q:
|
||||
for (coordinate, index) in slice:
|
||||
q = abs(dist(coordinate, p) - r);
|
||||
if pd1 less than all three values epsilon, pd2, and q:
|
||||
i = index - 1
|
||||
break
|
||||
|
||||
if q < D: D = q
|
||||
|
||||
minimizedDistance = min(q, minimizedDistance)
|
||||
pd2 = pd1
|
||||
pd1 = q
|
||||
|
||||
@@ -6746,24 +6748,29 @@ findClosest(start, x, y, r, LUT):
|
||||
<tr>
|
||||
<td>19</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>20</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
In words: given a <code>start</code> index, the circles <code>x</code>, <code>y</code>, and <code>r</code> values, and our LUT, we check
|
||||
where, closest to out <code>start</code> index, we can find a local minimum for the difference between the distance to the circle center,
|
||||
and the circle's radius value. We track this over three points, and we know we've found a local minimum if the distances
|
||||
<code>{pd2, pd1, index}</code> show that the middle value (<code>pd1</code>) is lower than the values on either side. When we do, we can
|
||||
set our "best guess related to <code>start</code>" as the index that belongs to that <code>pd1</code>. Of course, since we're not checking
|
||||
values relative to some <code>start</code> value, we might not find another candidate value at all, in which case we return
|
||||
<code>start - 1</code>, so that a simple "is the result less than <code>start</code>?" lets us determine that there are no more
|
||||
In words: given a <code>start</code> index, the circle center and radius, and our LUT, we check where (closest to out
|
||||
<code>start</code> index) we can find a local minimum for the difference between "the distance from the curve to the circle center", and
|
||||
the circle's radius. We track this by looking at three values (associated with the indices <code>index-2</code>, <code>index-1</code>, and
|
||||
<code>index</code>), and we know we've found a local minimum if the three values show that the middle value (<code>pd1</code>) is less
|
||||
than either value beside it. When we do, we can set our "best guess, relative to <code>start</code>" as <code>index-1</code>. Of course,
|
||||
since we're now checking values relative to some <code>start</code> value, we might not find another candidate value at all, in which case
|
||||
we return <code>start - 1</code>, so that a simple "is the result less than <code>start</code>?" lets us determine that there are no more
|
||||
intersections to find.
|
||||
</p>
|
||||
<p>
|
||||
So, the following graphic shows this off for the standard cubic curve (which you can move the coordinates around for, of course) and a
|
||||
circle with a controllable radius centered on the graphic's center, using the code approach described above.
|
||||
Finally, while not necessary for point projection, there is one more step we need to perform when we run the binary refinement function on
|
||||
our candidate LUT indices, because we've so far only been testing using distances "closest to the radius of the circle", and that's
|
||||
actually not good enough... we need distances that <em>are</em> the radius of the circle. So, after running the refinement for each of
|
||||
these indices, we need to discard any final value that isn't the circle radius. And because we're working with floating point numbers,
|
||||
what this really means is that we need to discard any value that's a pixel or more "off". Or, if we want to get really fancy, "some small
|
||||
<code>epsilon</code> value".
|
||||
</p>
|
||||
<p>
|
||||
Based on all of that, the following graphic shows this off for the standard cubic curve (which you can move the coordinates around for, of
|
||||
course) and a circle with a controllable radius centered on the graphic's center, using the code approach described above.
|
||||
</p>
|
||||
<graphics-element
|
||||
title="circle intersection"
|
||||
@@ -6775,7 +6782,7 @@ findClosest(start, x, y, r, LUT):
|
||||
>
|
||||
<fallback-image>
|
||||
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
|
||||
<img width="275px" height="275px" src="./images/chapters/circleintersection/ee119fad4c48f5e17871ff31268cf8cc.png" loading="lazy" />
|
||||
<img width="275px" height="275px" src="./images/chapters/circleintersection/3a3ea30971de247da93034de614a63a4.png" loading="lazy" />
|
||||
<label>circle intersection</label>
|
||||
</fallback-image>
|
||||
<input type="range" min="1" max="150" step="1" value="70" class="slide-control" />
|
||||
|