diff --git a/docs/Proportional-Interval-Stamp/Proportional-Interval-Stamp.mdx b/docs/Proportional-Interval-Stamp/Proportional-Interval-Stamp.mdx index 2b0e7a6..0de4768 100644 --- a/docs/Proportional-Interval-Stamp/Proportional-Interval-Stamp.mdx +++ b/docs/Proportional-Interval-Stamp/Proportional-Interval-Stamp.mdx @@ -175,28 +175,28 @@ and we already know the radius at a segment $r(x) = r_0 - \cos\theta x$. We need the stamp interval to be proportional to radius. Therefore, for each segment, define the stamp interval as $\eta r(x)$. -To find the number of stamps $\Delta N$ in a segment, divide the length by the interval: +To find the number of stamps $\Delta n$ in a segment, divide the length by the interval: $$ \Delta n = \frac{\Delta x}{\eta r(x)} $$ -To find the number of stamps $\Delta n$ over the edge, we need to integrate $\Delta n$ from 0 to x. +To find the number of stamps $\Delta n$ over the edge, integrate $\Delta n$ from 0 to x. $$ n(x) = \int_0^{x} \frac{dx}{\eta r(x)} $$ -Given $r(x) = r_0 - \cos\theta x$, we substitute this into the integral and get: +Given $r(x) = r_0 - \cos\theta x$, substitute it into the integral and get: $$ -\tag{1} n(x) = \frac{1}{\eta \cos\theta} \ln(r_0 - \cos\theta x) \bigg|_0^x = \frac{1}{\eta \cos\theta} \ln (1 - \frac{\cos\theta x}{r_0}) +\tag{1} n(x) = -\frac{1}{\eta \cos\theta} \ln(r_0 - \cos\theta x) \bigg|_0^x = -\frac{1}{\eta \cos\theta} \ln (1 - \frac{\cos\theta x}{r_0}) $$ Exchange the dependent and independent variable in formula (1) and get: $$ -\tag{2} x(n) = \frac{r_0}{\cos\theta}(1 - e^{\eta\cos\theta n}) +\tag{2} x(n) = \frac{r_0}{\cos\theta}(1 - e^{-\eta\cos\theta n}) $$ As $x = L$, we know the total stamp number on the edge, remind that $\cos\theta L = r_0-r_1$: @@ -205,21 +205,67 @@ $$ \tag{3} n(L) = \frac{1}{\eta \cos\theta}\ln \frac{r_0}{r_1} $$ -Soon we will use the formula (1)(2)(3) in our code. +We will soon use the formula (1)(2)(3) in our code. -![store](store-n.png) - -How do we place footprint on the polyline? +How do we place footprints? Imagine a point starts from the first vertex and moves along the polyline. -The stamp number to the first vertex grows with its movement. -Every time the number hits an integer, we place a footprint at this point. +Its stamp index (number of stamps from the first vertex) $n$ grows with its movement. +Each time this index hits an integer, place a footprint at that point. -Remind the figure below in the Stamp section. -Given a point or pixel, we calculate a range on the edge that can cover the point. -We label the two roots with $x_1$ and $x_2$. +Remind the below figure in the Stamp section. +Given a point, we calculate a range on the edge that can cover the point by solving a quadratic equation. +Label the two roots with $x_1$ and $x_2$. ![locate stamp](./locate-stamp.png) -With the stamp number of each edge $n(L)$, we can prefix sum $n(L)$ the number of stamps from each vertex to the first vertex. -We will store the number into each vertex. -For the current edge, we use $n_0$ and $n_1$ to indicate the stamp number at vertex0 and vertex1. +To calculate the nearest stamp point (the black dot in the figure) between $x_1$ and $x_2$, +we need to compute the number of stamps from $x_1$ to the polyline's first vertex, which is also called stamp index at $x_1$. +According to formula (1), on the current edge, the number of stamps from vertex0 to $x_1$ is $n(x_1)$. +Label the stamp index of vertex0 as $n_0$, and label vertex1's as $n_1$. + +![store](./store-n.png) + +Therefore, the stamp index of $x_1$ is $n_0 + n(x_1)$. +Because we place footprints at the points with integer stamp index, +the ceiling of $n_0 + n(x_1)$ is the nearest stamp's index, +denoted as $n_\mathrm{nearest} = \lceil n_0 + n(x_1) \rceil$. +Replace it into the formula (2) to get its position $x_{\mathrm{nearest}} = x(\lceil n_0 + n(x_1) \rceil)$, +and all other positions $x(n_\mathrm{nearest} + 1)$, $x(n_\mathrm{nearest} + 2) \dots$ + +Additionally, to determine $n_0$ and $n_1$, we can compute the prefix sum of the $n(L)$ in formula (3) over all edges. +Put them into vertex data and pass them into fragment shader, exactly same as the value `length` in the Stamp section. + +## Implementation + +## Corner case + +## Proof of properties +In the Stamp Pattern subsection, we claim that "stamp interval is always proportional to radius of stroke" and +"proportional interval has more consistent appearance". +Though they are intuitively correct, it's time to prove them mathematically! + +### Proportional interval +**Conclusion:** +Pick two consecutive footprints on an edge. +Label their stamp indices as $n$ and $n+1$, positions as $x(n)$ and $x(n+1)$, radii as $r(n)$ and $r(n+1)$. +Their interval $x(n+1) - x(n)$ is proportional to both $r(n)$ and $r(n+1)$: +$$ +\begin{aligned} +\frac{x(n+1) - x(n)}{r(n)} &= \frac{1 - e^{-\eta\cos\theta}}{\cos\theta}\\ +\frac{x(n+1) - x(n)}{r(n+1)} &= \frac{e^{\eta\cos\theta} - 1}{\cos\theta} +\end{aligned} +$$ + +**Proof:** +Take the formula (3) into $r(x) = r_0 - \cos\theta x$ to get $r(n) = r_0e^{-\eta\cos\theta n}$. +The rest of proof is straightforward. + +Notice the ratios do not depend on variable $n$. +I was amazed by this result. +Even now, it still fascinates me that how a naive rule (interval is proportional to the radius continuously) can produce an elegant result. + +### Consistent appearance +**Conclusion:** +For points that have the same stamp number that can cover it (i.e., $n(x_2) - n(x_1)$ value is the same), +they form a straight line. +I call this line "isocolor" line (analogy to "isoheight" line). \ No newline at end of file