1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-09-01 20:33:34 +02:00

splitting + image regen

This commit is contained in:
Pomax
2020-08-11 22:43:14 -07:00
parent e7506dba6e
commit 5b9693db11
30 changed files with 206 additions and 96 deletions

View File

@@ -2,7 +2,7 @@
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.
<Graphic title="Splitting a curve" setup={this.setupCubic} draw={this.drawSplit} />
<graphics-element title="Splitting a curve" width="825" src="./splitting.js"></graphics-element>
<div class="howtocode">
@@ -32,7 +32,3 @@ function drawCurve(points[], t):
After running this function for some value `t`, the `left` and `right` arrays will contain all the coordinates for two new curves - one to the "left" of our `t` value, the other on the "right". These new curves will have the same order as the original curve, and can be overlaid exactly on the original curve.
</div>
This is best illustrated with an animated graphic (click to play/pause):
<Graphic title="Bézier curve splitting" setup={this.setupCubic} draw={this.drawAnimated} onClick={this.togglePlay} />

View File

@@ -2,7 +2,7 @@
ベジエ曲線を分割して、繫ぎ合わせたときに元に戻るような小さい2曲線にしたい場合にも、ド・カステリョのアルゴリズムを使えば、これに必要な点をすべて求めることができます。ある値`t`に対してド・カステリョの骨格を組み立てると、その`t`で曲線を分割する際に必要になる点がすべて得られます。骨格内部の点のうち、曲線上の点から見て手前側にある点によって一方の曲線が定義され、向こう側にある点によってもう一方の曲線が定義されます。
<Graphic title="曲線の分割" setup={this.setupCubic} draw={this.drawSplit} />
<graphics-element title="曲線の分割" width="825" src="./splitting.js"></graphics-element>
<div class="howtocode">
@@ -32,7 +32,3 @@ function drawCurve(points[], t):
ある値`t`に対してこの関数を実行すると、`left``right`に新しい2曲線の座標が入ります。一方は`t`の「左」側、もう一方は「右」側の曲線です。この2曲線は元の曲線と同じ次数になり、また元の曲線とぴったり重なります。
</div>
これはアニメーションで見るのがわかりやすいでしょう(クリックで再生・停止します)。
<Graphic title="ベジエ曲線の分割" setup={this.setupCubic} draw={this.drawAnimated} onClick={this.togglePlay} />

View File

@@ -2,7 +2,7 @@
使用 de Casteljau 算法我们也可以将一条贝塞尔曲线分割成两条更小的曲线,二者拼接起来即可形成原来的曲线。当采用某个 `t` 值构造 de Casteljau 算法时,该过程会给到我们在 `t` 点分割曲线的所有点: 一条曲线包含该曲线上点之前的所有点,另一条曲线包含该曲线上点之后的所有点。
<Graphic title="分割一条曲线" setup={this.setupCubic} draw={this.drawSplit} />
<graphics-element title="分割一条曲线" width="825" src="./splitting.js"></graphics-element>
<div class="howtocode">
@@ -32,7 +32,3 @@ function drawCurve(points[], t):
对某个给定 `t` 值,该函数执行后,数组 `left``right` 将包含两条曲线的所有点的坐标 -- 一条是`t`值左侧的曲线,一条是`t`值右侧的曲线, 与原始曲线同序且完全重合。
</div>
以下是带动画效果的最好的演示(点击以播放/暂停):
<Graphic title="贝塞尔曲线的分割" setup={this.setupCubic} draw={this.drawAnimated} onClick={this.togglePlay} />

View File

@@ -0,0 +1,28 @@
/*
// 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<newpoints.length; i++) {
if(i === 0) left.push(points[i]);
if(i === newpoints.length-1) right.push(points[i+1]);
newpoints[i] = {
x: (1-t) * points[i].x + t * points[i+1].x,
y: (1-t) * points[i].y + t * points[i+1].y,
}
}
return this.getLeftAndRight(t, newpoints, left, right);
}
*/

View File

