1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-31 20:11:59 +02:00

image regeneration + circles

This commit is contained in:
Pomax
2020-09-05 14:01:36 -07:00
parent 7e5c6e2eba
commit bec07e3297
268 changed files with 51976 additions and 620 deletions

View File

@@ -0,0 +1,85 @@
let guess, w, h, pad = 75, r;
setup() {
w = this.width;
h = this.height;
r = w/2 - pad;
guess = new Bezier(this,
{ x: w - pad, y: h/2},
{ x: w - pad, y: h/2},
{ x: w - pad, y: h/2},
{ x: w - pad, y: h/2}
);
setSlider(`.slide-control`, `angle`, -PI/4, v => this.updateCurve(v));
}
draw() {
clear();
setColor(`lightgrey`);
line(0, h/2, w, h/2);
line(w/2, 0, w/2, h);
noFill();
setStroke(`red`);
circle(w/2, h/2, r);
noStroke();
setFill(`rgba(100,255,100,0.4)`);
let a = this.angle;
arc(w/2, h/2, r, a < 0 ? a : 0, a < 0 ? 0 : a, w/2, h/2);
guess.drawSkeleton(`lightblue`);
guess.drawCurve(`lightblue`);
let real = this.getRealCurve(guess.points[0], guess.points[3], this.angle);
real.drawSkeleton();
real.drawCurve();
setColor(`black`);
real.points.forEach(p => {
circle(p.x, p.y, 2);
text(`(${p.x|0},${p.y|0})`, p.x+5, p.y);
});
}
updateCurve(a) {
let angle = -a;
const S = guess.points[0],
B = {
x: w/2 + r * cos(angle/2),
y: h/2 + r * sin(angle/2)
},
E = guess.points[3] = {
x: w/2 + r * cos(angle),
y: h/2 + r * sin(angle)
};
guess = this.guessCurve(S,B,E);
return angle;
}
guessCurve(S, B, E) {
const C = { x: (S.x + E.x)/2, y: (S.y + E.y)/2 }, // we know we're working with t=0.5
A = { x: B.x + (B.x-C.x)/3, y: B.y + (B.y-C.y)/3 }, // cubic ratio at t=0.5 is 1/3
bx = (E.x-S.x)/4,
by = (E.y-S.y)/4,
e1 = { x: B.x - bx, y: B.y - by },
e2 = { x: B.x + bx, y: B.y + by },
v1 = { x: A.x + (e1.x-A.x)*2, y: A.y + (e1.y-A.y)*2 },
v2 = { x: A.x + (e2.x-A.x)*2, y: A.y + (e2.y-A.y)*2 },
C1 = { x: S.x + (v1.x-S.x)*2, y: S.y + (v1.y-S.y)*2 },
C2 = { x: E.x + (v2.x-E.x)*2, y: E.y + (v2.y-E.y)*2 };
return new Bezier(this, [S, C1, C2, E]);
}
getRealCurve(S, E, angle) {
const f = 4/3 * tan(angle/4);
const C1 = { x: w/2 + r, y: h/2 + r * f };
const C2 = {
x: w/2 + r * ( cos(angle) + f * sin(angle) ),
y: h/2 + r * ( sin(angle) - f * cos(angle) )
};
return new Bezier(this, [S, C1, C2, E]);
}

View File

@@ -0,0 +1,64 @@
let curve, r;
setup() {
r = (this.width/4) | 0;
curve = new Bezier(this, [
{ x: r, y: 0 },
{ x: r, y: 0.55228 * r },
{ x: 0.55228 * r, y: r},
{ x: 0, y: r }
]);
}
draw() {
clear();
translate(this.width/2, this.height/2);
const w = this.width, h = this.height;
setStroke(`lightgrey`);
line(0,-h,0,h);
line(-w,0,w,0);
setStroke(`black`);
line(-r,0,r,0);
line(0,-r,0,r);
setColor(`black`);
text(`r = ${r}`, r/2, 15, CENTER);
setColor(`red`);
curve.drawSkeleton(`red`);
curve.points.forEach(p => {
circle(p.x, p.y, 2);
text(`(${p.x},${p.y})`, p.x+5, p.y+15);
});
curve.drawCurve();
curve.points.forEach(p => p.y = -p.y);
curve.drawCurve(`#CC00CC40`);
setColor(`#CC00CC`);
line(r, 0, r, -0.55228 * r);
circle(r, -0.55228 * r, 2);
text(`reflected`, r + 7, -0.55228 * r + 3, LEFT);
setColor(`#CC00CC40`);
line(0, -r, 0.55228 * r, -r);
curve.points.forEach(p => {
p.x = -p.x;
p.y = -p.y;
});
curve.drawCurve(`#0000CC40`);
setColor(`#0000CC`);
line(0, r, -0.55228 * r, r);
circle(-0.55228 * r, r, 2);
text(`reflected`, -0.55228 * r - 5, r + 3, RIGHT);
setColor(`#0000CC40`);
line(-r, 0, -r, 0.55228 * r);
curve.points.forEach(p => p.y = -p.y);
curve.drawCurve(`#00000040`);
}

