archive complete
This commit is contained in:
13
projects/cubic_bezier_curve/cLine.js
Normal file
13
projects/cubic_bezier_curve/cLine.js
Normal file
@@ -0,0 +1,13 @@
|
||||
class cLine {
|
||||
constructor(a, b, col = color(180, 180, 180, 140), w = 2) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.col = col;
|
||||
this.w = w;
|
||||
}
|
||||
drawLine() {
|
||||
stroke(this.col);
|
||||
strokeWeight(this.w);
|
||||
line(this.a.x, this.a.y, this.b.x, this.b.y);
|
||||
}
|
||||
}
|
||||
44
projects/cubic_bezier_curve/cPoint.js
Normal file
44
projects/cubic_bezier_curve/cPoint.js
Normal file
@@ -0,0 +1,44 @@
|
||||
class cPoint {
|
||||
constructor(x, y, kind = 'control') {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.kind = kind;
|
||||
this.r = 9;
|
||||
}
|
||||
set changeX(nx) { this.x = nx; }
|
||||
set changeY(ny) { this.y = ny; }
|
||||
get changeX() { return this.x; }
|
||||
get changeY() { return this.y; }
|
||||
|
||||
isHit(mx, my, r = HIT_R) {
|
||||
return dist(mx, my, this.x, this.y) <= r;
|
||||
}
|
||||
|
||||
drawPoint(hovered = false, selected = false, styleOverride = null) {
|
||||
strokeWeight(2);
|
||||
if (styleOverride) {
|
||||
stroke(...styleOverride.stroke);
|
||||
fill(...styleOverride.fill);
|
||||
} else {
|
||||
if (this.kind === 'end') {
|
||||
stroke(0, 220, 90, 240);
|
||||
fill(40, 240, 120, 200);
|
||||
} else if (this.kind === 'control') {
|
||||
stroke(240, 140, 0, 240);
|
||||
fill(255, 180, 40, 200);
|
||||
} else {
|
||||
stroke(140, 160);
|
||||
fill(190, 160);
|
||||
}
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
stroke(255, 255, 0);
|
||||
strokeWeight(3);
|
||||
} else if (hovered) {
|
||||
stroke(255);
|
||||
}
|
||||
|
||||
circle(this.x, this.y, this.r * 2);
|
||||
}
|
||||
}
|
||||
15
projects/cubic_bezier_curve/index.html
Normal file
15
projects/cubic_bezier_curve/index.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
|
||||
<script src="cPoint.js"></script>
|
||||
<script src="cLine.js"></script>
|
||||
<script src="sketch.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
239
projects/cubic_bezier_curve/sketch.js
Normal file
239
projects/cubic_bezier_curve/sketch.js
Normal file
@@ -0,0 +1,239 @@
|
||||
let p = [], v = [];
|
||||
let lines = [];
|
||||
let theta = (3 / 2) * Math.PI, t = 0;
|
||||
let paused = false;
|
||||
let showLabels = false;
|
||||
let showInterPoints = true;
|
||||
|
||||
let hoverIndex = -1;
|
||||
let selectedIndex = -1;
|
||||
let prevMouse = { x: 0, y: 0 };
|
||||
let draggingAll = false;
|
||||
const HIT_R = 10;
|
||||
const MAX_TRAIL = 1000;
|
||||
|
||||
function setup() {
|
||||
createCanvas(1000, 800);
|
||||
initCubicCurve(true);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
background(32);
|
||||
|
||||
stroke(120, 120, 120, 180);
|
||||
strokeWeight(2);
|
||||
noFill();
|
||||
beginShape();
|
||||
vertex(p[0].x, p[0].y);
|
||||
vertex(p[1].x, p[1].y);
|
||||
vertex(p[2].x, p[2].y);
|
||||
vertex(p[3].x, p[3].y);
|
||||
endShape();
|
||||
|
||||
if (!paused) {
|
||||
if (theta > TWO_PI) theta = 0;
|
||||
theta += 0.01;
|
||||
t = (sin(theta) + 1) / 2; // 0..1
|
||||
}
|
||||
|
||||
lerpCubicCurve();
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
lines[i].drawLine();
|
||||
}
|
||||
|
||||
drawCurve();
|
||||
drawPointsAndLabels();
|
||||
drawHUD();
|
||||
}
|
||||
|
||||
function drawCurve() {
|
||||
if (v.length > MAX_TRAIL) v.splice(0, v.length - MAX_TRAIL);
|
||||
|
||||
stroke(0, 255, 0, 200);
|
||||
strokeWeight(5);
|
||||
noFill();
|
||||
beginShape();
|
||||
for (let i = 0; i < v.length; i++) {
|
||||
vertex(v[i].x, v[i].y);
|
||||
}
|
||||
endShape();
|
||||
}
|
||||
|
||||
function drawPointsAndLabels() {
|
||||
push();
|
||||
noStroke();
|
||||
fill(255, 70, 70, 230);
|
||||
circle(p[9].x, p[9].y, 14);
|
||||
pop();
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const hovered = (i === hoverIndex);
|
||||
const selected = (i === selectedIndex);
|
||||
p[i].drawPoint(hovered, selected);
|
||||
if (showLabels) {
|
||||
noStroke();
|
||||
fill(255);
|
||||
textSize(14);
|
||||
textAlign(LEFT, BOTTOM);
|
||||
text(`P${i}`, p[i].x + 10, p[i].y - 10);
|
||||
}
|
||||
}
|
||||
|
||||
if (showLabels) {
|
||||
noStroke(); fill(220);
|
||||
textSize(12); textAlign(LEFT, BOTTOM);
|
||||
text(`P4`, p[4].x + 8, p[4].y - 8);
|
||||
}
|
||||
|
||||
if (showInterPoints) {
|
||||
const styleMid = {
|
||||
stroke: [80, 180, 255, 100],
|
||||
fill: [80, 180, 255, 50]
|
||||
};
|
||||
for (let i = 4; i <= 8; i++) {
|
||||
p[i].drawPoint(false, false, styleMid);
|
||||
if (showLabels) {
|
||||
noStroke(); fill(200);
|
||||
textSize(12); textAlign(LEFT, BOTTOM);
|
||||
text(`P${i}`, p[i].x + 8, p[i].y - 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawHUD() {
|
||||
noStroke();
|
||||
fill(255);
|
||||
textSize(14);
|
||||
textAlign(LEFT, TOP);
|
||||
const hud = [
|
||||
`t = ${t.toFixed(3)}`,
|
||||
`Space = ${paused ? 'Resume' : 'Pause'}`,
|
||||
`H = Toggle points 5..8 | L = Toggle labels | R = Reset`,
|
||||
`Drag a handle (P0..P3). Hold Shift to move the entire curve.`
|
||||
];
|
||||
for (let i = 0; i < hud.length; i++) {
|
||||
text(hud[i], 12, 12 + i * 18);
|
||||
}
|
||||
}
|
||||
|
||||
function initCubicCurve(resetPositions = true) {
|
||||
p.length = 0;
|
||||
v.length = 0;
|
||||
lines.length = 0;
|
||||
theta = (3 / 2) * Math.PI;
|
||||
t = 0;
|
||||
|
||||
if (resetPositions) {
|
||||
p[0] = new cPoint(width * 0.15, height * 0.75, 'end');
|
||||
p[1] = new cPoint(width * 0.35, height * 0.20, 'control');
|
||||
p[2] = new cPoint(width * 0.65, height * 0.20, 'control');
|
||||
p[3] = new cPoint(width * 0.85, height * 0.75, 'end');
|
||||
} else {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (!p[i]) p[i] = new cPoint(width * (0.15 + i * 0.2), height / 2, i === 0 || i === 3 ? 'end' : 'control');
|
||||
}
|
||||
}
|
||||
|
||||
// Derived points
|
||||
p[4] = new cPoint(0, 0, 'derived');
|
||||
p[5] = new cPoint(0, 0, 'derived');
|
||||
p[6] = new cPoint(0, 0, 'derived');
|
||||
p[7] = new cPoint(0, 0, 'derived');
|
||||
p[8] = new cPoint(0, 0, 'derived');
|
||||
p[9] = new cPoint(0, 0, 'derived');
|
||||
|
||||
lines[0] = new cLine(p[4], p[5], color(160, 160, 160, 200), 2);
|
||||
lines[1] = new cLine(p[5], p[6], color(160, 160, 160, 200), 2);
|
||||
|
||||
lines[2] = new cLine(p[7], p[8], color(200, 200, 200, 220), 3);
|
||||
}
|
||||
|
||||
function lerpCubicCurve() {
|
||||
// Level 1
|
||||
p[4].changeX = (1 - t) * p[0].x + t * p[1].x;
|
||||
p[4].changeY = (1 - t) * p[0].y + t * p[1].y;
|
||||
|
||||
p[5].changeX = (1 - t) * p[1].x + t * p[2].x;
|
||||
p[5].changeY = (1 - t) * p[1].y + t * p[2].y;
|
||||
|
||||
p[6].changeX = (1 - t) * p[2].x + t * p[3].x;
|
||||
p[6].changeY = (1 - t) * p[2].y + t * p[3].y;
|
||||
|
||||
// Level 2
|
||||
p[7].changeX = (1 - t) * p[4].x + t * p[5].x;
|
||||
p[7].changeY = (1 - t) * p[4].y + t * p[5].y;
|
||||
|
||||
p[8].changeX = (1 - t) * p[5].x + t * p[6].x;
|
||||
p[8].changeY = (1 - t) * p[5].y + t * p[6].y;
|
||||
|
||||
// Final on-curve
|
||||
p[9].changeX = (1 - t) * p[7].x + t * p[8].x;
|
||||
p[9].changeY = (1 - t) * p[7].y + t * p[8].y;
|
||||
|
||||
v.push(new cPoint(p[9].x, p[9].y, 'derived'));
|
||||
}
|
||||
|
||||
function mouseMoved() {
|
||||
hoverIndex = -1;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (p[i].isHit(mouseX, mouseY)) {
|
||||
hoverIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mousePressed() {
|
||||
prevMouse.x = mouseX;
|
||||
prevMouse.y = mouseY;
|
||||
|
||||
selectedIndex = -1;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (p[i].isHit(mouseX, mouseY)) {
|
||||
selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
draggingAll = keyIsDown(SHIFT) && selectedIndex === -1;
|
||||
}
|
||||
|
||||
function mouseDragged() {
|
||||
const dx = mouseX - prevMouse.x;
|
||||
const dy = mouseY - prevMouse.y;
|
||||
|
||||
if (selectedIndex >= 0) {
|
||||
p[selectedIndex].changeX = mouseX;
|
||||
p[selectedIndex].changeY = mouseY;
|
||||
v.length = 0;
|
||||
} else if (draggingAll) {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
p[i].changeX = p[i].x + dx;
|
||||
p[i].changeY = p[i].y + dy;
|
||||
}
|
||||
v.length = 0;
|
||||
}
|
||||
|
||||
prevMouse.x = mouseX;
|
||||
prevMouse.y = mouseY;
|
||||
return false;
|
||||
}
|
||||
|
||||
function mouseReleased() {
|
||||
selectedIndex = -1;
|
||||
draggingAll = false;
|
||||
}
|
||||
|
||||
function keyPressed() {
|
||||
if (key === ' ') {
|
||||
paused = !paused;
|
||||
} else if (key === 'R' || key === 'r') {
|
||||
initCubicCurve(true);
|
||||
} else if (key === 'H' || key === 'h') {
|
||||
showInterPoints = !showInterPoints;
|
||||
} else if (key === 'L' || key === 'l') {
|
||||
showLabels = !showLabels;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user