xxxxxxxxxx
299
let circles = [];
let DEBUG = {
bezier:false,
circles:1,
circlenum:1,
}
let MAX_SEGMENT = 100
let numCircles = 36;
let distanceLock = 10;
let bezierPoints = [];
let snakespeed = 3;
let t = 0;
let currentCurve = [];
let speed = 4;
let arcLengthMapping;
let totalLength;
let currentLength = 0;
//debugging snakes
let SNAKES = [
[80, 75, 65, 60, 55, 55, 50, 50, 50, 45, 45, 45, 45, 40, 40, 40,35,30,25,20,15,15,15,15,15,10,10,10,15,15,15,25],
[5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,7,7,6,5,6,7,8,4,3,6,7,8,3,7,8,5,3]
]
function setup() {
createCanvas(800, 600);
for (const [i, size] of SNAKES[0].entries()) {
circles.push(new Circle(150 + i * distanceLock, 150, size));
}
// for (let i = 0; i < numCircles; i++) {
// circles.push(new Circle(150 + i * distanceLock, 150, size));
// }
newBezierCurve();
}
function calculateBezierLength(curve, numSamples = 100) {
let length = 0;
let prevPoint = getBezierPoint(curve, 0);
for (let i = 1; i <= numSamples; i++) {
let t = i / numSamples;
let currentPoint = getBezierPoint(curve, t);
length += p5.Vector.dist(prevPoint, currentPoint);
prevPoint = currentPoint;
}
return length;
}
function calculateArcLengthMapping(curve, numSamples = 100) {
let arcLengthMapping = [];
let length = 0;
let prevPoint = getBezierPoint(curve, 0);
arcLengthMapping.push({ t: 0, length: 0 });
for (let i = 1; i <= numSamples; i++) {
let t = i / numSamples;
let currentPoint = getBezierPoint(curve, t);
length += p5.Vector.dist(prevPoint, currentPoint);
arcLengthMapping.push({ t: t, length: length });
prevPoint = currentPoint;
}
return arcLengthMapping;
}
function getTForArcLength(arcLengthMapping, targetLength) {
for (let i = 0; i < arcLengthMapping.length - 1; i++) {
if (arcLengthMapping[i + 1].length >= targetLength) {
let segmentLength = arcLengthMapping[i + 1].length - arcLengthMapping[i].length;
let segmentT = arcLengthMapping[i + 1].t - arcLengthMapping[i].t;
let ratio = (targetLength - arcLengthMapping[i].length) / segmentLength;
return arcLengthMapping[i].t + ratio * segmentT;
}
}
return 1; // In case the target length exceeds the total length
}
function draw() {
background(255);
noFill();
stroke(200, 0, 0, 100);
if (currentLength >= totalLength) {
newBezierCurve();
currentLength = 0;
}
if (frameCount % (60*1) == 0){
spawnSegment()
}
let t = getTForArcLength(arcLengthMapping, currentLength);
let target = getBezierPoint(currentCurve, t);
const [a,b,c,d] = currentCurve;
if (DEBUG.bezier ){
bezier(a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y)
// print(a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y)
circle(target.x, target.y, 10)
}
circles[0].update(target.x, target.y);
for (let i = 1; i < circles.length; i++) {
let prevCircle = circles[i - 1];
let targetPos = calculateDistanceLock(prevCircle, distanceLock);
circles[i].update(targetPos.x, targetPos.y);
}
currentLength += speed;
// Draw circles and connecting lines
for (let i = 0; i < circles.length; i++) {
circles[i].display();
if (DEBUG.circlenum){
circles[i].debugdisplay(i);
}
}
// Connect circles with tangent lines
connectCircles(circles);
}
class Circle {
constructor(x, y, size) {
this.pos = createVector(x, y);
this.size = size;
this.maxDistance = snakespeed;
// this.maxSize = size + 25
// this.minSize = max(size-5, 5)
// this.growing = random () < .5
}
update(x, y) {
let newPos = createVector(x, y);
let distance = p5.Vector.dist(this.pos, newPos);
if (distance > this.maxDistance) {
let direction = p5.Vector.sub(newPos, this.pos).normalize();
newPos = p5.Vector.add(this.pos, direction.mult(this.maxDistance));
}
this.pos.set(newPos.x, newPos.y);
// if (this.growing){
// this.size += 1 * (this.growing? 1 : -1)
// if (this.size >= this.maxSize ||
// this.size <= this.minSize){
// this.growing = !this.growing
// }
// }
}
debugdisplay(num=0){
text(num, this.pos.x, this.pos.y)
}
display() {
if (DEBUG.circles){
ellipse(this.pos.x, this.pos.y, this.size * 2, this.size * 2);
}
}
}
function calculateDistanceLock(prevCircle, distance) {
let direction = p5.Vector.sub(prevCircle.pos, circles[circles.indexOf(prevCircle) + 1].pos).normalize();
return p5.Vector.add(prevCircle.pos, direction.mult(-distance));
}
function getBezierPoint(curve, t) {
let x = bezierPoint(curve[0].x, curve[1].x, curve[2].x, curve[3].x, t);
let y = bezierPoint(curve[0].y, curve[1].y, curve[2].y, curve[3].y, t);
return createVector(x, y);
}
function newBezierCurve() {
if (currentCurve[0]) target = getBezierPoint(currentCurve, .99);
else{
target = circles[0].pos
}
let startX = target.x;
let startY = target.y;
currentCurve = [
createVector(startX, startY),
createVector(random(width*1.2), random(height*1.2)),
createVector(random(width*1.2), random(height*1.2)),
createVector(random(width*1.2), random(height*1.2))
];
arcLengthMapping = calculateArcLengthMapping(currentCurve);
totalLength = arcLengthMapping[arcLengthMapping.length - 1].length;
currentLength = 0;
}
function spawnSegment(){
let lc = circles[circles.length-1]
let prevsize = circles[circles.length-1].size
let increment = int(random(-5,5))
let size = int(random(max(prevsize-5, 3), prevsize+3)) // max((prevsize + increment),3)
print(prevsize, size, increment)
circles.push(new Circle(lc.pos.x-1, lc.pos.y-1, size));
if (circles.length > MAX_SEGMENT) {
circles.splice(random(MAX_SEGMENT-3, MAX_SEGMENT+3), 1);
}
}
function mousePressed(){
spawnSegment()
}
function connectCircles(circles) {
push();
stroke('green')
for (let i = 0; i < circles.length - 2; i++) {
let circle1 = circles[i];
let circle2 = circles[i + 1];
let circle3 = circles[i + 2];
let tangentPoints1 = getOptimizedTangentPoints(circle1, circle2);
let tangentPoints2 = getOptimizedTangentPoints(circle2, circle3);
if (i == 0){ // head
line(tangentPoints1[0].x, tangentPoints1[0].y, tangentPoints1[2].x, tangentPoints1[2].y)
}
if (i== circles.length-3){
//tail
line(tangentPoints2[1].x, tangentPoints2[1].y, tangentPoints2[3].x, tangentPoints2[3].y)
}
if (tangentPoints1 && tangentPoints2) {
// Draw lines between the tangent points of the first pair
line(tangentPoints1[0].x, tangentPoints1[0].y, tangentPoints1[1].x, tangentPoints1[1].y);
line(tangentPoints1[2].x, tangentPoints1[2].y, tangentPoints1[3].x, tangentPoints1[3].y);
// Draw lines between the tangent points of the second pair
line(tangentPoints2[0].x, tangentPoints2[0].y, tangentPoints2[1].x, tangentPoints2[1].y);
line(tangentPoints2[2].x, tangentPoints2[2].y, tangentPoints2[3].x, tangentPoints2[3].y);
// Check for intersection and adjust connections
if (!linesIntersect(tangentPoints1[1], tangentPoints2[0])) {
line(tangentPoints1[1].x, tangentPoints1[1].y, tangentPoints2[0].x, tangentPoints2[0].y);
}
if (!linesIntersect(tangentPoints1[3], tangentPoints2[2])) {
line(tangentPoints1[3].x, tangentPoints1[3].y, tangentPoints2[2].x, tangentPoints2[2].y);
}
}
}
pop();
}
function getOptimizedTangentPoints(circle1, circle2) {
let x1 = circle1.pos.x;
let y1 = circle1.pos.y;
let r1 = circle1.size;
let x2 = circle2.pos.x;
let y2 = circle2.pos.y;
let r2 = circle2.size;
let theta = atan2(y2 - y1, x2 - x1);
// Calculate tangent points
let t1a = theta + HALF_PI;
let t1b = theta - HALF_PI;
return [
{ x: x1 + r1 * cos(t1a), y: y1 + r1 * sin(t1a) },
{ x: x2 + r2 * cos(t1a), y: y2 + r2 * sin(t1a) },
{ x: x1 + r1 * cos(t1b), y: y1 + r1 * sin(t1b) },
{ x: x2 + r2 * cos(t1b), y: y2 + r2 * sin(t1b) }
];
}
function linesIntersect(p1, p2) {
// Check if two lines intersect
let dx1 = p2.x - p1.x;
let dy1 = p2.y - p1.y;
let dx2 = p1.x - p2.x;
let dy2 = p1.y - p2.y;
return (dx1 * dy2 - dy1 * dx2) !== 0;
}