Created
March 27, 2024 16:17
-
-
Save comficker/9df4b1aba322bdf833fe617e0692b4d3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class Fish { | |
constructor() { | |
this.pos = createVector(R.random_num(0, W), R.random_num(0, H)); // Position vector of fish | |
this.poi = createVector(0, 0); // Position vector of interest | |
this.vel = createVector(R.random_dec() * M, R.random_dec() * M); // initialize velocity in unit vector | |
this.facing = this.vel.heading() // facing of fish | |
this.speed = R.random_num(0.3, 1) * M; // magnitude to scale vel | |
this.maxTurn = PI / R.random_int(512, 1024); | |
this.fl = round(R.random_int(175, 225) * M); | |
this.laziness = R.random_int(1, 1000); | |
this.jitter = round(R.random_num(0, 0.7), 2); | |
this.mirror = (R.random_dec() < 0.5) ? true : false; | |
this.turning = 0; | |
this.n_blobs = R.random_int(5, 15); | |
this.n_blobs2 = R.random_int(0, 5); | |
this.blobs = []; | |
this.blobs2 = []; | |
this.ftr = R.random_dec(); | |
if (this.ftr < 0.2) { | |
this.fin_type = "ephemeral"; | |
} else { | |
this.fin_type = "classic"; | |
} | |
this.pattern = "rorschach"; | |
this.pr = R.random_dec(); | |
if (this.pr < 0.1) { | |
this.palette = "white"; | |
} else if (this.pr < 0.25) { | |
this.palette = "black"; | |
} else if (this.pr < 0.5) { | |
this.palette = "gold"; | |
} else { | |
this.palette = "classic"; | |
} | |
this.col1 = this.palette_picker(this.palette)[0]; | |
this.col2 = this.palette_picker(this.palette)[1]; | |
this.col3 = this.palette_picker(this.palette)[2]; | |
this.fincol = color(strokeCol, 50); | |
// Patterns | |
// Initialize blobs | |
for (let i = 0; i < this.n_blobs; i++) { // define an x position and a radius for each blob | |
let cx = R.random_int(0, this.fl * 0.9); | |
let cy = R.random_num(-1, 1); | |
let r = R.random_num(0.1, 0.5); | |
this.blobs[i] = new p5.Vector(cx, cy, r); | |
} | |
for (let j = 0; j < this.n_blobs2; j++) { | |
let cx = R.random_int(0, this.fl * 0.9); | |
let cy = R.random_num(-1, 1); | |
let r = R.random_num(0, 0.3); | |
this.blobs2[j] = new p5.Vector(cx, cy, r); | |
} | |
} | |
palette_picker(p) { | |
let pal; | |
let c1; | |
let c2; | |
let c3; | |
if (p == "classic") { | |
c1 = color(255, 82, 45, 10); | |
c2 = color(10, 230); | |
c3 = color(250, 200); | |
} else if (p == "black") { | |
c1 = color(10, 10); | |
c2 = color(70, 250); | |
c3 = color(255, 82, 45, 230); | |
} else if (p == "white") { | |
c1 = color(250, 10); | |
c2 = color(255, 82, 45, 230); | |
c3 = color(10, 250); | |
} else if (p == "gold") { | |
c1 = color(243, 175, 10, 10); | |
c2 = color(20, 230); | |
c3 = color(250, 200); | |
} | |
pal = [c1, c2, c3]; | |
return (pal); | |
} | |
display_fin(fin_type) { | |
push(); | |
if (fin_type == "classic") { // use classic fin shape | |
push(); | |
rotate(sin(noise((time / 2 + this.speed)))); | |
fill(250, 50); | |
this.fincol.setAlpha(100); | |
stroke(this.fincol); | |
beginShape(); | |
curveVertex(0, 0); | |
curveVertex(0, 0); | |
curveVertex(this.fl * 0.25, 0); | |
curveVertex(this.fl * 0.2, this.fl * 0.15); | |
curveVertex(0, this.fl * 0.05); | |
curveVertex(0, this.fl * 0.05); | |
endShape(); | |
for (let i = 0; i < 5; i++) { | |
let x2 = lerp(this.fl * 0.25, this.fl * 0.2, i / 5); | |
let y2 = lerp(0, this.fl * 0.15, i / 5); | |
let y1 = lerp(0, this.fl * 0.05, i / 5); | |
line(0, y1, x2, y2); | |
} | |
pop(); | |
} else if (fin_type == "ephemeral") { // waterfall fin type | |
noFill(); | |
this.fincol.setAlpha(50); | |
stroke(this.fincol); | |
strokeWeight(3); | |
rotate(sin(noise(time / 2 + this.speed))) / 2; | |
//let r = time / 2 * this.speed; | |
for (let i = 0; i < 50; i += 5) { | |
let x1 = -0.025 * this.fl + i / 3 * M; | |
let x2 = 0.025 * this.fl + i * M; | |
let x3 = 0.2 * this.fl + i * M; | |
let x4 = this.speed / 4 * this.fl + 1 / (i + 1) * M; | |
let y1 = 0 + i * M; | |
let y2 = -0.05 * this.fl + i * M; | |
let y3 = 0.05 * this.fl + i * M; | |
let y4 = 0.05 * this.fl + 1 / (i + 1) * M; | |
bezier(x1, y1, x2, y2, x3, y3, x4, y4); | |
} | |
} | |
pop(); | |
} | |
display_top_fin(fin_type) { | |
push(); | |
if (fin_type == "classic") { | |
fill(this.fincol); | |
beginShape(); | |
let a = createVector(0, 0); | |
let b = createVector(0, 0); | |
let ang = 0; | |
for (let i = 0; i < this.fl * 0.75; i++) { | |
let diff = createVector(cos(ang), sin(ang)); | |
a.add(diff); | |
if (i < this.fl * 0.3) { | |
b.add(diff.add(0, -0.3)); // b is used for the tip of the fin | |
} | |
if (i > this.fl * 0.2) { | |
curveVertex(a.x, a.y); | |
} | |
ang += this.turning / this.fl; | |
} | |
vertex(b.x / 2 + b.x / 2 * noise(time / 2 + this.speed), b.y / 2 + b.y / 2 * noise(time / 2 + this.speed)); | |
endShape(CLOSE); | |
} else if (fin_type == "ephemeral") { | |
noFill(); | |
stroke(this.fincol); | |
strokeWeight(2); | |
let startn = this.fl * 0.2; | |
let endn = this.fl * 0.75; | |
let ang = this.turning / this.fl; | |
let startx = 0.5 + sin((2 * startn + 1) / 2 * ang) / (2 * sin(ang / 2)); | |
let starty = sin(startn * ang / 2) * sin((startn + 1) * ang / 2) / (sin(ang / 2)); | |
let endx = 0.5 + sin((2 * endn + 1) / 2 * ang) / (2 * sin(ang / 2)); | |
let endy = sin(endn * ang / 2) * sin((endn + 1) * ang / 2) / (sin(ang / 2)); | |
//let r = time / 2 * this.speed; | |
for (let i = 0; i < 50; i += 5) { | |
let x1 = startx; | |
let x2 = startx + 10 * cos(this.turning) * M - i * M; | |
let x3 = endx - cos(this.turning) * 4 * i * M; | |
let x4 = endx; | |
let y1 = starty; | |
let y2 = starty - i * M; | |
let y3 = endy - sin(this.turning) * 2 * i * M; | |
let y4 = endy; | |
bezier(x1, y1, x2, y2, x3, y3, x4, y4); | |
} | |
} | |
pop(); | |
} | |
display_pattern(p, blobs, col) { | |
push(); | |
fill(col); | |
if (p == "rorschach") { | |
for (let i = 0; i < this.fl; i++) { | |
translate(1, 0); | |
rotate(this.turning / this.fl); | |
let fw; | |
if (i < this.fl * 0.6) { | |
fw = 0.05 * this.fl + pow(this.fl * i, 0.4); | |
} else if (i < this.fl * 0.75) { | |
fw = 0.05 * this.fl + pow(this.fl * this.fl * 0.6, 0.4); | |
} else { | |
fw = 0.05 * this.fl + pow(this.fl * this.fl * 0.75 - this.fl * 5 * (i - this.fl * 0.75), 0.4); | |
} | |
for (let j = 0; j < blobs.length; j++) { | |
if (i == blobs[j].x) { | |
blob(0, fw / 2 * blobs[j].y - blobs[j].z * fw / 2, blobs[j].z * fw * 1.5, fw / 2, time + j, ns, false); | |
if (this.mirror) { | |
blob(0, -fw / 2 * blobs[j].y + blobs[j].z * fw / 2, blobs[j].z * fw * 1.5, fw / 2, time + j, ns, true); | |
} | |
} | |
} | |
} | |
} | |
pop(); | |
} | |
display_eyes() { | |
push(); | |
for (let i = 0; i < 0.75 * this.fl; i++) { | |
translate(1, 0); | |
rotate(this.turning / this.fl); | |
} | |
fill(255, 230); | |
//stroke(this.col1); | |
//strokeWeight(2); | |
ellipse(0, (0.05 * this.fl + pow(this.fl * this.fl * 0.6, 0.4)) / 2 - 0.025 * this.fl, this.fl * 0.1, this.fl * 0.035); | |
ellipse(0, -(0.05 * this.fl + pow(this.fl * this.fl * 0.6, 0.4)) / 2 + 0.025 * this.fl, this.fl * 0.1, this.fl * 0.035); | |
fill(0, 200); | |
//noStroke(); | |
ellipse(this.fl * 0.015, (0.05 * this.fl + pow(this.fl * this.fl * 0.6, 0.4)) / 2 - 0.025 * this.fl, this.fl * 0.05, this.fl * 0.03); | |
ellipse(this.fl * 0.015, -(0.05 * this.fl + pow(this.fl * this.fl * 0.6, 0.4)) / 2 + 0.025 * this.fl, this.fl * 0.05, this.fl * 0.03); | |
pop(); | |
} | |
display_shadow() { | |
push(); | |
translate(this.pos.x, this.pos.y); | |
translate(20 * M, 20 * M); | |
rotate(this.facing); | |
fill(20, 3); | |
for (let i = 0; i < this.fl; i++) { | |
translate(1, 0); | |
rotate(this.turning / this.fl); | |
if (i < this.fl * 0.6) { | |
ellipse(0, 0, this.fl * 0.05 + pow(this.fl * i, 0.4)); | |
} else if (i < this.fl * 0.75) { | |
ellipse(0, 0, this.fl * 0.05 + pow(this.fl * this.fl * 0.6, 0.4)); | |
} else { | |
ellipse(0, 0, this.fl * 0.05 + pow(this.fl * this.fl * 0.75 - this.fl * 5 * (i - this.fl * 0.75), 0.4)); | |
} | |
} | |
pop(); | |
} | |
display_body() { // Function to display the body of the fish + eyes | |
noStroke(); | |
smooth(); | |
push(); | |
translate(this.pos.x, this.pos.y); | |
rotate(this.facing); | |
// Draw rear fins | |
push(); | |
for (let i = 0; i < this.fl; i++) { | |
translate(1, 0); | |
rotate(this.turning / this.fl); | |
if (i == round(this.fl * 0.2)) { | |
push(); | |
translate(0, (this.fl * 0.05 + pow(this.fl * this.fl * 0.2, 0.4)) / 2); | |
rotate(0.65 * PI); | |
scale(0.5); | |
this.display_fin(this.fin_type); | |
pop(); | |
push(); | |
translate(0, -(this.fl * 0.05 + pow(this.fl * this.fl * 0.2, 0.4)) / 2); | |
rotate(-0.65 * PI); | |
scale(0.5, -0.5); | |
this.display_fin(this.fin_type); | |
pop(); | |
} else if (i == round(this.fl * 0.6)) { | |
push(); | |
translate(0, (this.fl * 0.05 + pow(this.fl * this.fl * 0.6, 0.4)) / 2); | |
rotate(0.65 * PI); | |
this.display_fin(this.fin_type); | |
pop(); | |
push(); | |
translate(0, -(this.fl * 0.05 + pow(this.fl * this.fl * 0.6, 0.4)) / 2); | |
rotate(-0.65 * PI); | |
scale(1, -1) | |
this.display_fin(this.fin_type); | |
pop(); | |
} | |
} | |
pop(); | |
// Draw the body | |
push(); | |
for (let i = 0; i < this.fl; i++) { | |
translate(1, 0); | |
rotate(this.turning / this.fl); | |
fill(this.col1); | |
if (i < this.fl * 0.6) { | |
ellipse(0, 0, this.fl * 0.05 + pow(this.fl * i, 0.4)); | |
} else if (i < this.fl * 0.75) { | |
ellipse(0, 0, this.fl * 0.05 + pow(this.fl * this.fl * 0.6, 0.4)); | |
} else { | |
ellipse(0, 0, this.fl * 0.05 + pow(this.fl * this.fl * 0.75 - this.fl * 5 * (i - this.fl * 0.75), 0.4)); | |
} | |
} | |
pop(); | |
// Draw the pattern | |
push(); | |
this.display_pattern(this.pattern, this.blobs, this.col2); | |
this.display_pattern(this.pattern, this.blobs2, this.col3); | |
pop(); | |
// Draw the eyes | |
this.display_eyes(); | |
// Draw the top fin | |
this.display_top_fin(this.fin_type); | |
pop(); | |
} | |
display_tail() { | |
push(); | |
translate(this.pos.x, this.pos.y); | |
rotate(this.facing); | |
rotate(-this.turning / 6); | |
scale(-1, 1); | |
if (this.fin_type == "classic") { | |
push(); | |
fill(250, 50); | |
this.fincol.setAlpha(100); | |
stroke(this.fincol); | |
rotate(sin(time * PI / 2 * this.speed) / 5); | |
smooth(); | |
beginShape(); | |
curveVertex(0, 0); | |
curveVertex(0, 0); | |
curveVertex(0, this.fl * 0.05); | |
curveVertex(this.fl * 0.35 * this.speed, this.fl * 0.15) | |
curveVertex(this.fl * 0.15, 0); | |
curveVertex(this.fl * 0.35 * this.speed, -this.fl * 0.15) | |
curveVertex(0, -this.fl * 0.05); | |
curveVertex(0, 0); | |
curveVertex(0, 0); | |
endShape(CLOSE); | |
pop(); | |
} else if (this.fin_type == "ephemeral") { | |
noFill(); | |
stroke(this.fincol); | |
strokeWeight(2); | |
rotate(sin(time * PI / 2 * this.speed) / 5); | |
let r = time / 2 * this.speed; | |
for (let i = -50; i < 50; i += 4) { | |
let x1 = 0; | |
let x2 = this.fl * 0.05 + this.fl * 0.25 * noise(r + 0); | |
let x3 = this.fl * 0.2 + i * noise(r + 15) * M; | |
let x4 = this.fl * 0.15 + 2 * abs(i) * noise(r + 30) * M; | |
let y1 = 0; | |
let y2 = 0 + i * noise(r + 40) * M; | |
let y3 = this.fl * 0.1 + 2 * i * noise(r + 50) * M; | |
let y4 = 0 + i * noise(r + 70) * M; | |
bezier(x1, y1, x2, y2, x3, y3, x4, y4); | |
} | |
} | |
pop(); | |
} | |
move() { | |
// Update x and y coords | |
this.pos.x += this.speed * cos(this.facing); | |
this.pos.y += this.speed * sin(this.facing); | |
// Periodic boundary conditions | |
if (this.pos.x >= W) { | |
this.pos.x -= W; | |
} | |
if (this.pos.y >= H) { | |
this.pos.y -= H; | |
} | |
if (this.pos.x < 0) { | |
this.pos.x += W; | |
} | |
if (this.pos.y < 0) { | |
this.pos.y += H; | |
} | |
} | |
turn() { | |
let v1 = p5.Vector.sub(this.poi, this.pos); | |
let ang = this.vel.angleBetween(v1); | |
if (ang < -0.01) { | |
this.turning -= this.maxTurn; | |
this.facing -= this.maxTurn / 2; | |
this.vel.rotate(-this.maxTurn / 2); | |
//this.facing += this.turning / 100; | |
} else if (ang > 0.01) { | |
this.turning += this.maxTurn; | |
this.facing += this.maxTurn / 2; | |
this.vel.rotate(this.maxTurn / 2); | |
//this.facing += this.turning / 100; | |
} | |
this.turning *= 0.99; // Decay turning | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment