xxxxxxxxxx
134
let segments = [];
let palette;
const MAX_SEGMENTS = 16;
const SEGMENT_SIZE = 40;
let cohesiveSlider, chaoticSlider;
function setup() {
createCanvas(800, 600);
palette = [
color(224, 13, 30),
color(255, 221, 10),
color(5, 56, 137),
color(231, 233, 232),
];
cohesiveSlider = createSlider(0, 1, 1, 0.01);
cohesiveSlider.position(20, 20);
chaoticSlider = createSlider(0, 1, 0, 0.01);
chaoticSlider.position(20, 50);
// Create serpent segments
for (let i = 0; i < MAX_SEGMENTS; i++) {
segments.push(
new Segment(
width / 2 + i * SEGMENT_SIZE * 0.8,
height / 2,
SEGMENT_SIZE,
palette[i % palette.length],
i
)
);
}
}
function draw() {
background(5, 56, 137);
let weightCohesive = cohesiveSlider.value();
let weightChaotic = chaoticSlider.value();
// Compute a blend factor from the two weights.
// When blendFactor is 0, behavior is fully cohesive; when 1, fully chaotic.
let totalWeight = weightCohesive + weightChaotic;
let blendFactor = 0;
if (totalWeight > 0) {
blendFactor = weightChaotic / totalWeight;
}
fill(255);
noStroke();
textSize(14);
text(
"Cohesive Weight: " + nf(weightCohesive, 1, 2),
cohesiveSlider.x * 2 + cohesiveSlider.width,
35
);
text(
"Chaotic Weight: " + nf(weightChaotic, 1, 2),
chaoticSlider.x * 2 + chaoticSlider.width,
65
);
// Update and display all segments using the computed blend factor.
segments.forEach((seg, index) => {
seg.update(index, blendFactor);
seg.display(blendFactor);
});
}
class Segment {
constructor(x, y, size, col, index) {
this.home = createVector(x, y);
this.position = createVector(x, y);
this.target = createVector(x, y);
this.size = size;
this.color = col;
this.index = index;
this.angle = 0;
this.energy = 0;
}
update(index, blendFactor) {
// For the head segment, blend the target between the mouse (cohesive) and a circular pattern (chaotic).
if (index === 0) {
let cohesiveTarget = createVector(mouseX, mouseY);
let chaoticTarget = createVector(
width / 2 + cos(frameCount * 0.05) * 200,
height / 2 + sin(frameCount * 0.07) * 150
);
this.target = p5.Vector.lerp(cohesiveTarget, chaoticTarget, blendFactor);
} else {
this.target = segments[index - 1].position.copy();
}
// Blend offsets: no offset for cohesive, sin offset for chaotic.
let cohesiveOffset = createVector(0, 0);
let chaoticOffset = createVector(
cos(frameCount * 0.1 + this.index) * 20,
sin(frameCount * 0.1 + this.index) * 20
);
let offset = p5.Vector.lerp(cohesiveOffset, chaoticOffset, blendFactor);
this.position.lerp(p5.Vector.add(this.target, offset), 0.2);
// Oscillate the size for a dynamic effect.
this.size = SEGMENT_SIZE * (0.8 + 0.2 * sin(frameCount * 0.1 + this.index));
// Blend rotation angles between cohesive and chaotic.
let cohesiveAngle = sin(frameCount * 0.05 + this.index) * 0.2;
let chaoticAngle = sin(frameCount * 0.15 + this.index) * 0.5;
this.angle = lerp(cohesiveAngle, chaoticAngle, blendFactor);
}
display(blendFactor) {
push();
translate(this.position.x, this.position.y);
rotate(this.angle);
fill(this.color);
noStroke();
rect(0, 0, this.size, this.size / 2, 5);
fill(palette[(this.index + 1) % palette.length]);
ellipse(-this.size / 3, 0, this.size / 4);
ellipse(this.size / 3, 0, this.size / 4);
// If some chaotic behavior is present, add a visual effect.
if (blendFactor > 0) {
stroke(255, blendFactor * 255);
strokeWeight(2);
noFill();
line(-this.size / 2, -this.size / 4, this.size / 2, this.size / 4);
}
pop();
}
}