View File

@@ -8,7 +8,9 @@ The first thing we can do is "guess" what the curve should look like, based on t
So have a graphical look at a "bad" guess versus the true fit, where we'll be using the bad guess and the description in the second paragraph to derive the maths for the true fit:
<Graphic title="Cubic Bézier arc approximation" setup={this.setup} draw={this.draw} onMouseMove={this.onMouseMove}/>
<graphics-element title="Cubic Bézier arc approximation" width="400" height="400" src="./arc-approximation.js">
<input type="range" min="-3.1415" max="3.1415" step="0.01" value="-0.7854" class="slide-control">
</graphics-element>
We see two curves here; in blue, our "guessed" curve and its control points, and in grey/black, the true curve fit, with proper control points that were shifted in, along line between our guessed control points, such that the derivatives at the start and end points are correct.
@@ -177,4 +179,4 @@ Which, in decimal values, rounded to six significant digits, is:
Of course, this is for a circle with radius 1, so if you have a different radius circle, simply multiply the coordinate by the radius you need. And then finally, forming a full curve is now a simple a matter of mirroring these coordinates about the origin:
<Graphic title="Cubic Bézier circle approximation" draw={this.drawCircle} static={true}/>
<graphics-element title="Cubic Bézier circle approximation" width="400" height="400" src="./circle.js"></graphics-element>

View File

