xxxxxxxxxx
372
// require https://cdn.jsdelivr.net/npm/tweakpane@3.0.7/dist/tweakpane.min.js
// require https://cdn.jsdelivr.net/npm/p5@latest/lib/p5.min.js
const pane = new Tweakpane.Pane();
// constants
const FR = 30;
const CANVAS_WIDTH = 600;
const CANVAS_HEIGHT = 600;
let isInverted = false;
const PARAMS = {
bgColor: "#53638A",
lineColor: "#1c1c1c",
frequency: 0.25,
maxShift: 10,
strokeWidth: 0.4,
seed: 100,
useMultiBlob: false,
shapes: 4,
diameter: 250,
shapePoints: 5,
angleSize: 60,
blobs: 6,
useOutline: true,
isDebug: true,
};
// global variables that are needed
let cutCount = 0;
let sphere_counter = 0;
function setup() {
createCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
frameRate(FR);
// add parameters
pane.addInput(PARAMS, "bgColor", {
view: "color",
});
pane.addInput(PARAMS, "frequency", {
min: 0.1,
max: 1,
});
pane.addInput(PARAMS, "maxShift", {
min: 1,
max: 100,
});
pane.addInput(PARAMS, "useOutline");
const f1 = pane.addFolder({
title: "Base Shape",
});
f1.addInput(PARAMS, "shapes", {
step: 1,
min: 1,
max: 20,
});
f1.addInput(PARAMS, "diameter", {
min: 20,
max: 400,
});
f1.addInput(PARAMS, "shapePoints", {
step: 1,
min: 2,
max: 20,
});
pane.addInput(PARAMS, "useMultiBlob");
const f2 = pane.addFolder({
title: "Multi Blob",
expanded: false,
});
f2.addInput(PARAMS, "angleSize", {
min: 10,
max: 180,
});
f2.addInput(PARAMS, "blobs", {
step: 1,
min: 1,
max: 10,
});
const f3 = pane.addFolder({
title: "Outline",
});
f3.addInput(PARAMS, "lineColor", {
view: "color",
});
f3.addInput(PARAMS, "strokeWidth", {
min: 0.1,
max: 2,
});
pane.addInput(PARAMS, "isDebug");
adjustValues();
pane.on("change", adjustValues);
}
function adjustValues() {
noiseSeed(PARAMS.seed);
}
function draw() {
isInverted ? background(PARAMS.lineColor) : background(PARAMS.bgColor);
translate(width / 2, height / 2);
if (PARAMS.useOutline) {
noFill();
strokeWeight(PARAMS.strokeWidth);
if (isInverted) {
stroke(PARAMS.bgColor);
} else {
stroke(PARAMS.lineColor);
}
} else {
noStroke();
fill(PARAMS.bgColor);
drawingContext.fillStyle = createGradient();
}
if (PARAMS.useMultiBlob) {
for (let b = 0; b < PARAMS.blobs; b++) {
rotate(TWO_PI / PARAMS.blobs);
// shapes
// bigger to smaller
for (let s = 0; s < PARAMS.shapes; s++) {
const tD = map(s, 0, PARAMS.shapes, PARAMS.diameter, 0);
beginShape();
// first point = center
curveVertex(0, 0);
for (let p = 0; p <= PARAMS.shapePoints; p++) {
const n = map(
noise(frameCount * 0.004, p * (s * 0.01)),
0,
0.8,
0.5,
1,
true
);
const x =
tD * sin((p / PARAMS.shapePoints) * radians(PARAMS.angleSize)) * n;
const y =
tD * cos((p / PARAMS.shapePoints) * radians(PARAMS.angleSize)) * n;
curveVertex(x, y);
}
curveVertex(0, 0);
endShape(CLOSE);
}
}
} else {
const shiftX = map(mouseX, 0, width, -1 * PARAMS.maxShift, PARAMS.maxShift);
const shiftY = map(
mouseY,
0,
height,
-1 * PARAMS.maxShift,
PARAMS.maxShift
);
// shapes
// bigger to smaller
for (let s = 0; s < PARAMS.shapes; s++) {
const shiftFactor = pow(1.08, PARAMS.shapes - s);
const points = [];
const factor = (s + 1) / PARAMS.shapes;
const diam = factor * PARAMS.diameter;
// the optimal distance for a bezier control point to get a circle
// https://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves
const cDist = (4 / 3) * tan(PI / (2 * PARAMS.shapePoints));
// first point = center
for (let p = 0; p < PARAMS.shapePoints; p++) {
const otherP = p === 0 ? PARAMS.shapePoints - 1 : p - 1;
const angle = (p / PARAMS.shapePoints) * TWO_PI;
const angle2 = (otherP / PARAMS.shapePoints) * TWO_PI; // get the next point
const n = noise((frameCount * PARAMS.frequency) / 100, p + s * 0.1);
const n2 = noise(
(frameCount * PARAMS.frequency) / 100,
otherP + s * 0.1
);
const x = sin(angle) * diam * n + shiftX * shiftFactor;
const y = cos(angle) * diam * n + shiftY * shiftFactor;
const x2 = sin(angle2) * diam * n2 + shiftX * shiftFactor;
const y2 = cos(angle2) * diam * n2 + shiftY * shiftFactor;
let cx1 = {
x: x2 + cDist * diam * n2,
y: y2,
};
let cx2 = {
x: x + cDist * diam * n,
y: y,
};
cx1 = rotate_point(x2, y2, TWO_PI - angle2 * n2, cx1);
cx2 = rotate_point(x, y, PI - angle * n, cx2);
points.push({
cx1: cx1.x,
cy1: cx1.y,
cx2: cx2.x,
cy2: cx2.y,
ax: x,
ay: y,
});
}
// additional last point as first point
points.push(points[0]);
beginShape();
for (let i = 0; i < points.length; i++) {
const point = points[i];
if (i === 0) {
vertex(point.ax, point.ay);
} else {
bezierVertex(
point.cx1,
point.cy1,
point.cx2,
point.cy2,
point.ax,
point.ay
);
}
}
endShape();
if (PARAMS.isDebug) {
push();
noStroke();
for (let i = 0; i < points.length; i++) {
const point = points[i];
if (i === 0) {
fill("#00f");
text(`S${s} P${i}`, point.ax + 24, point.ay + 5);
circle(point.ax, point.ay, 10);
} else {
const prevPoint = points[i - 1];
fill("red");
circle(point.cx1, point.cy1, 3);
circle(point.cx2, point.cy2, 3);
fill("#0f0");
circle(point.ax, point.ay, 3);
text(`S${s} P${i}`, point.ax + 6, point.ay + 5);
stroke("red");
line(point.cx1, point.cy1, prevPoint.ax, prevPoint.ay);
stroke("#0f0");
line(point.cx2, point.cy2, point.ax, point.ay);
}
}
pop();
}
/*const tD = map(s, 0, PARAMS.shapes, PARAMS.diameter, 0);
const factor = s / PARAMS.shapes;
const shiftFactor = pow(1.08,s)
const shiftX = map( mouseX, 0, width, -1 * PARAMS.maxShift, PARAMS.maxShift );
const shiftY = map( mouseY, 0, height, -1 * PARAMS.maxShift, PARAMS.maxShift );
beginShape();
// first point = center
for (let p = 0; p < PARAMS.shapePoints; p++) {
const n = noise( frameCount * PARAMS.frequency / 100, p + s*0.02 );
const x = sin(p / PARAMS.shapePoints * TWO_PI) * tD * n + ( shiftX * shiftFactor );
const y = cos(p / PARAMS.shapePoints * TWO_PI) * tD * n + ( shiftY * shiftFactor );
curveVertex(x, y);
}
endShape(CLOSE);*/
}
}
}
const createGradient = () => {
// define the start and stop points for the gradient
let gradient = drawingContext.createLinearGradient(
20,
20,
width - 20,
height - 20
);
let radius = 150;
let x1 = width / 2 - radius;
let y1 = height / 2 - radius;
let x2 = width / 2 + radius;
let y2 = height / 2 + radius;
// define the gradient with the points we calculated
let g = drawingContext.createLinearGradient(x1, y1, x2, y2);
let c1 = color(PARAMS.bgColor);
let c2 = color(PARAMS.lineColor);
let shadowColor = color(PARAMS.lineColor);
if (isInverted) {
c2 = color(PARAMS.bgColor);
c1 = color(PARAMS.lineColor);
shadowColor = color(PARAMS.bgColor);
}
g.addColorStop(0, c1.toString());
g.addColorStop(0.5, c2.toString());
g.addColorStop(1, c1.toString());
shadowColor.setAlpha(60);
drawingContext.shadowOffsetX = 0;
drawingContext.shadowOffsetY = 5;
drawingContext.shadowBlur = 30;
drawingContext.shadowColor = shadowColor.toString();
// then draw a shape with this gradient
/*drawingContext.fillStyle = g;
beginShape();
for (let a=0; a<TWO_PI; a+=radians(45)) {
let x = width/2 + cos(a) * radius;
let y = height/2 + sin(a) * radius;
vertex(x, y);
}
endShape(CLOSE);
*/
return g;
};
function mousePressed() {
// isInverted = !isInverted;
}
// https://stackoverflow.com/questions/2259476/rotating-a-point-about-another-point-2d
const rotate_point = (cx, cy, angle, p) => {
const s = sin(angle);
const c = cos(angle);
// translate point back to origin:
p.x -= cx;
p.y -= cy;
// rotate point
const xnew = p.x * c - p.y * s;
const ynew = p.x * s + p.y * c;
// translate point back:
p.x = xnew + cx;
p.y = ynew + cy;
return p;
};