@@ -0,0 +1,82 @@
setup() {
this.t = 0.5;
this.curve = Bezier.defaultCubic(this);
setMovable(this.curve.points);
}
draw() {
resetTransform();
clear();
let p = this.curve.get(this.t);
const struts = this.struts = this.curve.getStrutPoints(this.t);
const c1 = new Bezier(this, [struts[0], struts[4], struts[7], struts[9]]);
const c2 = new Bezier(this, [struts[9], struts[8], struts[6], struts[3]]);
this.drawBasics(p);
translate(this.width/3, 0);
setStroke(`black`);
line(0, 0, 0, this.height);
this.drawSegment(c1, p);
translate(this.width/3, 0);
setStroke(`black`);
line(0, 0, 0, this.height);
this.drawSegment(c2, p);
}
drawBasics(p) {
this.curve.drawCurve(`lightgrey`);
this.curve.drawSkeleton(`lightgrey`);
this.curve.drawPoints(false);
noFill();
setStroke(`red`);
circle(p.x, p.y, 3);
setStroke(`lightblue`);
this.curve.drawStruts(this.struts);
setFill(`black`)
text(`The full curve, with struts`, 10, 15);
}
drawSegment(c, p) {
setStroke(`lightblue`);
this.curve.drawCurve(`lightblue`);
this.curve.drawSkeleton(`lightblue`);
this.curve.drawStruts(this.struts);
c.drawCurve();
c.drawSkeleton(`black`);
noFill();
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();
}
onMouseMove() {
if (this.cursor.down && !this.currentPoint) {
this.t = map(this.cursor.y, 0,this.height, 0, 1);
} else {
this.curve.update();
}
redraw();
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 967 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -585,7 +585,7 @@
<img
width="825px"
height="275px"
src="images\chapters\whatis\b2e7a2bc650cbed9d750e8afc40a1aa3.png"
src="images\chapters\whatis\f91cf03cba43b012c66a8f310097ced6.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -1586,7 +1586,7 @@ function RationalBezier(3,t,w[],r[]):
<img
width="275px"
height="275px"
src="images\chapters\flattening\3eb70cd7ada74dfe1417ddb572730597.png"
src="images\chapters\flattening\6fd4fa0aca97b89939624de9339acf11.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -1602,7 +1602,7 @@ function RationalBezier(3,t,w[],r[]):
<img
width="275px"
height="275px"
src="images\chapters\flattening\04573510619447c4361c7768ac0175cf.png"
src="images\chapters\flattening\1a22ba71ef9a5aaf9c55e0b8c2f3f6e5.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -1660,11 +1660,22 @@ function RationalBezier(3,t,w[],r[]):
curve being defined by all the inside skeleton points after our
on-curve point.
</p>
<Graphic
<graphics-element
title="Splitting a curve"
setup="{this.setupCubic}"
draw="{this.drawSplit}"
/>
width="825"
height="275"
src="./chapters/splitting/splitting.js"
>
<fallback-image>
<img
width="825px"
height="275px"
src="images\chapters\splitting\9482f3b5bc36c11395525fefb67dd18b.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element
>
<div class="howtocode">
<h3>implementing curve splitting</h3>
@@ -1697,17 +1708,6 @@ function drawCurve(points[], t):
overlaid exactly on the original curve.
</p>
</div>
<p>
This is best illustrated with an animated graphic (click to
play/pause):
</p>
<Graphic
title="Bézier curve splitting"
setup="{this.setupCubic}"
draw="{this.drawAnimated}"
onClick="{this.togglePlay}"
/>
</section>
<section id="matrixsplit">
<h1><a href="#matrixsplit">Splitting curves using matrices</a></h1>

View File

@@ -487,7 +487,7 @@
<img
width="825px"
height="275px"
src="images\chapters\whatis\b2e7a2bc650cbed9d750e8afc40a1aa3.png"
src="images\chapters\whatis\f91cf03cba43b012c66a8f310097ced6.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -1229,7 +1229,7 @@ function RationalBezier(3,t,w[],r[]):
<img
width="275px"
height="275px"
src="images\chapters\flattening\3eb70cd7ada74dfe1417ddb572730597.png"
src="images\chapters\flattening\6fd4fa0aca97b89939624de9339acf11.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -1245,7 +1245,7 @@ function RationalBezier(3,t,w[],r[]):
<img
width="275px"
height="275px"
src="images\chapters\flattening\04573510619447c4361c7768ac0175cf.png"
src="images\chapters\flattening\1a22ba71ef9a5aaf9c55e0b8c2f3f6e5.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -1285,11 +1285,22 @@ function RationalBezier(3,t,w[],r[]):
<p>
ベジエ曲線を分割して、繫ぎ合わせたときに元に戻るような小さい2曲線にしたい場合にも、ド・カステリョのアルゴリズムを使えば、これに必要な点をすべて求めることができます。ある値<code>t</code>に対してド・カステリョの骨格を組み立てると、その<code>t</code>で曲線を分割する際に必要になる点がすべて得られます。骨格内部の点のうち、曲線上の点から見て手前側にある点によって一方の曲線が定義され、向こう側にある点によってもう一方の曲線が定義されます。
</p>
<Graphic
<graphics-element
title="曲線の分割"
setup="{this.setupCubic}"
draw="{this.drawSplit}"
/>
width="825"
height="275"
src="./chapters/splitting/splitting.js"
>
<fallback-image>
<img
width="825px"
height="275px"
src="images\chapters\splitting\9482f3b5bc36c11395525fefb67dd18b.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element
>
<div class="howtocode">
<h3>曲線分割の実装方法</h3>
@@ -1316,16 +1327,6 @@ function drawCurve(points[], t):
ある値<code>t</code>に対してこの関数を実行すると、<code>left</code><code>right</code>に新しい2曲線の座標が入ります。一方は<code>t</code>の「左」側、もう一方は「右」側の曲線です。この2曲線は元の曲線と同じ次数になり、また元の曲線とぴったり重なります。
</p>
</div>
<p>
これはアニメーションで見るのがわかりやすいでしょう(クリックで再生・停止します)。
</p>
<Graphic
title="ベジエ曲線の分割"
setup="{this.setupCubic}"
draw="{this.drawAnimated}"
onClick="{this.togglePlay}"
/>
</section>
<section id="matrixsplit">
<h1><a href="#matrixsplit">行列による曲線の分割</a></h1>

View File

@@ -71,11 +71,11 @@ class Bezier extends Original {
return closest;
}
drawCurve() {
drawCurve(color = `#333`) {
const ctx = this.ctx;
ctx.cacheStyle();
ctx.lineWidth = 2;
ctx.strokeStyle = `#333`;
ctx.strokeStyle = color;
ctx.beginPath();
const lut = this.getLUT().slice();
let p = lut.shift();
@@ -88,7 +88,7 @@ class Bezier extends Original {
ctx.restoreStyle();
}
drawPoints() {
drawPoints(labels = true) {
const colors = [`red`, `green`, `blue`, `yellow`];
const api = this.api;
const ctx = this.ctx;
@@ -99,50 +99,59 @@ class Bezier extends Original {
this.points.forEach((p, i) => {
api.setFill(colors[i % colors.length]);
api.circle(p.x, p.y, 5);
api.setFill(`black`);
api.text(`(${p.x},${p.y})`, p.x + 10, p.y + 10);
if (labels) {
api.setFill(`black`);
api.text(`(${p.x},${p.y})`, p.x + 10, p.y + 10);
}
});
ctx.restoreStyle();
}
drawSkeleton(t = false) {
drawSkeleton(color = `#555`) {
const api = this.api;
const ctx = this.ctx;
ctx.cacheStyle();
const p = this.points;
ctx.strokeStyle = `#555`;
ctx.beginPath();
ctx.moveTo(p[0].x, p[0].y);
ctx.lineTo(p[1].x, p[1].y);
ctx.lineTo(p[2].x, p[2].y);
if (p[3]) {
ctx.lineTo(p[3].x, p[3].y);
if (t !== false) {
// TODO: additional cubic struts
// ... code goes here ...
}
}
ctx.stroke();
api.noFill();
api.setStroke(color);
api.start();
p.forEach((v) => api.vertex(v.x, v.y));
api.end();
ctx.restoreStyle();
}
drawStruts(t) {
getStrutPoints(t) {
const p = this.points.map((p) => new Vector(p));
const mt = 1 - t;
let s = 0;
let n = p.length + 1;
while (--n > 1) {
let list = p.slice(s, s + n);
for (let i = 0, e = list.length - 1; i < e; i++) {
let pt = list[i + 1].subtract(list[i + 1].subtract(list[i]).scale(mt));
p.push(pt);
}
s += n;
}
return p;
}
drawStruts(t) {
const p = t.forEach ? t : this.getStrutPoints(t);
const api = this.api;
const ctx = api.ctx;
ctx.cacheStyle();
api.noFill();
let s = 0;
let n = p.length + 1;
let s = this.points.length;
let n = this.points.length;
while (--n > 1) {
let list = p.slice(s, s + n);
api.start();
for (let i = 0, e = list.length - 1; i < e; i++) {
let pt = list[i + 1].subtract(list[i + 1].subtract(list[i]).scale(mt));
p.push(pt);
for (let i = 0; i < n; i++) {
let pt = p[s + i];
api.vertex(pt.x, pt.y);
api.circle(pt.x, pt.y, 5);
}
@@ -150,8 +159,6 @@ class Bezier extends Original {
s += n;
}
ctx.restoreStyle();
return p;
}
}

View File

@@ -62,7 +62,8 @@ async function createIndexPages(locale, localeStrings, chapters) {
const start = Date.now();
const data = prettier.format(index, { parser: `html` });
const end = Date.now();
console.log(`beautification for ${locale} took ${(end - start) / 1000}s`);
//console.log(`beautification for ${locale} took ${(end - start) / 1000}s`);
if (locale === defaultLocale) {
fs.writeFileSync(`index.html`, data, `utf8`);

View File

@@ -77,7 +77,7 @@ async function processLocale(locale, localeStrings, chapterFiles) {
}
const end = Date.now();
console.log(`Processing ${locale} took ${(end - start) / 1000}s`);
// console.log(`Processing ${locale} took ${(end - start) / 1000}s`);
return chapters;
}

View File

@@ -472,7 +472,7 @@
<img
width="825px"
height="275px"
src="images\chapters\whatis\b2e7a2bc650cbed9d750e8afc40a1aa3.png"
src="images\chapters\whatis\f91cf03cba43b012c66a8f310097ced6.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -1195,7 +1195,7 @@ function RationalBezier(3,t,w[],r[]):
<img
width="275px"
height="275px"
src="images\chapters\flattening\3eb70cd7ada74dfe1417ddb572730597.png"
src="images\chapters\flattening\6fd4fa0aca97b89939624de9339acf11.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -1211,7 +1211,7 @@ function RationalBezier(3,t,w[],r[]):
<img
width="275px"
height="275px"
src="images\chapters\flattening\04573510619447c4361c7768ac0175cf.png"
src="images\chapters\flattening\1a22ba71ef9a5aaf9c55e0b8c2f3f6e5.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -1253,11 +1253,22 @@ function RationalBezier(3,t,w[],r[]):
<code>t</code> 点分割曲线的所有点:
一条曲线包含该曲线上点之前的所有点,另一条曲线包含该曲线上点之后的所有点。
</p>
<Graphic
<graphics-element
title="分割一条曲线"
setup="{this.setupCubic}"
draw="{this.drawSplit}"
/>
width="825"
height="275"
src="./chapters/splitting/splitting.js"
>
<fallback-image>
<img
width="825px"
height="275px"
src="images\chapters\splitting\9482f3b5bc36c11395525fefb67dd18b.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element
>
<div class="howtocode">
<h3>分割曲线的代码实习</h3>
@@ -1289,14 +1300,6 @@ function drawCurve(points[], t):
与原始曲线同序且完全重合。
</p>
</div>
<p>以下是带动画效果的最好的演示(点击以播放/暂停):</p>
<Graphic
title="贝塞尔曲线的分割"
setup="{this.setupCubic}"
draw="{this.drawAnimated}"
onClick="{this.togglePlay}"
/>
</section>
<section id="matrixsplit">
<h1><a href="#matrixsplit">Splitting curves using matrices</a></h1>