diff --git a/docs/chapters/control/content.en-GB.md b/docs/chapters/control/content.en-GB.md index 55cea268..1786f622 100644 --- a/docs/chapters/control/content.en-GB.md +++ b/docs/chapters/control/content.en-GB.md @@ -5,9 +5,17 @@ Bézier curves are, like all "splines", interpolation functions. This means that The following graphs show the interpolation functions for quadratic and cubic curves, with "S" being the strength of a point's contribution to the total sum of the Bézier function. Click-and-drag to see the interpolation percentages for each curve-defining point at a specific t value.
- - - + + + + + + + + + + +
Also shown is the interpolation function for a 15th order Bézier function. As you can see, the start and end point contribute considerably more to the curve's shape than any other point in the control point set. diff --git a/docs/chapters/control/content.ja-JP.md b/docs/chapters/control/content.ja-JP.md index 82447736..ad7218a8 100644 --- a/docs/chapters/control/content.ja-JP.md +++ b/docs/chapters/control/content.ja-JP.md @@ -5,9 +5,17 @@ 下のグラフは、2次ベジエ曲線や3次ベジエ曲線の補間関数を表しています。ここでSは、ベジエ関数全体に対しての、その点の寄与の大きさを示します。あるtにおいて、ベジエ曲線を定義する各点の補間率がどのようになっているのか、クリックドラッグをして確かめてみてください。
- - - + + + + + + + + + + +
あわせて、15次ベジエ関数における補間関数も示しています。始点と終点は他の制御点と比較して、曲線の形に対してかなり大きな影響を与えていることがわかります。 diff --git a/docs/chapters/control/content.zh-CN.md b/docs/chapters/control/content.zh-CN.md index cc51ae43..be9a3445 100644 --- a/docs/chapters/control/content.zh-CN.md +++ b/docs/chapters/control/content.zh-CN.md @@ -5,9 +5,17 @@ 下面的图形显示了二次曲线和三次曲线的差值方程,“S”代表了点对贝塞尔方程总和的贡献。点击拖动点来看看在特定的t值时,每个曲线定义的点的插值百分比。
- - - + + + + + + + + + + +
上面有一张是15th阶的插值方程。如你所见,在所有控制点中,起点和终点对曲线形状的贡献比其他点更大些。 diff --git a/docs/chapters/control/lerp-cubic.js b/docs/chapters/control/lerp-cubic.js index 7424436a..af229d29 100644 --- a/docs/chapters/control/lerp-cubic.js +++ b/docs/chapters/control/lerp-cubic.js @@ -10,6 +10,12 @@ setup() { ]; this.s = this.f.map(f => plot(f) ); + this.position = 0; + setSlider(`.slide-control`, v => this.setPosition(v)) +} + +setPosition(v) { + this.position = v; } draw() { @@ -33,30 +39,23 @@ draw() { } drawHighlight() { - if (this.cursor.down) { + let c = screenToWorld({ + x: map(this.position, 0, 1, -10, this.width + 10), + y: this.height/2 + }); - let c = screenToWorld(this.cursor); - if (c.x < 0) return; - if (c.x > this.width) return; + if (c.x < 0) return; + if (c.x > this.width) return; - noStroke(); - setFill(`rgba(255,0,0,0.3)`); - rect(c.x - 2, 0, 5, this.height); + noStroke(); + setFill(`rgba(255,0,0,0.3)`); + rect(c.x - 2, 0, 5, this.height); - const p = this.f.map(f => f(c.x / this.width)); + const p = this.f.map(f => f(c.x / this.width)); - setFill(`black`); - p.forEach(p => { - circle(p.x, p.y, 3); - text(`${ round(100 * p.y/this.height) }%`, p.x + 10, p.y); - }); - } -} - -onMouseMove() { - redraw(); -} - -onMouseUp() { - redraw(); + setFill(`black`); + p.forEach(p => { + circle(p.x, p.y, 3); + text(`${ round(100 * p.y/this.height) }%`, p.x + 10, p.y); + }); } diff --git a/docs/chapters/control/lerp-fifteenth.js b/docs/chapters/control/lerp-fifteenth.js index ee6a9fb2..a6d8655a 100644 --- a/docs/chapters/control/lerp-fifteenth.js +++ b/docs/chapters/control/lerp-fifteenth.js @@ -2,6 +2,12 @@ setup() { this.degree = 15; this.triangle = [[1], [1,1]]; this.generate(); + this.position = 0; + setSlider(`.slide-control`, v => this.setPosition(v)) +} + +setPosition(v) { + this.position = v; } binomial(n,k) { @@ -52,30 +58,23 @@ draw() { } drawHighlight() { - if (this.cursor.down) { + let c = screenToWorld({ + x: map(this.position, 0, 1, -10, this.width + 10), + y: this.height/2 + }); - let c = screenToWorld(this.cursor); - if (c.x < 0) return; - if (c.x > this.width) return; + if (c.x < 0) return; + if (c.x > this.width) return; - noStroke(); - setFill(`rgba(255,0,0,0.3)`); - rect(c.x - 2, 0, 5, this.height); + noStroke(); + setFill(`rgba(255,0,0,0.3)`); + rect(c.x - 2, 0, 5, this.height); - const p = this.f.map(f => f(c.x / this.width)); + const p = this.f.map(f => f(c.x / this.width)); - setFill(`black`); - p.forEach(p => { - circle(p.x, p.y, 3); - text(`${ round(100 * p.y/this.height) }%`, p.x + 10, p.y); - }); - } -} - -onMouseMove() { - redraw(); -} - -onMouseUp() { - redraw(); + setFill(`black`); + p.forEach(p => { + circle(p.x, p.y, 3); + text(`${ round(100 * p.y/this.height) }%`, p.x + 10, p.y); + }); } diff --git a/docs/chapters/control/lerp-quadratic.js b/docs/chapters/control/lerp-quadratic.js index e1634356..292840c5 100644 --- a/docs/chapters/control/lerp-quadratic.js +++ b/docs/chapters/control/lerp-quadratic.js @@ -9,6 +9,12 @@ setup() { ]; this.s = this.f.map(f => plot(f) ); + this.position = 0; + setSlider(`.slide-control`, v => this.setPosition(v)) +} + +setPosition(v) { + this.position = v; } draw() { @@ -32,30 +38,23 @@ draw() { } drawHighlight() { - if (this.cursor.down) { + let c = screenToWorld({ + x: map(this.position, 0, 1, -10, this.width + 10), + y: this.height/2 + }); - let c = screenToWorld(this.cursor); - if (c.x < 0) return; - if (c.x > this.width) return; + if (c.x < 0) return; + if (c.x > this.width) return; - noStroke(); - setFill(`rgba(255,0,0,0.3)`); - rect(c.x - 2, 0, 5, this.height); + noStroke(); + setFill(`rgba(255,0,0,0.3)`); + rect(c.x - 2, 0, 5, this.height); - const p = this.f.map(f => f(c.x / this.width)); + const p = this.f.map(f => f(c.x / this.width)); - setFill(`black`); - p.forEach(p => { - circle(p.x, p.y, 3); - text(`${ round(100 * p.y/this.height) }%`, p.x + 10, p.y); - }); - } -} - -onMouseMove() { - redraw(); -} - -onMouseUp() { - redraw(); + setFill(`black`); + p.forEach(p => { + circle(p.x, p.y, 3); + text(`${ round(100 * p.y/this.height) }%`, p.x + 10, p.y); + }); } diff --git a/docs/chapters/decasteljau/content.en-GB.md b/docs/chapters/decasteljau/content.en-GB.md index dcce3307..e0b4f79c 100644 --- a/docs/chapters/decasteljau/content.en-GB.md +++ b/docs/chapters/decasteljau/content.en-GB.md @@ -15,7 +15,9 @@ Rather than using our calculus function to find `x/y` values for `t`, let's do t To see this in action, mouse-over the following sketch. Moving the mouse changes which curve point is explicitly evaluated using de Casteljau's algorithm, moving the cursor left-to-right (or, of course, right-to-left), shows you how a curve is generated using this approach. - + + +
diff --git a/docs/chapters/decasteljau/content.ja-JP.md b/docs/chapters/decasteljau/content.ja-JP.md index 0624b1dc..21c8336f 100644 --- a/docs/chapters/decasteljau/content.ja-JP.md +++ b/docs/chapters/decasteljau/content.ja-JP.md @@ -15,7 +15,9 @@ 下の図にマウスを乗せると、この様子を実際に見ることができます。ド・カステリョのアルゴリズムによって曲線上の点を明示的に計算していますが、マウスを動かすと求める点が変わります。マウスカーソルを左から右へ(もちろん、右から左へでも)動かせば、このアルゴリズムによって曲線が生成される様子がわかります。 - + + +
diff --git a/docs/chapters/decasteljau/content.zh-CN.md b/docs/chapters/decasteljau/content.zh-CN.md index 2157792f..b69e80f8 100644 --- a/docs/chapters/decasteljau/content.zh-CN.md +++ b/docs/chapters/decasteljau/content.zh-CN.md @@ -15,7 +15,9 @@ 我们通过实际操作来观察这个过程。在以下的图表中,移动鼠标来改变用de Casteljau算法计算得到的曲线点,左右移动鼠标,可以实时看到曲线是如何生成的。 - + + +
diff --git a/docs/chapters/decasteljau/decasteljau.js b/docs/chapters/decasteljau/decasteljau.js index a3dc0360..192dddbb 100644 --- a/docs/chapters/decasteljau/decasteljau.js +++ b/docs/chapters/decasteljau/decasteljau.js @@ -1,28 +1,37 @@ -let curve; - setup() { - curve = new Bezier(this, 90, 200, 25, 100, 220, 40, 210, 240); - setMovable(curve.points); + this.curve = new Bezier(this, 90, 200, 25, 100, 220, 40, 210, 240); + setMovable(this.curve.points); + this.position = 0; + setSlider(`.slide-control`, v => this.setPosition(v)); +} + +setPosition(v) { + this.position = v; } draw() { clear(); + const curve = this.curve; curve.drawSkeleton(); curve.drawCurve(); setStroke("rgb(200,100,100)"); - let dim = this.height; - let t = this.cursor.x / dim; - curve.drawStruts(t); + let t = this.position; + if (0 < t && t < 1) { + curve.drawStruts(t); + } + curve.drawPoints(); - let p = curve.get(t); - circle(p.x, p.y, 5); + if (0 < t && t < 1) { + let p = curve.get(t); + circle(p.x, p.y, 5); - let perc = (t*100)|0; - t = perc/100; - text(`Sequential interpolation for ${perc}% (t=${t})`, 10, 15); + let perc = (t*100)|0; + let rt = perc/100; + text(`Sequential interpolation for ${perc}% (t=${rt})`, 10, 15); + } } onMouseMove() { diff --git a/docs/chapters/explanation/circle.js b/docs/chapters/explanation/circle.js index ca282293..64eb804a 100644 --- a/docs/chapters/explanation/circle.js +++ b/docs/chapters/explanation/circle.js @@ -1,5 +1,10 @@ setup() { this.step = 5; + setSlider(`.slide-control`, v => this.setStep(v)); +} + +setStep(v) { + this.step = v; } draw() { @@ -36,28 +41,3 @@ draw() { } } } - -onKeyDown() { - const key = this.keyboard.currentKey; - if (key === `ArrowUp`) { this.step += 0.1; } - if (key === `ArrowDown`) { this.step -= 0.1; } - if (this.step < 1) this.step = 1; - redraw(); -} - -onMouseDown() { - this.mark = this.cursor.y; -} - -onMouseUp() { - this.mark = false; -} - -onMouseMove() { - if (this.mark) { - let diff = this.mark - this.cursor.y, - mapped = map(diff, -this.height/2, this.height/2, 0, 10, true); - this.step = mapped; - redraw(); - } -} diff --git a/docs/chapters/explanation/content.en-GB.md b/docs/chapters/explanation/content.en-GB.md index 578c74a2..b6d088f8 100644 --- a/docs/chapters/explanation/content.en-GB.md +++ b/docs/chapters/explanation/content.en-GB.md @@ -39,9 +39,11 @@ Multiple functions, but only one variable. If we change the value for t, There we go. x/y coordinates, linked through some mystery value t. -So, parametric curves don't define a y coordinate in terms of an x coordinate, like normal functions do, but they instead link the values to a "control" variable. If we vary the value of t, then with every change we get two values, which we can use as (x,y) coordinates in a graph. The above set of functions, for instance, generates points on a circle: We can range t from negative to positive infinity, and the resulting (x,y) coordinates will always lie on a circle with radius 1 around the origin (0,0). If we plot it for t from 0 to 5, we get this (use your up and down arrow keys to change the plot end value): +So, parametric curves don't define a y coordinate in terms of an x coordinate, like normal functions do, but they instead link the values to a "control" variable. If we vary the value of t, then with every change we get two values, which we can use as (x,y) coordinates in a graph. The above set of functions, for instance, generates points on a circle: We can range t from negative to positive infinity, and the resulting (x,y) coordinates will always lie on a circle with radius 1 around the origin (0,0). If we plot it for t from 0 to 5, we get this: - + + + Bézier curves are just one out of the many classes of parametric functions, and are characterised by using the same base function for all of the output values. In the example we saw above, the x and y values were generated by different functions (one uses a sine, the other a cosine); but Bézier curves use the "binomial polynomial" for both the x and y outputs. So what are binomial polynomials? diff --git a/docs/chapters/explanation/content.ja-JP.md b/docs/chapters/explanation/content.ja-JP.md index 01aa1164..a0c299ea 100644 --- a/docs/chapters/explanation/content.ja-JP.md +++ b/docs/chapters/explanation/content.ja-JP.md @@ -37,9 +37,11 @@ きました。x/y座標です。謎の値tを通して繫がっています。 -というわけで、普通の関数ではy座標をx座標によって定義しますが、パラメトリック曲線ではそうではなく、座標の値を「制御」変数と結びつけます。tの値を変化させるたびに2つの値が変化するので、これをグラフ上の座標 (x,y)として使うことができます。例えば、先ほどの関数の組は円周上の点を生成します。負の無限大から正の無限大へとtを動かすと、得られる座標(x,y)は常に中心(0,0)・半径1の円の上に乗ります。tを0から5まで変化させてプロットした場合は、このようになります(上下キーでプロットの上限を変更できます)。 +というわけで、普通の関数ではy座標をx座標によって定義しますが、パラメトリック曲線ではそうではなく、座標の値を「制御」変数と結びつけます。tの値を変化させるたびに2つの値が変化するので、これをグラフ上の座標 (x,y)として使うことができます。例えば、先ほどの関数の組は円周上の点を生成します。負の無限大から正の無限大へとtを動かすと、得られる座標(x,y)は常に中心(0,0)・半径1の円の上に乗ります。tを0から5まで変化させてプロットした場合は、このようになります。 - + + + ベジエ曲線はパラメトリック関数の一種であり、どの次元に対しても同じ基底関数を使うという点で特徴づけられます。先ほどの例では、xの値とyの値とで異なる関数(正弦関数と余弦関数)を使っていましたが、ベジエ曲線ではxyの両方で「二項係数多項式」を使います。では、二項係数多項式とは何でしょう? diff --git a/docs/chapters/explanation/content.zh-CN.md b/docs/chapters/explanation/content.zh-CN.md index c16ff7d9..5a416feb 100644 --- a/docs/chapters/explanation/content.zh-CN.md +++ b/docs/chapters/explanation/content.zh-CN.md @@ -37,9 +37,11 @@ 好了,通过一些神秘的t值将x/y坐标系联系起来。 -所以,参数曲线不像一般函数那样,通过x坐标来定义y坐标,而是用一个“控制”变量将它们连接起来。如果改变t的值,每次变化时我们都能得到两个值,这可以作为图形中的(x,y)坐标。比如上面的方程组,生成位于一个圆上的点:我们可以使t在正负极值间变化,得到的输出(x,y)都会位于一个以原点(0,0)为中心且半径为1的圆上。如果我们画出t从0到5时的值,将得到如下图像(你可以用上下键来改变画的点和值): +所以,参数曲线不像一般函数那样,通过x坐标来定义y坐标,而是用一个“控制”变量将它们连接起来。如果改变t的值,每次变化时我们都能得到两个值,这可以作为图形中的(x,y)坐标。比如上面的方程组,生成位于一个圆上的点:我们可以使t在正负极值间变化,得到的输出(x,y)都会位于一个以原点(0,0)为中心且半径为1的圆上。如果我们画出t从0到5时的值,将得到如下图像: - + + + 贝塞尔曲线是(一种)参数方程,并在它的多个维度上使用相同的基本方程。在上述的例子中x值和y值使用了不同的方程,与此不同的是,贝塞尔曲线的xy都用了“二项多项式”。那什么是二项多项式呢? diff --git a/docs/chapters/flattening/content.en-GB.md b/docs/chapters/flattening/content.en-GB.md index 17222773..3c654042 100644 --- a/docs/chapters/flattening/content.en-GB.md +++ b/docs/chapters/flattening/content.en-GB.md @@ -4,8 +4,15 @@ We can also simplify the drawing process by "sampling" the curve at certain poin We can do this is by saying "we want X segments", and then sampling the curve at intervals that are spaced such that we end up with the number of segments we wanted. The advantage of this method is that it's fast: instead of evaluating 100 or even 1000 curve coordinates, we can sample a much lower number and still end up with a curve that sort-of-kind-of looks good enough. The disadvantage of course is that we lose the precision of working with "the real curve", so we usually can't use the flattened for for doing true intersection detection, or curvature alignment. - - +
+ + + + + + + +
Try clicking on the sketch and using your up and down arrow keys to lower the number of segments for both the quadratic and cubic curve. You'll notice that for certain curvatures, a low number of segments works quite well, but for more complex curvatures (try this for the cubic curve), a higher number is required to capture the curvature changes properly. diff --git a/docs/chapters/flattening/content.ja-JP.md b/docs/chapters/flattening/content.ja-JP.md index ef33b235..e59ae55d 100644 --- a/docs/chapters/flattening/content.ja-JP.md +++ b/docs/chapters/flattening/content.ja-JP.md @@ -4,8 +4,15 @@ 例えば「X個の線分がほしい」場合には、分割数がそうなるようにサンプリング間隔を選び、曲線をサンプリングします。この方法の利点は速さです。曲線の座標を100個だの1000個だの計算するのではなく、ずっと少ない回数のサンプリングでも、十分きれいに見えるような曲線を作ることができるのです。欠点はもちろん、「本物の曲線」に比べて精度が損なわれてしまうことです。したがって、交点の検出や曲線の位置揃えを正しく行いたい場合には、平坦化した曲線は普通利用できません。 - - +
+ + + + + + + +
2次ベジエ曲線も3次ベジエ曲線も、図をクリックして上下キーを押すと曲線の分割数が増減しますので、試してみてください。ある曲線では分割数が少なくてもうまくいきますが、曲線が複雑になればなるほど、曲率の変化を正確に捉えるためにはより多くの分割数が必要になることがわかります(3次ベジエ曲線で試してみてください)。 diff --git a/docs/chapters/flattening/content.zh-CN.md b/docs/chapters/flattening/content.zh-CN.md index b08dbe1f..2dbf5a67 100644 --- a/docs/chapters/flattening/content.zh-CN.md +++ b/docs/chapters/flattening/content.zh-CN.md @@ -4,8 +4,15 @@ 我们可以先确定“想要X个分段”,然后在间隔的地方采样曲线,得到一定数量的分段。这种方法的优点是速度很快:比起遍历100甚至1000个曲线坐标,我们可以采样比较少的点,仍然得到看起来足够好的曲线。这么做的缺点是,我们失去了“真正的曲线”的精度,因此不能用此方法来做真实的相交检测或曲率对齐。 - - +
+ + + + + + + +
试着点击图形,并用上下键来降低二次曲线和三次曲线的分段数量。你会发现对某些曲率来说,数量少的分段也能做的很好,但对于复杂的曲率(在三次曲线上试试),足够多的分段才能很好地满足曲率的变化。 diff --git a/docs/chapters/flattening/cubic.js b/docs/chapters/flattening/cubic.js index 84552746..25d6ba80 100644 --- a/docs/chapters/flattening/cubic.js +++ b/docs/chapters/flattening/cubic.js @@ -2,6 +2,11 @@ setup() { this.steps = 8; this.curve = Bezier.defaultCubic(this); setMovable(this.curve.points); + setSlider(`.slide-control`, v => this.setStep(v)); +} + +setStep(v) { + this.steps = v; } draw() { @@ -23,23 +28,6 @@ draw() { text(`Flattened to ${this.steps} segments`, 10, 15); } -onKeyDown() { - let key = this.keyboard.currentKey; - - if(key === `ArrowUp`) { - this.steps++; - } - - if(key === `ArrowDown`) { - if(this.steps > 1) this.steps--; - } - - redraw(); -} - onMouseMove() { - if (this.cursor.down && !this.currentPoint) { - this.steps = round( map(this.cursor.y, 0,this.height, 24, 1) ); - } redraw(); } diff --git a/docs/chapters/flattening/quadratic.js b/docs/chapters/flattening/quadratic.js index 96e9e4e5..66570162 100644 --- a/docs/chapters/flattening/quadratic.js +++ b/docs/chapters/flattening/quadratic.js @@ -2,6 +2,11 @@ setup() { this.steps = 4; this.curve = Bezier.defaultQuadratic(this); setMovable(this.curve.points); + setSlider(`.slide-control`, v => this.setStep(v)); +} + +setStep(v) { + this.steps = v; } draw() { @@ -23,23 +28,6 @@ draw() { text(`Flattened to ${this.steps} segments`, 10, 15); } -onKeyDown() { - let key = this.keyboard.currentKey; - - if(key === `ArrowUp`) { - this.steps++; - } - - if(key === `ArrowDown`) { - if(this.steps > 1) this.steps--; - } - - redraw(); -} - onMouseMove() { - if (this.cursor.down && !this.currentPoint) { - this.steps = round( map(this.cursor.y, 0,this.height, 16, 1) ); - } redraw(); } diff --git a/docs/chapters/pointvectors3d/content.en-GB.md b/docs/chapters/pointvectors3d/content.en-GB.md index 5ac64a75..46aa1514 100644 --- a/docs/chapters/pointvectors3d/content.en-GB.md +++ b/docs/chapters/pointvectors3d/content.en-GB.md @@ -24,7 +24,9 @@ Let's unpack that a little: And then we're done, we found "the" normal vector for a 3D curve. Let's see what that looks like for a sample curve, shall we? You can move your cursor across the graphic from left to right, to show the normal at a point with a t value that is based on your cursor position: all the way on the left is 0, all the way on the right = 1, midway is t=0.5, etc: - + + + However, if you've played with that graphic a bit, you might have noticed something odd. The normal seems to "suddenly twist around" around between t=0.5 and t=0.9... Why is doing that? @@ -109,8 +111,10 @@ Ignoring comments, this is certainly more code than when we were just computing Speaking of better looking, what does this actually look like? Let's revisit that earlier curve, but this time use rotation minimising frames rather than Frenet frames: - + + + -That looks much better! +That looks so much better! For those reading along with the code: we don't even strictly speaking need a Frenet frame to start with: we could, for instance, treat the z-axis as our initial axis of rotation, so that our initial normal is **(0,0,1) × tangent**, and then take things from there, but having that initial "mathematically correct" frame so that the initial normal seems to line up based on the curve's orientation in 3D space is just nice. diff --git a/docs/chapters/pointvectors3d/frenet.js b/docs/chapters/pointvectors3d/frenet.js index 6e777bbc..3868d8b6 100644 --- a/docs/chapters/pointvectors3d/frenet.js +++ b/docs/chapters/pointvectors3d/frenet.js @@ -26,6 +26,11 @@ setup() { this.cxz = new Bezier(this, points.map(p => projectXZ(p))); this.cyz = new Bezier(this, points.map(p => projectYZ(p))); this.t = 0; + setSlider(`.slide-control`, v => this.setPosition(v)); +} + +setPosition(v) { + this.t = v; } draw() { @@ -134,24 +139,3 @@ drawVector(from, vec, length, color, label) { }); text(label, from.x + txt.x, from.y + txt.y); } - -onMouseMove() { - this.t = constrain( - map(this.cursor.x,0,this.width,-0.1, 1.1) - ,0,1 - ); - redraw(); -} - -onKeyDown() { - let key = this.keyboard.currentKey; - if (key === `ArrowUp`) { - this.t += 0.01; - if (this.t > 1) this.t = 1; - } - if (key === `ArrowDown`) { - this.t -= 0.01; - if (this.t < 0) this.t = 0; - } - redraw(); -} \ No newline at end of file diff --git a/docs/chapters/pointvectors3d/rotation-minimizing.js b/docs/chapters/pointvectors3d/rotation-minimizing.js index 4414c983..373c6772 100644 --- a/docs/chapters/pointvectors3d/rotation-minimizing.js +++ b/docs/chapters/pointvectors3d/rotation-minimizing.js @@ -26,6 +26,11 @@ setup() { this.cxz = new Bezier(this, points.map(p => projectXZ(p))); this.cyz = new Bezier(this, points.map(p => projectYZ(p))); this.t = 0; + setSlider(`.slide-control`, v => this.setPosition(v)); +} + +setPosition(v) { + this.t = v; } draw() { @@ -190,24 +195,3 @@ drawVector(from, vec, length, color, label) { }); text(label, from.x + txt.x, from.y + txt.y); } - -onMouseMove() { - this.t = constrain( - map(this.cursor.x,0,this.width,-0.1, 1.1) - ,0,1 - ); - redraw(); -} - -onKeyDown() { - let key = this.keyboard.currentKey; - if (key === `ArrowUp`) { - this.t += 0.01; - if (this.t > 1) this.t = 1; - } - if (key === `ArrowDown`) { - this.t -= 0.01; - if (this.t < 0) this.t = 0; - } - redraw(); -} \ No newline at end of file diff --git a/docs/chapters/reordering/content.en-GB.md b/docs/chapters/reordering/content.en-GB.md index 4b038637..9d523642 100644 --- a/docs/chapters/reordering/content.en-GB.md +++ b/docs/chapters/reordering/content.en-GB.md @@ -134,4 +134,7 @@ The steps taken here are: And we're done: we now have an expression that lets us approximate an `n+1`th order curve with a lower `n`th order curve. It won't be an exact fit, but it's definitely a best approximation. So, let's implement these rules for raising and lowering curve order to a (semi) random curve, using the following graphic. Select the sketch, which has movable control points, and press your up and down arrow keys to raise or lower the curve order. - + + + + diff --git a/docs/chapters/reordering/reorder.js b/docs/chapters/reordering/reorder.js index 2e08e2bb..3618dc06 100644 --- a/docs/chapters/reordering/reorder.js +++ b/docs/chapters/reordering/reorder.js @@ -9,6 +9,15 @@ setup() { }); } setMovable(points); + this.bindButtons(); + } + +bindButtons() { + let rbutton = find(`.raise`); + if (rbutton) rbutton.listen(`click`, v => this.raise()); + + let lbutton = find(`.lower`); + if (lbutton) lbutton.listen(`click`, v => this.lower()); } draw() { @@ -63,6 +72,7 @@ raise() { this.points = np; resetMovable(this.points); + redraw(); } lower() { @@ -116,43 +126,9 @@ lower() { })); resetMovable(this.points); -} - -onKeyDown() { - const key = this.keyboard.currentKey; - if (key === `ArrowUp`) { - this.raise(); - } - if (key === `ArrowDown`) { - this.lower(); - } redraw(); } onMouseMove() { - if (this.cursor.down && !this.currentPoint) { - if (this.cursor.y < this.height/2) { - this.lowerTimer = clearInterval(this.lowerTimer); - if (!this.raiseTimer) { - this.raiseTimer = setInterval(() => { - this.raise(); - redraw(); - }, 1000); - } - } - if (this.cursor.y > this.height/2) { - this.raiseTimer = clearInterval(this.raiseTimer); - if (!this.lowerTimer) { - this.lowerTimer = setInterval(() => { - this.lower(); - redraw(); - }, 1000); - } - } - } else { redraw(); } -} - -onMouseUp() { - this.raiseTimer = clearInterval(this.raiseTimer); - this.lowerTimer = clearInterval(this.lowerTimer); + redraw(); } diff --git a/docs/chapters/splitting/content.en-GB.md b/docs/chapters/splitting/content.en-GB.md index 932e1952..ae7e0616 100644 --- a/docs/chapters/splitting/content.en-GB.md +++ b/docs/chapters/splitting/content.en-GB.md @@ -2,7 +2,9 @@ Using de Casteljau's algorithm, we can also find all the points we need to split up a Bézier curve into two, smaller curves, which taken together form the original curve. When we construct de Casteljau's skeleton for some value `t`, the procedure gives us all the points we need to split a curve at that `t` value: one curve is defined by all the inside skeleton points found prior to our on-curve point, with the other curve being defined by all the inside skeleton points after our on-curve point. - + + +
diff --git a/docs/chapters/splitting/content.ja-JP.md b/docs/chapters/splitting/content.ja-JP.md index 1703be80..eae1490a 100644 --- a/docs/chapters/splitting/content.ja-JP.md +++ b/docs/chapters/splitting/content.ja-JP.md @@ -2,7 +2,9 @@ ベジエ曲線を分割して、繫ぎ合わせたときに元に戻るような小さい2曲線にしたい場合にも、ド・カステリョのアルゴリズムを使えば、これに必要な点をすべて求めることができます。ある値`t`に対してド・カステリョの骨格を組み立てると、その`t`で曲線を分割する際に必要になる点がすべて得られます。骨格内部の点のうち、曲線上の点から見て手前側にある点によって一方の曲線が定義され、向こう側にある点によってもう一方の曲線が定義されます。 - + + +
diff --git a/docs/chapters/splitting/content.zh-CN.md b/docs/chapters/splitting/content.zh-CN.md index 102d1f15..dfd04f91 100644 --- a/docs/chapters/splitting/content.zh-CN.md +++ b/docs/chapters/splitting/content.zh-CN.md @@ -2,7 +2,9 @@ 使用 de Casteljau 算法我们也可以将一条贝塞尔曲线分割成两条更小的曲线,二者拼接起来即可形成原来的曲线。当采用某个 `t` 值构造 de Casteljau 算法时,该过程会给到我们在 `t` 点分割曲线的所有点: 一条曲线包含该曲线上点之前的所有点,另一条曲线包含该曲线上点之后的所有点。 - + + +
diff --git a/docs/chapters/splitting/handler.js b/docs/chapters/splitting/handler.js deleted file mode 100644 index 574a4cf8..00000000 --- a/docs/chapters/splitting/handler.js +++ /dev/null @@ -1,91 +0,0 @@ -module.exports = { - setupCubic: function(api) { - var curve = api.getDefaultCubic(); - api.setCurve(curve); - api.forward = true; - }, - - drawSplit: function(api, curve) { - api.setPanelCount(2); - api.reset(); - api.drawSkeleton(curve); - api.drawCurve(curve); - - var offset = {x:0, y:0}; - var t = 0.5; - var pt = curve.get(0.5); - var split = curve.split(t); - api.drawCurve(split.left); - api.drawCurve(split.right); - api.setColor("red"); - api.drawCircle(pt,3); - - api.setColor("black"); - offset.x = api.getPanelWidth(); - api.drawLine({x:0,y:0},{x:0,y:api.getPanelHeight()}, offset); - - api.setColor("lightgrey"); - api.drawCurve(curve, offset); - api.drawCircle(pt,4); - - offset.x -= 20; - offset.y -= 20; - api.drawSkeleton(split.left, offset, true); - api.drawCurve(split.left, offset); - - offset.x += 40; - offset.y += 40; - api.drawSkeleton(split.right, offset, true); - api.drawCurve(split.right, offset); - }, - - drawAnimated: function(api, curve) { - api.setPanelCount(3); - api.reset(); - - var frame = api.getFrame(); - var interval = 5 * api.getPlayInterval(); - var t = (frame%interval)/interval; - var forward = (frame%(2*interval)) < interval; - if (forward) { t = t%1; } else { t = 1 - t%1; } - var offset = {x:0, y:0}; - - api.setColor("lightblue"); - api.drawHull(curve, t); - api.drawSkeleton(curve); - api.drawCurve(curve); - var pt = curve.get(t); - api.drawCircle(pt, 4); - - api.setColor("black"); - offset.x += api.getPanelWidth(); - api.drawLine({x:0,y:0},{x:0,y:api.getPanelHeight()}, offset); - - var split = curve.split(t); - - api.setColor("lightgrey"); - api.drawCurve(curve, offset); - api.drawHull(curve, t, offset); - api.setColor("black"); - api.drawCurve(split.left, offset); - api.drawPoints(split.left.points, offset); - api.setFill("black"); - api.text("Left side of curve split at t = " + (((100*t)|0)/100), {x: 10 + offset.x, y: 15 + offset.y}); - - offset.x += api.getPanelWidth(); - api.drawLine({x:0,y:0},{x:0,y:api.getPanelHeight()}, offset); - - api.setColor("lightgrey"); - api.drawCurve(curve, offset); - api.drawHull(curve, t, offset); - api.setColor("black"); - api.drawCurve(split.right, offset); - api.drawPoints(split.right.points, offset); - api.setFill("black"); - api.text("Right side of curve split at t = " + (((100*t)|0)/100), {x: 10 + offset.x, y: 15 + offset.y}); - }, - - togglePlay: function(evt, api) { - if (api.playing) { api.pause(); } else { api.play(); } - } -}; diff --git a/docs/chapters/splitting/keepthisornot.txt b/docs/chapters/splitting/keepthisornot.txt deleted file mode 100644 index dba06483..00000000 --- a/docs/chapters/splitting/keepthisornot.txt +++ /dev/null @@ -1,28 +0,0 @@ - - -/* - -// const { left, right } = this.getLeftAndRight(this.t, this.curve.points); -// const c1 = new Bezier(this, left); -// const c2 = new Bezier(this, right); - -getLeftAndRight(t, points, left=[], right=[]) { - if(points.length === 1) { - left.push(points[0]); - right.push(points[0]); - return { left, right }; - } - - let newpoints = points.slice(1); - for(let i=0; i this.setPosition(v)); +} + +setPosition(v) { + this.t = v; } draw() { @@ -19,13 +24,13 @@ draw() { setStroke(`black`); line(0, 0, 0, this.height); - this.drawSegment(c1, p); + this.drawSegment(c1, p, `first`); translate(this.width/3, 0); setStroke(`black`); line(0, 0, 0, this.height); - this.drawSegment(c2, p); + this.drawSegment(c2, p, `second`); } drawBasics(p) { @@ -42,7 +47,7 @@ drawBasics(p) { text(`The full curve, with struts`, 10, 15); } -drawSegment(c, p) { +drawSegment(c, p, halfLabel) { setStroke(`lightblue`); this.curve.drawCurve(`lightblue`); this.curve.drawSkeleton(`lightblue`); @@ -53,28 +58,9 @@ drawSegment(c, p) { setStroke(`red`); circle(p.x, p.y, 3); setFill(`black`) - text(`The first half`, 10, 15); -} - -onKeyDown() { - let key = this.keyboard.currentKey; - - if(key === `ArrowUp`) { - this.t += 0.01; - } - - if(key === `ArrowDown`) { - this.t -= 0.01 - } - - this.t = this.t < 0 ? 0 : this.t > 1 ? 1 : this.t; - - redraw(); + text(`The ${halfLabel} half`, 10, 15); } onMouseMove() { - if (this.cursor.down && !this.currentPoint) { - this.t = map(this.cursor.y, 0,this.height, 0, 1); - } redraw(); } diff --git a/docs/chapters/whatis/content.en-GB.md b/docs/chapters/whatis/content.en-GB.md index 1f3bbf28..71647d47 100644 --- a/docs/chapters/whatis/content.en-GB.md +++ b/docs/chapters/whatis/content.en-GB.md @@ -19,7 +19,9 @@ Given \left ( So let's look at that in action: the following graphic is interactive in that you can use your up and down arrow keys to increase or decrease the interpolation ratio, to see what happens. We start with three points, which gives us two lines. Linear interpolation over those lines gives us two points, between which we can again perform linear interpolation, yielding a single point. And that point —and all points we can form in this way for all ratios taken together— form our Bézier curve: - + + + And that brings us to the complicated maths: calculus. diff --git a/docs/chapters/whatis/content.ja-JP.md b/docs/chapters/whatis/content.ja-JP.md index 0861d57f..89c15357 100644 --- a/docs/chapters/whatis/content.ja-JP.md +++ b/docs/chapters/whatis/content.ja-JP.md @@ -19,7 +19,9 @@ では、実際に見てみましょう。下の図はインタラクティブになっています。上下キーで補間の比率が増減しますので、どうなるか確かめてみましょう。最初に3点があり、それを結んで2本の直線が引かれています。この直線の上でそれぞれ線形補間を行うと、2つの点が得られます。この2点の間でさらに線形補間を行うと、1つの点を得ることができます。そして、あらゆる比率に対して同様に点を求め、それをすべて集めると、このようにベジエ曲線ができるのです。 - + + + また、これが複雑な方の数学につながっていきます。微積分です。 diff --git a/docs/chapters/whatis/content.zh-CN.md b/docs/chapters/whatis/content.zh-CN.md index 9f271266..76445eee 100644 --- a/docs/chapters/whatis/content.zh-CN.md +++ b/docs/chapters/whatis/content.zh-CN.md @@ -19,7 +19,9 @@ Given \left ( 让我们来通过实际操作看一下:下面的图形都是可交互的,因此你可以通过上下键来增加或减少插值距离,来观察图形的变化。我们从三个点构成的两条线段开始。通过对各条线段进行线性插值得到两个点,对点之间的线段再进行线性插值,产生一个新的点。最终这些点——所有的点都可以通过选取不同的距离插值产生——构成了贝塞尔曲线 : - + + + 这为我们引出了复杂的数学:微积分。 diff --git a/docs/chapters/whatis/interpolation.js b/docs/chapters/whatis/interpolation.js index fa1fe9e4..8e631bad 100644 --- a/docs/chapters/whatis/interpolation.js +++ b/docs/chapters/whatis/interpolation.js @@ -2,6 +2,11 @@ setup() { this.step = 25; this.curve = Bezier.defaultQuadratic(this); setMovable(this.curve.points); + setSlider(`.slide-control`, v => this.setStep(v)) +} + +setStep(v) { + this.step = 100 - v; } draw() { @@ -101,41 +106,6 @@ drawCurveCoordinates() { this.curve.drawPoints(); } -onKeyDown() { - this.mark = false; - if (this.keyboard[`ArrowDown`]) { - this.stepDown(); - } - if (this.keyboard[`ArrowUp`]) { - this.stepUp(); - } - redraw(); -} - -stepDown(value = 1) { - this.step -= value; - if (this.step < 10) this.step = 10; -} - -stepUp(value = 1) { - this.step += value; - if (this.step > 90) this.step = 90; -} - -onMouseDown() { - this.mark = this.cursor.y; -} - -onMouseUp() { - this.mark = false; -} - onMouseMove() { - if (this.mark && !this.currentPoint) { - let diff = this.mark - this.cursor.y, - mapped = map(diff, -this.height/2, this.height/2, 10, 90, true); - this.step = mapped | 0; - } - redraw(); } diff --git a/docs/images/chapters/control/13bba6ecec2fa000c575813d0cda815c.png b/docs/images/chapters/control/13bba6ecec2fa000c575813d0cda815c.png deleted file mode 100644 index aaad94f5..00000000 Binary files a/docs/images/chapters/control/13bba6ecec2fa000c575813d0cda815c.png and /dev/null differ diff --git a/docs/images/chapters/control/15e8c8492908851ddde1cb35297326bb.png b/docs/images/chapters/control/15e8c8492908851ddde1cb35297326bb.png deleted file mode 100644 index 9f2947b6..00000000 Binary files a/docs/images/chapters/control/15e8c8492908851ddde1cb35297326bb.png and /dev/null differ diff --git a/docs/images/chapters/control/23dae5b8acf92135ea4463d8d0342190.png b/docs/images/chapters/control/23dae5b8acf92135ea4463d8d0342190.png deleted file mode 100644 index 53f8a7f4..00000000 Binary files a/docs/images/chapters/control/23dae5b8acf92135ea4463d8d0342190.png and /dev/null differ diff --git a/docs/images/chapters/control/53d95f337568a2108c525c559aa66e2b.png b/docs/images/chapters/control/53d95f337568a2108c525c559aa66e2b.png new file mode 100644 index 00000000..bb0f9ac1 Binary files /dev/null and b/docs/images/chapters/control/53d95f337568a2108c525c559aa66e2b.png differ diff --git a/docs/images/chapters/control/bf717f39221d5210e79ab8b0bcb38948.png b/docs/images/chapters/control/bf717f39221d5210e79ab8b0bcb38948.png new file mode 100644 index 00000000..ca982e5c Binary files /dev/null and b/docs/images/chapters/control/bf717f39221d5210e79ab8b0bcb38948.png differ diff --git a/docs/images/chapters/control/f319958c931e9f28e41b889c9689c87e.png b/docs/images/chapters/control/f319958c931e9f28e41b889c9689c87e.png new file mode 100644 index 00000000..14b5721d Binary files /dev/null and b/docs/images/chapters/control/f319958c931e9f28e41b889c9689c87e.png differ diff --git a/docs/images/chapters/decasteljau/425ee92efb13c790f63f8b3821327d3b.png b/docs/images/chapters/decasteljau/425ee92efb13c790f63f8b3821327d3b.png deleted file mode 100644 index 19e750b4..00000000 Binary files a/docs/images/chapters/decasteljau/425ee92efb13c790f63f8b3821327d3b.png and /dev/null differ diff --git a/docs/images/chapters/decasteljau/817f7e557caed67e173039d9032b3ab3.png b/docs/images/chapters/decasteljau/817f7e557caed67e173039d9032b3ab3.png new file mode 100644 index 00000000..010218bd Binary files /dev/null and b/docs/images/chapters/decasteljau/817f7e557caed67e173039d9032b3ab3.png differ diff --git a/docs/images/chapters/explanation/1078bb1b1c8b7239aaa555e2d239e44d.png b/docs/images/chapters/explanation/1078bb1b1c8b7239aaa555e2d239e44d.png new file mode 100644 index 00000000..4fa09b53 Binary files /dev/null and b/docs/images/chapters/explanation/1078bb1b1c8b7239aaa555e2d239e44d.png differ diff --git a/docs/images/chapters/explanation/fdea63696e525033c5ea74fa8f90009a.png b/docs/images/chapters/explanation/fdea63696e525033c5ea74fa8f90009a.png deleted file mode 100644 index 7871e858..00000000 Binary files a/docs/images/chapters/explanation/fdea63696e525033c5ea74fa8f90009a.png and /dev/null differ diff --git a/docs/images/chapters/flattening/1a22ba71ef9a5aaf9c55e0b8c2f3f6e5.png b/docs/images/chapters/flattening/1a22ba71ef9a5aaf9c55e0b8c2f3f6e5.png deleted file mode 100644 index 4893b3fc..00000000 Binary files a/docs/images/chapters/flattening/1a22ba71ef9a5aaf9c55e0b8c2f3f6e5.png and /dev/null differ diff --git a/docs/images/chapters/flattening/4d4a648e8cac72a7041555ff885cbc2b.png b/docs/images/chapters/flattening/4d4a648e8cac72a7041555ff885cbc2b.png deleted file mode 100644 index d292ab0d..00000000 Binary files a/docs/images/chapters/flattening/4d4a648e8cac72a7041555ff885cbc2b.png and /dev/null differ diff --git a/docs/images/chapters/flattening/a8f7f97cb3b14c99e3cdf0c5283d7be4.png b/docs/images/chapters/flattening/a8f7f97cb3b14c99e3cdf0c5283d7be4.png new file mode 100644 index 00000000..03bef0ec Binary files /dev/null and b/docs/images/chapters/flattening/a8f7f97cb3b14c99e3cdf0c5283d7be4.png differ diff --git a/docs/images/chapters/flattening/ffe43c40bea1277394df5ff82100a966.png b/docs/images/chapters/flattening/ffe43c40bea1277394df5ff82100a966.png new file mode 100644 index 00000000..b1db83a8 Binary files /dev/null and b/docs/images/chapters/flattening/ffe43c40bea1277394df5ff82100a966.png differ diff --git a/docs/images/chapters/pointvectors3d/769ab953d7f3542ab4c3a383f151b1bc.png b/docs/images/chapters/pointvectors3d/769ab953d7f3542ab4c3a383f151b1bc.png new file mode 100644 index 00000000..81bc9aaf Binary files /dev/null and b/docs/images/chapters/pointvectors3d/769ab953d7f3542ab4c3a383f151b1bc.png differ diff --git a/docs/images/chapters/pointvectors3d/b11dfd6fef9931ac8715209785f63e0c.png b/docs/images/chapters/pointvectors3d/b11dfd6fef9931ac8715209785f63e0c.png deleted file mode 100644 index f0f2c8eb..00000000 Binary files a/docs/images/chapters/pointvectors3d/b11dfd6fef9931ac8715209785f63e0c.png and /dev/null differ diff --git a/docs/images/chapters/pointvectors3d/efd37ce3b7f4d8f084c287ec871c6b69.png b/docs/images/chapters/pointvectors3d/efd37ce3b7f4d8f084c287ec871c6b69.png new file mode 100644 index 00000000..81bc9aaf Binary files /dev/null and b/docs/images/chapters/pointvectors3d/efd37ce3b7f4d8f084c287ec871c6b69.png differ diff --git a/docs/images/chapters/pointvectors3d/f5cf3e34415eccd1b03c4ef478862d44.png b/docs/images/chapters/pointvectors3d/f5cf3e34415eccd1b03c4ef478862d44.png deleted file mode 100644 index f0f2c8eb..00000000 Binary files a/docs/images/chapters/pointvectors3d/f5cf3e34415eccd1b03c4ef478862d44.png and /dev/null differ diff --git a/docs/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png b/docs/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png deleted file mode 100644 index d1ced7c8..00000000 Binary files a/docs/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png and /dev/null differ diff --git a/docs/images/chapters/reordering/5ea06407f13c4b68a507c16d72fcb3e7.png b/docs/images/chapters/reordering/5ea06407f13c4b68a507c16d72fcb3e7.png new file mode 100644 index 00000000..39083929 Binary files /dev/null and b/docs/images/chapters/reordering/5ea06407f13c4b68a507c16d72fcb3e7.png differ diff --git a/docs/images/chapters/splitting/77d77c07832ad3adc3a3dec129a137bb.png b/docs/images/chapters/splitting/77d77c07832ad3adc3a3dec129a137bb.png new file mode 100644 index 00000000..6467bf6a Binary files /dev/null and b/docs/images/chapters/splitting/77d77c07832ad3adc3a3dec129a137bb.png differ diff --git a/docs/images/chapters/splitting/7ad9b19d2a951c5eaf057edba3a37a5b.png b/docs/images/chapters/splitting/7ad9b19d2a951c5eaf057edba3a37a5b.png deleted file mode 100644 index 466e9603..00000000 Binary files a/docs/images/chapters/splitting/7ad9b19d2a951c5eaf057edba3a37a5b.png and /dev/null differ diff --git a/docs/images/chapters/whatis/4d6b98490713508b5c560e29ffa535ed.png b/docs/images/chapters/whatis/4d6b98490713508b5c560e29ffa535ed.png new file mode 100644 index 00000000..6494d1ac Binary files /dev/null and b/docs/images/chapters/whatis/4d6b98490713508b5c560e29ffa535ed.png differ diff --git a/docs/images/chapters/whatis/d39b17854b29fbb3c70bec7a12820aa1.png b/docs/images/chapters/whatis/d39b17854b29fbb3c70bec7a12820aa1.png deleted file mode 100644 index f316f4aa..00000000 Binary files a/docs/images/chapters/whatis/d39b17854b29fbb3c70bec7a12820aa1.png and /dev/null differ diff --git a/docs/index.html b/docs/index.html index eac83e73..1c0b0acf 100644 --- a/docs/index.html +++ b/docs/index.html @@ -599,12 +599,20 @@ Scripts are disabled. Showing fallback image. - + + +

And that brings us to the complicated maths: calculus.

@@ -712,8 +720,7 @@ We can range t from negative to positive infinity, and the resulting (x,y) coordinates will always lie on a circle with radius 1 around the origin (0,0). If we plot it for - t from 0 to 5, we get this (use your up and down arrow keys - to change the plot end value): + t from 0 to 5, we get this:

Scripts are disabled. Showing fallback image. - + + +

Bézier curves are just one out of the many classes of parametric @@ -941,12 +956,21 @@ function Bezier(3,t): Scripts are disabled. Showing fallback image. - + + + + Scripts are disabled. Showing fallback image. - + + + + Scripts are disabled. Showing fallback image. - + + +

@@ -1525,12 +1566,20 @@ function RationalBezier(3,t,w[],r[]): Scripts are disabled. Showing fallback image. - + + +

How to implement de Casteljau's algorithm

@@ -1590,38 +1639,57 @@ function RationalBezier(3,t,w[],r[]): usually can't use the flattened for for doing true intersection detection, or curvature alignment.

- - - + + + + Scripts are disabled. Showing fallback image. + + - Scripts are disabled. Showing fallback image. - - - - + + + + + Scripts are disabled. Showing fallback image. + + - Scripts are disabled. Showing fallback image. - + +

Try clicking on the sketch and using your up and down arrow keys to @@ -1684,12 +1752,20 @@ function RationalBezier(3,t,w[],r[]): Scripts are disabled. Showing fallback image. - + + +

implementing curve splitting

@@ -2266,12 +2342,14 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + + + +

Derivatives

@@ -2758,12 +2836,20 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + + +

However, if you've played with that graphic a bit, you might have @@ -2908,14 +2994,22 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + + + -

That looks much better!

+

That looks so much better!

For those reading along with the code: we don't even strictly speaking need a Frenet frame to start with: we could, for instance, diff --git a/docs/ja-JP/index.html b/docs/ja-JP/index.html index 7a4b23d3..9158710a 100644 --- a/docs/ja-JP/index.html +++ b/docs/ja-JP/index.html @@ -571,12 +571,20 @@ Scripts are disabled. Showing fallback image. - + + +

また、これが複雑な方の数学につながっていきます。微積分です。

@@ -637,7 +645,7 @@

というわけで、普通の関数ではy座標をx座標によって定義しますが、パラメトリック曲線ではそうではなく、座標の値を「制御」変数と結びつけます。tの値を変化させるたびに2つの値が変化するので、これをグラフ上の座標 - (x,y)として使うことができます。例えば、先ほどの関数の組は円周上の点を生成します。負の無限大から正の無限大へとtを動かすと、得られる座標(x,y)は常に中心(0,0)・半径1の円の上に乗ります。tを0から5まで変化させてプロットした場合は、このようになります(上下キーでプロットの上限を変更できます)。 + (x,y)として使うことができます。例えば、先ほどの関数の組は円周上の点を生成します。負の無限大から正の無限大へとtを動かすと、得られる座標(x,y)は常に中心(0,0)・半径1の円の上に乗ります。tを0から5まで変化させてプロットした場合は、このようになります。

Scripts are disabled. Showing fallback image. - + + +

ベジエ曲線はパラメトリック関数の一種であり、どの次元に対しても同じ基底関数を使うという点で特徴づけられます。先ほどの例では、xの値とyの値とで異なる関数(正弦関数と余弦関数)を使っていましたが、ベジエ曲線ではxyの両方で「二項係数多項式」を使います。では、二項係数多項式とは何でしょう? @@ -792,21 +808,80 @@ function Bezier(3,t): 下のグラフは、2次ベジエ曲線や3次ベジエ曲線の補間関数を表しています。ここでSは、ベジエ関数全体に対しての、その点の寄与の大きさを示します。あるtにおいて、ベジエ曲線を定義する各点の補間率がどのようになっているのか、クリックドラッグをして確かめてみてください。

- - + + + Scripts are disabled. Showing fallback image. + + + + + - + + + Scripts are disabled. Showing fallback image. + + + + + + width="275" + height="275" + src="./chapters/control/lerp-fifteenth.js" + > + + + Scripts are disabled. Showing fallback image. + + +

@@ -1269,12 +1344,20 @@ function RationalBezier(3,t,w[],r[]): Scripts are disabled. Showing fallback image. - + + +

ド・カステリョのアルゴリズムの実装方法

@@ -1313,38 +1396,57 @@ function RationalBezier(3,t,w[],r[]):

例えば「X個の線分がほしい」場合には、分割数がそうなるようにサンプリング間隔を選び、曲線をサンプリングします。この方法の利点は速さです。曲線の座標を100個だの1000個だの計算するのではなく、ずっと少ない回数のサンプリングでも、十分きれいに見えるような曲線を作ることができるのです。欠点はもちろん、「本物の曲線」に比べて精度が損なわれてしまうことです。したがって、交点の検出や曲線の位置揃えを正しく行いたい場合には、平坦化した曲線は普通利用できません。

- - - + + + + Scripts are disabled. Showing fallback image. + + - Scripts are disabled. Showing fallback image. - - - - + + + + + Scripts are disabled. Showing fallback image. + + - Scripts are disabled. Showing fallback image. - + +

2次ベジエ曲線も3次ベジエ曲線も、図をクリックして上下キーを押すと曲線の分割数が増減しますので、試してみてください。ある曲線では分割数が少なくてもうまくいきますが、曲線が複雑になればなるほど、曲率の変化を正確に捉えるためにはより多くの分割数が必要になることがわかります(3次ベジエ曲線で試してみてください)。 @@ -1389,12 +1491,20 @@ function RationalBezier(3,t,w[],r[]): Scripts are disabled. Showing fallback image. - + + +

曲線分割の実装方法

@@ -1926,12 +2036,14 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + + + +

Derivatives

@@ -2424,12 +2536,20 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + + +

However, if you've played with that graphic a bit, you might have @@ -2574,14 +2694,22 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + + + -

That looks much better!

+

That looks so much better!

For those reading along with the code: we don't even strictly speaking need a Frenet frame to start with: we could, for instance, diff --git a/docs/js/custom-element/api/graphics-api.js b/docs/js/custom-element/api/graphics-api.js index 2bc5b6fd..7c3e41e9 100644 --- a/docs/js/custom-element/api/graphics-api.js +++ b/docs/js/custom-element/api/graphics-api.js @@ -138,6 +138,17 @@ class GraphicsAPI extends BaseAPI { points.forEach((p) => this.movable.push(p)); } + setSlider(qs, handler, redraw = true) { + let slider = this.find(qs); + if (slider) { + slider.listen(`input`, (evt) => { + handler(parseFloat(evt.target.value)); + if (redraw) this.redraw(); + }); + } + return slider; + } + /** * Convert the canvas to an image */ diff --git a/docs/js/site/faster-lazy-loading.js b/docs/js/site/faster-lazy-loading.js index e7223f7b..476bda0e 100644 --- a/docs/js/site/faster-lazy-loading.js +++ b/docs/js/site/faster-lazy-loading.js @@ -7,8 +7,8 @@ */ const images = Array.from( - document.querySelectorAll(`img[loading=lazy]`) -).filter((img) => img.parentNode.nodeName !== `FALLBACK-IMAGE`); + document.querySelectorAll(`img.LaTeX.SVG[loading=lazy]`) +); // First, make images inert. As this happens before the document // becomes active, this prevents images from loading anything. @@ -41,12 +41,10 @@ function testImages() { lock = true; - let top = window.scrollY; let height = document.documentElement.clientHeight; - let bottom = top + height; for (let pos = images.length - 1; pos >= 0; pos--) { - test(images[pos], pos, top, bottom, height); + test(images[pos], pos, height); } if (images.length === 0) { @@ -60,9 +58,12 @@ function testImages() { /** * Test individual images for whether or not they should load. */ -function test(img, pos, top, bottom, threshold) { - top = Math.abs(img.offsetTop - top) < threshold; - bottom = Math.abs(img.offsetTop + img.offsetHeight - bottom) < threshold; +function test(img, pos, height) { + let top = img.getBoundingClientRect().top; + top = top < height; + + let bottom = img.getBoundingClientRect().bottom; + bottom = bottom > -height; if (top || bottom) { img.src = img.dataset.src; diff --git a/docs/placeholder-style.css b/docs/placeholder-style.css index a866b96f..6a457e42 100644 --- a/docs/placeholder-style.css +++ b/docs/placeholder-style.css @@ -140,4 +140,8 @@ p code { font-family: monospace; font-size: 1.2em; letter-spacing: -2px; +} + +.slide-control { + width: 100%; } \ No newline at end of file diff --git a/docs/zh-CN/index.html b/docs/zh-CN/index.html index 11828102..577eb338 100644 --- a/docs/zh-CN/index.html +++ b/docs/zh-CN/index.html @@ -550,12 +550,20 @@ Scripts are disabled. Showing fallback image. - + + +

这为我们引出了复杂的数学:微积分。

@@ -616,7 +624,7 @@ 好了,通过一些神秘的t值将x/y坐标系联系起来。

- 所以,参数曲线不像一般函数那样,通过x坐标来定义y坐标,而是用一个“控制”变量将它们连接起来。如果改变t的值,每次变化时我们都能得到两个值,这可以作为图形中的(x,y)坐标。比如上面的方程组,生成位于一个圆上的点:我们可以使t在正负极值间变化,得到的输出(x,y)都会位于一个以原点(0,0)为中心且半径为1的圆上。如果我们画出t从0到5时的值,将得到如下图像(你可以用上下键来改变画的点和值): + 所以,参数曲线不像一般函数那样,通过x坐标来定义y坐标,而是用一个“控制”变量将它们连接起来。如果改变t的值,每次变化时我们都能得到两个值,这可以作为图形中的(x,y)坐标。比如上面的方程组,生成位于一个圆上的点:我们可以使t在正负极值间变化,得到的输出(x,y)都会位于一个以原点(0,0)为中心且半径为1的圆上。如果我们画出t从0到5时的值,将得到如下图像:

Scripts are disabled. Showing fallback image. - + + +

贝塞尔曲线是(一种)参数方程,并在它的多个维度上使用相同的基本方程。在上述的例子中x值和y值使用了不同的方程,与此不同的是,贝塞尔曲线的xy都用了“二项多项式”。那什么是二项多项式呢? @@ -769,21 +785,80 @@ function Bezier(3,t): 下面的图形显示了二次曲线和三次曲线的差值方程,“S”代表了点对贝塞尔方程总和的贡献。点击拖动点来看看在特定的t值时,每个曲线定义的点的插值百分比。

- - + + + Scripts are disabled. Showing fallback image. + + + + + - + + + Scripts are disabled. Showing fallback image. + + + + + + width="275" + height="275" + src="./chapters/control/lerp-fifteenth.js" + > + + + Scripts are disabled. Showing fallback image. + + +

@@ -1224,12 +1299,20 @@ function RationalBezier(3,t,w[],r[]): Scripts are disabled. Showing fallback image. - + + +

如何实现de Casteljau算法

@@ -1269,38 +1352,57 @@ function RationalBezier(3,t,w[],r[]):

我们可以先确定“想要X个分段”,然后在间隔的地方采样曲线,得到一定数量的分段。这种方法的优点是速度很快:比起遍历100甚至1000个曲线坐标,我们可以采样比较少的点,仍然得到看起来足够好的曲线。这么做的缺点是,我们失去了“真正的曲线”的精度,因此不能用此方法来做真实的相交检测或曲率对齐。

- - - + + + + Scripts are disabled. Showing fallback image. + + - Scripts are disabled. Showing fallback image. - - - - + + + + + Scripts are disabled. Showing fallback image. + + - Scripts are disabled. Showing fallback image. - + +

试着点击图形,并用上下键来降低二次曲线和三次曲线的分段数量。你会发现对某些曲率来说,数量少的分段也能做的很好,但对于复杂的曲率(在三次曲线上试试),足够多的分段才能很好地满足曲率的变化。 @@ -1347,12 +1449,20 @@ function RationalBezier(3,t,w[],r[]): Scripts are disabled. Showing fallback image. - + + +

分割曲线的代码实习

@@ -1936,12 +2046,14 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + + + +

Derivatives

@@ -2434,12 +2546,20 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + + +

However, if you've played with that graphic a bit, you might have @@ -2584,14 +2704,22 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + + + -

That looks much better!

+

That looks so much better!

For those reading along with the code: we don't even strictly speaking need a Frenet frame to start with: we could, for instance, diff --git a/package-lock.json b/package-lock.json index 7b108bc3..5a7a5b4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -210,13 +210,12 @@ } }, "canvas": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.1.tgz", - "integrity": "sha512-S98rKsPcuhfTcYbtF53UIJhcbgIAK533d1kJKMwsMwAIFgfd58MOyxRud3kktlzWiEkFliaJtvyZCBtud/XVEA==", + "version": "git://github.com/Automattic/node-canvas.git#595d5590a0c4199c483d4244b59d28341086edc9", + "from": "git://github.com/Automattic/node-canvas.git#master", "dev": true, "requires": { "nan": "^2.14.0", - "node-pre-gyp": "^0.11.0", + "node-pre-gyp": "^0.15.0", "simple-get": "^3.0.3" } }, @@ -1261,21 +1260,21 @@ "dev": true }, "node-pre-gyp": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", - "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz", + "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==", "dev": true, "requires": { "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", + "mkdirp": "^0.5.3", + "needle": "^2.5.0", "nopt": "^4.0.1", "npm-packlist": "^1.1.6", "npmlog": "^4.0.2", "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", - "tar": "^4" + "tar": "^4.4.2" } }, "nopt": { diff --git a/package.json b/package.json index e8fdc0ce..1790a730 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "ebook" ], "devDependencies": { - "canvas": "^2.6.1", + "canvas": "git://github.com/Automattic/node-canvas#master", "chokidar-cli": "^2.1.0", "fs-extra": "^9.0.1", "glob": "^7.1.6", diff --git a/src/build.js b/src/build.js index fe6cf128..b732bb31 100644 --- a/src/build.js +++ b/src/build.js @@ -8,12 +8,18 @@ import { createIndexPages } from "./build/create-index-page.js"; * - aggregate all content files organized by locale * - */ -getAllChapterFiles().then((chapterFiles) => { +getAllChapterFiles().then(async (chapterFiles) => { + const start = Date.now(); const languageCodes = Object.keys(chapterFiles); - languageCodes.forEach(async (locale) => { - const localeStrings = new LocaleStrings(locale); - const chapters = await processLocale(locale, localeStrings, chapterFiles); - createIndexPages(locale, localeStrings, chapters); - }); + await Promise.all( + languageCodes.map(async (locale) => { + const localeStrings = new LocaleStrings(locale); + const chapters = await processLocale(locale, localeStrings, chapterFiles); + return createIndexPages(locale, localeStrings, chapters); + }) + ); + + const runtime = Date.now() - start; + console.log(`Finished build (${(runtime / 1000).toFixed(2)}s)`); });