diff --git a/docs/Basics/Basics/Basics.mdx b/docs/Basics/Basics/Basics.mdx
index b012054..602603b 100644
--- a/docs/Basics/Basics/Basics.mdx
+++ b/docs/Basics/Basics/Basics.mdx
@@ -22,7 +22,7 @@ The vanilla and stamp strokes share the exact same vertex placement method.
For your better understanding, avoid jumping right into the stamp part.
Maybe the stamp strokes with various styles don't interest you, feel free to ignore it and learn the vanilla stroke only.
-Knowing how to render a line is very handy when drawing UIs or debugging your 3D scenes,
+Knowing how to render a line is very handy when drawing GUIs or debugging your 3D scenes,
and our method may be one of the simplest and most elegant ways of line rendering.
While you may recognize a brush stroke by its stylization, another crucial property could be ignored:
@@ -38,8 +38,8 @@ The one with variable width on the right has more expressive appearance.
The width values are typically generated from the pressure values as a stylus presses and moves on a tablet.
-After the artists install a new painting program,
-one of the highest priorities is to configure the mapping function from pen pressure to brush radius.
+After the artists install a new paint program,
+one of the highest priorities is to configure the mapping function from pen pressure to stroke width.
In case you don't know about tablets and styluses
@@ -56,8 +56,8 @@ If you're unfamiliar with tablets and styluses, you can watch the video below fo
To store the variable radius in brush stroke, we will render an uncommon type of vector curve:
An ordered list of points (polyline) with radius values assigned to each point.
As a user presses a stylus on a tablet and moves, a paint program generates a sequence of points to record the trace of movement.
-Meanwhile, the pen pressure is transformed into the radius value assigned to each point.
-After rendering the stroke on the polyline, the user feels like drawing on canvas.
+Meanwhile, the pen pressure is transformed into the stroke width value assigned to each point.
+After rendering the stroke on the polyline, the user feels like drawing on a canvas.
![Monkey](./monkey.png)
@@ -66,7 +66,7 @@ The monkey Suzanne in Blender (Grease Pencil), the orange dots on the right side
We can approximate any type of curve by increasing the number of points in a polyline, whether freehand-drawn or mathematically defined.
-Try to change the `maxRadius` and `segmentCount` values in the code editor below to see how the vanilla stroke changes.
+Try to change the `maxRadius` (radius is half width) and `segmentCount` values in the code editor below to see how the vanilla stroke changes.
I will elaborate on how to render this stroke in the next section.
Feel free to change any other parts of the code as long as the function returns the `position` and `radius` array correctly.
@@ -76,4 +76,4 @@ import { ArticulatedLine2D } from "@site/src/components/ArticulatedLine2D";
Blender Grease Pencil team has developed many novel tools to edit polylines.
I recommend that everyone researching digital painting techniques learn about them.
-https://www.youtube.com/watch?v=nZyB30-xZFs
\ No newline at end of file
+Here is a tutorial video on how to use blender grease pencil: https://www.youtube.com/watch?v=nZyB30-xZFs
\ No newline at end of file
diff --git a/docs/Basics/Stamp/Stamp.mdx b/docs/Basics/Stamp/Stamp.mdx
index bedd5f3..776ec44 100644
--- a/docs/Basics/Stamp/Stamp.mdx
+++ b/docs/Basics/Stamp/Stamp.mdx
@@ -20,7 +20,7 @@ Therefore, we will focus on the most critical technique, how to place the footpr
A naive solution is to place a footprint at each vertex.
![naive](./dotted-monkey.png)
-_Place a dot texture at each vertex._
+ Place a dot texture at each vertex.
But it's not good enough for the most usages.
The rendering result of strokes depends on polylines' vertex density.
@@ -79,7 +79,7 @@ Given the pixel $p$, we can get this segment by solving the geometry shown in th
I label the segment can cover the pixel $p$ with the thicker black line.
The two dashed circles intersect at $p$.
-I label their centers with vertical line ticks, which are the start and end points of the segment.
+Their centers are labeled with vertical line ticks, which are the start and end points of the segment.
The centers are the farthest points where a stamp placed can cover the pixel $p$.
Any stamps outside the segment have no chance to affect the pixel's color.
@@ -100,9 +100,9 @@ $$
It's not hard to derive $r(x)$ from the figure below.
$$
-r(x) = \frac{x}{l}r_1 + \left(1-\frac{x}{l}\right)r_0
+r(x) = \frac{x}{L}r_1 + \left(1-\frac{x}{L}\right)r_0
$$
-$l$ is the length of the current edge.
+$L$ is the length of the current edge.
![xradius](./stamp-xradius.png)
By replacing $r(x)$, we get a quadratic equation $ax^2 + bx + c = 0$:
@@ -112,7 +112,7 @@ b = - 2 * (r_0\cos\theta + x_p);
c = x_p^2 + y_p^2 - r_0^2
$$
-Remind that $\cos\theta = (r_0 - r_1)/l$.
+Remind that $\cos\theta = (r_0 - r_1)/L$.
Applying the formula for solving quadratic equations,
two roots of the equation are the X value of min and max points of the segment.
Therefore, we know the range in the fragment shader and pixels only loop through stamps that can cover it.
@@ -129,7 +129,7 @@ we set RGB values with users' brush setting and sample alpha values from a monoc
whose pixel's gray scale determines the opacity.
Here is the pseudocode in the fragment shader:
-```
+``` glsl
uniform vec3 RGB; // Users' brush setting
void main(){
float A = 0.0;
diff --git a/docs/Proportional-Interval-Stamp/Artboard 1.png b/docs/Proportional-Interval-Stamp/Artboard 1.png
deleted file mode 100644
index 61af870..0000000
Binary files a/docs/Proportional-Interval-Stamp/Artboard 1.png and /dev/null differ
diff --git a/docs/Proportional-Interval-Stamp/Proportional-Interval-Stamp.mdx b/docs/Proportional-Interval-Stamp/Proportional-Interval-Stamp.mdx
index 3f7b93a..d25e7b5 100644
--- a/docs/Proportional-Interval-Stamp/Proportional-Interval-Stamp.mdx
+++ b/docs/Proportional-Interval-Stamp/Proportional-Interval-Stamp.mdx
@@ -19,12 +19,22 @@ This content is under construction and not peer-reviewed. Learn it under your ow
+
+
+
+© 2024 Shen Ciao. All rights reserved on this webpage.
+
+Unauthorized use and/or duplication of this material without express and written permission from this site’s author and/or owner is strictly prohibited.
+Excerpts and links may be used, provided that full and clear credit is given to Shen Ciao with appropriate and specific direction to the original content.
+
+
+
:::
## Introduction to stamp patterns
### Fixed interval
-In the [Stamp](../Basics/Stamp) section, I introduced stamp brush and methods to render it.
+In the [Stamp](../Basics/Stamp) section, we learned stamp brush and methods to render it.
We assumed the interval between stamp is a fixed value along a stroke.
> While a user paints on a canvas, we render the texture onto the canvas **equidistantly** along the drawing trace.
@@ -121,7 +131,7 @@ I'll soon explain it with precise mathematical formulas, so don't worry about it
![interval animation](./interval-ratio-animation.gif)
- Proportional interval stamp stroke animation by reducing the interval ratio $\eta$.
+ Proportional interval animation by reducing the interval ratio $\eta$.
Proportional interval has another benefit: Strokes have more consistent appearance!
@@ -132,6 +142,40 @@ Actually, it's a [fractal](https://en.wikipedia.org/wiki/Fractal) like mandelbro
Krita supports the proportional interval.
We can find the "Auto" toggle button under the "Spacing" setting, as shown in the figure below.
-When enabled, the brush draws proportional interval strokes; when disabled, it draws fixed interval stroke.
+When enabled, the brush draws proportional interval strokes; when disabled, it draws fixed interval strokes.
+
+![spacing auto setting](./krita-spacing-auto.png)
+
+Implementing the proportional interval on the CPU is straightforward.
+The interval can be computed dynamically using the current drawn footprint.
+Here's the pseudocode for the process:
+
+``` cpp
+vec2 currPos = firstStampPosition;
+float currRadius = firstStampRadius;
+while (currPos != endPos){
+ drawFootprint(currPos, currRadius);
+ // highlight-next-line
+ float interval = currRadius * interval_ratio;
+ vec2 nextPos = addAlongThePolyline(currPos, interval);
+ vec2 nextRadius = radius(nextPos);
+ currPos = nextPos; currRadius = nextRadius;
+}
+```
-![spacing auto setting](./krita-spacing-auto.png)
\ No newline at end of file
+The `nextPos` depends on `currPos`, so the process cannot be GPU-accelerated.
+To overcome this, we need to eliminate this dependency while maintaining the appearance of the stroke.
+It's a significant challenge and will require some calculus to solve it.
+
+## Theory
+We want the interval to be proportional to radius.
+Let's define this in a calculus way!
+
+Given a polyline and pick one of its edges, cut the edge into infinite small segments with length $\Delta L$.
+Each segment has its local coordinate $x$, and we know its radius $r(x) = r_0 - \cos\theta x$.
+Within a small segment, define the stamp interval as $\eta r(x)$.
+So the number of stamps $\Delta n$ in this segment is $\frac{\Delta L}{\eta r(x)}$.
+Sum $\Delta n$ over the edge, we get the number of stamps $n(x)$ on this edge;
+sum the number on all edges, we get the count of stamps on the polyline.
+So we have the stamp number $n$ grows along the polyline.
+Every time this $n$ value hits an integer, we stamp on that point.
\ No newline at end of file