@@ -1,205 +0,0 @@
var sin = Math.sin, cos = Math.cos, tan = Math.tan;
module.exports = {
setup: function(api) {
api.setSize(400,400);
api.w = api.getPanelWidth();
api.h = api.getPanelHeight();
api.pad = 80;
api.r = api.w/2 - api.pad;
api.mousePt = false;
api.angle = 0;
var spt = { x: api.w-api.pad, y: api.h/2 };
api.setCurve(new api.Bezier(spt, spt, spt, spt));
},
guessCurve: function(S, B, E) {
var C = {
x: (S.x + E.x)/2,
y: (S.y + E.y)/2
},
A = {
x: B.x + (B.x-C.x)/3, // cubic ratio at t=0.5 is 1/3
y: B.y + (B.y-C.y)/3
},
bx = (E.x-S.x)/4,
by = (E.y-S.y)/4,
e1 = {
x: B.x - bx,
y: B.y - by
},
e2 = {
x: B.x + bx,
y: B.y + by
},
v1 = {
x: A.x + (e1.x-A.x)*2,
y: A.y + (e1.y-A.y)*2
},
v2 = {
x: A.x + (e2.x-A.x)*2,
y: A.y + (e2.y-A.y)*2
},
nc1 = {
x: S.x + (v1.x-S.x)*2,
y: S.y + (v1.y-S.y)*2
},
nc2 = {
x: E.x + (v2.x-E.x)*2,
y: E.y + (v2.y-E.y)*2
};
return [nc1, nc2];
},
draw: function(api, curve) {
api.reset();
api.setColor("lightgrey");
api.drawGrid(1,1);
api.setColor("rgba(255,0,0,0.4)");
api.drawCircle({x:api.w/2,y:api.h/2},api.r);
api.setColor("transparent");
api.setFill("rgba(100,255,100,0.4)");
var p = {
x: api.w/2,
y: api.h/2,
r: api.r,
s: api.angle < 0 ? api.angle : 0,
e: api.angle < 0 ? 0 : api.angle
};
api.drawArc(p);
// guessed curve
var B = {
x: api.w/2 + api.r * cos(api.angle/2),
y: api.w/2 + api.r * sin(api.angle/2)
};
var S = curve.points[0],
E = curve.points[3],
nc = this.guessCurve(S,B,E);
var guess = new api.Bezier([S, nc[0], nc[1], E]);
api.setColor("rgb(140,140,255)");
api.drawLine(guess.points[0], guess.points[1]);
api.drawLine(guess.points[1], guess.points[2]);
api.drawLine(guess.points[2], guess.points[3]);
api.setColor("blue");
api.drawCurve(guess);
api.drawCircle(guess.points[1], 3);
api.drawCircle(guess.points[2], 3);
// real curve
api.drawSkeleton(curve);
api.setColor("black");
api.drawLine(curve.points[1], curve.points[2]);
api.drawCurve(curve);
},
onMouseMove: function(evt, api) {
var x = evt.offsetX - api.w/2,
y = evt.offsetY - api.h/2;
if (x>api.w/2) return;
var angle = Math.atan2(y,x);
if (angle < 0) {
angle = 2*Math.PI + angle;
}
var pts = api.curve.points;
// new control 1
var r = api.r,
f = (4 * tan(angle/4)) /3;
pts[1] = {
x: api.w/2 + r,
y: api.w/2 + r * f
};
// new control 2
pts[2] = {
x: api.w/2 + api.r * (cos(angle) + f*sin(angle)),
y: api.w/2 + api.r * (sin(angle) - f*cos(angle))
};
// new endpoint
pts[3] = {
x: api.w/2 + api.r * cos(angle),
y: api.w/2 + api.r * sin(angle)
};
api.setCurve(new api.Bezier(pts));
api.angle = angle;
},
drawCircle: function(api) {
api.setSize(325,325);
api.reset();
var w = api.getPanelWidth(),
h = api.getPanelHeight(),
pad = 60,
r = w/2 - pad,
k = 0.55228,
offset = {x: -pad/2, y:-pad/4};
var curve = new api.Bezier([
{x:w/2 + r, y:h/2},
{x:w/2 + r, y:h/2 + k*r},
{x:w/2 + k*r, y:h/2 + r},
{x:w/2, y:h/2 + r}
]);
api.setColor("lightgrey");
api.drawLine({x:0,y:h/2}, {x:w+pad,y:h/2}, offset);
api.drawLine({x:w/2,y:0}, {x:w/2,y:h+pad}, offset);
var pts = curve.points;
api.setColor("red");
api.drawPoint(pts[0], offset);
api.drawPoint(pts[1], offset);
api.drawPoint(pts[2], offset);
api.drawPoint(pts[3], offset);
api.drawCurve(curve, offset);
api.setColor("rgb(255,160,160)");
api.drawLine(pts[0],pts[1],offset);
api.drawLine(pts[1],pts[2],offset);
api.drawLine(pts[2],pts[3],offset);
api.setFill("red");
api.text((pts[0].x - w/2) + "," + (pts[0].y - h/2), {x: pts[0].x + 7, y: pts[0].y + 3}, offset);
api.text((pts[1].x - w/2) + "," + (pts[1].y - h/2), {x: pts[1].x + 7, y: pts[1].y + 3}, offset);
api.text((pts[2].x - w/2) + "," + (pts[2].y - h/2), {x: pts[2].x + 7, y: pts[2].y + 7}, offset);
api.text((pts[3].x - w/2) + "," + (pts[3].y - h/2), {x: pts[3].x, y: pts[3].y + 13}, offset);
pts.forEach(p => { p.x = -(p.x - w); });
api.setColor("blue");
api.drawCurve(curve, offset);
api.drawLine(pts[2],pts[3],offset);
api.drawPoint(pts[2],offset);
api.setFill("blue");
api.text("reflected", {x: pts[2].x - pad/2, y: pts[2].y + 13}, offset);
api.setColor("rgb(200,200,255)");
api.drawLine(pts[1],pts[0],offset);
api.drawPoint(pts[1],offset);
pts.forEach(p => { p.y = -(p.y - h); });
api.setColor("green");
api.drawCurve(curve, offset);
pts.forEach(p => { p.x = -(p.x - w); });
api.setColor("purple");
api.drawCurve(curve, offset);
api.drawLine(pts[1],pts[0],offset);
api.drawPoint(pts[1],offset);
api.setFill("purple");
api.text("reflected", {x: pts[1].x + 10, y: pts[1].y + 3}, offset);
api.setColor("rgb(200,200,255)");
api.drawLine(pts[2],pts[3],offset);
api.drawPoint(pts[2],offset);
api.setColor("black");
api.setFill("black");
api.drawLine({x:w/2, y:h/2}, {x:w/2 + r -2, y:h/2}, offset);
api.drawLine({x:w/2, y:h/2}, {x:w/2, y:h/2 + r -2}, offset);
api.text("r = " + r, {x:w/2 + r/3, y:h/2 + 10}, offset);
}
};