1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-18 22:41:49 +02:00

circle refinement

This commit is contained in:
Pomax
2020-11-21 11:10:31 -08:00
parent c4a729140b
commit 94a5ea879d
84 changed files with 313 additions and 260 deletions

View File

@@ -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() {

View File

@@ -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">

View File

@@ -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
```

View File

@@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 49 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 49 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -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 ╯

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -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>

View File

@@ -159,6 +159,7 @@ main {
top: 0;
z-index: 2;
border-bottom: 1px solid black;
box-shadow: 0 0 0 0.5em white;
}
}

View File

@@ -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" />

View File

@@ -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" />