Debugging Jamie's faces.
-
-
Save dribnet/fadd7007eff40c7b24879840745bbddb to your computer and use it in GitHub Desktop.
license: mit |
{ | |
"commits": [ | |
{ | |
"sha": "0c6c3cfbf221d991635b1c1946ee777533194f47", | |
"name": "Face Logic" | |
}, | |
{ | |
"sha": "8973b99fa773cba2f2575f30c11cf5cff14cd51d", | |
"name": "Train Neighbors and Quiz" | |
}, | |
{ | |
"sha": "7adbeed1dabb00bc073f3a59144ca9d139875333", | |
"name": "first version of training" | |
}, | |
{ | |
"sha": "0e61ad32bbd42601bd25620421c35100ab813119", | |
"name": "added image to sample_images.json" | |
}, | |
{ | |
"sha": "21777e8da0a797bac8f042b3b509266012b28dc1", | |
"name": "using face positions" | |
}, | |
{ | |
"sha": "a683736618d1513f1c2477cfe0eade4a202dac8d", | |
"name": "mickey mouse example" | |
} | |
] | |
} |
<head> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.3/seedrandom.min.js"></script> | |
<script src="https://d3js.org/d3-random.v1.min.js"></script> | |
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script> | |
<script language="javascript" type="text/javascript" src="z_focused_random.js"></script> | |
<script language="javascript" type="text/javascript" src="p5.func.js"></script> | |
<script type="text/javascript" src="LZWEncoder.js"></script> | |
<script type="text/javascript" src="NeuQuant.js"></script> | |
<script type="text/javascript" src="GIFEncoder.js"></script> | |
<script type="text/javascript" src="b64.js"></script> | |
<script type="text/javascript" src="z_recorder.js"></script> | |
<script language="javascript" type="text/javascript" src="simplex-noise.js"></script> | |
<script language="javascript" type="text/javascript" src="canvas_size.js"></script> | |
<script language="javascript" type="text/javascript" src="draw_one_frame.js"></script> | |
<script language="javascript" type="text/javascript" src="anim_runner.js"></script> | |
<style> body {padding: 0; margin: 0;} </style> | |
</head> | |
<body style="background-color:white"> | |
<div class="outer"> | |
<div class="inner"> | |
<div id="canvasContainer"></div> | |
</div> | |
</div> | |
</table> | |
<br> | |
<pre> | |
keys: | |
spacebar => toggle debugZoom | |
d => toggle debug view | |
r => start recording | |
1,2,3 => change framerate | |
! = screen grab files | |
</pre> | |
<a href="sketch.html">sketch</a><br> | |
<a href="draw_one_frame.js">source code</a><br> | |
<a href="preview.jpg">preview image</a><br> | |
<a href="index.html">(anim [this one])</a><br> | |
</body> |
// these can be customized | |
const debugViewText = "#ff0000"; | |
const debugZoomBackground = "#555588" | |
const debugZoomScale = 0.5; | |
// this can be modified after we discuss in lecture | |
const buffersPerFrame = 1; | |
// probably best not to modify anything below this line | |
const frameMax = 96; | |
let recording = false; | |
let gifRecorder = null; | |
let debugZoom = false; | |
let debugView = false; | |
let stickFrame = 0; | |
// *note: canvasWidth and canvasHeight will be defined before this script runs) | |
function setup () { | |
let main_canvas = createCanvas(canvasWidth,canvasHeight); | |
let r = random(100); | |
main_canvas.parent('canvasContainer'); | |
frameRate(24 * buffersPerFrame); | |
} | |
function mousePressed(){ | |
} | |
function draw () { | |
let animation_max_frames = frameMax * buffersPerFrame; | |
let sticky_max_frames = animation_max_frames + stickFrame; | |
let cur_frame = frameCount % sticky_max_frames; | |
if (cur_frame >= animation_max_frames) { | |
cur_frame = 0; | |
} | |
let cur_frac = map(cur_frame, 0, animation_max_frames, 0, 1); | |
background(debugZoomBackground); | |
push(); | |
if(debugZoom) { | |
translate(0.5 * width, 0.5 * height); | |
scale(debugZoomScale); | |
translate(0.5 * -width, 0.5 * -height); | |
} | |
draw_one_frame(cur_frac); | |
pop(); | |
if(debugView) { | |
textSize(28); | |
fill(debugViewText); | |
text("" + (cur_frame/buffersPerFrame).toFixed(2) + " / " + | |
(animation_max_frames/buffersPerFrame).toFixed(2) + " = " + | |
cur_frac.toFixed(2), 50, 50); | |
} | |
if(recording) { | |
textSize(24); | |
gifRecorder.addBuffer(); | |
} | |
} | |
function keyTyped() { | |
if (key == '!') { | |
saveBlocksImages(); | |
} | |
if (key == ' ') { | |
debugZoom = !debugZoom; | |
} | |
if (key == 'd') { | |
debugView = !debugView; | |
} | |
if (key == '1') { | |
frameRate(1); | |
stickFrame = 0; | |
} | |
if (key == '2') { | |
frameRate(5); | |
stickFrame = 5; | |
} | |
if (key == '3') { | |
frameRate(30); | |
stickFrame = 0; | |
} | |
if (key == 'r') { | |
if (recording==false){ | |
recording = true; | |
gifRecorder = new p5recorder (frameMax, 'wallpaper.gif', buffersPerFrame); | |
} | |
} | |
} |
function encode64(input) { | |
var output = "", i = 0, l = input.length, | |
key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", | |
chr1, chr2, chr3, enc1, enc2, enc3, enc4; | |
while (i < l) { | |
chr1 = input.charCodeAt(i++); | |
chr2 = input.charCodeAt(i++); | |
chr3 = input.charCodeAt(i++); | |
enc1 = chr1 >> 2; | |
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); | |
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); | |
enc4 = chr3 & 63; | |
if (isNaN(chr2)) enc3 = enc4 = 64; | |
else if (isNaN(chr3)) enc4 = 64; | |
output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4); | |
} | |
return output; | |
} |
var canvasWidth = 960; | |
var canvasHeight = 540; |
const ease = new p5.Ease(); | |
function draw_one_frame(cur_frac) { | |
var addXSingle = width / 960; | |
var addYSingle = height / 520; | |
fill(255); | |
rect(0, 0, width, height); | |
//drawing the background gradient | |
c1 = color(240, 254, 254); //very light blue | |
c2 = color(154, 242, 245); //darker blue | |
for (let yBG = 0; yBG < height + 1; yBG++) { | |
n = map(yBG, 0, height, 0, 1); | |
let newc = lerpColor(c1, c2, n); | |
stroke(newc); | |
line(0, yBG, width, yBG); | |
} | |
let cur_x = map(cur_frac, 0, 1, 0, width) //- half_width; | |
//all of my colours to use when I call a leaf function! | |
var longLeafFill1 = color(167, 212, 138); | |
var longLeafFill2 = color(134, 181, 103); | |
var longLeafStroke1 = color(108, 148, 99); | |
var longLeafFill3 = color(92, 145, 84); //saturated dark green | |
var longLeafStroke3 = color(92, 145, 84); | |
var longLeafFill4 = color(49, 94, 89); //darkest green | |
var longLeafStroke4 = color(49, 94, 89); | |
var longLeafFill5 = color(83, 144, 111); | |
var fatLeafFill = color(200, 231, 128); | |
var darkBlueGreen = color(71, 125, 99); | |
var teal = color(87, 141, 122); | |
var slate_green = color(91, 151, 110); | |
var dull_greren = color(109, 171, 94); | |
var muted_green = color(102, 159, 90); | |
var pale_olive_green = color(160, 213, 148); | |
var pale_olive = color(173, 202, 151); | |
var flower_fill = color(230, 251, 150); | |
var medium_spring_bud = color(191, 225, 129); | |
var oxley = color(105, 159, 132); | |
var limed_spruce = color(57, 69, 83); | |
var skinnyLongLeafCol = color(103, 142, 149); | |
var secondFlowerLeaf = color(115, 166, 137); | |
let randRoat = getNoiseValue(0, 0, cur_frac, "randRoat", -.3, .3, .1); //random jitter on the long leaf using noise | |
push(); //fat leaf right at the back on the right | |
translate(width / 1.23, height / 1); | |
drawFatLeaf(70, 0.6, -1, darkBlueGreen); | |
pop(); | |
push(); //fat leaf right at the back on the left | |
translate(width * .16, height * 1.07); | |
drawFatLeaf(-43, .8, -1, darkBlueGreen); | |
pop(); | |
//leaves on the right behind the flowers | |
push(); | |
translate(width / 1.4, height * 1.04); | |
drawLongLeaf(0, 0, 1.2, 20, longLeafFill4, longLeafStroke4); | |
pop(); | |
push(); | |
translate(width / 1.28, height * 1.1); | |
drawLongLeaf(0, 0, 1.05, 23, longLeafFill3, longLeafStroke3); | |
pop(); | |
push(); | |
translate(width / 1.45, height * 1.02); | |
drawLongLeaf(0, 0, 1.05, 0, longLeafFill5, longLeafFill5); | |
pop(); | |
//setting up the flowers to move back and forth | |
let going_right = true; | |
let amount_across = 0; | |
if (cur_frac < 0.5) { | |
going_right = true; | |
amount_across = cur_frac * 2; | |
} else { | |
going_right = false; | |
amount_across = (cur_frac - 0.5) * 2; | |
} | |
const ease_amount_across = ease.doubleQuadraticSigmoid(amount_across); | |
const ease_amount_across_fatLeaf = ease.circularFillet(amount_across); | |
const ease_amount_acrosslongLeaf = ease.normalizedLogitSigmoid(amount_across); | |
if (going_right) { | |
flowerSway = map(ease_amount_across, 0, 0.5, addXSingle * 0, addXSingle * 2) | |
flower2Sway = map(ease_amount_across, 0, 0.5, addXSingle * 0, addXSingle * .9) | |
fLSway = map(ease_amount_across_fatLeaf, 0, 0.5, addXSingle * 0, addXSingle * 0.3) | |
longLeafSway = map(ease_amount_acrosslongLeaf, 0, 0.5, addXSingle * 0, addXSingle * 0.2) | |
} else { | |
flowerSway = map(ease_amount_across, 0.5, 1, addXSingle * 2, addXSingle * 0) | |
flower2Sway = map(ease_amount_across, 0.5, 1, addXSingle * .9, addXSingle * 0) | |
fLSway = map(ease_amount_across_fatLeaf, 0.5, 1, addXSingle * 0.3, addXSingle * 0) | |
longLeafSway = map(ease_amount_acrosslongLeaf, 0.5, 1, addXSingle * 0.2, addXSingle * 0) | |
} | |
//drawing long leaf with the jitter | |
push(); | |
translate(width / 1.6, height / 1.02); | |
rotate(0); | |
drawLongLeaf(0, 0, 1.2, (randRoat / 3) + longLeafSway, longLeafFill1, longLeafStroke1); | |
pop(); | |
//setting up the flowers | |
var flower1StartX = width / 1.2 + flowerSway; //flower sway makes the flower bud AND the stalk move | |
var flower1StartY = height / 4.9; | |
var flower2StartX = width *.92 + flower2Sway; | |
var flower2StartY = height *.33974; | |
strokeWeight(width / 960 * 2); | |
stroke(93, 107, 54); | |
noFill(); | |
beginShape(); //flower 1 stalk | |
curveVertex(flower1StartX, flower1StartY); | |
curveVertex(flower1StartX, flower1StartY); | |
curveVertex(width / 1.2, height *.6); | |
curveVertex(width / 1.3, height*.89); | |
curveVertex(width / 1.3, height*.89); | |
endShape(); | |
//draw the actual flower (biggest one) | |
push(); | |
translate(flower1StartX, flower1StartY); | |
rotate(-25); | |
scale(1.4); | |
drawFlower(0, 0, flower_fill); //bigger top flower | |
pop(); | |
//drawing leaves on the biggest flowerr | |
push(); | |
translate(width / 1.196 + (flowerSway / 2), height / 2.3); //top down | |
drawSkinnyLongLeaf(-40, .9, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.194 + (flowerSway / 2.2), height / 2.15); | |
drawSkinnyLongLeaf(35, 1, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.196 + (flowerSway / 2.6), height / 2.05); | |
drawSkinnyLongLeaf(-60, 1.1, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.194 + (flowerSway / 2.8), height / 1.9); | |
drawSkinnyLongLeaf(55, 1.08, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.196 + (flowerSway / 2.9), height / 1.7); | |
drawSkinnyLongLeaf(-57, 1.25, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.194 + (flowerSway / 2.8), height / 1.65); | |
drawSkinnyLongLeaf(59, 1.25, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.215, height / 1.55); | |
drawSkinnyLongLeaf(-50, 1.3, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.223, height / 1.4); | |
drawSkinnyLongLeaf(-69, 1.5, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.27, height / 1.21); | |
drawSkinnyLongLeaf(-62, 1.5, skinnyLongLeafCol); | |
pop(); | |
push(); ////////leaves in the middle of the screen/////// | |
translate(width * .49, height * 1.19); | |
drawShortWavyLeaf(longLeafFill3, longLeafFill3, 13, 3); | |
pop(); | |
push(); | |
translate(width * .604, height * 1.047); | |
drawShortWavyLeaf(teal, oxley, -17, 2.7); | |
pop(); | |
push(); | |
translate(width * .2, height * 1.1); | |
drawShortWavyLeaf(longLeafFill2, longLeafFill2, -20, 3); | |
pop(); | |
//second flower stalk | |
strokeWeight(width / 960 * 2); | |
stroke(93, 107, 54); | |
noFill(); | |
beginShape(); //flower 2 stalk | |
curveVertex(flower2StartX, flower2StartY); | |
curveVertex(flower2StartX, flower2StartY); | |
curveVertex(width *.896, height *.52); | |
curveVertex(width / 1.3, height *.89); | |
curveVertex(width / 1.3, height *.89); | |
endShape(); | |
//second flower | |
push(); | |
translate(flower2StartX, flower2StartY); | |
rotate(25); | |
scale(1); | |
drawFlower(0, 0, flower_fill); //lower smaller flower | |
pop(); | |
//ssecond flower leaves | |
push(); | |
translate(width*.908 + (flower2Sway / 4), height / 2.18); | |
drawSkinnyLongLeaf(-28, .84, secondFlowerLeaf); | |
pop(); | |
push(); | |
translate(width *.906 + (flower2Sway / 2.8), height / 2.1); | |
drawSkinnyLongLeaf(48, .87, secondFlowerLeaf); | |
pop(); | |
push(); | |
translate(width / 1.13, height / 1.84); | |
drawSkinnyLongLeaf(-21, 1, secondFlowerLeaf); | |
pop(); | |
push(); | |
translate(width / 1.15, height / 1.69); | |
drawSkinnyLongLeaf(70, 1.07, secondFlowerLeaf); | |
pop(); | |
push(); | |
translate(width / 1.17, height / 1.55); | |
drawSkinnyLongLeaf(-33, 1.2, secondFlowerLeaf); | |
pop(); | |
push(); | |
translate(width / 1.2, height / 1.4); | |
drawSkinnyLongLeaf(63, 1.3, secondFlowerLeaf); | |
pop(); | |
//draw the two fat leaves in front of the flowers on the RIGHT | |
push(); | |
translate(width / 1.29, height * 1.03); | |
drawFatLeaf(45 + fLSway, 0.9, 1, fatLeafFill); | |
pop(); | |
push(); | |
translate(width / 1.43, height * 1.1); | |
drawFatLeaf(-15 + (fLSway * 4), 0.9, -1, fatLeafFill); | |
pop(); | |
const ease_amount_across_fatLeaf2 = ease.circularArcThroughAPoint(amount_across); | |
const ease_amount_up_down = ease.doubleQuadraticSigmoid(amount_across); | |
if (going_right) { | |
flowerSway3 = map(ease_amount_across, 0, 0.5, addXSingle * .5, addXSingle * 0); | |
fatLeaf2Sway = map(ease_amount_across_fatLeaf2, 0, 0.5, addXSingle * 0.6, addXSingle * 0); | |
branchupdown = map(ease_amount_up_down, 0, 0.5, addXSingle * 0.6, addXSingle * 0) | |
} else { | |
flowerSway3 = map(ease_amount_across, 0, 0.5, addXSingle * 0, addXSingle * .5); | |
fatLeaf2Sway = map(ease_amount_across_fatLeaf2, 0, 0.5, addXSingle * 0, addXSingle * 0.6); | |
branchupdown = map(ease_amount_up_down, 0.5, 1, addXSingle * 0, addXSingle * 0.6); | |
} | |
/////////behind shrt wavy leaves | |
push(); | |
translate(width * .43, height * 1.04); | |
drawLongLeaf(0, 0, 1.18, -23, limed_spruce, limed_spruce); | |
pop(); | |
push(); | |
translate(width * .42, height * 1.03); | |
drawLongLeaf(0, 0, 1.1, 1, longLeafFill4, longLeafStroke4); | |
pop(); | |
push(); | |
translate(width * .30, height * 1.03); | |
drawLongLeaf(0, 0, 1.2, -30 + longLeafSway / 2, longLeafFill4, longLeafStroke4); | |
pop(); | |
push(); | |
translate(width * .37, height); | |
drawLongLeaf(0, 0, 1, -30, longLeafFill2, longLeafStroke1); | |
pop(); | |
//fly on the left | |
let flyScale = 0.55; | |
let flyX; | |
//setting it up off-beat, so it grows starting at 0.3 cur frac and loops back to then (instead of 0-1) | |
if (cur_frac >= 0.3) { | |
flyScale = map(cur_frac, 0.3, 0.5, 0, 0.55); | |
} | |
if (cur_frac >= 0.5) { | |
flyScale = map(cur_frac, 0.5, 1, 0.55, 0.55); | |
} | |
if (cur_frac <= 0.3) { | |
flyScale = map(cur_frac, 0, 0.3, 0.55, 0); | |
} | |
if (cur_frac <= 0.3) { //this is for the flies movement | |
flyX = map(cur_frac, 0, 0.3, addXSingle * 200, addXSingle * 250) | |
} else if (cur_frac >= 0.3) { | |
flyX = map(cur_frac, 0.3, 1, addXSingle * 100, addXSingle * 200) | |
} | |
push(); //draw the actual fly | |
translate(width / 1.9 + flyX, height / 1.5); //centre of the screen | |
drawFly(0, 0, cur_frac, flyScale,1); | |
pop(); | |
//////////short wavy leaves stalk//////////// | |
stroke(52, 93, 88); | |
strokeWeight(addXSingle * 3); | |
noFill(); | |
var endOfBranchX = width * .12; | |
var endOfBranchY = height / 3.9; | |
var strokeColWavy1 = color(42, 86, 79); | |
//main branch (going left) | |
beginShape(); | |
curveVertex(width / 3, height / 1.7); | |
curveVertex(width / 3, height / 1.7); | |
curveVertex(width / 3.8, height / 4.4); | |
curveVertex(endOfBranchX, endOfBranchY + branchupdown); | |
curveVertex(endOfBranchX, endOfBranchY + branchupdown); | |
endShape(); | |
//second branch going right | |
beginShape(); | |
curveVertex(width * .31, height * .419); | |
curveVertex(width * .31, height * .419); | |
curveVertex(width * .34, height * .16); | |
curveVertex(width * .41, height * .1); | |
curveVertex(width * .41, height * .1); | |
endShape(); | |
////first brranch leaves | |
push(); | |
translate(endOfBranchX, endOfBranchY + branchupdown); | |
drawShortWavyLeaf(teal, strokeColWavy1, -80 - (branchupdown / 1.5), 1); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 6, endOfBranchY + addYSingle * -1 + branchupdown); | |
drawShortWavyLeaf(slate_green, slate_green, -120 + (branchupdown / 1.3), .8); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 20, endOfBranchY + addYSingle * -3 + (branchupdown / 1.2)); | |
drawShortWavyLeaf(teal, strokeColWavy1, -170, .8); | |
pop(); | |
push(); //////behind the other leaves | |
translate(endOfBranchX + addXSingle * 167, endOfBranchY + addYSingle * 40); | |
drawShortWavyLeaf(slate_green, slate_green, -80, 0.93); | |
pop(); | |
push(); //////behind the other leaves on other branch | |
translate(endOfBranchX + addXSingle * 157, endOfBranchY + addYSingle * 10); | |
drawShortWavyLeaf(slate_green, slate_green, 19, 0.8); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 100, endOfBranchY + addYSingle * -25 + (branchupdown / 1.9)); | |
drawShortWavyLeaf(teal, strokeColWavy1, -20 - (branchupdown / 2), .75); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 48, endOfBranchY + addYSingle * -20 + (branchupdown / 1.5)); | |
drawShortWavyLeaf(teal, strokeColWavy1, -50 + (branchupdown / 1.8), .82); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 68, endOfBranchY + addYSingle * -20 + (branchupdown / 1.6)); | |
drawShortWavyLeaf(teal, strokeColWavy1, -200 + (branchupdown / 2), .93); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 118, endOfBranchY + addYSingle * -25); | |
drawShortWavyLeaf(teal, strokeColWavy1, -180, .8); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 180, endOfBranchY + addYSingle * 90); | |
drawShortWavyLeaf(teal, strokeColWavy1, -100, .94); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 190, endOfBranchY + addYSingle * 120); | |
drawShortWavyLeaf(teal, strokeColWavy1, 40, 1.06); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 196, endOfBranchY + addYSingle * 150); | |
drawShortWavyLeaf(teal, strokeColWavy1, -87, 1.1); | |
pop(); | |
///////////second branch (going right) | |
push(); | |
translate(endOfBranchX + addXSingle * 187, endOfBranchY + addYSingle * 80); | |
drawShortWavyLeaf(teal, strokeColWavy1, 60, .9); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 191, endOfBranchY + addYSingle * 40); | |
drawShortWavyLeaf(teal, strokeColWavy1, 50, 1); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 201, endOfBranchY + addYSingle * -20); | |
drawShortWavyLeaf(teal, strokeColWavy1, 70, .8); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 191, endOfBranchY + addYSingle * 20); | |
drawShortWavyLeaf(teal, strokeColWavy1, -35, .86); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 207, endOfBranchY + addYSingle * -45); | |
drawShortWavyLeaf(teal, strokeColWavy1, -45, .8); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 230, endOfBranchY + addYSingle * -71); | |
drawShortWavyLeaf(teal, strokeColWavy1, -25, .70); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 260, endOfBranchY + addYSingle * -81); | |
drawShortWavyLeaf(teal, strokeColWavy1, 5, .55); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 280, endOfBranchY + addYSingle * -84); | |
drawShortWavyLeaf(teal, strokeColWavy1, 28, .5); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 280, endOfBranchY + addYSingle * -84); | |
drawShortWavyLeaf(teal, strokeColWavy1, 80, .5); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 271, endOfBranchY + addYSingle * -80); | |
drawShortWavyLeaf(teal, strokeColWavy1, -250, .5); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 271, endOfBranchY + addYSingle * -80); | |
drawShortWavyLeaf(teal, strokeColWavy1, -250, .5); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 238, endOfBranchY + addYSingle * -73); | |
drawShortWavyLeaf(teal, strokeColWavy1, -250, .59); | |
pop(); | |
//fly 1 (LEFT faster fly)/// | |
//this fly is moving 0-1 | |
flyScale = 0.6; | |
if (cur_frac <= 0.3) { | |
flyScale = map(cur_frac, 0, 0.3, 0, 0.6); | |
} else if (cur_frac >= 0.7) { | |
flyScale = map(cur_frac, 0.7, 1, 0.6, 0); | |
} | |
flyX = map(cur_frac, 0, 1, addXSingle * 300, addXSingle * 0) | |
push(); | |
translate(width / 5 + flyX, height / 3.4); | |
drawFly(0, 0, cur_frac, flyScale,-1); | |
pop(); | |
//////in front of branches on the left | |
push(); | |
translate(width * .45, height * 1.1); | |
drawLongLeaf(0, 0, 1, -24, dull_greren, muted_green); | |
pop(); | |
//short leaves in front on the LEFT ////////////////////////// | |
push(); | |
translate(width * .41, height * 1.1); | |
drawFatLeaf(-20 + (fatLeaf2Sway / -10), 1, -1, pale_olive); | |
pop(); | |
push(); | |
translate(width * .43, height * 1.1); | |
drawFatLeaf(10 + (fatLeaf2Sway / 1.2), 0.9, 1, fatLeafFill); | |
pop(); | |
push(); | |
translate(width * .37, height * 1.1); | |
drawFatLeaf(-56 + (fatLeaf2Sway / 1.8), 0.9, -1, fatLeafFill); | |
pop(); | |
////////bottom left flowers | |
let backFloStartX = width * .19 + (flowerSway3 / 2.6); | |
let backFloStartY = height * .75; | |
//stalk 4 back flower | |
strokeWeight(addXSingle * 1.5); | |
stroke(longLeafFill3); | |
noFill(); | |
beginShape(); | |
curveVertex(backFloStartX - addXSingle * 16, backFloStartY); | |
curveVertex(backFloStartX - addXSingle * 16, backFloStartY); | |
curveVertex(width * .15, height * .88); | |
curveVertex(width * .16, height); | |
curveVertex(width * .16, height); | |
endShape(); | |
push(); | |
translate(backFloStartX, backFloStartY); | |
rotate(30); | |
scale(1.3); | |
drawFlower(0, 0, medium_spring_bud); | |
pop(); | |
let bigFloStartX = width * .1 + flower2Sway; | |
let bigFloStartY = height * .8; | |
beginShape(); | |
curveVertex(bigFloStartX, bigFloStartY); | |
curveVertex(bigFloStartX, bigFloStartY); | |
curveVertex(width * .135, height * .91); | |
curveVertex(width * .15, height); | |
curveVertex(width * .15, height); | |
endShape(); | |
push(); | |
translate(bigFloStartX, height * .8); | |
rotate(-40); | |
scale(1.3); | |
drawFlower(0, 0, flower_fill); | |
pop(); | |
var smolFloX = width * .2 + flowerSway / 4; | |
var smolFloY = height * .85; | |
beginShape(); | |
curveVertex(smolFloX, smolFloY); | |
curveVertex(smolFloX, smolFloY); | |
curveVertex(width * .17, height * .95); | |
curveVertex(width * .172, height); | |
curveVertex(width * .172, height); | |
endShape(); | |
push(); | |
translate(smolFloX, smolFloY); | |
rotate(10); | |
scale(1); | |
drawFlower(0, 0, flower_fill); | |
pop(); | |
} | |
function drawShortWavyLeaf(fillCol, strokeColWavy, shortWavyRot, shortWavyScale) { | |
//drawn on the upper left | |
var addXSingle = width / 960; | |
var addYSingle = height / 540; | |
var shortWavyLeafX = 0; | |
var shortWavyLeafY = 0; | |
// | |
fill(fillCol); | |
push(); | |
scale(shortWavyScale); | |
rotate(shortWavyRot); | |
noStroke(); | |
beginShape(); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX + addXSingle * -9, shortWavyLeafY + addYSingle * -10); | |
curveVertex(shortWavyLeafX + addXSingle * -11, shortWavyLeafY + addYSingle * -18); | |
curveVertex(shortWavyLeafX + addXSingle * -8, shortWavyLeafY + addYSingle * -28); | |
curveVertex(shortWavyLeafX + addXSingle * -10, shortWavyLeafY + addYSingle * -40); | |
curveVertex(shortWavyLeafX + addXSingle * -12, shortWavyLeafY + addYSingle * -47); | |
curveVertex(shortWavyLeafX + addXSingle * -13, shortWavyLeafY + addYSingle * -55); | |
curveVertex(shortWavyLeafX + addXSingle * -11.5, shortWavyLeafY + addYSingle * -60); | |
curveVertex(shortWavyLeafX + addXSingle * -8, shortWavyLeafY + addYSingle * -65); | |
curveVertex(shortWavyLeafX + addXSingle * -9.5, shortWavyLeafY + addYSingle * -73); | |
curveVertex(shortWavyLeafX + addXSingle * -10, shortWavyLeafY + addYSingle * -80); | |
curveVertex(shortWavyLeafX + addXSingle * -8, shortWavyLeafY + addYSingle * -83); | |
curveVertex(shortWavyLeafX + addXSingle * -3, shortWavyLeafY + addYSingle * -88); | |
curveVertex(shortWavyLeafX, shortWavyLeafY + addYSingle * -95); | |
curveVertex(shortWavyLeafX + addXSingle * 2, shortWavyLeafY + addYSingle * -100); | |
curveVertex(shortWavyLeafX + addXSingle * 5, shortWavyLeafY + addYSingle * -92); | |
curveVertex(shortWavyLeafX + addXSingle * 10, shortWavyLeafY + addYSingle * -86); | |
curveVertex(shortWavyLeafX + addXSingle * 16, shortWavyLeafY + addYSingle * -80); | |
curveVertex(shortWavyLeafX + addXSingle * 17.5, shortWavyLeafY + addYSingle * -73); | |
curveVertex(shortWavyLeafX + addXSingle * 16.5, shortWavyLeafY + addYSingle * -67); | |
curveVertex(shortWavyLeafX + addXSingle * 15, shortWavyLeafY + addYSingle * -59); | |
curveVertex(shortWavyLeafX + addXSingle * 18, shortWavyLeafY + addYSingle * -48); | |
curveVertex(shortWavyLeafX + addXSingle * 16, shortWavyLeafY + addYSingle * -40); | |
curveVertex(shortWavyLeafX + addXSingle * 13, shortWavyLeafY + addYSingle * -33); | |
curveVertex(shortWavyLeafX + addXSingle * 15, shortWavyLeafY + addYSingle * -20); | |
curveVertex(shortWavyLeafX + addXSingle * 13, shortWavyLeafY + addYSingle * -13); | |
curveVertex(shortWavyLeafX + addXSingle * 5.5, shortWavyLeafY + addYSingle * -6); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
endShape(); | |
fill(strokeColWavy); | |
stroke(strokeColWavy); | |
strokeWeight(addXSingle); | |
beginShape(); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX + addXSingle * -2, shortWavyLeafY + addYSingle * -17); | |
curveVertex(shortWavyLeafX + addXSingle * 0, shortWavyLeafY + addYSingle * -28); | |
curveVertex(shortWavyLeafX + addXSingle * -1.3, shortWavyLeafY + addYSingle * -39); | |
curveVertex(shortWavyLeafX + addXSingle * .4, shortWavyLeafY + addYSingle * -50); | |
curveVertex(shortWavyLeafX + addXSingle * -.7, shortWavyLeafY + addYSingle * -60); | |
curveVertex(shortWavyLeafX + addXSingle * 1.7, shortWavyLeafY + addYSingle * -68); | |
curveVertex(shortWavyLeafX + addXSingle * 1, shortWavyLeafY + addYSingle * -80); | |
curveVertex(shortWavyLeafX + addXSingle * 1.6, shortWavyLeafY + addYSingle * -87); | |
curveVertex(shortWavyLeafX + addXSingle * 3, shortWavyLeafY + addYSingle * -80); | |
curveVertex(shortWavyLeafX + addXSingle * 4.5, shortWavyLeafY + addYSingle * -68); | |
curveVertex(shortWavyLeafX + addXSingle * 3.5, shortWavyLeafY + addYSingle * -60); | |
curveVertex(shortWavyLeafX + addXSingle * 5, shortWavyLeafY + addYSingle * -50); | |
curveVertex(shortWavyLeafX + addXSingle * 2.3, shortWavyLeafY + addYSingle * -39); | |
curveVertex(shortWavyLeafX + addXSingle * 3.5, shortWavyLeafY + addYSingle * -28); | |
curveVertex(shortWavyLeafX + addXSingle * 1, shortWavyLeafY + addYSingle * -17); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
endShape(); | |
pop(); | |
} | |
function drawFatLeaf(fatLeafRot, fatLeafYScal, negative, fatLeafFill) { | |
//drawn at the front of the screen normally, bright | |
push(); | |
var addXSingle = width / 960 * negative; | |
var addYSingle = height / 540; | |
var fatLeafX = 0; | |
var fatLeafY = 0; | |
fill(fatLeafFill); | |
noStroke(); | |
rotate(fatLeafRot); | |
scale(fatLeafYScal) | |
beginShape(); | |
curveVertex(fatLeafY, fatLeafY); | |
curveVertex(fatLeafY, fatLeafY); | |
curveVertex(fatLeafX + addXSingle * -70, fatLeafY + addYSingle * -50); | |
curveVertex(fatLeafX + addXSingle * -80, fatLeafY + addYSingle * -90); | |
curveVertex(fatLeafX + addXSingle * -70, fatLeafY + addYSingle * -120); | |
curveVertex(fatLeafX + addXSingle * -60, fatLeafY + addYSingle * -140); | |
curveVertex(fatLeafX + addXSingle * -50, fatLeafY + addYSingle * -170); | |
curveVertex(fatLeafX + addXSingle * -57, fatLeafY + addYSingle * -200); | |
curveVertex(fatLeafX + addXSingle * -45, fatLeafY + addYSingle * -230); | |
curveVertex(fatLeafX + addXSingle * -4, fatLeafY + addYSingle * -260); | |
curveVertex(fatLeafX, fatLeafY + addYSingle * -262); | |
curveVertex(fatLeafX + addXSingle * 4, fatLeafY + addYSingle * -260); | |
curveVertex(fatLeafX + addXSingle * 45, fatLeafY + addYSingle * -230); | |
curveVertex(fatLeafX + addXSingle * 54, fatLeafY + addYSingle * -210); | |
curveVertex(fatLeafX + addXSingle * 45, fatLeafY + addYSingle * -190); | |
curveVertex(fatLeafX + addXSingle * 50, fatLeafY + addYSingle * -170); | |
curveVertex(fatLeafX + addXSingle * 65, fatLeafY + addYSingle * -150); | |
curveVertex(fatLeafX + addXSingle * 70, fatLeafY + addYSingle * -135); | |
curveVertex(fatLeafX + addXSingle * 60, fatLeafY + addYSingle * -105); | |
curveVertex(fatLeafX + addXSingle * 70, fatLeafY + addYSingle * -85); | |
curveVertex(fatLeafX + addXSingle * 78, fatLeafY + addYSingle * -65); | |
curveVertex(fatLeafX + addXSingle * 65, fatLeafY + addYSingle * -40); | |
curveVertex(fatLeafX, fatLeafY); | |
curveVertex(fatLeafX, fatLeafY); | |
endShape(); | |
stroke(118, 154, 87); | |
strokeWeight(height / 520 * 3); | |
noFill(); | |
beginShape(); | |
curveVertex(fatLeafX, fatLeafY); | |
curveVertex(fatLeafX, fatLeafY); | |
curveVertex(fatLeafX + addXSingle * -1, fatLeafY + addYSingle * -35); | |
curveVertex(fatLeafX + addXSingle * 4, fatLeafY + addYSingle * -75); | |
curveVertex(fatLeafX + addXSingle * -5, fatLeafY + addYSingle * -115); | |
curveVertex(fatLeafX + addXSingle * 3, fatLeafY + addYSingle * -150); | |
curveVertex(fatLeafX + addXSingle * -4, fatLeafY + addYSingle * -175); | |
curveVertex(fatLeafX + addXSingle * 3, fatLeafY + addYSingle * -195); | |
curveVertex(fatLeafX + addXSingle * -2, fatLeafY + addYSingle * -210); | |
curveVertex(fatLeafX + addXSingle * 1, fatLeafY + addYSingle * -225); | |
//curveVertex(fatLeafX+addXSingle*1,fatLeafY+addYSingle*-225); | |
curveVertex(fatLeafX + addXSingle * -1, fatLeafY + addYSingle * -240); | |
curveVertex(fatLeafX + addXSingle, fatLeafY + addYSingle * -250); | |
curveVertex(fatLeafX + addXSingle, fatLeafY + addYSingle * -250); | |
endShape(); | |
pop(); | |
} | |
function drawSkinnyLongLeaf(skinnyLongLeafRot, skinnyLongLeafScale, skinnyLongLeafCol) { | |
//leaves on the rose branches | |
var addXSingle = width / 960; | |
var addYSingle = height / 540; | |
var skinnyLongLeafX = 0; | |
var skinnyLongLeafY = 0; | |
push(); | |
rotate(skinnyLongLeafRot); | |
scale(skinnyLongLeafScale); | |
fill(skinnyLongLeafCol); | |
noStroke(); | |
beginShape(); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY); | |
curveVertex(skinnyLongLeafX - addXSingle * 7, skinnyLongLeafY - addYSingle * 51); | |
curveVertex(skinnyLongLeafX - addXSingle * 1, skinnyLongLeafY - addYSingle * 100); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY - addYSingle * 105); | |
curveVertex(skinnyLongLeafX + addXSingle * 1, skinnyLongLeafY - addYSingle * 100); | |
curveVertex(skinnyLongLeafX + addXSingle * 8, skinnyLongLeafY - addYSingle * 49); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY); | |
endShape(); | |
stroke(45, 78, 109); | |
noFill(); | |
strokeWeight(addXSingle * .7); | |
beginShape(); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY - addYSingle * 21); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY - addYSingle * 21); | |
curveVertex(skinnyLongLeafX + addXSingle * 1, skinnyLongLeafY - addYSingle * 49); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY - addYSingle * 80); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY - addYSingle * 80); | |
endShape(); | |
pop(); | |
} | |
function drawFly(flyX, flyY, cur_frac, flyScale,negative) { | |
let wingMove; //set up animated wing variable | |
var addXSingle = width / 960 * negative; | |
var addYSingle = height / 540; | |
var flyBodyCol = color(74, 77, 76); | |
var flyWingCol = color(166, 237, 234, 180); | |
//setting it up so the flies wings can flap more tha once a second | |
var wingFlapNum = 360 * 5 //how many times the wing flaps per second , multiples of 360 | |
var flapAmmount = round(sin(map(cur_frac, 0, 1, 1, wingFlapNum))); | |
var flapAmmountSmooth = sin(map(cur_frac, 0, 1, 1, wingFlapNum)); | |
if (flapAmmount == 0) { | |
wingMove = map(flapAmmountSmooth, 0, -1, 0, 10); | |
} else if (flapAmmount == -1) { | |
wingMove = map(flapAmmountSmooth, -1, 0, 10, 0); | |
} | |
noStroke(); | |
fill(flyBodyCol); | |
scale(flyScale); | |
ellipse(flyX + addXSingle * 15, flyY, addXSingle * 17, addYSingle * 17); //head | |
ellipse(flyX, flyY, addXSingle * 30, addYSingle * 22); //body | |
rect(flyX + addXSingle * 21, flyY + addYSingle * 1, addXSingle * 8, -addYSingle * 4); //snout | |
ellipse(flyX + addXSingle * 30, flyY - addYSingle * 1, addXSingle * 4, addYSingle * 7) //end of snout | |
//wings! | |
fill(flyWingCol); | |
push(); | |
rotate(35*negative + wingMove); | |
ellipse(flyX - addXSingle * 6, flyY - addYSingle * 1, addXSingle * 20, addYSingle * 10); | |
rotate(40*negative + wingMove); | |
ellipse(flyX - addXSingle * 11, flyY - addYSingle * 3, addXSingle * 30, addYSingle * 15); | |
pop(); | |
} | |
function drawFlower(flowerBaseX, flowerBaseY, flowerFill) { | |
noStroke(); | |
var addXSingle = width / 960; //1 | |
var addYSingle = height / 540; | |
fill(flowerFill); | |
arc(flowerBaseX, flowerBaseY, addXSingle * 80, addYSingle * 70, 0, 180); | |
stroke(144, 179, 118); | |
strokeWeight(addXSingle); | |
beginShape(); | |
curveVertex(flowerBaseX - addXSingle * 35, flowerBaseY); | |
curveVertex(flowerBaseX - addXSingle * 35, flowerBaseY); | |
curveVertex(flowerBaseX - addXSingle * 28, flowerBaseY - addYSingle * 18); | |
curveVertex(flowerBaseX - addXSingle * 24, flowerBaseY - addYSingle * 20); | |
curveVertex(flowerBaseX - addXSingle * 21, flowerBaseY - addYSingle * 19); | |
curveVertex(flowerBaseX - addXSingle * 17, flowerBaseY - addYSingle * 23); | |
curveVertex(flowerBaseX - addXSingle * 12, flowerBaseY - addYSingle * 25); | |
curveVertex(flowerBaseX - addXSingle * 6, flowerBaseY - addYSingle * 22); | |
curveVertex(flowerBaseX - addXSingle * 1, flowerBaseY - addYSingle * 27); | |
curveVertex(flowerBaseX + addXSingle * 2, flowerBaseY - addYSingle * 28); | |
curveVertex(flowerBaseX + addXSingle * 5, flowerBaseY - addYSingle * 27); | |
curveVertex(flowerBaseX + addXSingle * 10, flowerBaseY - addYSingle * 21); | |
curveVertex(flowerBaseX + addXSingle * 17, flowerBaseY - addYSingle * 23); | |
curveVertex(flowerBaseX + addXSingle * 20, flowerBaseY - addYSingle * 20); | |
curveVertex(flowerBaseX + addXSingle * 26, flowerBaseY - addYSingle * 21); | |
curveVertex(flowerBaseX + addXSingle * 30, flowerBaseY - addYSingle * 18); | |
curveVertex(flowerBaseX + addXSingle * 34, flowerBaseY); | |
curveVertex(flowerBaseX + addXSingle * 34, flowerBaseY); | |
endShape(); | |
beginShape(); | |
curveVertex(flowerBaseX - addXSingle * 4, flowerBaseY); | |
curveVertex(flowerBaseX - addXSingle * 4, flowerBaseY); | |
curveVertex(flowerBaseX + addXSingle * 3, flowerBaseY - addYSingle * 12); | |
curveVertex(flowerBaseX + addXSingle * 9, flowerBaseY - addYSingle * 14); | |
curveVertex(flowerBaseX + addXSingle * 15, flowerBaseY - addYSingle * 10); | |
curveVertex(flowerBaseX + addXSingle * 20, flowerBaseY - addYSingle * 14); | |
curveVertex(flowerBaseX + addXSingle * 24, flowerBaseY - addYSingle * 15); | |
curveVertex(flowerBaseX + addXSingle * 29, flowerBaseY - addYSingle * 11); | |
curveVertex(flowerBaseX + addXSingle * 34, flowerBaseY - addYSingle * 15); | |
curveVertex(flowerBaseX + addXSingle * 38, flowerBaseY - addYSingle * 11); | |
curveVertex(flowerBaseX + addXSingle * 40, flowerBaseY); | |
curveVertex(flowerBaseX + addXSingle * 40, flowerBaseY); | |
endShape(); | |
beginShape(); | |
curveVertex(flowerBaseX - addXSingle * 40, flowerBaseY); | |
curveVertex(flowerBaseX - addXSingle * 40, flowerBaseY); | |
curveVertex(flowerBaseX - addXSingle * 38, flowerBaseY - addYSingle * 10); | |
curveVertex(flowerBaseX - addXSingle * 32, flowerBaseY - addYSingle * 14); | |
curveVertex(flowerBaseX - addXSingle * 27, flowerBaseY - addYSingle * 10); | |
curveVertex(flowerBaseX - addXSingle * 20, flowerBaseY - addYSingle * 16); | |
curveVertex(flowerBaseX - addXSingle * 14, flowerBaseY - addYSingle * 12); | |
curveVertex(flowerBaseX - addXSingle * 10, flowerBaseY - addYSingle * 15); | |
curveVertex(flowerBaseX - addXSingle * 4, flowerBaseY - addYSingle * 11); | |
curveVertex(flowerBaseX - addXSingle * 2, flowerBaseY - addYSingle * 6); | |
curveVertex(flowerBaseX + addXSingle * 1, flowerBaseY - addYSingle * 7.5) | |
curveVertex(flowerBaseX + addXSingle * 6, flowerBaseY - addYSingle * 8); | |
curveVertex(flowerBaseX + addXSingle * 9, flowerBaseY - addYSingle * 6) | |
curveVertex(flowerBaseX + addXSingle * 11, flowerBaseY - addYSingle * 1); | |
curveVertex(flowerBaseX + addXSingle * 17, flowerBaseY - addYSingle * 2); | |
curveVertex(flowerBaseX + addXSingle * 22, flowerBaseY + addYSingle * 3); | |
curveVertex(flowerBaseX + addXSingle * 28, flowerBaseY + addYSingle * 3); | |
curveVertex(flowerBaseX + addXSingle * 36, flowerBaseY + addYSingle * 11); | |
curveVertex(flowerBaseX + addXSingle * 36, flowerBaseY + addYSingle * 11); | |
endShape(); | |
} | |
function drawLongLeaf(longLeafBaseX, longLeafBaseY, longLeafScale, longLLeafRotation, longLeafFill, longLeafStrokeCol) { | |
//kelp looking leaf | |
noStroke(); | |
angleMode(DEGREES); | |
push(); | |
rotate(longLLeafRotation); | |
var longLeafAddXSingle = width / 960; //1 | |
var longLeafAddYSingle = height / 540; // 1 | |
scale(longLeafScale); | |
fill(longLeafFill); | |
beginShape(); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 40), longLeafBaseY - longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 40), longLeafBaseY - longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 50), longLeafBaseY - longLeafAddYSingle * 45); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 36), longLeafBaseY - longLeafAddYSingle * 80); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 41), longLeafBaseY - longLeafAddYSingle * 125); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 27), longLeafBaseY - longLeafAddYSingle * 156); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 31), longLeafBaseY - longLeafAddYSingle * 200); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 18), longLeafBaseY - longLeafAddYSingle * 235); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 22), longLeafBaseY - longLeafAddYSingle * 270); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 13), longLeafBaseY - longLeafAddYSingle * 295); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 11), longLeafBaseY - longLeafAddYSingle * 325); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 2), longLeafBaseY - longLeafAddYSingle * 340); | |
curveVertex(longLeafBaseX, longLeafBaseY - longLeafAddYSingle * 355); | |
vertex(longLeafBaseX + (longLeafAddXSingle * 14), longLeafBaseY - longLeafAddYSingle * 379); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 26), longLeafBaseY - longLeafAddYSingle * 365); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 24), longLeafBaseY - longLeafAddYSingle * 345); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 28), longLeafBaseY - longLeafAddYSingle * 330); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 25), longLeafBaseY - longLeafAddYSingle * 310); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 35), longLeafBaseY - longLeafAddYSingle * 285); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 30), longLeafBaseY - longLeafAddYSingle * 255); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 40), longLeafBaseY - longLeafAddYSingle * 228); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 36), longLeafBaseY - longLeafAddYSingle * 200); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 45), longLeafBaseY - longLeafAddYSingle * 165); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 40), longLeafBaseY - longLeafAddYSingle * 132); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 49), longLeafBaseY - longLeafAddYSingle * 95); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 41), longLeafBaseY - longLeafAddYSingle * 57); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 50), longLeafBaseY - longLeafAddYSingle * 25); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 40), longLeafBaseY + longLeafAddYSingle * 12); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 40), longLeafBaseY + longLeafAddYSingle * 12); | |
endShape(); | |
stroke(longLeafStrokeCol); | |
noFill(); | |
strokeWeight(longLeafAddXSingle * 4.5); | |
beginShape(); | |
curveVertex(longLeafBaseX, longLeafBaseY + longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX, longLeafBaseY + longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 25); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 54); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 6, longLeafBaseY - longLeafAddYSingle * 85); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 2, longLeafBaseY - longLeafAddYSingle * 118); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 152); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 4, longLeafBaseY - longLeafAddYSingle * 185); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 7, longLeafBaseY - longLeafAddYSingle * 211); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 238); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 11, longLeafBaseY - longLeafAddYSingle * 265); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 289); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 313); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 335); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 12, longLeafBaseY - longLeafAddYSingle * 351); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 12, longLeafBaseY - longLeafAddYSingle * 351); | |
endShape(); | |
noFill(); | |
strokeWeight(longLeafAddXSingle * 3); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 2, longLeafBaseY - longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 2, longLeafBaseY - longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 15); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 20); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 20); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 20); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 20); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 33); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 40); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 40); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 78); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 78); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 13, longLeafBaseY - longLeafAddYSingle * 85); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 33, longLeafBaseY - longLeafAddYSingle * 89); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 33, longLeafBaseY - longLeafAddYSingle * 89); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX, longLeafBaseY - longLeafAddYSingle * 105); | |
curveVertex(longLeafBaseX, longLeafBaseY - longLeafAddYSingle * 105); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 13, longLeafBaseY - longLeafAddYSingle * 120); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 24, longLeafBaseY - longLeafAddYSingle * 122); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 24, longLeafBaseY - longLeafAddYSingle * 122); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 149); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 149); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 14, longLeafBaseY - longLeafAddYSingle * 155); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 158); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 158); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 4, longLeafBaseY - longLeafAddYSingle * 185); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 4, longLeafBaseY - longLeafAddYSingle * 185); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 192); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 19, longLeafBaseY - longLeafAddYSingle * 195); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 19, longLeafBaseY - longLeafAddYSingle * 195); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 7, longLeafBaseY - longLeafAddYSingle * 210); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 7, longLeafBaseY - longLeafAddYSingle * 210); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 14, longLeafBaseY - longLeafAddYSingle * 219); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 26, longLeafBaseY - longLeafAddYSingle * 225); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 26, longLeafBaseY - longLeafAddYSingle * 225); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 8, longLeafBaseY - longLeafAddYSingle * 255); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 8, longLeafBaseY - longLeafAddYSingle * 255); | |
curveVertex(longLeafBaseX, longLeafBaseY - longLeafAddYSingle * 262); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 265); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 265); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 275); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 275); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 18, longLeafBaseY - longLeafAddYSingle * 280); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 24, longLeafBaseY - longLeafAddYSingle * 282); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 24, longLeafBaseY - longLeafAddYSingle * 282); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 311); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 311); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 317); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 319); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 319); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 327); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 327); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 15, longLeafBaseY - longLeafAddYSingle * 332); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 19, longLeafBaseY - longLeafAddYSingle * 334); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 19, longLeafBaseY - longLeafAddYSingle * 334); | |
endShape(); | |
translate(width / 4, height / 4 * 3); | |
pop(); | |
} |
const ease = new p5.Ease(); | |
function draw_one_frame(cur_frac) { | |
// note: to clear the screen draw a rectangle | |
// note: all shape sizes, line widths, etc. should be a function of width and height | |
// note: animation should progress depending on the value of cur_frac which goes from 0 to 1, and it should loop seamlessly | |
angleMode(DEGREES); | |
noStroke(); | |
fill(255, 130, 155); | |
rect(0, 0, width, height); //the background | |
// let sunR = [228, 255, 255, 255, 255]; | |
// let sunG = [153, 153, 153, 156, 202]; | |
// let sunB = [255, 236, 177, 110, 87]; | |
// let sunPulse = [width*1, width*1.1, width*1.15, width*1.2, width*1.2, width*1.15, width*1.1, width*1]; | |
// let sunScale = [1, 0.95, 0.6, 0.4, 0.2]; | |
//this is the gradient behind the sun | |
for (i=0; i<50; i++){ | |
fill(255, 188, 64, 15); | |
push(); | |
let sunScaleFac = i*0.02; | |
ellipse(width/2, height/1.5, (width*sunScaleFac)); | |
pop(); | |
} | |
//these are the sunrays | |
push(); | |
noFill(); | |
strokeWeight(height/20); | |
translate(width/2, height/1.5); | |
for(let i=0; i<10; i++){ | |
stroke(254, 255, 166) | |
rotate(360/10); | |
sunRay(width/2, height/1.5, cur_frac); | |
} | |
pop(); | |
//this is the actual sun | |
fill(255, 202, 87) | |
ellipse(width/2, height/1.5, (width*0.2)); | |
//these are the hills I'm drawing, as well as the squiggle lines on top of them | |
push(); | |
fill(105, 54, 0); | |
beginShape(); | |
curveVertex(0, height); | |
curveVertex(-width/20, height/2.6); | |
curveVertex(width/8, height/2.1); | |
curveVertex(width/4.5, height/2.05); | |
curveVertex(width/3, height/1.6); | |
curveVertex(width/2.1, height/1.4); | |
curveVertex(width/1.75, height); | |
curveVertex(width/2, height); | |
curveVertex(0, height); | |
curveVertex(0, height); | |
endShape(); | |
noFill(); | |
stroke(122, 73, 18); | |
strokeWeight(width/500); | |
beginShape(); | |
curveVertex(0, height); | |
curveVertex(-width/20, height/2.2); | |
curveVertex(width/8, height/1.85); | |
curveVertex(width/4.5, height/1.75); | |
curveVertex(width/3, height/1.5); | |
curveVertex(width/2.6, height/1.35); | |
curveVertex(width/4.75, height/1.35); | |
curveVertex(width/8, height/1.3); | |
curveVertex(0, height/1.5); | |
curveVertex(0, height/1.7); | |
endShape(); | |
beginShape(); | |
curveVertex(0, height); | |
curveVertex(-width/20, height/2.1); | |
curveVertex(width/8, height/1.75); | |
curveVertex(width/4.5, height/1.65); | |
curveVertex(width/3.5, height/1.5); | |
curveVertex(width/3, height/1.4); | |
curveVertex(width/4.75, height/1.4); | |
curveVertex(width/8, height/1.35); | |
curveVertex(0, height/1.6); | |
curveVertex(0, height/1.9); | |
endShape(); | |
beginShape(); | |
curveVertex(0, height); | |
curveVertex(-width/20, height/2); | |
curveVertex(width/8, height/1.65); | |
curveVertex(width/4.5, height/1.55); | |
curveVertex(width/3.6, height/1.475); | |
curveVertex(width/3.5, height/1.44); | |
curveVertex(width/4.75, height/1.475); | |
curveVertex(width/8, height/1.4); | |
curveVertex(0, height/1.7); | |
curveVertex(0, height/2); | |
endShape(); | |
fill(117, 74, 29); | |
beginShape(); | |
curveVertex(width/3, height); | |
curveVertex(width/2.25, height/1.3); | |
curveVertex(width/1.75, height/1.6); | |
curveVertex(width/1.3, height/1.7); | |
curveVertex(width/0.98, height/1.95); | |
curveVertex(width, height); | |
curveVertex(width/3, height); | |
curveVertex(width/3, height); | |
endShape(); | |
noFill(); | |
stroke(145, 104, 61); | |
strokeWeight(width/500); | |
beginShape(); | |
curveVertex(width/0.98, height/1.7); | |
curveVertex(width/0.8, height/1.33); | |
curveVertex(width/1.2, height/1.43); | |
curveVertex(width/1.5, height/1.43); | |
curveVertex(width/1.6, height/1.43); | |
curveVertex(width/1.67, height/1.42); | |
curveVertex(width/1.56, height/1.47); | |
curveVertex(width/1.3, height/1.47); | |
curveVertex(width/1.1, height/1.65); | |
curveVertex(width/0.98, height/1.7); | |
curveVertex(width/0.98, height/1.7); | |
endShape(); | |
beginShape(); | |
curveVertex(width/0.98, height/1.75); | |
curveVertex(width/0.8, height/1.3); | |
curveVertex(width/1.2, height/1.4); | |
curveVertex(width/1.5, height/1.4); | |
curveVertex(width/1.7, height/1.38); | |
curveVertex(width/1.75, height/1.4); | |
curveVertex(width/1.56, height/1.5); | |
curveVertex(width/1.3, height/1.5); | |
curveVertex(width/1.1, height/1.7); | |
curveVertex(width/0.98, height/1.75); | |
curveVertex(width/0.98, height/1.75); | |
endShape(); | |
beginShape(); | |
curveVertex(width/0.98, height/1.8); | |
curveVertex(width/0.8, height/1.25); | |
curveVertex(width/1.2, height/1.35); | |
curveVertex(width/1.5, height/1.35); | |
curveVertex(width/1.75, height/1.33); | |
curveVertex(width/1.8, height/1.45); | |
curveVertex(width/1.56, height/1.55); | |
curveVertex(width/1.3, height/1.55); | |
curveVertex(width/1.1, height/1.75); | |
curveVertex(width/0.98, height/1.8); | |
curveVertex(width/0.98, height/1.8); | |
endShape(); | |
beginShape(); | |
curveVertex(width/0.98, height/1.8); | |
curveVertex(width/0.8, height/1.2); | |
curveVertex(width/1.2, height/1.3); | |
curveVertex(width/1.5, height/1.3); | |
curveVertex(width/1.75, height/1.28); | |
curveVertex(width/1.9, height/1.45); | |
curveVertex(width/1.56, height/1.6); | |
curveVertex(width/1.3, height/1.6); | |
curveVertex(width/1.1, height/1.8); | |
curveVertex(width/0.98, height/1.85); | |
curveVertex(width/0.98, height/1.85); | |
endShape(); | |
pop(); | |
//this is the lake | |
fill(166, 249, 255); | |
ellipse(width/2, height*1.3, width*2, height) | |
//these are the clouds | |
const ease_amount_cloud = ease.cubicInOut(cur_frac); | |
//this makes the clouds move accross the page | |
let cloudxPlace = [-width*0.25, 0, width*0.25, width*0.5, width*0.75, width, width*1.25]; | |
let cloudyPlace = [height/8, height/200, height/10, height/12]; | |
for(let i=0; i<cloudxPlace.length-1; i++) { | |
let cloudXpos1 = map(ease_amount_cloud, 0, 1, cloudxPlace[i], cloudxPlace[i+1]) | |
let cloudYpos1 = map(ease_amount_cloud, 0, 1, cloudyPlace[i], cloudyPlace[i+1]) | |
cloud(cloudXpos1, cloudYpos1); | |
} | |
//these are the waves | |
let waveXoffset = [0, width/20, -width/40]; | |
let waveYoffset = [0, height/20, height/50]; | |
waves(width/4+waveXoffset[0], height/1.1+waveYoffset[0], cur_frac); | |
waves(width/4+waveXoffset[1], height/1.1+waveYoffset[1], cur_frac); | |
waves(width/4+waveXoffset[2], height/1.1+waveYoffset[2], cur_frac); | |
let waveX2offset = [0, width/100]; | |
waves(width/1.8+waveX2offset[0], height/1.17+waveX2offset[0], cur_frac); | |
waves(width/1.8+waveX2offset[1], height/1.17+waveX2offset[1], cur_frac); | |
//these are the flowers in the front left | |
flower2(width/10, height/1.4, cur_frac); | |
flower3(width/5.5, height/1.35, cur_frac) | |
flower1(width/100, height/1.3, cur_frac); | |
flower1(width/8, height/1.2, cur_frac); | |
flower2(width/4, height/1.18, cur_frac); | |
flower3(width/30, height/1.1, cur_frac) | |
//these are the flowers in the front right | |
flower1(width-width/55, height/1.1, cur_frac); | |
flower1(width-width/3, height/1.1, cur_frac); | |
flower1(width-width/10, height/1.4, cur_frac); | |
flower2(width-width/100, height/1.3, cur_frac); | |
flower3(width-width/100, height/1.6, cur_frac) | |
flower2(width-width/5.5, height/1.2, cur_frac); | |
flower3(width-width/3.75, height/1.13, cur_frac) | |
flower1(width-width/3, height/1.1, cur_frac); | |
flower3(width-width/10, height/1.17, cur_frac) | |
} | |
//from here down are just the different elements that I put in a function so it was easier to call on them mulitple times | |
//the names should make it pretty self explanatory | |
function cloud (x, y) { | |
fill(255, 227, 238); | |
push(); | |
scale(2.25, 1.5); | |
ellipse(x+width/9.5, y+height/11, width/8, width/25); | |
ellipse(x+width/25, y+height/10, width/9, width/30); | |
ellipse(x+width/11, y+height/16, width/11, width/22); | |
ellipse(x+width/17, y+height/15, width/12, width/40); | |
ellipse(x+width/6.2, y+height/8.5, width/20, width/50); | |
pop(); | |
} | |
function sunRay(x, y, cur_frac) { | |
push(); | |
noFill(); | |
translate(width*0.08, 0); | |
let squiggle_up = true; | |
if (cur_frac < 0.5) { | |
squiggle_up = true; | |
amount_squiggle = cur_frac * 2; | |
} | |
else { | |
squiggle_up = false; | |
amount_squiggle = (cur_frac-0.5) * 2; | |
} | |
const ease_amount_squiggle = ease.quadraticOut(amount_squiggle); | |
let strokeTransparencyArray = [225, 75, 125, 175, 200, 75, 125, 175, 200]; | |
let rayOffsetArray = [0, height/12, height/15, height/20, height/30, -height/12, -height/15, -height/20, -height/30]; | |
for(let j=0; j<4; j++){ | |
let rayOffset = rayOffsetArray[j]; | |
strokeWeight(j*height/10+height/50); | |
stroke(255, 246, 176, 100); | |
beginShape(); | |
for (let i=0; i<360; i++){ | |
var waveX = map(i, 0, 360, 0, width); | |
var waveY = sin(i*2.5) * height/10; | |
let rayYpos; | |
if(cur_frac<0.5){ | |
rayYpos = map(ease_amount_squiggle, 0, 0.5, -waveY/4, waveY/4); | |
} | |
else{ | |
rayYpos = map(ease_amount_squiggle, 0.5, 1, waveY/4, -waveY/4); | |
} | |
vertex(waveX, rayYpos); | |
} | |
endShape(); | |
} | |
pop(); | |
} | |
function flower1 (x, y, cur_frac){ | |
let petalxOffset; | |
let petalyOffset; | |
petalxOffset = [0, width/30, width/20, width/30, 0, -width/30, -width/20, -width/30]; | |
petalyOffset = [0, width/50, width/20, width/12.5, width/10, width/12.5, width/20, width/50]; | |
push(); | |
for (let i = 0; i < 8; i++) { | |
push(); | |
translate(petalxOffset[i], petalyOffset[i]); | |
noStroke(); | |
fill(99, 45, 93, 100); | |
ellipse(x+width/200, y+width/200, width/20) | |
fill(255, 179, 249); | |
ellipse(x, y, width/20) | |
pop(); | |
} | |
let flowerCentre = true; | |
if (cur_frac < 0.5) { | |
flowerCentre = true; | |
amount_scale = cur_frac * 2; | |
} | |
else { | |
flowerCentre = false; | |
amount_scale = map(cur_frac, 0.5, 1, 1, 0); | |
} | |
const ease_amount_scale = ease.cubicInOut(amount_scale); | |
const ease_amount_alpha = ease.circularInOut(amount_scale); | |
let flowerSize = width/30 | |
let flowerScale = map(ease_amount_scale, 0, 1, flowerSize/2, flowerSize); | |
let flowerAlpha = map(ease_amount_alpha, 0, 1, 0, 150); | |
fill(191, 164, 90, 100); | |
ellipse(x, y+(0.09*height), width/14); | |
fill(252, 214, 109); | |
ellipse(x, y+(0.09*height), width/15); | |
fill(252, 214, 109, flowerAlpha); | |
strokeWeight(width/1000); | |
stroke(237, 162, 0, flowerAlpha); | |
ellipse(x, y+(0.09*height), flowerScale*2); | |
stroke(237, 162, 0, flowerAlpha+25); | |
ellipse(x, y+(0.09*height), flowerScale*1.66); | |
stroke(237, 162, 0, flowerAlpha+50); | |
ellipse(x, y+(0.09*height), flowerScale*1.33); | |
stroke(237, 162, 0, flowerAlpha+100); | |
ellipse(x, y+(0.09*height), flowerScale); | |
stroke(237, 162, 0, flowerAlpha+150); | |
ellipse(x, y+(0.09*height), flowerScale*0.66); | |
stroke(237, 162, 0, flowerAlpha+200); | |
ellipse(x, y+(0.09*height), flowerScale*0.33); | |
ellipse(x, y+(0.09*height), flowerScale*0.1); | |
ellipse(x, y+(0.09*height), flowerScale*0.05); | |
pop(); | |
pop(); | |
} | |
function flower2 (x, y, cur_frac){ | |
let petalxOffset; | |
let petalyOffset; | |
noStroke(); | |
petalxOffset = [0, width/37.5, width/22.5, width/22.5, width/37.5, 0, -width/37.5, -width/22.5, -width/22.5, -width/37.5]; | |
petalyOffset = [width/200, width/60, width/25, width/15, width/11, width/10, width/11, width/15, width/25, width/60]; | |
for (let i = 0; i < 10; i++) { | |
push(); | |
translate(petalxOffset[i], petalyOffset[i]); | |
noStroke(); | |
fill(99, 45, 93, 100); | |
ellipse(x+width/200, y+width/200, width/20) | |
fill(245, 100, 120); | |
ellipse(x, y, width/25) | |
pop(); | |
} | |
let flowerCentre = true; | |
if (cur_frac < 0.5) { | |
flowerCentre = true; | |
amount_scale = cur_frac * 2; | |
} | |
else { | |
flowerCentre = false; | |
amount_scale = map(cur_frac, 0.5, 1, 1, 0); | |
} | |
const ease_amount_scale = ease.circularIn(amount_scale); | |
const ease_amount_alpha = ease.circularInOut(amount_scale); | |
let flowerSize = width/30 | |
let flowerScale = map(ease_amount_scale, 0, 1, flowerSize/2, flowerSize); | |
let flowerAlpha = map(ease_amount_alpha, 0, 1, 0, 150); | |
fill(74, 53, 27, 100); | |
ellipse(x, y+(0.09*height), width/14); | |
fill(138, 110, 77); | |
ellipse(x, y+(0.09*height), width/15); | |
fill(138, 110, 77, flowerAlpha); | |
strokeWeight(width/1000); | |
stroke(56, 31, 0, flowerAlpha); | |
ellipse(x, y+(0.09*height), flowerScale*2); | |
stroke(56, 31, 0, flowerAlpha+25); | |
ellipse(x, y+(0.09*height), flowerScale*1.66); | |
stroke(56, 31, 0, flowerAlpha+50); | |
ellipse(x, y+(0.09*height), flowerScale*1.33); | |
stroke(56, 31, 0, flowerAlpha+100); | |
ellipse(x, y+(0.09*height), flowerScale); | |
stroke(56, 31, 0, flowerAlpha+150); | |
ellipse(x, y+(0.09*height), flowerScale*0.66); | |
stroke(56, 31, 0, flowerAlpha+200); | |
ellipse(x, y+(0.09*height), flowerScale*0.33); | |
stroke(56, 31, 0, flowerAlpha+200); | |
ellipse(x, y+(0.09*height), flowerScale*0.1); | |
pop(); | |
} | |
function flower3 (x, y, cur_frac){ | |
let petalxOffset; | |
let petalyOffset; | |
petalxOffset = [0, width/55, width/35, width/55, 0, -width/55, -width/35, -width/55]; | |
petalyOffset = [width/45, width/35, width/20, width/14, width/12.5, width/14, width/20, width/35]; | |
noStroke(); | |
push(); | |
for (let i = 0; i < 8; i++) { | |
push(); | |
translate(petalxOffset[i], petalyOffset[i]); | |
noStroke(); | |
fill(99, 45, 93, 100); | |
ellipse(x+width/200, y+width/200, width/40) | |
fill(255); | |
ellipse(x, y, width/40) | |
pop(); | |
} | |
let flowerCentre = true; | |
if (cur_frac < 0.5) { | |
flowerCentre = true; | |
amount_scale = cur_frac * 2; | |
} | |
else { | |
flowerCentre = false; | |
amount_scale = map(cur_frac, 0.5, 1, 1, 0); | |
} | |
const ease_amount_scale = ease.quadraticOut(amount_scale); | |
const ease_amount_alpha = ease.circularInOut(amount_scale); | |
let flowerSize = width/45 | |
let flowerScale = map(ease_amount_scale, 0, 1, flowerSize/2, flowerSize); | |
let flowerAlpha = map(ease_amount_alpha, 0, 1, 0, 150); | |
fill(191, 164, 90, 100); | |
ellipse(x, y+(0.09*height), width/20); | |
fill(252, 214, 109); | |
ellipse(x, y+(0.09*height), width/22.5); | |
fill(252, 214, 109, flowerAlpha); | |
strokeWeight(width/1000); | |
stroke(237, 162, 0, flowerAlpha); | |
ellipse(x, y+(0.09*height), flowerScale*2); | |
stroke(237, 162, 0, flowerAlpha+25); | |
ellipse(x, y+(0.09*height), flowerScale*1.66); | |
stroke(237, 162, 0, flowerAlpha+50); | |
ellipse(x, y+(0.09*height), flowerScale*1.33); | |
stroke(237, 162, 0, flowerAlpha+100); | |
ellipse(x, y+(0.09*height), flowerScale); | |
stroke(237, 162, 0, flowerAlpha+150); | |
ellipse(x, y+(0.09*height), flowerScale*0.66); | |
stroke(237, 162, 0, flowerAlpha+200); | |
ellipse(x, y+(0.09*height), flowerScale*0.33); | |
ellipse(x, y+(0.09*height), flowerScale*0.1); | |
ellipse(x, y+(0.09*height), flowerScale*0.05); | |
pop(); | |
} | |
function waves (x, y, cur_frac) { | |
push(); | |
noFill(); | |
let going_up = true; | |
strokeWeight(height/150); | |
translate(width*0.08, 0); | |
if (cur_frac < 0.5) { | |
going_up = true; | |
amount_down = cur_frac * 2; | |
} | |
else { | |
going_up = false; | |
amount_down = (cur_frac-0.5) * 2; | |
} | |
const ease_amount_up = ease.quadraticIn(amount_down); | |
let strokeAlpha = map(amount_down, 0, 1, 200, 100); | |
stroke(255, 255, 255, strokeAlpha); | |
beginShape(); | |
var yoff = 0; | |
let nudgeScale = getNoiseValue(x, y, ease_amount_up, "nudgeScale", 0, height/500, 10); | |
for (let i=0; i<360; i++){ | |
var waveX1 = map(i, 0, 360, -width/10, 0); | |
var waveNoiseY = map(noise(yoff), 0, 1, 0, height/50); | |
var waveSineY = sin(i*3) * height/300 + sin(i*2.5) * height/500; | |
var waveY = (waveSineY+waveNoiseY+nudgeScale) | |
let waveYpos; | |
let waveXpos; | |
if(cur_frac<0.5){ | |
waveYpos = map(ease_amount_up, 0, 0.5, -waveY/4, waveY/4); | |
waveX2 = map(ease_amount_up, 0, 0.5, width/11, width/12); | |
} | |
else{ | |
waveYpos = map(ease_amount_up, 0.5, 1, waveY/4, -waveY/4); | |
waveX2 = map(ease_amount_up, 0.5, 1, width/12, width/11); | |
} | |
waveXpos = waveX1+waveX2 | |
vertex(waveXpos+x, waveYpos+y); | |
yoff += 0.01 | |
} | |
endShape(); | |
pop(); | |
} |
const ease = new p5.Ease(); | |
function draw_one_frame(cur_frac) { | |
var addXSingle = width / 960; | |
var addYSingle = height / 520; | |
fill(255); | |
rect(0, 0, width, height); | |
//drawing the background gradient | |
c1 = color(240, 254, 254); //very light blue | |
c2 = color(154, 242, 245); //darker blue | |
for (let yBG = 0; yBG < height + 1; yBG++) { | |
n = map(yBG, 0, height, 0, 1); | |
let newc = lerpColor(c1, c2, n); | |
stroke(newc); | |
line(0, yBG, width, yBG); | |
} | |
let cur_x = map(cur_frac, 0, 1, 0, width) //- half_width; | |
//all of my colours to use when I call a leaf function! | |
var longLeafFill1 = color(167, 212, 138); | |
var longLeafFill2 = color(134, 181, 103); | |
var longLeafStroke1 = color(108, 148, 99); | |
var longLeafFill3 = color(92, 145, 84); //saturated dark green | |
var longLeafStroke3 = color(92, 145, 84); | |
var longLeafFill4 = color(49, 94, 89); //darkest green | |
var longLeafStroke4 = color(49, 94, 89); | |
var longLeafFill5 = color(83, 144, 111); | |
var fatLeafFill = color(200, 231, 128); | |
var darkBlueGreen = color(71, 125, 99); | |
var teal = color(87, 141, 122); | |
var slate_green = color(91, 151, 110); | |
var dull_greren = color(109, 171, 94); | |
var muted_green = color(102, 159, 90); | |
var pale_olive_green = color(160, 213, 148); | |
var pale_olive = color(173, 202, 151); | |
var flower_fill = color(230, 251, 150); | |
var medium_spring_bud = color(191, 225, 129); | |
var oxley = color(105, 159, 132); | |
var limed_spruce = color(57, 69, 83); | |
var skinnyLongLeafCol = color(103, 142, 149); | |
var secondFlowerLeaf = color(115, 166, 137); | |
let randRoat = getNoiseValue(0, 0, cur_frac, "randRoat", -.3, .3, .1); //random jitter on the long leaf using noise | |
push(); //fat leaf right at the back on the right | |
translate(width / 1.23, height / 1); | |
drawFatLeaf(70, 0.6, -1, darkBlueGreen); | |
pop(); | |
push(); //fat leaf right at the back on the left | |
translate(width * .16, height * 1.07); | |
drawFatLeaf(-43, .8, -1, darkBlueGreen); | |
pop(); | |
//leaves on the right behind the flowers | |
push(); | |
translate(width / 1.4, height * 1.04); | |
drawLongLeaf(0, 0, 1.2, 20, longLeafFill4, longLeafStroke4); | |
pop(); | |
push(); | |
translate(width / 1.28, height * 1.1); | |
drawLongLeaf(0, 0, 1.05, 23, longLeafFill3, longLeafStroke3); | |
pop(); | |
push(); | |
translate(width / 1.45, height * 1.02); | |
drawLongLeaf(0, 0, 1.05, 0, longLeafFill5, longLeafFill5); | |
pop(); | |
//setting up the flowers to move back and forth | |
let going_right = true; | |
let amount_across = 0; | |
if (cur_frac < 0.5) { | |
going_right = true; | |
amount_across = cur_frac * 2; | |
} else { | |
going_right = false; | |
amount_across = (cur_frac - 0.5) * 2; | |
} | |
const ease_amount_across = ease.doubleQuadraticSigmoid(amount_across); | |
const ease_amount_across_fatLeaf = ease.circularFillet(amount_across); | |
const ease_amount_acrosslongLeaf = ease.normalizedLogitSigmoid(amount_across); | |
if (going_right) { | |
flowerSway = map(ease_amount_across, 0, 0.5, addXSingle * 0, addXSingle * 2) | |
flower2Sway = map(ease_amount_across, 0, 0.5, addXSingle * 0, addXSingle * .9) | |
fLSway = map(ease_amount_across_fatLeaf, 0, 0.5, addXSingle * 0, addXSingle * 0.3) | |
longLeafSway = map(ease_amount_acrosslongLeaf, 0, 0.5, addXSingle * 0, addXSingle * 0.2) | |
} else { | |
flowerSway = map(ease_amount_across, 0.5, 1, addXSingle * 2, addXSingle * 0) | |
flower2Sway = map(ease_amount_across, 0.5, 1, addXSingle * .9, addXSingle * 0) | |
fLSway = map(ease_amount_across_fatLeaf, 0.5, 1, addXSingle * 0.3, addXSingle * 0) | |
longLeafSway = map(ease_amount_acrosslongLeaf, 0.5, 1, addXSingle * 0.2, addXSingle * 0) | |
} | |
//drawing long leaf with the jitter | |
push(); | |
translate(width / 1.6, height / 1.02); | |
rotate(0); | |
drawLongLeaf(0, 0, 1.2, (randRoat / 3) + longLeafSway, longLeafFill1, longLeafStroke1); | |
pop(); | |
//setting up the flowers | |
var flower1StartX = width / 1.2 + flowerSway; //flower sway makes the flower bud AND the stalk move | |
var flower1StartY = height / 4.9; | |
var flower2StartX = width *.92 + flower2Sway; | |
var flower2StartY = height *.33974; | |
strokeWeight(width / 960 * 2); | |
stroke(93, 107, 54); | |
noFill(); | |
beginShape(); //flower 1 stalk | |
curveVertex(flower1StartX, flower1StartY); | |
curveVertex(flower1StartX, flower1StartY); | |
curveVertex(width / 1.2, height *.6); | |
curveVertex(width / 1.3, height*.89); | |
curveVertex(width / 1.3, height*.89); | |
endShape(); | |
//draw the actual flower (biggest one) | |
push(); | |
translate(flower1StartX, flower1StartY); | |
rotate(-25); | |
scale(1.4); | |
drawFlower(0, 0, flower_fill); //bigger top flower | |
pop(); | |
//drawing leaves on the biggest flowerr | |
push(); | |
translate(width / 1.196 + (flowerSway / 2), height / 2.3); //top down | |
drawSkinnyLongLeaf(-40, .9, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.194 + (flowerSway / 2.2), height / 2.15); | |
drawSkinnyLongLeaf(35, 1, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.196 + (flowerSway / 2.6), height / 2.05); | |
drawSkinnyLongLeaf(-60, 1.1, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.194 + (flowerSway / 2.8), height / 1.9); | |
drawSkinnyLongLeaf(55, 1.08, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.196 + (flowerSway / 2.9), height / 1.7); | |
drawSkinnyLongLeaf(-57, 1.25, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.194 + (flowerSway / 2.8), height / 1.65); | |
drawSkinnyLongLeaf(59, 1.25, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.215, height / 1.55); | |
drawSkinnyLongLeaf(-50, 1.3, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.223, height / 1.4); | |
drawSkinnyLongLeaf(-69, 1.5, skinnyLongLeafCol); | |
pop(); | |
push(); | |
translate(width / 1.27, height / 1.21); | |
drawSkinnyLongLeaf(-62, 1.5, skinnyLongLeafCol); | |
pop(); | |
push(); ////////leaves in the middle of the screen/////// | |
translate(width * .49, height * 1.19); | |
drawShortWavyLeaf(longLeafFill3, longLeafFill3, 13, 3); | |
pop(); | |
push(); | |
translate(width * .604, height * 1.047); | |
drawShortWavyLeaf(teal, oxley, -17, 2.7); | |
pop(); | |
push(); | |
translate(width * .2, height * 1.1); | |
drawShortWavyLeaf(longLeafFill2, longLeafFill2, -20, 3); | |
pop(); | |
//second flower stalk | |
strokeWeight(width / 960 * 2); | |
stroke(93, 107, 54); | |
noFill(); | |
beginShape(); //flower 2 stalk | |
curveVertex(flower2StartX, flower2StartY); | |
curveVertex(flower2StartX, flower2StartY); | |
curveVertex(width *.896, height *.52); | |
curveVertex(width / 1.3, height *.89); | |
curveVertex(width / 1.3, height *.89); | |
endShape(); | |
//second flower | |
push(); | |
translate(flower2StartX, flower2StartY); | |
rotate(25); | |
scale(1); | |
drawFlower(0, 0, flower_fill); //lower smaller flower | |
pop(); | |
//ssecond flower leaves | |
push(); | |
translate(width*.908 + (flower2Sway / 4), height / 2.18); | |
drawSkinnyLongLeaf(-28, .84, secondFlowerLeaf); | |
pop(); | |
push(); | |
translate(width *.906 + (flower2Sway / 2.8), height / 2.1); | |
drawSkinnyLongLeaf(48, .87, secondFlowerLeaf); | |
pop(); | |
push(); | |
translate(width / 1.13, height / 1.84); | |
drawSkinnyLongLeaf(-21, 1, secondFlowerLeaf); | |
pop(); | |
push(); | |
translate(width / 1.15, height / 1.69); | |
drawSkinnyLongLeaf(70, 1.07, secondFlowerLeaf); | |
pop(); | |
push(); | |
translate(width / 1.17, height / 1.55); | |
drawSkinnyLongLeaf(-33, 1.2, secondFlowerLeaf); | |
pop(); | |
push(); | |
translate(width / 1.2, height / 1.4); | |
drawSkinnyLongLeaf(63, 1.3, secondFlowerLeaf); | |
pop(); | |
//draw the two fat leaves in front of the flowers on the RIGHT | |
push(); | |
translate(width / 1.29, height * 1.03); | |
drawFatLeaf(45 + fLSway, 0.9, 1, fatLeafFill); | |
pop(); | |
push(); | |
translate(width / 1.43, height * 1.1); | |
drawFatLeaf(-15 + (fLSway * 4), 0.9, -1, fatLeafFill); | |
pop(); | |
const ease_amount_across_fatLeaf2 = ease.circularArcThroughAPoint(amount_across); | |
const ease_amount_up_down = ease.doubleQuadraticSigmoid(amount_across); | |
if (going_right) { | |
flowerSway3 = map(ease_amount_across, 0, 0.5, addXSingle * .5, addXSingle * 0); | |
fatLeaf2Sway = map(ease_amount_across_fatLeaf2, 0, 0.5, addXSingle * 0.6, addXSingle * 0); | |
branchupdown = map(ease_amount_up_down, 0, 0.5, addXSingle * 0.6, addXSingle * 0) | |
} else { | |
flowerSway3 = map(ease_amount_across, 0, 0.5, addXSingle * 0, addXSingle * .5); | |
fatLeaf2Sway = map(ease_amount_across_fatLeaf2, 0, 0.5, addXSingle * 0, addXSingle * 0.6); | |
branchupdown = map(ease_amount_up_down, 0.5, 1, addXSingle * 0, addXSingle * 0.6); | |
} | |
/////////behind shrt wavy leaves | |
push(); | |
translate(width * .43, height * 1.04); | |
drawLongLeaf(0, 0, 1.18, -23, limed_spruce, limed_spruce); | |
pop(); | |
push(); | |
translate(width * .42, height * 1.03); | |
drawLongLeaf(0, 0, 1.1, 1, longLeafFill4, longLeafStroke4); | |
pop(); | |
push(); | |
translate(width * .30, height * 1.03); | |
drawLongLeaf(0, 0, 1.2, -30 + longLeafSway / 2, longLeafFill4, longLeafStroke4); | |
pop(); | |
push(); | |
translate(width * .37, height); | |
drawLongLeaf(0, 0, 1, -30, longLeafFill2, longLeafStroke1); | |
pop(); | |
//fly on the left | |
let flyScale = 0.55; | |
let flyX; | |
//setting it up off-beat, so it grows starting at 0.3 cur frac and loops back to then (instead of 0-1) | |
if (cur_frac >= 0.3) { | |
flyScale = map(cur_frac, 0.3, 0.5, 0, 0.55); | |
} | |
if (cur_frac >= 0.5) { | |
flyScale = map(cur_frac, 0.5, 1, 0.55, 0.55); | |
} | |
if (cur_frac <= 0.3) { | |
flyScale = map(cur_frac, 0, 0.3, 0.55, 0); | |
} | |
if (cur_frac <= 0.3) { //this is for the flies movement | |
flyX = map(cur_frac, 0, 0.3, addXSingle * 200, addXSingle * 250) | |
} else if (cur_frac >= 0.3) { | |
flyX = map(cur_frac, 0.3, 1, addXSingle * 100, addXSingle * 200) | |
} | |
push(); //draw the actual fly | |
translate(width / 1.9 + flyX, height / 1.5); //centre of the screen | |
drawFly(0, 0, cur_frac, flyScale,1); | |
pop(); | |
//////////short wavy leaves stalk//////////// | |
stroke(52, 93, 88); | |
strokeWeight(addXSingle * 3); | |
noFill(); | |
var endOfBranchX = width * .12; | |
var endOfBranchY = height / 3.9; | |
var strokeColWavy1 = color(42, 86, 79); | |
//main branch (going left) | |
beginShape(); | |
curveVertex(width / 3, height / 1.7); | |
curveVertex(width / 3, height / 1.7); | |
curveVertex(width / 3.8, height / 4.4); | |
curveVertex(endOfBranchX, endOfBranchY + branchupdown); | |
curveVertex(endOfBranchX, endOfBranchY + branchupdown); | |
endShape(); | |
//second branch going right | |
beginShape(); | |
curveVertex(width * .31, height * .419); | |
curveVertex(width * .31, height * .419); | |
curveVertex(width * .34, height * .16); | |
curveVertex(width * .41, height * .1); | |
curveVertex(width * .41, height * .1); | |
endShape(); | |
////first brranch leaves | |
push(); | |
translate(endOfBranchX, endOfBranchY + branchupdown); | |
drawShortWavyLeaf(teal, strokeColWavy1, -80 - (branchupdown / 1.5), 1); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 6, endOfBranchY + addYSingle * -1 + branchupdown); | |
drawShortWavyLeaf(slate_green, slate_green, -120 + (branchupdown / 1.3), .8); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 20, endOfBranchY + addYSingle * -3 + (branchupdown / 1.2)); | |
drawShortWavyLeaf(teal, strokeColWavy1, -170, .8); | |
pop(); | |
push(); //////behind the other leaves | |
translate(endOfBranchX + addXSingle * 167, endOfBranchY + addYSingle * 40); | |
drawShortWavyLeaf(slate_green, slate_green, -80, 0.93); | |
pop(); | |
push(); //////behind the other leaves on other branch | |
translate(endOfBranchX + addXSingle * 157, endOfBranchY + addYSingle * 10); | |
drawShortWavyLeaf(slate_green, slate_green, 19, 0.8); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 100, endOfBranchY + addYSingle * -25 + (branchupdown / 1.9)); | |
drawShortWavyLeaf(teal, strokeColWavy1, -20 - (branchupdown / 2), .75); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 48, endOfBranchY + addYSingle * -20 + (branchupdown / 1.5)); | |
drawShortWavyLeaf(teal, strokeColWavy1, -50 + (branchupdown / 1.8), .82); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 68, endOfBranchY + addYSingle * -20 + (branchupdown / 1.6)); | |
drawShortWavyLeaf(teal, strokeColWavy1, -200 + (branchupdown / 2), .93); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 118, endOfBranchY + addYSingle * -25); | |
drawShortWavyLeaf(teal, strokeColWavy1, -180, .8); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 180, endOfBranchY + addYSingle * 90); | |
drawShortWavyLeaf(teal, strokeColWavy1, -100, .94); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 190, endOfBranchY + addYSingle * 120); | |
drawShortWavyLeaf(teal, strokeColWavy1, 40, 1.06); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 196, endOfBranchY + addYSingle * 150); | |
drawShortWavyLeaf(teal, strokeColWavy1, -87, 1.1); | |
pop(); | |
///////////second branch (going right) | |
push(); | |
translate(endOfBranchX + addXSingle * 187, endOfBranchY + addYSingle * 80); | |
drawShortWavyLeaf(teal, strokeColWavy1, 60, .9); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 191, endOfBranchY + addYSingle * 40); | |
drawShortWavyLeaf(teal, strokeColWavy1, 50, 1); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 201, endOfBranchY + addYSingle * -20); | |
drawShortWavyLeaf(teal, strokeColWavy1, 70, .8); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 191, endOfBranchY + addYSingle * 20); | |
drawShortWavyLeaf(teal, strokeColWavy1, -35, .86); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 207, endOfBranchY + addYSingle * -45); | |
drawShortWavyLeaf(teal, strokeColWavy1, -45, .8); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 230, endOfBranchY + addYSingle * -71); | |
drawShortWavyLeaf(teal, strokeColWavy1, -25, .70); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 260, endOfBranchY + addYSingle * -81); | |
drawShortWavyLeaf(teal, strokeColWavy1, 5, .55); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 280, endOfBranchY + addYSingle * -84); | |
drawShortWavyLeaf(teal, strokeColWavy1, 28, .5); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 280, endOfBranchY + addYSingle * -84); | |
drawShortWavyLeaf(teal, strokeColWavy1, 80, .5); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 271, endOfBranchY + addYSingle * -80); | |
drawShortWavyLeaf(teal, strokeColWavy1, -250, .5); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 271, endOfBranchY + addYSingle * -80); | |
drawShortWavyLeaf(teal, strokeColWavy1, -250, .5); | |
pop(); | |
push(); | |
translate(endOfBranchX + addXSingle * 238, endOfBranchY + addYSingle * -73); | |
drawShortWavyLeaf(teal, strokeColWavy1, -250, .59); | |
pop(); | |
//fly 1 (LEFT faster fly)/// | |
//this fly is moving 0-1 | |
flyScale = 0.6; | |
if (cur_frac <= 0.3) { | |
flyScale = map(cur_frac, 0, 0.3, 0, 0.6); | |
} else if (cur_frac >= 0.7) { | |
flyScale = map(cur_frac, 0.7, 1, 0.6, 0); | |
} | |
flyX = map(cur_frac, 0, 1, addXSingle * 300, addXSingle * 0) | |
push(); | |
translate(width / 5 + flyX, height / 3.4); | |
drawFly(0, 0, cur_frac, flyScale,-1); | |
pop(); | |
//////in front of branches on the left | |
push(); | |
translate(width * .45, height * 1.1); | |
drawLongLeaf(0, 0, 1, -24, dull_greren, muted_green); | |
pop(); | |
//short leaves in front on the LEFT ////////////////////////// | |
push(); | |
translate(width * .41, height * 1.1); | |
drawFatLeaf(-20 + (fatLeaf2Sway / -10), 1, -1, pale_olive); | |
pop(); | |
push(); | |
translate(width * .43, height * 1.1); | |
drawFatLeaf(10 + (fatLeaf2Sway / 1.2), 0.9, 1, fatLeafFill); | |
pop(); | |
push(); | |
translate(width * .37, height * 1.1); | |
drawFatLeaf(-56 + (fatLeaf2Sway / 1.8), 0.9, -1, fatLeafFill); | |
pop(); | |
////////bottom left flowers | |
let backFloStartX = width * .19 + (flowerSway3 / 2.6); | |
let backFloStartY = height * .75; | |
//stalk 4 back flower | |
strokeWeight(addXSingle * 1.5); | |
stroke(longLeafFill3); | |
noFill(); | |
beginShape(); | |
curveVertex(backFloStartX - addXSingle * 16, backFloStartY); | |
curveVertex(backFloStartX - addXSingle * 16, backFloStartY); | |
curveVertex(width * .15, height * .88); | |
curveVertex(width * .16, height); | |
curveVertex(width * .16, height); | |
endShape(); | |
push(); | |
translate(backFloStartX, backFloStartY); | |
rotate(30); | |
scale(1.3); | |
drawFlower(0, 0, medium_spring_bud); | |
pop(); | |
let bigFloStartX = width * .1 + flower2Sway; | |
let bigFloStartY = height * .8; | |
beginShape(); | |
curveVertex(bigFloStartX, bigFloStartY); | |
curveVertex(bigFloStartX, bigFloStartY); | |
curveVertex(width * .135, height * .91); | |
curveVertex(width * .15, height); | |
curveVertex(width * .15, height); | |
endShape(); | |
push(); | |
translate(bigFloStartX, height * .8); | |
rotate(-40); | |
scale(1.3); | |
drawFlower(0, 0, flower_fill); | |
pop(); | |
var smolFloX = width * .2 + flowerSway / 4; | |
var smolFloY = height * .85; | |
beginShape(); | |
curveVertex(smolFloX, smolFloY); | |
curveVertex(smolFloX, smolFloY); | |
curveVertex(width * .17, height * .95); | |
curveVertex(width * .172, height); | |
curveVertex(width * .172, height); | |
endShape(); | |
push(); | |
translate(smolFloX, smolFloY); | |
rotate(10); | |
scale(1); | |
drawFlower(0, 0, flower_fill); | |
pop(); | |
} | |
function drawShortWavyLeaf(fillCol, strokeColWavy, shortWavyRot, shortWavyScale) { | |
//drawn on the upper left | |
var addXSingle = width / 960; | |
var addYSingle = height / 540; | |
var shortWavyLeafX = 0; | |
var shortWavyLeafY = 0; | |
// | |
fill(fillCol); | |
push(); | |
scale(shortWavyScale); | |
rotate(shortWavyRot); | |
noStroke(); | |
beginShape(); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX + addXSingle * -9, shortWavyLeafY + addYSingle * -10); | |
curveVertex(shortWavyLeafX + addXSingle * -11, shortWavyLeafY + addYSingle * -18); | |
curveVertex(shortWavyLeafX + addXSingle * -8, shortWavyLeafY + addYSingle * -28); | |
curveVertex(shortWavyLeafX + addXSingle * -10, shortWavyLeafY + addYSingle * -40); | |
curveVertex(shortWavyLeafX + addXSingle * -12, shortWavyLeafY + addYSingle * -47); | |
curveVertex(shortWavyLeafX + addXSingle * -13, shortWavyLeafY + addYSingle * -55); | |
curveVertex(shortWavyLeafX + addXSingle * -11.5, shortWavyLeafY + addYSingle * -60); | |
curveVertex(shortWavyLeafX + addXSingle * -8, shortWavyLeafY + addYSingle * -65); | |
curveVertex(shortWavyLeafX + addXSingle * -9.5, shortWavyLeafY + addYSingle * -73); | |
curveVertex(shortWavyLeafX + addXSingle * -10, shortWavyLeafY + addYSingle * -80); | |
curveVertex(shortWavyLeafX + addXSingle * -8, shortWavyLeafY + addYSingle * -83); | |
curveVertex(shortWavyLeafX + addXSingle * -3, shortWavyLeafY + addYSingle * -88); | |
curveVertex(shortWavyLeafX, shortWavyLeafY + addYSingle * -95); | |
curveVertex(shortWavyLeafX + addXSingle * 2, shortWavyLeafY + addYSingle * -100); | |
curveVertex(shortWavyLeafX + addXSingle * 5, shortWavyLeafY + addYSingle * -92); | |
curveVertex(shortWavyLeafX + addXSingle * 10, shortWavyLeafY + addYSingle * -86); | |
curveVertex(shortWavyLeafX + addXSingle * 16, shortWavyLeafY + addYSingle * -80); | |
curveVertex(shortWavyLeafX + addXSingle * 17.5, shortWavyLeafY + addYSingle * -73); | |
curveVertex(shortWavyLeafX + addXSingle * 16.5, shortWavyLeafY + addYSingle * -67); | |
curveVertex(shortWavyLeafX + addXSingle * 15, shortWavyLeafY + addYSingle * -59); | |
curveVertex(shortWavyLeafX + addXSingle * 18, shortWavyLeafY + addYSingle * -48); | |
curveVertex(shortWavyLeafX + addXSingle * 16, shortWavyLeafY + addYSingle * -40); | |
curveVertex(shortWavyLeafX + addXSingle * 13, shortWavyLeafY + addYSingle * -33); | |
curveVertex(shortWavyLeafX + addXSingle * 15, shortWavyLeafY + addYSingle * -20); | |
curveVertex(shortWavyLeafX + addXSingle * 13, shortWavyLeafY + addYSingle * -13); | |
curveVertex(shortWavyLeafX + addXSingle * 5.5, shortWavyLeafY + addYSingle * -6); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
endShape(); | |
fill(strokeColWavy); | |
stroke(strokeColWavy); | |
strokeWeight(addXSingle); | |
beginShape(); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX + addXSingle * -2, shortWavyLeafY + addYSingle * -17); | |
curveVertex(shortWavyLeafX + addXSingle * 0, shortWavyLeafY + addYSingle * -28); | |
curveVertex(shortWavyLeafX + addXSingle * -1.3, shortWavyLeafY + addYSingle * -39); | |
curveVertex(shortWavyLeafX + addXSingle * .4, shortWavyLeafY + addYSingle * -50); | |
curveVertex(shortWavyLeafX + addXSingle * -.7, shortWavyLeafY + addYSingle * -60); | |
curveVertex(shortWavyLeafX + addXSingle * 1.7, shortWavyLeafY + addYSingle * -68); | |
curveVertex(shortWavyLeafX + addXSingle * 1, shortWavyLeafY + addYSingle * -80); | |
curveVertex(shortWavyLeafX + addXSingle * 1.6, shortWavyLeafY + addYSingle * -87); | |
curveVertex(shortWavyLeafX + addXSingle * 3, shortWavyLeafY + addYSingle * -80); | |
curveVertex(shortWavyLeafX + addXSingle * 4.5, shortWavyLeafY + addYSingle * -68); | |
curveVertex(shortWavyLeafX + addXSingle * 3.5, shortWavyLeafY + addYSingle * -60); | |
curveVertex(shortWavyLeafX + addXSingle * 5, shortWavyLeafY + addYSingle * -50); | |
curveVertex(shortWavyLeafX + addXSingle * 2.3, shortWavyLeafY + addYSingle * -39); | |
curveVertex(shortWavyLeafX + addXSingle * 3.5, shortWavyLeafY + addYSingle * -28); | |
curveVertex(shortWavyLeafX + addXSingle * 1, shortWavyLeafY + addYSingle * -17); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
curveVertex(shortWavyLeafX, shortWavyLeafY); | |
endShape(); | |
pop(); | |
} | |
function drawFatLeaf(fatLeafRot, fatLeafYScal, negative, fatLeafFill) { | |
//drawn at the front of the screen normally, bright | |
push(); | |
var addXSingle = width / 960 * negative; | |
var addYSingle = height / 540; | |
var fatLeafX = 0; | |
var fatLeafY = 0; | |
fill(fatLeafFill); | |
noStroke(); | |
rotate(fatLeafRot); | |
scale(fatLeafYScal) | |
beginShape(); | |
curveVertex(fatLeafY, fatLeafY); | |
curveVertex(fatLeafY, fatLeafY); | |
curveVertex(fatLeafX + addXSingle * -70, fatLeafY + addYSingle * -50); | |
curveVertex(fatLeafX + addXSingle * -80, fatLeafY + addYSingle * -90); | |
curveVertex(fatLeafX + addXSingle * -70, fatLeafY + addYSingle * -120); | |
curveVertex(fatLeafX + addXSingle * -60, fatLeafY + addYSingle * -140); | |
curveVertex(fatLeafX + addXSingle * -50, fatLeafY + addYSingle * -170); | |
curveVertex(fatLeafX + addXSingle * -57, fatLeafY + addYSingle * -200); | |
curveVertex(fatLeafX + addXSingle * -45, fatLeafY + addYSingle * -230); | |
curveVertex(fatLeafX + addXSingle * -4, fatLeafY + addYSingle * -260); | |
curveVertex(fatLeafX, fatLeafY + addYSingle * -262); | |
curveVertex(fatLeafX + addXSingle * 4, fatLeafY + addYSingle * -260); | |
curveVertex(fatLeafX + addXSingle * 45, fatLeafY + addYSingle * -230); | |
curveVertex(fatLeafX + addXSingle * 54, fatLeafY + addYSingle * -210); | |
curveVertex(fatLeafX + addXSingle * 45, fatLeafY + addYSingle * -190); | |
curveVertex(fatLeafX + addXSingle * 50, fatLeafY + addYSingle * -170); | |
curveVertex(fatLeafX + addXSingle * 65, fatLeafY + addYSingle * -150); | |
curveVertex(fatLeafX + addXSingle * 70, fatLeafY + addYSingle * -135); | |
curveVertex(fatLeafX + addXSingle * 60, fatLeafY + addYSingle * -105); | |
curveVertex(fatLeafX + addXSingle * 70, fatLeafY + addYSingle * -85); | |
curveVertex(fatLeafX + addXSingle * 78, fatLeafY + addYSingle * -65); | |
curveVertex(fatLeafX + addXSingle * 65, fatLeafY + addYSingle * -40); | |
curveVertex(fatLeafX, fatLeafY); | |
curveVertex(fatLeafX, fatLeafY); | |
endShape(); | |
stroke(118, 154, 87); | |
strokeWeight(height / 520 * 3); | |
noFill(); | |
beginShape(); | |
curveVertex(fatLeafX, fatLeafY); | |
curveVertex(fatLeafX, fatLeafY); | |
curveVertex(fatLeafX + addXSingle * -1, fatLeafY + addYSingle * -35); | |
curveVertex(fatLeafX + addXSingle * 4, fatLeafY + addYSingle * -75); | |
curveVertex(fatLeafX + addXSingle * -5, fatLeafY + addYSingle * -115); | |
curveVertex(fatLeafX + addXSingle * 3, fatLeafY + addYSingle * -150); | |
curveVertex(fatLeafX + addXSingle * -4, fatLeafY + addYSingle * -175); | |
curveVertex(fatLeafX + addXSingle * 3, fatLeafY + addYSingle * -195); | |
curveVertex(fatLeafX + addXSingle * -2, fatLeafY + addYSingle * -210); | |
curveVertex(fatLeafX + addXSingle * 1, fatLeafY + addYSingle * -225); | |
//curveVertex(fatLeafX+addXSingle*1,fatLeafY+addYSingle*-225); | |
curveVertex(fatLeafX + addXSingle * -1, fatLeafY + addYSingle * -240); | |
curveVertex(fatLeafX + addXSingle, fatLeafY + addYSingle * -250); | |
curveVertex(fatLeafX + addXSingle, fatLeafY + addYSingle * -250); | |
endShape(); | |
pop(); | |
} | |
function drawSkinnyLongLeaf(skinnyLongLeafRot, skinnyLongLeafScale, skinnyLongLeafCol) { | |
//leaves on the rose branches | |
var addXSingle = width / 960; | |
var addYSingle = height / 540; | |
var skinnyLongLeafX = 0; | |
var skinnyLongLeafY = 0; | |
push(); | |
rotate(skinnyLongLeafRot); | |
scale(skinnyLongLeafScale); | |
fill(skinnyLongLeafCol); | |
noStroke(); | |
beginShape(); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY); | |
curveVertex(skinnyLongLeafX - addXSingle * 7, skinnyLongLeafY - addYSingle * 51); | |
curveVertex(skinnyLongLeafX - addXSingle * 1, skinnyLongLeafY - addYSingle * 100); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY - addYSingle * 105); | |
curveVertex(skinnyLongLeafX + addXSingle * 1, skinnyLongLeafY - addYSingle * 100); | |
curveVertex(skinnyLongLeafX + addXSingle * 8, skinnyLongLeafY - addYSingle * 49); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY); | |
endShape(); | |
stroke(45, 78, 109); | |
noFill(); | |
strokeWeight(addXSingle * .7); | |
beginShape(); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY - addYSingle * 21); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY - addYSingle * 21); | |
curveVertex(skinnyLongLeafX + addXSingle * 1, skinnyLongLeafY - addYSingle * 49); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY - addYSingle * 80); | |
curveVertex(skinnyLongLeafX, skinnyLongLeafY - addYSingle * 80); | |
endShape(); | |
pop(); | |
} | |
function drawFly(flyX, flyY, cur_frac, flyScale,negative) { | |
let wingMove; //set up animated wing variable | |
var addXSingle = width / 960 * negative; | |
var addYSingle = height / 540; | |
var flyBodyCol = color(74, 77, 76); | |
var flyWingCol = color(166, 237, 234, 180); | |
//setting it up so the flies wings can flap more tha once a second | |
var wingFlapNum = 360 * 5 //how many times the wing flaps per second , multiples of 360 | |
var flapAmmount = round(sin(map(cur_frac, 0, 1, 1, wingFlapNum))); | |
var flapAmmountSmooth = sin(map(cur_frac, 0, 1, 1, wingFlapNum)); | |
if (flapAmmount == 0) { | |
wingMove = map(flapAmmountSmooth, 0, -1, 0, 10); | |
} else if (flapAmmount == -1) { | |
wingMove = map(flapAmmountSmooth, -1, 0, 10, 0); | |
} | |
noStroke(); | |
fill(flyBodyCol); | |
scale(flyScale); | |
ellipse(flyX + addXSingle * 15, flyY, addXSingle * 17, addYSingle * 17); //head | |
ellipse(flyX, flyY, addXSingle * 30, addYSingle * 22); //body | |
rect(flyX + addXSingle * 21, flyY + addYSingle * 1, addXSingle * 8, -addYSingle * 4); //snout | |
ellipse(flyX + addXSingle * 30, flyY - addYSingle * 1, addXSingle * 4, addYSingle * 7) //end of snout | |
//wings! | |
fill(flyWingCol); | |
push(); | |
rotate(35*negative + wingMove); | |
ellipse(flyX - addXSingle * 6, flyY - addYSingle * 1, addXSingle * 20, addYSingle * 10); | |
rotate(40*negative + wingMove); | |
ellipse(flyX - addXSingle * 11, flyY - addYSingle * 3, addXSingle * 30, addYSingle * 15); | |
pop(); | |
} | |
function drawFlower(flowerBaseX, flowerBaseY, flowerFill) { | |
noStroke(); | |
var addXSingle = width / 960; //1 | |
var addYSingle = height / 540; | |
fill(flowerFill); | |
arc(flowerBaseX, flowerBaseY, addXSingle * 80, addYSingle * 70, 0, 180); | |
stroke(144, 179, 118); | |
strokeWeight(addXSingle); | |
beginShape(); | |
curveVertex(flowerBaseX - addXSingle * 35, flowerBaseY); | |
curveVertex(flowerBaseX - addXSingle * 35, flowerBaseY); | |
curveVertex(flowerBaseX - addXSingle * 28, flowerBaseY - addYSingle * 18); | |
curveVertex(flowerBaseX - addXSingle * 24, flowerBaseY - addYSingle * 20); | |
curveVertex(flowerBaseX - addXSingle * 21, flowerBaseY - addYSingle * 19); | |
curveVertex(flowerBaseX - addXSingle * 17, flowerBaseY - addYSingle * 23); | |
curveVertex(flowerBaseX - addXSingle * 12, flowerBaseY - addYSingle * 25); | |
curveVertex(flowerBaseX - addXSingle * 6, flowerBaseY - addYSingle * 22); | |
curveVertex(flowerBaseX - addXSingle * 1, flowerBaseY - addYSingle * 27); | |
curveVertex(flowerBaseX + addXSingle * 2, flowerBaseY - addYSingle * 28); | |
curveVertex(flowerBaseX + addXSingle * 5, flowerBaseY - addYSingle * 27); | |
curveVertex(flowerBaseX + addXSingle * 10, flowerBaseY - addYSingle * 21); | |
curveVertex(flowerBaseX + addXSingle * 17, flowerBaseY - addYSingle * 23); | |
curveVertex(flowerBaseX + addXSingle * 20, flowerBaseY - addYSingle * 20); | |
curveVertex(flowerBaseX + addXSingle * 26, flowerBaseY - addYSingle * 21); | |
curveVertex(flowerBaseX + addXSingle * 30, flowerBaseY - addYSingle * 18); | |
curveVertex(flowerBaseX + addXSingle * 34, flowerBaseY); | |
curveVertex(flowerBaseX + addXSingle * 34, flowerBaseY); | |
endShape(); | |
beginShape(); | |
curveVertex(flowerBaseX - addXSingle * 4, flowerBaseY); | |
curveVertex(flowerBaseX - addXSingle * 4, flowerBaseY); | |
curveVertex(flowerBaseX + addXSingle * 3, flowerBaseY - addYSingle * 12); | |
curveVertex(flowerBaseX + addXSingle * 9, flowerBaseY - addYSingle * 14); | |
curveVertex(flowerBaseX + addXSingle * 15, flowerBaseY - addYSingle * 10); | |
curveVertex(flowerBaseX + addXSingle * 20, flowerBaseY - addYSingle * 14); | |
curveVertex(flowerBaseX + addXSingle * 24, flowerBaseY - addYSingle * 15); | |
curveVertex(flowerBaseX + addXSingle * 29, flowerBaseY - addYSingle * 11); | |
curveVertex(flowerBaseX + addXSingle * 34, flowerBaseY - addYSingle * 15); | |
curveVertex(flowerBaseX + addXSingle * 38, flowerBaseY - addYSingle * 11); | |
curveVertex(flowerBaseX + addXSingle * 40, flowerBaseY); | |
curveVertex(flowerBaseX + addXSingle * 40, flowerBaseY); | |
endShape(); | |
beginShape(); | |
curveVertex(flowerBaseX - addXSingle * 40, flowerBaseY); | |
curveVertex(flowerBaseX - addXSingle * 40, flowerBaseY); | |
curveVertex(flowerBaseX - addXSingle * 38, flowerBaseY - addYSingle * 10); | |
curveVertex(flowerBaseX - addXSingle * 32, flowerBaseY - addYSingle * 14); | |
curveVertex(flowerBaseX - addXSingle * 27, flowerBaseY - addYSingle * 10); | |
curveVertex(flowerBaseX - addXSingle * 20, flowerBaseY - addYSingle * 16); | |
curveVertex(flowerBaseX - addXSingle * 14, flowerBaseY - addYSingle * 12); | |
curveVertex(flowerBaseX - addXSingle * 10, flowerBaseY - addYSingle * 15); | |
curveVertex(flowerBaseX - addXSingle * 4, flowerBaseY - addYSingle * 11); | |
curveVertex(flowerBaseX - addXSingle * 2, flowerBaseY - addYSingle * 6); | |
curveVertex(flowerBaseX + addXSingle * 1, flowerBaseY - addYSingle * 7.5) | |
curveVertex(flowerBaseX + addXSingle * 6, flowerBaseY - addYSingle * 8); | |
curveVertex(flowerBaseX + addXSingle * 9, flowerBaseY - addYSingle * 6) | |
curveVertex(flowerBaseX + addXSingle * 11, flowerBaseY - addYSingle * 1); | |
curveVertex(flowerBaseX + addXSingle * 17, flowerBaseY - addYSingle * 2); | |
curveVertex(flowerBaseX + addXSingle * 22, flowerBaseY + addYSingle * 3); | |
curveVertex(flowerBaseX + addXSingle * 28, flowerBaseY + addYSingle * 3); | |
curveVertex(flowerBaseX + addXSingle * 36, flowerBaseY + addYSingle * 11); | |
curveVertex(flowerBaseX + addXSingle * 36, flowerBaseY + addYSingle * 11); | |
endShape(); | |
} | |
function drawLongLeaf(longLeafBaseX, longLeafBaseY, longLeafScale, longLLeafRotation, longLeafFill, longLeafStrokeCol) { | |
//kelp looking leaf | |
noStroke(); | |
angleMode(DEGREES); | |
push(); | |
rotate(longLLeafRotation); | |
var longLeafAddXSingle = width / 960; //1 | |
var longLeafAddYSingle = height / 540; // 1 | |
scale(longLeafScale); | |
fill(longLeafFill); | |
beginShape(); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 40), longLeafBaseY - longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 40), longLeafBaseY - longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 50), longLeafBaseY - longLeafAddYSingle * 45); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 36), longLeafBaseY - longLeafAddYSingle * 80); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 41), longLeafBaseY - longLeafAddYSingle * 125); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 27), longLeafBaseY - longLeafAddYSingle * 156); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 31), longLeafBaseY - longLeafAddYSingle * 200); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 18), longLeafBaseY - longLeafAddYSingle * 235); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 22), longLeafBaseY - longLeafAddYSingle * 270); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 13), longLeafBaseY - longLeafAddYSingle * 295); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 11), longLeafBaseY - longLeafAddYSingle * 325); | |
curveVertex(longLeafBaseX - (longLeafAddXSingle * 2), longLeafBaseY - longLeafAddYSingle * 340); | |
curveVertex(longLeafBaseX, longLeafBaseY - longLeafAddYSingle * 355); | |
vertex(longLeafBaseX + (longLeafAddXSingle * 14), longLeafBaseY - longLeafAddYSingle * 379); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 26), longLeafBaseY - longLeafAddYSingle * 365); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 24), longLeafBaseY - longLeafAddYSingle * 345); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 28), longLeafBaseY - longLeafAddYSingle * 330); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 25), longLeafBaseY - longLeafAddYSingle * 310); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 35), longLeafBaseY - longLeafAddYSingle * 285); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 30), longLeafBaseY - longLeafAddYSingle * 255); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 40), longLeafBaseY - longLeafAddYSingle * 228); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 36), longLeafBaseY - longLeafAddYSingle * 200); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 45), longLeafBaseY - longLeafAddYSingle * 165); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 40), longLeafBaseY - longLeafAddYSingle * 132); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 49), longLeafBaseY - longLeafAddYSingle * 95); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 41), longLeafBaseY - longLeafAddYSingle * 57); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 50), longLeafBaseY - longLeafAddYSingle * 25); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 40), longLeafBaseY + longLeafAddYSingle * 12); | |
curveVertex(longLeafBaseX + (longLeafAddXSingle * 40), longLeafBaseY + longLeafAddYSingle * 12); | |
endShape(); | |
stroke(longLeafStrokeCol); | |
noFill(); | |
strokeWeight(longLeafAddXSingle * 4.5); | |
beginShape(); | |
curveVertex(longLeafBaseX, longLeafBaseY + longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX, longLeafBaseY + longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 25); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 54); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 6, longLeafBaseY - longLeafAddYSingle * 85); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 2, longLeafBaseY - longLeafAddYSingle * 118); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 152); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 4, longLeafBaseY - longLeafAddYSingle * 185); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 7, longLeafBaseY - longLeafAddYSingle * 211); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 238); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 11, longLeafBaseY - longLeafAddYSingle * 265); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 289); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 313); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 335); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 12, longLeafBaseY - longLeafAddYSingle * 351); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 12, longLeafBaseY - longLeafAddYSingle * 351); | |
endShape(); | |
noFill(); | |
strokeWeight(longLeafAddXSingle * 3); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 2, longLeafBaseY - longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 2, longLeafBaseY - longLeafAddYSingle * 5); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 15); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 20); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 20); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 20); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 20); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 33); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 40); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 40); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 78); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 78); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 13, longLeafBaseY - longLeafAddYSingle * 85); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 33, longLeafBaseY - longLeafAddYSingle * 89); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 33, longLeafBaseY - longLeafAddYSingle * 89); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX, longLeafBaseY - longLeafAddYSingle * 105); | |
curveVertex(longLeafBaseX, longLeafBaseY - longLeafAddYSingle * 105); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 13, longLeafBaseY - longLeafAddYSingle * 120); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 24, longLeafBaseY - longLeafAddYSingle * 122); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 24, longLeafBaseY - longLeafAddYSingle * 122); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 149); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 5, longLeafBaseY - longLeafAddYSingle * 149); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 14, longLeafBaseY - longLeafAddYSingle * 155); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 158); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 30, longLeafBaseY - longLeafAddYSingle * 158); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 4, longLeafBaseY - longLeafAddYSingle * 185); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 4, longLeafBaseY - longLeafAddYSingle * 185); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 192); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 19, longLeafBaseY - longLeafAddYSingle * 195); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 19, longLeafBaseY - longLeafAddYSingle * 195); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 7, longLeafBaseY - longLeafAddYSingle * 210); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 7, longLeafBaseY - longLeafAddYSingle * 210); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 14, longLeafBaseY - longLeafAddYSingle * 219); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 26, longLeafBaseY - longLeafAddYSingle * 225); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 26, longLeafBaseY - longLeafAddYSingle * 225); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 8, longLeafBaseY - longLeafAddYSingle * 255); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 8, longLeafBaseY - longLeafAddYSingle * 255); | |
curveVertex(longLeafBaseX, longLeafBaseY - longLeafAddYSingle * 262); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 265); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 10, longLeafBaseY - longLeafAddYSingle * 265); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 275); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 275); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 18, longLeafBaseY - longLeafAddYSingle * 280); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 24, longLeafBaseY - longLeafAddYSingle * 282); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 24, longLeafBaseY - longLeafAddYSingle * 282); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 311); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 311); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 317); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 319); | |
curveVertex(longLeafBaseX - longLeafAddXSingle * 3, longLeafBaseY - longLeafAddYSingle * 319); | |
endShape(); | |
beginShape(); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 327); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 9, longLeafBaseY - longLeafAddYSingle * 327); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 15, longLeafBaseY - longLeafAddYSingle * 332); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 19, longLeafBaseY - longLeafAddYSingle * 334); | |
curveVertex(longLeafBaseX + longLeafAddXSingle * 19, longLeafBaseY - longLeafAddYSingle * 334); | |
endShape(); | |
translate(width / 4, height / 4 * 3); | |
pop(); | |
} |
const ease = new p5.Ease(); | |
function draw_one_frame(cur_frac) { | |
angleMode(DEGREES); | |
noStroke(); | |
fill(191, 228, 255); | |
rect(0, 0, width, height); //background | |
noFill(); | |
stroke(168, 207, 237); //tile shadows | |
strokeWeight(height*0.015); | |
for (let ii=0; ii<4; ii++){ | |
for (let i=0; i<7; i++) { | |
line(width/14.8+width/7*i, 0, width/14.8+width/7*i, height); | |
line(0, width/14.8+width/7*ii, width, width/14.8+width/7*ii); | |
} | |
} | |
stroke(224, 242, 255); //tile grout | |
strokeWeight(height*0.005); | |
for (let ii=0; ii<4; ii++){ | |
for (let i=0; i<7; i++) { | |
line(width/14+width/7*i, 0, width/14+width/7*i, height); | |
line(0, width/14+width/7*ii, width, width/14+width/7*ii); | |
} | |
} | |
let row1_x = 0.45 * width; //small row position | |
let sml2offset = 0.2 * width; //2nd small row offset | |
let sml3offset = -0.33 * width; //3rd small row offset | |
let row2_x = 0.3 * width; //medium row position | |
let mid2offset = 0.45 * width; //2nd medium row offset | |
let mid3offset = 0.5 * width; //3rd medium row offset | |
let row3_x = 0.6 * width; //big row position | |
let b1_size = height/10; //bubble sizes | |
let b2_size = height/7; | |
let b3_size = height/4.1; | |
const ease_frac = ease.smootherStep(cur_frac); //eased frames 0-1 | |
let grid_points1 = [ //points along the path of bubbles | |
1.25 * height, | |
1.00 * height, | |
0.75 * height, | |
0.50 * height, | |
0.25 * height, | |
0.0 * height, | |
-0.25 * height | |
] | |
let wave_grid_points1 = []; //adjust grid points into wave pattern using noise | |
for (let i=0; i<grid_points1.length; i++) { | |
let wave = getNoiseValue(grid_points1[i], row1_x, 0, "wave_grid_points1", -b1_size*0.75, b1_size*0.75, 100); | |
wave_grid_points1.push(row1_x + wave); | |
} | |
let wave_grid_points1a = []; //another set of wave points for variation | |
for (let i=0; i<grid_points1.length; i++) { | |
let wave1 = getNoiseValue(grid_points1[i], row1_x, 0, "wave_grid_points1a", -b1_size*0.75, b1_size*0.75, 100); | |
wave_grid_points1a.push(row1_x + wave1); | |
} | |
//draw small bubbles | |
for(let i=0; i<grid_points1.length-1; i++) { | |
let cur_y_pos = map(cur_frac, 0, 1, grid_points1[i], grid_points1[i+1]); | |
let cur_x_pos = map(ease_frac, 0, 1, wave_grid_points1[i], wave_grid_points1[i+1]); | |
let cur_x_pos2 = map(ease_frac, 0, 1, wave_grid_points1a[i], wave_grid_points1a[i+1]); | |
drawBubble(cur_x_pos2, cur_y_pos, b1_size); | |
drawBubble(cur_x_pos+sml2offset, cur_y_pos-height*0.2, b1_size); | |
drawBubble(cur_x_pos+sml3offset, cur_y_pos-height*0.1, b1_size); | |
} | |
let grid_points2 = [ | |
1.05 * height, | |
0.70 * height, | |
0.35 * height, | |
0 * height, | |
-0.35 * height | |
] | |
let wave_grid_points2 = []; | |
for (let i=0; i<grid_points2.length; i++) { | |
let wave2 = getNoiseValue(grid_points2[i], row2_x, 0, "wave_grid_points2", -b2_size*0.5, b2_size*0.5, 100); | |
wave_grid_points2.push(row2_x + wave2); | |
} | |
let wave_grid_points2a = []; | |
for (let i=0; i<grid_points2.length; i++) { | |
let wave2a = getNoiseValue(grid_points2[i], row2_x, 0, "wave_grid_points2a", -b2_size*0.5, b2_size*0.5, 100); | |
wave_grid_points2a.push(row2_x + wave2a); | |
} | |
//draw medium bubbles | |
for(let i=0; i<grid_points2.length-1; i++) { | |
let cur_y_pos = map(cur_frac, 0, 1, grid_points2[i], grid_points2[i+1]); | |
let cur_x_pos = map(ease_frac, 0, 1, wave_grid_points2[i], wave_grid_points2[i+1]); | |
let cur_x_pos2 = map(ease_frac, 0, 1, wave_grid_points1a[i], wave_grid_points1a[i+1]); | |
let cur_x_pos3 = map(ease_frac, 0, 1, wave_grid_points2a[i], wave_grid_points2a[i+1]); | |
drawBubble(cur_x_pos3, cur_y_pos, b2_size); | |
drawBubble(cur_x_pos+mid2offset, cur_y_pos+height*0.2, b2_size); | |
drawBubble(cur_x_pos2+mid3offset, cur_y_pos+height*0.1, b2_size); | |
} | |
let grid_points3 = [ | |
1.10 * height, | |
0.60 * height, | |
0.10 * height, | |
-0.40 * height | |
] | |
let wave_grid_points3 = []; | |
for (let i=0; i<grid_points3.length; i++) { | |
let wave3 = getNoiseValue(grid_points3[i], row3_x, 0, "wave_grid_points3", -b3_size*0.35, b3_size*0.35, 100); | |
wave_grid_points3.push(row3_x + wave3); | |
} | |
//draw big bubbles | |
for(let i=0; i<grid_points3.length-1; i++) { | |
let cur_y_pos = map(cur_frac, 0, 1, grid_points3[i], grid_points3[i+1]); | |
let cur_x_pos = map(ease_frac, 0, 1, wave_grid_points3[i], wave_grid_points3[i+1]); | |
drawBubble(cur_x_pos, cur_y_pos, b3_size); | |
} | |
//drawing the cat | |
push(); | |
scale(1.2); | |
translate(0,-height*0.15); | |
let change_direction = true; //set up variables for 50/50 back and forth | |
let amount_along = 0; | |
if (cur_frac < 0.5) { | |
change_direction = true; | |
amount_along = cur_frac * 2; | |
} | |
else { | |
change_direction = false; | |
amount_along = (cur_frac-0.5) * 2; | |
} | |
const eye_min = height*0.732; | |
const eye_max = height*0.768; | |
const ease_amount_along = ease.circularInOut(amount_along); | |
const ease_eyes = ease.circularInOut(cur_frac); | |
if(cur_frac <= 0.25){ //eyes move down for 1/4, up for 3/4 | |
eye_y_pos = map(ease_amount_along, 0, 1, eye_min, eye_max); | |
} | |
else{ | |
eye_y_pos = map(ease_eyes, 0, 1, eye_max, eye_min); | |
} | |
if(change_direction) { //tail flicking back and forth | |
tail_pos1 = map(ease_amount_along, 0, 1, height*0.325, height*0.375); | |
tail_pos2 = map(ease_amount_along, 0, 1, height*0.37, height*0.33); | |
tail_pos3 = map(ease_amount_along, 0, 1, height*0.36, height*0.34); | |
} | |
else { | |
tail_pos1 = map(ease_amount_along, 0, 1, height*0.375, height*0.325); | |
tail_pos2 = map(ease_amount_along, 0, 1, height*0.33, height*0.37); | |
tail_pos3 = map(ease_amount_along, 0, 1, height*0.34, height*0.36); | |
} | |
let catGrey = color(115, 131, 158); | |
let catLight = color(153, 170, 196); | |
let lightGreen = color(111, 189, 158); | |
let darkGreen = color(10, 144, 90); | |
noStroke(); | |
fill(155, 194, 224, 185); //shadows | |
let shadowX = height*0.025; | |
let shadowY = height*0.05; | |
beginShape(); //ears shadow | |
curveVertex(height*0.08+shadowX, height*0.52+shadowY); | |
curveVertex(height*0.08+shadowX, height*0.52+shadowY); | |
curveVertex(height*0.23+shadowX, height*0.55+shadowY); | |
curveVertex(height*0.155+shadowX, height*0.625+shadowY); | |
curveVertex(height*0.155+shadowX, height*0.625+shadowY); | |
endShape(CLOSE); | |
beginShape(); | |
curveVertex(height*0.08+shadowX, height*0.84+shadowY); | |
curveVertex(height*0.08+shadowX, height*0.84+shadowY); | |
curveVertex(height*0.23+shadowX, height*0.8+shadowY); | |
curveVertex(height*0.155+shadowX, height*0.735+shadowY); | |
curveVertex(height*0.155+shadowX, height*0.735+shadowY); | |
endShape(CLOSE); | |
fill(155, 194, 224, 205); | |
ellipse(width*0.005+shadowX, height*0.68+shadowY, height*0.31, height*0.36); //head shadow | |
noFill(); | |
stroke(155, 194, 224, 185); //tail shadow | |
strokeWeight(height*0.07); | |
beginShape(); | |
curveVertex(-height*0.05+shadowX, height*0.35+shadowY); | |
curveVertex(-height*0.05+shadowX, height*0.35+shadowY); | |
curveVertex(height*0.12+shadowX, tail_pos1+shadowY); | |
curveVertex(height*0.25+shadowX, tail_pos2+shadowY); | |
curveVertex(height*0.3+shadowX, tail_pos3+shadowY); | |
curveVertex(height*0.3+shadowX, tail_pos3+shadowY); | |
endShape(); | |
strokeWeight(height*0.007); //whisker shadow | |
line(width*0.015+shadowX, height*0.858+shadowY, width*0.02+shadowX, height*0.88+shadowY); | |
line(width*0.001+shadowX, height*0.858+shadowY, width*0.005+shadowX, height*0.89+shadowY); | |
//end of shadows | |
noStroke(); | |
fill(catGrey); | |
beginShape(); //ears | |
curveVertex(height*0.08, height*0.52); | |
curveVertex(height*0.08, height*0.52); | |
curveVertex(height*0.23, height*0.55); | |
curveVertex(height*0.13, height*0.64); | |
curveVertex(height*0.13, height*0.64); | |
endShape(CLOSE); | |
beginShape(); | |
curveVertex(height*0.08, height*0.84); | |
curveVertex(height*0.08, height*0.84); | |
curveVertex(height*0.23, height*0.8); | |
curveVertex(height*0.13, height*0.72); | |
curveVertex(height*0.13, height*0.72); | |
endShape(CLOSE); | |
fill(103, 118, 143); //ear shading | |
beginShape(); | |
curveVertex(height*0.08, height*0.84); | |
curveVertex(height*0.08, height*0.84); | |
curveVertex(height*0.23, height*0.8); | |
curveVertex(height*0.09, height*0.75); | |
curveVertex(height*0.09, height*0.75); | |
endShape(CLOSE); | |
push(); | |
fill(catLight); | |
scale(0.6); | |
beginShape(); //inner ears | |
curveVertex(height*0.15, height*0.91); | |
curveVertex(height*0.15, height*0.91); | |
curveVertex(height*0.35, height*0.94); | |
curveVertex(height*0.25, height*1.03); | |
curveVertex(height*0.25, height*1.03); | |
endShape(CLOSE); | |
beginShape(); | |
curveVertex(height*0.15, height*1.355); | |
curveVertex(height*0.15, height*1.355); | |
curveVertex(height*0.35, height*1.315); | |
curveVertex(height*0.25, height*1.235); | |
curveVertex(height*0.25, height*1.235); | |
endShape(CLOSE); | |
fill(143, 160, 186); //inner ear shading | |
beginShape(); | |
curveVertex(height*0.15, height*1.355); | |
curveVertex(height*0.15, height*1.355); | |
curveVertex(height*0.35, height*1.317); | |
curveVertex(height*0.23, height*1.27); | |
curveVertex(height*0.23, height*1.27); | |
endShape(CLOSE); | |
pop(); | |
fill(103, 118, 143); //head shading | |
ellipse(width*0.005, height*0.68, height*0.31, height*0.36); | |
fill(catGrey); //head | |
ellipse(width*0.005, height*0.67, height*0.295, height*0.345); | |
push(); | |
translate(-height*0.003, height*0.012); //tail shading | |
noFill(); | |
stroke(103, 118, 143); | |
strokeWeight(height*0.074); | |
beginShape(); | |
curveVertex(-height*0.05, height*0.35); | |
curveVertex(-height*0.05, height*0.35); | |
curveVertex(height*0.12, tail_pos1); | |
curveVertex(height*0.25, tail_pos2); | |
curveVertex(height*0.3, tail_pos3); | |
curveVertex(height*0.3, tail_pos3); | |
endShape(); | |
stroke(143, 160, 186); | |
strokeWeight(height*0.075); | |
strokeCap(SQUARE); | |
beginShape(); | |
curveVertex(height*0.09, tail_pos1); | |
curveVertex(height*0.25, tail_pos2); | |
curveVertex(height*0.3, tail_pos3); | |
curveVertex(height*0.3, tail_pos3); | |
endShape(); | |
noStroke(); | |
fill(143, 160, 186); | |
ellipse(height*0.3, tail_pos3, height*0.075); | |
pop(); | |
noFill(); | |
stroke(catGrey); //drawing tail | |
strokeWeight(height*0.07); | |
beginShape(); | |
curveVertex(-height*0.05, height*0.35); | |
curveVertex(-height*0.05, height*0.35); | |
curveVertex(height*0.12, tail_pos1); | |
curveVertex(height*0.25, tail_pos2); | |
curveVertex(height*0.3, tail_pos3); | |
curveVertex(height*0.3, tail_pos3); | |
endShape(); | |
noStroke(); | |
fill(143, 160, 186); //extra shading | |
ellipse(height*0.25, tail_pos2+height*0.033, height*0.031); | |
noFill(); | |
stroke(catLight); //lighter end of tail | |
strokeWeight(height*0.071); | |
strokeCap(SQUARE); | |
beginShape(); | |
curveVertex(height*0.09, tail_pos1); | |
curveVertex(height*0.25, tail_pos2); | |
curveVertex(height*0.3, tail_pos3); | |
curveVertex(height*0.3, tail_pos3); | |
endShape(); | |
noStroke(); | |
fill(catLight); //extra circles for end of tail | |
ellipse(height*0.3, tail_pos3, height*0.071); | |
ellipse(height*0.25, tail_pos2, height*0.031); | |
ellipse(height*0.25, tail_pos2+height*0.02, height*0.031); | |
ellipse(height*0.25, tail_pos2-height*0.02, height*0.031); | |
noStroke(); | |
fill(lightGreen); //eyes | |
ellipse(width*0.025, height*0.75, height*0.055, height*0.06); | |
ellipse(width*0.025, height*0.61, height*0.055, height*0.06); | |
fill(lerpColor(lightGreen,darkGreen,0.25)); //eye gradient | |
ellipse(width*0.025, eye_y_pos, height*0.048, height*0.053); | |
ellipse(width*0.025, eye_y_pos-height*0.14, height*0.048, height*0.053); | |
fill(lerpColor(lightGreen,darkGreen,0.5)); | |
ellipse(width*0.025, eye_y_pos, height*0.043, height*0.048); | |
ellipse(width*0.025, eye_y_pos-height*0.14, height*0.043, height*0.048); | |
fill(lerpColor(lightGreen,darkGreen,0.75)); | |
ellipse(width*0.025, eye_y_pos, height*0.038, height*0.043); | |
ellipse(width*0.025, eye_y_pos-height*0.14, height*0.038, height*0.043); | |
fill(darkGreen); | |
ellipse(width*0.025, eye_y_pos, height*0.033, height*0.038); | |
ellipse(width*0.025, eye_y_pos-height*0.14, height*0.033, height*0.038); | |
fill(10); //pupils | |
ellipse(width*0.025, eye_y_pos, height*0.04, height*0.015); | |
ellipse(width*0.025, eye_y_pos-height*0.14, height*0.04, height*0.015); | |
fill(240, 245, 255); //shine | |
ellipse(width*0.023, height*0.73, height*0.01, height*0.015); | |
ellipse(width*0.023, height*0.59, height*0.01, height*0.015); | |
noFill(); //circle around eyes to cover extra circles | |
strokeWeight(height*0.05); | |
stroke(catGrey); | |
ellipse(width*0.025, height*0.75, height*0.105, height*0.11); | |
ellipse(width*0.025, height*0.61, height*0.105, height*0.11); | |
stroke(catLight); //whiskers | |
strokeCap(ROUND); | |
strokeWeight(height*0.007); | |
line(-width*0.018, height*0.72, width*0.02, height*0.88); | |
line(-width*0.018, height*0.72, width*0.005, height*0.89); | |
line(-width*0.018, height*0.64, width*0.02, height*0.48); | |
line(-width*0.018, height*0.64, width*0.005, height*0.47); | |
pop(); | |
} | |
function drawBubble(x,y,s){ | |
angleMode(DEGREES); | |
let b1_size = height/10; //bubble sizes | |
let b2_size = height/7; | |
let b3_size = height/4.1; | |
let green = color(82, 235, 150, 50); | |
let blue = color(70, 154, 250, 50); | |
let purple = color(191, 106, 247, 50); | |
let pink = color(252, 109, 248, 50); | |
noStroke(); | |
fill(168, 207, 237, 150); | |
if(s==b3_size){ | |
fill(168, 207, 237, 115); | |
ellipse(x+height*0.06,y+height*0.1,s*0.9); | |
} | |
else if(s==b2_size){ | |
ellipse(x+height*0.04,y+height*0.054,s*0.9); | |
} | |
else{ | |
ellipse(x+height*0.015,y+height*0.03,s*0.9); | |
} | |
noFill(); | |
//colour gradient | |
strokeWeight(s*0.1); | |
stroke(green); | |
ellipse(x,y,s*0.3); | |
ellipse(x,y,s*0.333); | |
stroke(lerpColor(green,blue,0.33)); | |
ellipse(x,y,s*0.366); | |
ellipse(x,y,s*0.4); | |
stroke(lerpColor(green,blue,0.66)); | |
ellipse(x,y,s*0.433); | |
ellipse(x,y,s*0.466); | |
ellipse(x,y,s*0.5); | |
stroke(blue); | |
ellipse(x,y,s*0.533); | |
ellipse(x,y,s*0.566); | |
ellipse(x,y,s*0.6); | |
stroke(lerpColor(blue,purple,0.33)); | |
ellipse(x,y,s*0.633); | |
ellipse(x,y,s*0.666); | |
ellipse(x,y,s*0.7); | |
ellipse(x,y,s*0.733); | |
stroke(lerpColor(blue,purple,0.66)); | |
ellipse(x,y,s*0.766); | |
ellipse(x,y,s*0.8); | |
ellipse(x,y,s*0.833); | |
stroke(purple); | |
ellipse(x,y,s*0.866); | |
ellipse(x,y,s*0.9); | |
stroke(lerpColor(purple,pink,0.5)); | |
ellipse(x,y,s*0.933); | |
stroke(pink); | |
strokeWeight(s*0.05); | |
ellipse(x,y,s*1); | |
//white highlights | |
stroke(255,230); | |
strokeWeight(s*0.05); | |
arc(x,y,s,s,-185,-70); | |
arc(x,y,s,s,-210,-200); | |
arc(x,y,s,s,25,80); | |
stroke(255,240); | |
strokeWeight(s*0.225); | |
arc(x,y,s/2,s/2,-180,-80); | |
strokeWeight(s*0.2); | |
arc(x,y,s/2,s/2,35,60); | |
} | |
/* | |
* FaceMap class - holds all informaiton about one mapped | |
* face and is able to draw itself. | |
*/ | |
// remove this or set to false to enable full program (load will be slower) | |
var DEBUG_MODE = true; | |
// this can be used to set the number of sliders to show | |
var NUM_SLIDERS = 3; | |
// other variables can be in here too | |
const fg_color = [255]; | |
// example of a global function | |
// given a segment, this returns the average point [x, y] | |
function segment_average(segment) { | |
let sum_x = 0; | |
let sum_y = 0; | |
let s_len = segment.length; | |
for (let i=0; i<s_len; i++) { | |
sum_x = sum_x + segment[i][0]; | |
sum_y = sum_y + segment[i][1]; | |
} | |
return [sum_x / s_len , sum_y / s_len ]; | |
} | |
// This where you define your own face object | |
function Face() { | |
// these are state variables for a face | |
// (your variables should be different!) | |
this.eye_type = 1; // can be either 1 (cyclops) or 2 (two eyes) | |
this.eye_shift = -1; // range is -10 to 10 | |
this.mouth_value = 1; // range is 0.5 to 8 | |
// example of a function *inside* the face object. | |
// this draws a segment, and do_loop will connect the ends if true | |
this.draw_segment = function(segment, do_loop) { | |
for(let i=0; i<segment.length; i++) { | |
let px = segment[i][0]; | |
let py = segment[i][1]; | |
if(i < segment.length - 1) { | |
let nx = segment[i+1][0]; | |
let ny = segment[i+1][1]; | |
ellipse(px,py,.1) | |
line(px, py, nx, ny); | |
} | |
else if(do_loop) { | |
let nx = segment[0][0]; | |
let ny = segment[0][1]; | |
line(px, py, nx, ny); | |
} | |
} | |
}; | |
/* | |
* Draw the face with position lists that include: | |
* chin, right_eye, left_eye, right_eyebrow, left_eyebrow | |
* bottom_lip, top_lip, nose_tip, nose_bridge, | |
*/ | |
this.draw = function(positions) { | |
rectMode(CENTER) | |
strokeWeight(.01); | |
// eyebrows | |
stroke(fg_color); | |
let left_eyebrow_pos = segment_average(positions.left_eyebrow); | |
this.draw_segment(positions.left_eyebrow); | |
let right_eyebrow_pos = segment_average(positions.right_eyebrow); | |
this.draw_segment(positions.right_eyebrow); | |
// draw segments of face using points | |
stroke(fg_color); | |
this.draw_segment(positions.chin); | |
stroke(fg_color); | |
this.draw_segment(positions.nose_bridge); | |
let nose_tip_pos = segment_average(positions.nose_tip); | |
this.draw_segment(positions.nose_tip); | |
stroke(fg_color); | |
this.draw_segment(positions.top_lip); | |
let top_lip_pos = segment_average(positions.top_lip); | |
this.draw_segment(positions.bottom_lip); | |
stroke(fg_color); | |
let left_eye_pos = segment_average(positions.left_eye); | |
this.draw_segment(positions.left_eye, true); | |
let right_eye_pos = segment_average(positions.right_eye); | |
this.draw_segment(positions.right_eye, true); | |
// eyes | |
noStroke(); | |
let curEyeShift = 0.04 * this.eye_shift; | |
fill(fg_color); | |
ellipse(left_eye_pos[0] + curEyeShift, left_eye_pos[1], 0.18); | |
ellipse(right_eye_pos[0] + curEyeShift, right_eye_pos[1], 0.18); | |
var gridSize = 3; | |
var cellSize = .75; | |
/*** TOM ADDED THIS LOOP #1 to "color in" the face ***/ | |
// this is a lookup table that keeps track of previous cells | |
let leftmost_cell_in_row = []; | |
for (let y = -gridSize; y < gridSize; y+=cellSize) { | |
// each row we'll draw one line from left_x to right_x | |
let left_x = null; | |
let right_x = null; | |
for (let x = -gridSize; x < gridSize; x+=cellSize) { | |
let lastX = null; | |
let lastY = null; | |
for (let i = 0; i < positions.chin.length; i++) { | |
if (positions.chin[i][0] > (x - cellSize/2) && positions.chin[i][0] < (x + cellSize/2) && positions.chin[i][1] > (y - cellSize/2) && positions.chin[i][1] < (y + cellSize/2)) { | |
if (x != lastX && y != lastY) { | |
lastX = x; | |
lastY = y; | |
// first check if this is the first one | |
if (left_x == null) { | |
// just record this for both left and right | |
left_x = x; | |
right_x = x; | |
} | |
else if(x < left_x) { | |
// new left_x | |
left_x = x; | |
} | |
else if(x > right_x) { | |
right_x = x; | |
} | |
} | |
} | |
} | |
noFill(); | |
stroke(200,50) | |
rect(x, y, cellSize, cellSize); | |
} | |
if (left_x != null) { | |
// now draw the line (series of cells) | |
fill(255,255,0); | |
stroke(200,10) | |
for(let j=left_x; j<=right_x; j+=cellSize) { | |
rect(j, y, cellSize, cellSize); | |
} | |
} | |
} | |
/*** END OF TOM'S ADDITONS ***/ | |
for (let x = -gridSize; x < gridSize; x+=cellSize) { | |
for (let y = -gridSize; y < gridSize; y+=cellSize) { | |
let lastX = null; | |
let lastY = null; | |
if (left_eye_pos[0] > (x - cellSize/2) && left_eye_pos[0] < (x + cellSize/2) && left_eye_pos[1] > (y - cellSize/2) && left_eye_pos[1] < (y + cellSize/2)) { | |
if (this.eye_type === 1) { | |
eye1(x,y); | |
} else { | |
eye2(x,y); | |
} | |
} else if (right_eye_pos[0] > (x - cellSize/2) && right_eye_pos[0] < (x + cellSize/2) && right_eye_pos[1] > (y - cellSize/2) && right_eye_pos[1] < (y + cellSize/2)) { | |
if (this.eye_type === 1) { | |
eye1(x,y); | |
} else { | |
eye2(x,y); | |
} | |
} else if (positions.nose_bridge[0][0] > (x - cellSize/2) && positions.nose_bridge[0][0] < (x + cellSize/2) && positions.nose_bridge[0][1] > (y - cellSize/2) && positions.nose_bridge[0][1] < (y + cellSize/2)) { | |
noseBridge(x, y); | |
} else if (positions.nose_bridge[3][0] > (x - cellSize/2) && positions.nose_bridge[3][0] < (x + cellSize/2) && positions.nose_bridge[3][1] > (y - cellSize/2) && positions.nose_bridge[3][1] < (y + cellSize/2)) { | |
noseTip(x, y) | |
} else if (top_lip_pos[0] > (x - cellSize/2) && top_lip_pos[0] < (x + cellSize/2) && top_lip_pos[1] > (y - cellSize/2) && top_lip_pos[1] < (y + cellSize/2)) { | |
mouth(x, y); | |
} else if (left_eyebrow_pos[0] > (x - cellSize/2) && left_eyebrow_pos[0] < (x + cellSize/2) && left_eyebrow_pos[1] > (y - cellSize/2) && left_eyebrow_pos[1] < (y + cellSize/2)) { | |
eyebrow(x, y); | |
} else if (right_eyebrow_pos[0] > (x - cellSize/2) && right_eyebrow_pos[0] < (x + cellSize/2) && right_eyebrow_pos[1] > (y - cellSize/2) && right_eyebrow_pos[1] < (y + cellSize/2)) { | |
eyebrow(x, y); | |
} else { | |
for (let i = 0; i < positions.chin.length; i++) { | |
if (positions.chin[i][0] > (x - cellSize/2) && positions.chin[i][0] < (x + cellSize/2) && positions.chin[i][1] > (y - cellSize/2) && positions.chin[i][1] < (y + cellSize/2)) { | |
if (x != lastX && y != lastY) { | |
fill(255,0,0); | |
stroke(200,10) | |
lastX = x; | |
lastY = y; | |
rect(x, y, cellSize, cellSize); | |
} | |
} | |
} | |
} | |
noFill(); | |
stroke(200,50) | |
rect(x, y, cellSize, cellSize); | |
} | |
} | |
// print(this.eye_type) | |
function eye1(x, y) { | |
fill(255, 0, 0); | |
beginShape() | |
vertex(x - cellSize/2, y - cellSize/2); | |
vertex(x + cellSize/2, y - cellSize/2); | |
vertex(x + cellSize/2, y + cellSize/2); | |
vertex(x - cellSize/2, y + cellSize/2); | |
endShape(CLOSE); | |
fill(255); | |
beginShape(); | |
vertex(x - cellSize/2, y); | |
bezierVertex(x - cellSize/4, y, x - cellSize/4, y + cellSize/4, x, y + cellSize/4); | |
bezierVertex(x + cellSize/4, y + cellSize/4, x + cellSize/4, y, x + cellSize/2, y); | |
bezierVertex(x + cellSize/4, y, x + cellSize/4, y - cellSize/4, x, y - cellSize/4); | |
bezierVertex(x - cellSize/4, y - cellSize/4, x - cellSize/4, y, x - cellSize/2, y); | |
endShape(); | |
fill(0) | |
ellipse(x,y,cellSize/4) | |
} | |
function eye2(x, y) { | |
fill(255, 0, 0); | |
beginShape() | |
vertex(x - cellSize/2, y - cellSize/2); | |
vertex(x + cellSize/2, y - cellSize/2); | |
vertex(x + cellSize/2, y + cellSize/2); | |
vertex(x - cellSize/2, y + cellSize/2); | |
endShape(CLOSE); | |
fill(255); | |
ellipse(x, y, cellSize) | |
fill(0) | |
ellipse(x,y,cellSize/4) | |
} | |
function noseBridge(x,y) { | |
fill(255,0,0) | |
beginShape(); | |
vertex(x - cellSize/2, y - cellSize/2); | |
vertex(x + positions.nose_bridge[0][0], y - cellSize/2); | |
vertex(x + positions.nose_bridge[0][0], y + cellSize/2); | |
vertex(x - cellSize/2, y + cellSize/2); | |
endShape(CLOSE); | |
fill(255); | |
beginShape(); | |
vertex(x + cellSize/2, y - cellSize/2); | |
vertex(x + positions.nose_bridge[0][0], y - cellSize/2); | |
vertex(x + positions.nose_bridge[0][0], y + cellSize/2); | |
vertex(x + cellSize/2, y + cellSize/2); | |
endShape(CLOSE); | |
} | |
function noseTip(x,y) { | |
fill(255,0,0) | |
beginShape(); | |
vertex(x - cellSize/2, y + nose_tip_pos[1]); | |
vertex(x + cellSize/2, y + nose_tip_pos[1]); | |
vertex(x + cellSize/2, y + cellSize/2); | |
vertex(x - cellSize/2, y + cellSize/2); | |
endShape(CLOSE); | |
fill(255); | |
beginShape(); | |
vertex(x - cellSize/2, y + nose_tip_pos[1]); | |
vertex(x + cellSize/2, y + nose_tip_pos[1]); | |
vertex(x + cellSize/2, y - cellSize/2); | |
vertex(x - cellSize/2, y - cellSize/2); | |
endShape(CLOSE); | |
} | |
function mouth(x,y) { | |
fill(255,0,0) | |
beginShape(); | |
vertex(x - cellSize/2, y + cellSize/8); | |
vertex(x + cellSize/2, y + cellSize/8); | |
vertex(x + cellSize/2, y + cellSize/2); | |
vertex(x - cellSize/2, y + cellSize/2); | |
endShape(CLOSE); | |
fill(255,0,0) | |
beginShape(); | |
vertex(x - cellSize/2, y - cellSize/8); | |
vertex(x + cellSize/2, y - cellSize/8); | |
vertex(x + cellSize/2, y - cellSize/2); | |
vertex(x - cellSize/2, y - cellSize/2); | |
endShape(CLOSE); | |
fill(255) | |
beginShape(); | |
vertex(x - cellSize/2, y + cellSize/8); | |
vertex(x - cellSize/2, y - cellSize/8); | |
vertex(x + cellSize/2, y - cellSize/8); | |
vertex(x + cellSize/2, y + cellSize/8); | |
endShape(CLOSE); | |
} | |
function eyebrow(x,y) { | |
fill(255) | |
beginShape(); | |
vertex(x - cellSize/2, y + cellSize/8); | |
vertex(x - cellSize/2, y - cellSize/8); | |
vertex(x + cellSize/2, y - cellSize/8); | |
vertex(x + cellSize/2, y + cellSize/8); | |
endShape(CLOSE); | |
} | |
} | |
/* set internal properties based on list numbers 0-100 */ | |
this.setProperties = function(settings) { | |
this.eye_type = int(map(settings[0], 0, 100, 1, 2)); | |
this.eye_shift = map(settings[1], 0, 100, -2, 2); | |
this.mouth_value = map(settings[2], 0, 100, 0.5, 8); | |
} | |
/* get internal properties as list of numbers 0-100 */ | |
this.getProperties = function() { | |
let settings = new Array(3); | |
settings[0] = map(this.eye_type, 1, 2, 0, 100); | |
settings[1] = map(this.eye_shift, -2, 2, 0, 100); | |
settings[2] = map(this.mouth_value, 0.5, 8, 0, 100); | |
return settings; | |
} | |
} |
/** | |
* This class lets you encode animated GIF files | |
* Base class : http://www.java2s.com/Code/Java/2D-Graphics-GUI/AnimatedGifEncoder.htm | |
* @author Kevin Weiner (original Java version - [email protected]) | |
* @author Thibault Imbert (AS3 version - bytearray.org) | |
* @author Kevin Kwok (JavaScript version - https://github.com/antimatter15/jsgif) | |
* @version 0.1 AS3 implementation | |
*/ | |
GIFEncoder = function() { | |
for (var i = 0, chr = {}; i < 256; i++) | |
chr[i] = String.fromCharCode(i); | |
function ByteArray() { | |
this.bin = []; | |
} | |
ByteArray.prototype.getData = function() { | |
for (var v = '', l = this.bin.length, i = 0; i < l; i++) | |
v += chr[this.bin[i]]; | |
return v; | |
}; | |
ByteArray.prototype.writeByte = function(val) { | |
this.bin.push(val); | |
}; | |
ByteArray.prototype.writeUTFBytes = function(string) { | |
for (var l = string.length, i = 0; i < l; i++) | |
this.writeByte(string.charCodeAt(i)); | |
}; | |
ByteArray.prototype.writeBytes = function(array, offset, length) { | |
for (var l = length || array.length, i = offset || 0; i < l; i++) | |
this.writeByte(array[i]); | |
}; | |
var exports = {}; | |
var width; // image size | |
var height; | |
var transparent = null; // transparent color if given | |
var transIndex; // transparent index in color table | |
var repeat = -1; // no repeat | |
var delay = 0; // frame delay (hundredths) | |
var started = false; // ready to output frames | |
var out; | |
var image; // current frame | |
var pixels; // BGR byte array from frame | |
var indexedPixels; // converted frame indexed to palette | |
var colorDepth; // number of bit planes | |
var colorTab; // RGB palette | |
var usedEntry = []; // active palette entries | |
var palSize = 7; // color table size (bits-1) | |
var dispose = -1; // disposal code (-1 = use default) | |
var closeStream = false; // close stream when finished | |
var firstFrame = true; | |
var sizeSet = false; // if false, get size from first frame | |
var sample = 10; // default sample interval for quantizer | |
var comment = "Generated by jsgif (https://github.com/antimatter15/jsgif/)"; // default comment for generated gif | |
/** | |
* Sets the delay time between each frame, or changes it for subsequent frames | |
* (applies to last frame added) | |
* int delay time in milliseconds | |
* @param ms | |
*/ | |
var setDelay = exports.setDelay = function setDelay(ms) { | |
delay = Math.round(ms / 10); | |
}; | |
/** | |
* Sets the GIF frame disposal code for the last added frame and any | |
* | |
* subsequent frames. Default is 0 if no transparent color has been set, | |
* otherwise 2. | |
* @param code | |
* int disposal code. | |
*/ | |
var setDispose = exports.setDispose = function setDispose(code) { | |
if (code >= 0) dispose = code; | |
}; | |
/** | |
* Sets the number of times the set of GIF frames should be played. Default is | |
* 1; 0 means play indefinitely. Must be invoked before the first image is | |
* added. | |
* | |
* @param iter | |
* int number of iterations. | |
* @return | |
*/ | |
var setRepeat = exports.setRepeat = function setRepeat(iter) { | |
if (iter >= 0) repeat = iter; | |
}; | |
/** | |
* Sets the transparent color for the last added frame and any subsequent | |
* frames. Since all colors are subject to modification in the quantization | |
* process, the color in the final palette for each frame closest to the given | |
* color becomes the transparent color for that frame. May be set to null to | |
* indicate no transparent color. | |
* @param | |
* Color to be treated as transparent on display. | |
*/ | |
var setTransparent = exports.setTransparent = function setTransparent(c) { | |
transparent = c; | |
}; | |
/** | |
* Sets the comment for the block comment | |
* @param | |
* string to be insterted as comment | |
*/ | |
var setComment = exports.setComment = function setComment(c) { | |
comment = c; | |
}; | |
/** | |
* The addFrame method takes an incoming BitmapData object to create each frames | |
* @param | |
* BitmapData object to be treated as a GIF's frame | |
*/ | |
var addFrame = exports.addFrame = function addFrame(im, is_imageData) { | |
if ((im === null) || !started || out === null) { | |
throw new Error("Please call start method before calling addFrame"); | |
} | |
var ok = true; | |
try { | |
if (!is_imageData) { | |
image = im.getImageData(0, 0, im.canvas.width, im.canvas.height).data; | |
if (!sizeSet) setSize(im.canvas.width, im.canvas.height); | |
} else { | |
if(im instanceof ImageData) { | |
image = im.data; | |
if(!sizeset || width!=im.width || height!=im.height) { | |
setSize(im.width,im.height); | |
} else { | |
} | |
} else if(im instanceof Uint8ClampedArray) { | |
if(im.length==(width*height*4)) { | |
image=im; | |
} else { | |
console.log("Please set the correct size: ImageData length mismatch"); | |
ok=false; | |
} | |
} else { | |
console.log("Please provide correct input"); | |
ok=false; | |
} | |
} | |
getImagePixels(); // convert to correct format if necessary | |
analyzePixels(); // build color table & map pixels | |
if (firstFrame) { | |
writeLSD(); // logical screen descriptior | |
writePalette(); // global color table | |
if (repeat >= 0) { | |
// use NS app extension to indicate reps | |
writeNetscapeExt(); | |
} | |
} | |
writeGraphicCtrlExt(); // write graphic control extension | |
if (comment !== '') { | |
writeCommentExt(); // write comment extension | |
} | |
writeImageDesc(); // image descriptor | |
if (!firstFrame) writePalette(); // local color table | |
writePixels(); // encode and write pixel data | |
firstFrame = false; | |
} catch (e) { | |
ok = false; | |
} | |
return ok; | |
}; | |
/** | |
* @description: Downloads the encoded gif with the given name | |
* No need of any conversion from the stream data (out) to base64 | |
* Solves the issue of large file sizes when there are more frames | |
* and does not involve in creation of any temporary data in the process | |
* so no wastage of memory, and speeds up the process of downloading | |
* to just calling this function. | |
* @parameter {String} filename filename used for downloading the gif | |
*/ | |
var download = exports.download = function download(filename) { | |
if(out===null || closeStream==false) { | |
console.log("Please call start method and add frames and call finish method before calling download"); | |
} else { | |
filename= filename !== undefined ? ( filename.endsWith(".gif")? filename: filename+".gif" ): "download.gif"; | |
var templink = document.createElement("a"); | |
templink.download=filename; | |
templink.href= URL.createObjectURL(new Blob([new Uint8Array(out.bin)], {type : "image/gif" } )); | |
templink.click(); | |
} | |
} | |
/** | |
* Adds final trailer to the GIF stream, if you don't call the finish method | |
* the GIF stream will not be valid. | |
*/ | |
var finish = exports.finish = function finish() { | |
if (!started) return false; | |
var ok = true; | |
started = false; | |
try { | |
out.writeByte(0x3b); // gif trailer | |
closeStream=true; | |
} catch (e) { | |
ok = false; | |
} | |
return ok; | |
}; | |
/** | |
* Resets some members so that a new stream can be started. | |
* This method is actually called by the start method | |
*/ | |
var reset = function reset() { | |
// reset for subsequent use | |
transIndex = 0; | |
image = null; | |
pixels = null; | |
indexedPixels = null; | |
colorTab = null; | |
closeStream = false; | |
firstFrame = true; | |
}; | |
/** | |
* * Sets frame rate in frames per second. Equivalent to | |
* <code>setDelay(1000/fps)</code>. | |
* @param fps | |
* float frame rate (frames per second) | |
*/ | |
var setFrameRate = exports.setFrameRate = function setFrameRate(fps) { | |
if (fps != 0xf) delay = Math.round(100 / fps); | |
}; | |
/** | |
* Sets quality of color quantization (conversion of images to the maximum 256 | |
* colors allowed by the GIF specification). Lower values (minimum = 1) | |
* produce better colors, but slow processing significantly. 10 is the | |
* default, and produces good color mapping at reasonable speeds. Values | |
* greater than 20 do not yield significant improvements in speed. | |
* @param quality | |
* int greater than 0. | |
* @return | |
*/ | |
var setQuality = exports.setQuality = function setQuality(quality) { | |
if (quality < 1) quality = 1; | |
sample = quality; | |
}; | |
/** | |
* Sets the GIF frame size. The default size is the size of the first frame | |
* added if this method is not invoked. | |
* @param w | |
* int frame width. | |
* @param h | |
* int frame width. | |
*/ | |
var setSize = exports.setSize = function setSize(w, h) { | |
if (started && !firstFrame) return; | |
width = w; | |
height = h; | |
if (width < 1) width = 320; | |
if (height < 1) height = 240; | |
sizeSet = true; | |
}; | |
/** | |
* Initiates GIF file creation on the given stream. | |
* @param os | |
* OutputStream on which GIF images are written. | |
* @return false if initial write failed. | |
*/ | |
var start = exports.start = function start() { | |
reset(); | |
var ok = true; | |
closeStream = false; | |
out = new ByteArray(); | |
try { | |
out.writeUTFBytes("GIF89a"); // header | |
} catch (e) { | |
ok = false; | |
} | |
return started = ok; | |
}; | |
var cont = exports.cont = function cont() { | |
reset(); | |
var ok = true; | |
closeStream = false; | |
out = new ByteArray(); | |
return started = ok; | |
}; | |
/** | |
* Analyzes image colors and creates color map. | |
*/ | |
var analyzePixels = function analyzePixels() { | |
var len = pixels.length; | |
var nPix = len / 3; | |
indexedPixels = []; | |
var nq = new NeuQuant(pixels, len, sample); | |
// initialize quantizer | |
colorTab = nq.process(); // create reduced palette | |
// map image pixels to new palette | |
var k = 0; | |
for (var j = 0; j < nPix; j++) { | |
var index = nq.map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff); | |
usedEntry[index] = true; | |
indexedPixels[j] = index; | |
} | |
pixels = null; | |
colorDepth = 8; | |
palSize = 7; | |
// get closest match to transparent color if specified | |
if (transparent !== null) { | |
transIndex = findClosest(transparent); | |
} | |
}; | |
/** | |
* Returns index of palette color closest to c | |
*/ | |
var findClosest = function findClosest(c) { | |
if (colorTab === null) return -1; | |
var r = (c & 0xFF0000) >> 16; | |
var g = (c & 0x00FF00) >> 8; | |
var b = (c & 0x0000FF); | |
var minpos = 0; | |
var dmin = 256 * 256 * 256; | |
var len = colorTab.length; | |
for (var i = 0; i < len;) { | |
var dr = r - (colorTab[i++] & 0xff); | |
var dg = g - (colorTab[i++] & 0xff); | |
var db = b - (colorTab[i] & 0xff); | |
var d = dr * dr + dg * dg + db * db; | |
var index = i / 3; | |
if (usedEntry[index] && (d < dmin)) { | |
dmin = d; | |
minpos = index; | |
} | |
i++; | |
} | |
return minpos; | |
}; | |
/** | |
* Extracts image pixels into byte array "pixels | |
*/ | |
var getImagePixels = function getImagePixels() { | |
var w = width; | |
var h = height; | |
pixels = []; | |
var data = image; | |
var count = 0; | |
for (var i = 0; i < h; i++) { | |
for (var j = 0; j < w; j++) { | |
var b = (i * w * 4) + j * 4; | |
pixels[count++] = data[b]; | |
pixels[count++] = data[b + 1]; | |
pixels[count++] = data[b + 2]; | |
} | |
} | |
}; | |
/** | |
* Writes Graphic Control Extension | |
*/ | |
var writeGraphicCtrlExt = function writeGraphicCtrlExt() { | |
out.writeByte(0x21); // extension introducer | |
out.writeByte(0xf9); // GCE label | |
out.writeByte(4); // data block size | |
var transp; | |
var disp; | |
if (transparent === null) { | |
transp = 0; | |
disp = 0; // dispose = no action | |
} else { | |
transp = 1; | |
disp = 2; // force clear if using transparent color | |
} | |
if (dispose >= 0) { | |
disp = dispose & 7; // user override | |
} | |
disp <<= 2; | |
// packed fields | |
out.writeByte(0 | // 1:3 reserved | |
disp | // 4:6 disposal | |
0 | // 7 user input - 0 = none | |
transp); // 8 transparency flag | |
WriteShort(delay); // delay x 1/100 sec | |
out.writeByte(transIndex); // transparent color index | |
out.writeByte(0); // block terminator | |
}; | |
/** | |
* Writes Comment Extention | |
*/ | |
var writeCommentExt = function writeCommentExt() { | |
out.writeByte(0x21); // extension introducer | |
out.writeByte(0xfe); // comment label | |
out.writeByte(comment.length); // Block Size (s) | |
out.writeUTFBytes(comment); | |
out.writeByte(0); // block terminator | |
}; | |
/** | |
* Writes Image Descriptor | |
*/ | |
var writeImageDesc = function writeImageDesc() { | |
out.writeByte(0x2c); // image separator | |
WriteShort(0); // image position x,y = 0,0 | |
WriteShort(0); | |
WriteShort(width); // image size | |
WriteShort(height); | |
// packed fields | |
if (firstFrame) { | |
// no LCT - GCT is used for first (or only) frame | |
out.writeByte(0); | |
} else { | |
// specify normal LCT | |
out.writeByte(0x80 | // 1 local color table 1=yes | |
0 | // 2 interlace - 0=no | |
0 | // 3 sorted - 0=no | |
0 | // 4-5 reserved | |
palSize); // 6-8 size of color table | |
} | |
}; | |
/** | |
* Writes Logical Screen Descriptor | |
*/ | |
var writeLSD = function writeLSD() { | |
// logical screen size | |
WriteShort(width); | |
WriteShort(height); | |
// packed fields | |
out.writeByte((0x80 | // 1 : global color table flag = 1 (gct used) | |
0x70 | // 2-4 : color resolution = 7 | |
0x00 | // 5 : gct sort flag = 0 | |
palSize)); // 6-8 : gct size | |
out.writeByte(0); // background color index | |
out.writeByte(0); // pixel aspect ratio - assume 1:1 | |
}; | |
/** | |
* Writes Netscape application extension to define repeat count. | |
*/ | |
var writeNetscapeExt = function writeNetscapeExt() { | |
out.writeByte(0x21); // extension introducer | |
out.writeByte(0xff); // app extension label | |
out.writeByte(11); // block size | |
out.writeUTFBytes("NETSCAPE" + "2.0"); // app id + auth code | |
out.writeByte(3); // sub-block size | |
out.writeByte(1); // loop sub-block id | |
WriteShort(repeat); // loop count (extra iterations, 0=repeat forever) | |
out.writeByte(0); // block terminator | |
}; | |
/** | |
* Writes color table | |
*/ | |
var writePalette = function writePalette() { | |
out.writeBytes(colorTab); | |
var n = (3 * 256) - colorTab.length; | |
for (var i = 0; i < n; i++) out.writeByte(0); | |
}; | |
var WriteShort = function WriteShort(pValue) { | |
out.writeByte(pValue & 0xFF); | |
out.writeByte((pValue >> 8) & 0xFF); | |
}; | |
/** | |
* Encodes and writes pixel data | |
*/ | |
var writePixels = function writePixels() { | |
var myencoder = new LZWEncoder(width, height, indexedPixels, colorDepth); | |
myencoder.encode(out); | |
}; | |
/** | |
* Retrieves the GIF stream | |
*/ | |
var stream = exports.stream = function stream() { | |
return out; | |
}; | |
var setProperties = exports.setProperties = function setProperties(has_start, is_first) { | |
started = has_start; | |
firstFrame = is_first; | |
}; | |
return exports; | |
}; |
<head> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.3/seedrandom.min.js"></script> | |
<script src="https://d3js.org/d3-random.v1.min.js"></script> | |
<script language="javascript" type="text/javascript" src="p5.func.js"></script> | |
<script type="text/javascript" src="LZWEncoder.js"></script> | |
<script type="text/javascript" src="NeuQuant.js"></script> | |
<script type="text/javascript" src="GIFEncoder.js"></script> | |
<script type="text/javascript" src="b64.js"></script> | |
<script type="text/javascript" src="z_recorder.js"></script> | |
<script language="javascript" type="text/javascript" src="simplex-noise.js"></script> | |
<script language="javascript" type="text/javascript" src="canvas_size.js"></script> | |
<script language="javascript" type="text/javascript" src="draw_one_frame.js"></script> | |
<script src="z_clmtrackr.js"></script> | |
<script src="z_model_pca_20_svm.js"></script> | |
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script> | |
<script language="javascript" type="text/javascript" src="z_focused_random.js"></script> | |
<script language="javascript" type="text/javascript" src="z_face_system.js"></script> | |
<script language="javascript" type="text/javascript" src="z_kdTree.js"></script> | |
<script language="javascript" type="text/javascript" src="z_face-api.js"></script> | |
<script language="javascript" type="text/javascript" src="z_training_images.js"></script> | |
<script language="javascript" type="text/javascript" src="z_testing_images.js"></script> | |
<script language="javascript" type="text/javascript" src="face.js"></script> | |
<script language="javascript" type="text/javascript" src="system_runner.js"></script> | |
<style> | |
body { padding: 0; margin: 0; } | |
.inner { position: absolute; } | |
#controls { | |
font: 300 12px "Helvetica Neue"; | |
padding: 5; | |
margin: 5; | |
background: #f0f0f0; | |
opacity: 0.0; | |
-webkit-transition: opacity 0.2s ease; | |
-moz-transition: opacity 0.2s ease; | |
-o-transition: opacity 0.2s ease; | |
-ms-transition: opacity 0.2s ease; | |
} | |
#controls:hover { opacity: 0.9; } | |
</style> | |
</head> | |
<body style="background-color:white"> | |
<div class="outer"> | |
<div class="inner" id="controls" height="500"> | |
<table> | |
<tr> | |
<td>setting 1</td> | |
<td id="slider1Container"></td> | |
</tr> | |
<tr> | |
<td>setting 2</td> | |
<td id="slider2Container"></td> | |
</tr> | |
<tr> | |
<td>setting 3</td> | |
<td id="slider3Container"></td> | |
</tr> | |
<tr> | |
<td>setting 4</td> | |
<td id="slider4Container"></td> | |
</tr> | |
<tr> | |
<td>setting 5</td> | |
<td id="slider5Container"></td> | |
</tr> | |
<tr> | |
<td>setting 6</td> | |
<td id="slider6Container"></td> | |
</tr> | |
<tr> | |
<td>setting 7</td> | |
<td id="slider7Container"></td> | |
</tr> | |
<tr> | |
<td>setting 8</td> | |
<td id="slider8Container"></td> | |
</tr> | |
<!-- YOU CAN ADD MORE SLIDERS HERE, LIKE THIS | |
<tr> | |
<td>setting 9</td> | |
<td id="slider9Container"></td> | |
</tr> | |
<tr> | |
<td>setting 10</td> | |
<td id="slider10Container"></td> | |
</tr> | |
<tr> | |
<td>setting 11</td> | |
<td id="slider11Container"></td> | |
</tr> | |
<tr> | |
<td>setting 12</td> | |
<td id="slider12Container"></td> | |
</tr> | |
... AND KEEP GOING | |
--> | |
<tr> | |
</tr> | |
<tr> | |
<td>show target</td> | |
<td id="sliderTintContainer"></td> | |
</tr> | |
<tr> | |
<td>Draw function</td> | |
<td id="selector1Container"></td> | |
</tr> | |
<tr> | |
<td>Face Draw</td> | |
<td id="checkbox1Container"></td> | |
</tr> | |
<tr> | |
<td>Face Targets</td> | |
<td id="checkbox2Container"></td> | |
</tr> | |
<tr> | |
<td>Face Points</td> | |
<td id="checkbox3Container"></td> | |
</tr> | |
<tr> | |
<td></td> | |
<td id="button1Container"></td> | |
</tr> | |
<tr> | |
<td></td> | |
<td id="button2Container"></td> | |
</tr> | |
<tr> | |
<td></td> | |
<td id="button3Container"></td> | |
</tr> | |
<tr> | |
<td></td> | |
<td id="button4Container"></td> | |
</tr> | |
</table> | |
</div> | |
<div> | |
<div id="canvasContainer"></div> | |
<a href="face.js">face code</a><br> | |
<a href="sketch.html">sketches</a> | |
</div> | |
</div> | |
<pre> | |
<p id="output"> | |
</p> | |
</pre> | |
</body> |
/** | |
* This class handles LZW encoding | |
* Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott. | |
* @author Kevin Weiner (original Java version - [email protected]) | |
* @author Thibault Imbert (AS3 version - bytearray.org) | |
* @author Kevin Kwok (JavaScript version - https://github.com/antimatter15/jsgif) | |
* @version 0.1 AS3 implementation | |
*/ | |
LZWEncoder = function() { | |
var exports = {}; | |
var EOF = -1; | |
var imgW; | |
var imgH; | |
var pixAry; | |
var initCodeSize; | |
var remaining; | |
var curPixel; | |
// GIFCOMPR.C - GIF Image compression routines | |
// Lempel-Ziv compression based on 'compress'. GIF modifications by | |
// David Rowley ([email protected]) | |
// General DEFINEs | |
var BITS = 12; | |
var HSIZE = 5003; // 80% occupancy | |
// GIF Image compression - modified 'compress' | |
// Based on: compress.c - File compression ala IEEE Computer, June 1984. | |
// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) | |
// Jim McKie (decvax!mcvax!jim) | |
// Steve Davies (decvax!vax135!petsd!peora!srd) | |
// Ken Turkowski (decvax!decwrl!turtlevax!ken) | |
// James A. Woods (decvax!ihnp4!ames!jaw) | |
// Joe Orost (decvax!vax135!petsd!joe) | |
var n_bits; // number of bits/code | |
var maxbits = BITS; // user settable max # bits/code | |
var maxcode; // maximum code, given n_bits | |
var maxmaxcode = 1 << BITS; // should NEVER generate this code | |
var htab = []; | |
var codetab = []; | |
var hsize = HSIZE; // for dynamic table sizing | |
var free_ent = 0; // first unused entry | |
// block compression parameters -- after all codes are used up, | |
// and compression rate changes, start over. | |
var clear_flg = false; | |
// Algorithm: use open addressing double hashing (no chaining) on the | |
// prefix code / next character combination. We do a variant of Knuth's | |
// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime | |
// secondary probe. Here, the modular division first probe is gives way | |
// to a faster exclusive-or manipulation. Also do block compression with | |
// an adaptive reset, whereby the code table is cleared when the compression | |
// ratio decreases, but after the table fills. The variable-length output | |
// codes are re-sized at this point, and a special CLEAR code is generated | |
// for the decompressor. Late addition: construct the table according to | |
// file size for noticeable speed improvement on small files. Please direct | |
// questions about this implementation to ames!jaw. | |
var g_init_bits; | |
var ClearCode; | |
var EOFCode; | |
// output | |
// Output the given code. | |
// Inputs: | |
// code: A n_bits-bit integer. If == -1, then EOF. This assumes | |
// that n_bits =< wordsize - 1. | |
// Outputs: | |
// Outputs code to the file. | |
// Assumptions: | |
// Chars are 8 bits long. | |
// Algorithm: | |
// Maintain a BITS character long buffer (so that 8 codes will | |
// fit in it exactly). Use the VAX insv instruction to insert each | |
// code in turn. When the buffer fills up empty it and start over. | |
var cur_accum = 0; | |
var cur_bits = 0; | |
var masks = [0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF]; | |
// Number of characters so far in this 'packet' | |
var a_count; | |
// Define the storage for the packet accumulator | |
var accum = []; | |
var LZWEncoder = exports.LZWEncoder = function LZWEncoder(width, height, pixels, color_depth) { | |
imgW = width; | |
imgH = height; | |
pixAry = pixels; | |
initCodeSize = Math.max(2, color_depth); | |
}; | |
// Add a character to the end of the current packet, and if it is 254 | |
// characters, flush the packet to disk. | |
var char_out = function char_out(c, outs) { | |
accum[a_count++] = c; | |
if (a_count >= 254) flush_char(outs); | |
}; | |
// Clear out the hash table | |
// table clear for block compress | |
var cl_block = function cl_block(outs) { | |
cl_hash(hsize); | |
free_ent = ClearCode + 2; | |
clear_flg = true; | |
output(ClearCode, outs); | |
}; | |
// reset code table | |
var cl_hash = function cl_hash(hsize) { | |
for (var i = 0; i < hsize; ++i) htab[i] = -1; | |
}; | |
var compress = exports.compress = function compress(init_bits, outs) { | |
var fcode; | |
var i; /* = 0 */ | |
var c; | |
var ent; | |
var disp; | |
var hsize_reg; | |
var hshift; | |
// Set up the globals: g_init_bits - initial number of bits | |
g_init_bits = init_bits; | |
// Set up the necessary values | |
clear_flg = false; | |
n_bits = g_init_bits; | |
maxcode = MAXCODE(n_bits); | |
ClearCode = 1 << (init_bits - 1); | |
EOFCode = ClearCode + 1; | |
free_ent = ClearCode + 2; | |
a_count = 0; // clear packet | |
ent = nextPixel(); | |
hshift = 0; | |
for (fcode = hsize; fcode < 65536; fcode *= 2) | |
++hshift; | |
hshift = 8 - hshift; // set hash code range bound | |
hsize_reg = hsize; | |
cl_hash(hsize_reg); // clear hash table | |
output(ClearCode, outs); | |
outer_loop: while ((c = nextPixel()) != EOF) { | |
fcode = (c << maxbits) + ent; | |
i = (c << hshift) ^ ent; // xor hashing | |
if (htab[i] == fcode) { | |
ent = codetab[i]; | |
continue; | |
} | |
else if (htab[i] >= 0) { // non-empty slot | |
disp = hsize_reg - i; // secondary hash (after G. Knott) | |
if (i === 0) disp = 1; | |
do { | |
if ((i -= disp) < 0) | |
i += hsize_reg; | |
if (htab[i] == fcode) { | |
ent = codetab[i]; | |
continue outer_loop; | |
} | |
} while (htab[i] >= 0); | |
} | |
output(ent, outs); | |
ent = c; | |
if (free_ent < maxmaxcode) { | |
codetab[i] = free_ent++; // code -> hashtable | |
htab[i] = fcode; | |
} | |
else cl_block(outs); | |
} | |
// Put out the final code. | |
output(ent, outs); | |
output(EOFCode, outs); | |
}; | |
// ---------------------------------------------------------------------------- | |
var encode = exports.encode = function encode(os) { | |
os.writeByte(initCodeSize); // write "initial code size" byte | |
remaining = imgW * imgH; // reset navigation variables | |
curPixel = 0; | |
compress(initCodeSize + 1, os); // compress and write the pixel data | |
os.writeByte(0); // write block terminator | |
}; | |
// Flush the packet to disk, and reset the accumulator | |
var flush_char = function flush_char(outs) { | |
if (a_count > 0) { | |
outs.writeByte(a_count); | |
outs.writeBytes(accum, 0, a_count); | |
a_count = 0; | |
} | |
}; | |
var MAXCODE = function MAXCODE(n_bits) { | |
return (1 << n_bits) - 1; | |
}; | |
// ---------------------------------------------------------------------------- | |
// Return the next pixel from the image | |
// ---------------------------------------------------------------------------- | |
var nextPixel = function nextPixel() { | |
if (remaining === 0) return EOF; | |
--remaining; | |
var pix = pixAry[curPixel++]; | |
return pix & 0xff; | |
}; | |
var output = function output(code, outs) { | |
cur_accum &= masks[cur_bits]; | |
if (cur_bits > 0) cur_accum |= (code << cur_bits); | |
else cur_accum = code; | |
cur_bits += n_bits; | |
while (cur_bits >= 8) { | |
char_out((cur_accum & 0xff), outs); | |
cur_accum >>= 8; | |
cur_bits -= 8; | |
} | |
// If the next entry is going to be too big for the code size, | |
// then increase it, if possible. | |
if (free_ent > maxcode || clear_flg) { | |
if (clear_flg) { | |
maxcode = MAXCODE(n_bits = g_init_bits); | |
clear_flg = false; | |
} else { | |
++n_bits; | |
if (n_bits == maxbits) maxcode = maxmaxcode; | |
else maxcode = MAXCODE(n_bits); | |
} | |
} | |
if (code == EOFCode) { | |
// At EOF, write the rest of the buffer. | |
while (cur_bits > 0) { | |
char_out((cur_accum & 0xff), outs); | |
cur_accum >>= 8; | |
cur_bits -= 8; | |
} | |
flush_char(outs); | |
} | |
}; | |
LZWEncoder.apply(this, arguments); | |
return exports; | |
}; |
/* | |
* NeuQuant Neural-Net Quantization Algorithm | |
* ------------------------------------------ | |
* | |
* Copyright (c) 1994 Anthony Dekker | |
* | |
* NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See | |
* "Kohonen neural networks for optimal colour quantization" in "Network: | |
* Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of | |
* the algorithm. | |
* | |
* Any party obtaining a copy of these files from the author, directly or | |
* indirectly, is granted, free of charge, a full and unrestricted irrevocable, | |
* world-wide, paid up, royalty-free, nonexclusive right and license to deal in | |
* this software and documentation files (the "Software"), including without | |
* limitation the rights to use, copy, modify, merge, publish, distribute, | |
* sublicense, and/or sell copies of the Software, and to permit persons who | |
* receive copies from any such party to do so, with the only requirement being | |
* that this copyright notice remain intact. | |
*/ | |
/* | |
* This class handles Neural-Net quantization algorithm | |
* @author Kevin Weiner (original Java version - [email protected]) | |
* @author Thibault Imbert (AS3 version - bytearray.org) | |
* @author Kevin Kwok (JavaScript version - https://github.com/antimatter15/jsgif) | |
* @version 0.1 AS3 implementation | |
*/ | |
NeuQuant = function() { | |
var exports = {}; | |
var netsize = 256; /* number of colours used */ | |
/* four primes near 500 - assume no image has a length so large */ | |
/* that it is divisible by all four primes */ | |
var prime1 = 499; | |
var prime2 = 491; | |
var prime3 = 487; | |
var prime4 = 503; | |
var minpicturebytes = (3 * prime4); /* minimum size for input image */ | |
/* | |
* Program Skeleton ---------------- [select samplefac in range 1..30] [read | |
* image from input file] pic = (unsigned char*) malloc(3*width*height); | |
* initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output | |
* image header, using writecolourmap(f)] inxbuild(); write output image using | |
* inxsearch(b,g,r) | |
*/ | |
/* | |
* Network Definitions ------------------- | |
*/ | |
var maxnetpos = (netsize - 1); | |
var netbiasshift = 4; /* bias for colour values */ | |
var ncycles = 100; /* no. of learning cycles */ | |
/* defs for freq and bias */ | |
var intbiasshift = 16; /* bias for fractions */ | |
var intbias = (1 << intbiasshift); | |
var gammashift = 10; /* gamma = 1024 */ | |
var gamma = (1 << gammashift); | |
var betashift = 10; | |
var beta = (intbias >> betashift); /* beta = 1/1024 */ | |
var betagamma = (intbias << (gammashift - betashift)); | |
/* defs for decreasing radius factor */ | |
var initrad = (netsize >> 3); /* for 256 cols, radius starts */ | |
var radiusbiasshift = 6; /* at 32.0 biased by 6 bits */ | |
var radiusbias = (1 << radiusbiasshift); | |
var initradius = (initrad * radiusbias); /* and decreases by a */ | |
var radiusdec = 30; /* factor of 1/30 each cycle */ | |
/* defs for decreasing alpha factor */ | |
var alphabiasshift = 10; /* alpha starts at 1.0 */ | |
var initalpha = (1 << alphabiasshift); | |
var alphadec; /* biased by 10 bits */ | |
/* radbias and alpharadbias used for radpower calculation */ | |
var radbiasshift = 8; | |
var radbias = (1 << radbiasshift); | |
var alpharadbshift = (alphabiasshift + radbiasshift); | |
var alpharadbias = (1 << alpharadbshift); | |
/* | |
* Types and Global Variables -------------------------- | |
*/ | |
var thepicture; /* the input image itself */ | |
var lengthcount; /* lengthcount = H*W*3 */ | |
var samplefac; /* sampling factor 1..30 */ | |
// typedef int pixel[4]; /* BGRc */ | |
var network; /* the network itself - [netsize][4] */ | |
var netindex = []; | |
/* for network lookup - really 256 */ | |
var bias = []; | |
/* bias and freq arrays for learning */ | |
var freq = []; | |
var radpower = []; | |
var NeuQuant = exports.NeuQuant = function NeuQuant(thepic, len, sample) { | |
var i; | |
var p; | |
thepicture = thepic; | |
lengthcount = len; | |
samplefac = sample; | |
network = new Array(netsize); | |
for (i = 0; i < netsize; i++) { | |
network[i] = new Array(4); | |
p = network[i]; | |
p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize; | |
freq[i] = intbias / netsize; /* 1/netsize */ | |
bias[i] = 0; | |
} | |
}; | |
var colorMap = function colorMap() { | |
var map = []; | |
var index = new Array(netsize); | |
for (var i = 0; i < netsize; i++) | |
index[network[i][3]] = i; | |
var k = 0; | |
for (var l = 0; l < netsize; l++) { | |
var j = index[l]; | |
map[k++] = (network[j][0]); | |
map[k++] = (network[j][1]); | |
map[k++] = (network[j][2]); | |
} | |
return map; | |
}; | |
/* | |
* Insertion sort of network and building of netindex[0..255] (to do after | |
* unbias) | |
* ------------------------------------------------------------------------------- | |
*/ | |
var inxbuild = function inxbuild() { | |
var i; | |
var j; | |
var smallpos; | |
var smallval; | |
var p; | |
var q; | |
var previouscol; | |
var startpos; | |
previouscol = 0; | |
startpos = 0; | |
for (i = 0; i < netsize; i++) { | |
p = network[i]; | |
smallpos = i; | |
smallval = p[1]; /* index on g */ | |
/* find smallest in i..netsize-1 */ | |
for (j = i + 1; j < netsize; j++) { | |
q = network[j]; | |
if (q[1] < smallval) { /* index on g */ | |
smallpos = j; | |
smallval = q[1]; /* index on g */ | |
} | |
} | |
q = network[smallpos]; | |
/* swap p (i) and q (smallpos) entries */ | |
if (i != smallpos) { | |
j = q[0]; | |
q[0] = p[0]; | |
p[0] = j; | |
j = q[1]; | |
q[1] = p[1]; | |
p[1] = j; | |
j = q[2]; | |
q[2] = p[2]; | |
p[2] = j; | |
j = q[3]; | |
q[3] = p[3]; | |
p[3] = j; | |
} | |
/* smallval entry is now in position i */ | |
if (smallval != previouscol) { | |
netindex[previouscol] = (startpos + i) >> 1; | |
for (j = previouscol + 1; j < smallval; j++) netindex[j] = i; | |
previouscol = smallval; | |
startpos = i; | |
} | |
} | |
netindex[previouscol] = (startpos + maxnetpos) >> 1; | |
for (j = previouscol + 1; j < 256; j++) netindex[j] = maxnetpos; /* really 256 */ | |
}; | |
/* | |
* Main Learning Loop ------------------ | |
*/ | |
var learn = function learn() { | |
var i; | |
var j; | |
var b; | |
var g; | |
var r; | |
var radius; | |
var rad; | |
var alpha; | |
var step; | |
var delta; | |
var samplepixels; | |
var p; | |
var pix; | |
var lim; | |
if (lengthcount < minpicturebytes) samplefac = 1; | |
alphadec = 30 + ((samplefac - 1) / 3); | |
p = thepicture; | |
pix = 0; | |
lim = lengthcount; | |
samplepixels = lengthcount / (3 * samplefac); | |
delta = (samplepixels / ncycles) | 0; | |
alpha = initalpha; | |
radius = initradius; | |
rad = radius >> radiusbiasshift; | |
if (rad <= 1) rad = 0; | |
for (i = 0; i < rad; i++) radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad)); | |
if (lengthcount < minpicturebytes) step = 3; | |
else if ((lengthcount % prime1) !== 0) step = 3 * prime1; | |
else { | |
if ((lengthcount % prime2) !== 0) step = 3 * prime2; | |
else { | |
if ((lengthcount % prime3) !== 0) step = 3 * prime3; | |
else step = 3 * prime4; | |
} | |
} | |
i = 0; | |
while (i < samplepixels) { | |
b = (p[pix + 0] & 0xff) << netbiasshift; | |
g = (p[pix + 1] & 0xff) << netbiasshift; | |
r = (p[pix + 2] & 0xff) << netbiasshift; | |
j = contest(b, g, r); | |
altersingle(alpha, j, b, g, r); | |
if (rad !== 0) alterneigh(rad, j, b, g, r); /* alter neighbours */ | |
pix += step; | |
if (pix >= lim) pix -= lengthcount; | |
i++; | |
if (delta === 0) delta = 1; | |
if (i % delta === 0) { | |
alpha -= alpha / alphadec; | |
radius -= radius / radiusdec; | |
rad = radius >> radiusbiasshift; | |
if (rad <= 1) rad = 0; | |
for (j = 0; j < rad; j++) radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad)); | |
} | |
} | |
}; | |
/* | |
** Search for BGR values 0..255 (after net is unbiased) and return colour | |
* index | |
* ---------------------------------------------------------------------------- | |
*/ | |
var map = exports.map = function map(b, g, r) { | |
var i; | |
var j; | |
var dist; | |
var a; | |
var bestd; | |
var p; | |
var best; | |
bestd = 1000; /* biggest possible dist is 256*3 */ | |
best = -1; | |
i = netindex[g]; /* index on g */ | |
j = i - 1; /* start at netindex[g] and work outwards */ | |
while ((i < netsize) || (j >= 0)) { | |
if (i < netsize) { | |
p = network[i]; | |
dist = p[1] - g; /* inx key */ | |
if (dist >= bestd) i = netsize; /* stop iter */ | |
else { | |
i++; | |
if (dist < 0) dist = -dist; | |
a = p[0] - b; | |
if (a < 0) a = -a; | |
dist += a; | |
if (dist < bestd) { | |
a = p[2] - r; | |
if (a < 0) a = -a; | |
dist += a; | |
if (dist < bestd) { | |
bestd = dist; | |
best = p[3]; | |
} | |
} | |
} | |
} | |
if (j >= 0) { | |
p = network[j]; | |
dist = g - p[1]; /* inx key - reverse dif */ | |
if (dist >= bestd) j = -1; /* stop iter */ | |
else { | |
j--; | |
if (dist < 0) dist = -dist; | |
a = p[0] - b; | |
if (a < 0) a = -a; | |
dist += a; | |
if (dist < bestd) { | |
a = p[2] - r; | |
if (a < 0) a = -a; | |
dist += a; | |
if (dist < bestd) { | |
bestd = dist; | |
best = p[3]; | |
} | |
} | |
} | |
} | |
} | |
return (best); | |
}; | |
var process = exports.process = function process() { | |
learn(); | |
unbiasnet(); | |
inxbuild(); | |
return colorMap(); | |
}; | |
/* | |
* Unbias network to give byte values 0..255 and record position i to prepare | |
* for sort | |
* ----------------------------------------------------------------------------------- | |
*/ | |
var unbiasnet = function unbiasnet() { | |
var i; | |
var j; | |
for (i = 0; i < netsize; i++) { | |
network[i][0] >>= netbiasshift; | |
network[i][1] >>= netbiasshift; | |
network[i][2] >>= netbiasshift; | |
network[i][3] = i; /* record colour no */ | |
} | |
}; | |
/* | |
* Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in | |
* radpower[|i-j|] | |
* --------------------------------------------------------------------------------- | |
*/ | |
var alterneigh = function alterneigh(rad, i, b, g, r) { | |
var j; | |
var k; | |
var lo; | |
var hi; | |
var a; | |
var m; | |
var p; | |
lo = i - rad; | |
if (lo < -1) lo = -1; | |
hi = i + rad; | |
if (hi > netsize) hi = netsize; | |
j = i + 1; | |
k = i - 1; | |
m = 1; | |
while ((j < hi) || (k > lo)) { | |
a = radpower[m++]; | |
if (j < hi) { | |
p = network[j++]; | |
try { | |
p[0] -= (a * (p[0] - b)) / alpharadbias; | |
p[1] -= (a * (p[1] - g)) / alpharadbias; | |
p[2] -= (a * (p[2] - r)) / alpharadbias; | |
} catch (e) {} // prevents 1.3 miscompilation | |
} | |
if (k > lo) { | |
p = network[k--]; | |
try { | |
p[0] -= (a * (p[0] - b)) / alpharadbias; | |
p[1] -= (a * (p[1] - g)) / alpharadbias; | |
p[2] -= (a * (p[2] - r)) / alpharadbias; | |
} catch (e) {} | |
} | |
} | |
}; | |
/* | |
* Move neuron i towards biased (b,g,r) by factor alpha | |
* ---------------------------------------------------- | |
*/ | |
var altersingle = function altersingle(alpha, i, b, g, r) { | |
/* alter hit neuron */ | |
var n = network[i]; | |
n[0] -= (alpha * (n[0] - b)) / initalpha; | |
n[1] -= (alpha * (n[1] - g)) / initalpha; | |
n[2] -= (alpha * (n[2] - r)) / initalpha; | |
}; | |
/* | |
* Search for biased BGR values ---------------------------- | |
*/ | |
var contest = function contest(b, g, r) { | |
/* finds closest neuron (min dist) and updates freq */ | |
/* finds best neuron (min dist-bias) and returns position */ | |
/* for frequently chosen neurons, freq[i] is high and bias[i] is negative */ | |
/* bias[i] = gamma*((1/netsize)-freq[i]) */ | |
var i; | |
var dist; | |
var a; | |
var biasdist; | |
var betafreq; | |
var bestpos; | |
var bestbiaspos; | |
var bestd; | |
var bestbiasd; | |
var n; | |
bestd = ~ (1 << 31); | |
bestbiasd = bestd; | |
bestpos = -1; | |
bestbiaspos = bestpos; | |
for (i = 0; i < netsize; i++) { | |
n = network[i]; | |
dist = n[0] - b; | |
if (dist < 0) dist = -dist; | |
a = n[1] - g; | |
if (a < 0) a = -a; | |
dist += a; | |
a = n[2] - r; | |
if (a < 0) a = -a; | |
dist += a; | |
if (dist < bestd) { | |
bestd = dist; | |
bestpos = i; | |
} | |
biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift)); | |
if (biasdist < bestbiasd) { | |
bestbiasd = biasdist; | |
bestbiaspos = i; | |
} | |
betafreq = (freq[i] >> betashift); | |
freq[i] -= betafreq; | |
bias[i] += (betafreq << gammashift); | |
} | |
freq[bestpos] += beta; | |
bias[bestpos] -= betagamma; | |
return (bestbiaspos); | |
}; | |
NeuQuant.apply(this, arguments); | |
return exports; | |
}; |
/*! p5.func.js v0.0.1 2017-05-27 */ | |
/** | |
* @module p5.func | |
* @submodule p5.func | |
* @for p5.func | |
* @main | |
*/ | |
/** | |
* p5.func | |
* R. Luke DuBois ([email protected]) | |
* Integrated Digital Media / Brooklyn Experimental Media Center | |
* New York University | |
* The MIT License (MIT). | |
* | |
* https://github.com/IDMNYU/p5.js-func | |
* | |
* the p5.func module contains five new objects for extending p5.js : | |
* p5.Gen() : function generators (waveforms, curves, window functions, noise, etc.) | |
* p5.Ease() : easing / interpolation functions | |
* p5.ArrayEval() : equation evaluator to generate pre-computed arrays | |
* p5.Filt() : biquadratic filter object | |
* p5.FastFourierTransform() : signal neutral FFT implementation | |
* | |
* p5.func also contains some miscellaneous functions: | |
* imap() : constrainted integer mapping function | |
* wrap() : wrapping function | |
* fold() : folding function | |
* createArray()/normalizeArray()/resizeArray()/multiplyArray()/addArray()/subtractArray()/divideArray()/moduloArray()/sumArray() : array functions | |
* f2ib() / ib2f() : int<->float coercion with bit parity | |
* sinc() : sinc (sinus cardinalis) function | |
* besselI0() : Bessel function | |
* fplot() : formattable console plot of any array | |
* | |
* primary sources: | |
* RTcmix Scorefile Commands: http://rtcmix.org/reference/scorefile/ | |
* Robert Penner's Easing Functions: http://robertpenner.com/easing/ | |
* Golan Levin's Pattern Master: https://github.com/golanlevin/Pattern_Master | |
* Robert Bristow-Johnson's Audio EQ Cookbook: http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt | |
* Corban Brook's dsp.js: https://github.com/corbanbrook/dsp.js/ | |
*/ | |
(function (root, factory) { | |
if (typeof define === 'function' && define.amd) | |
define('p5.func', ['p5'], function (p5) { (factory(p5));}); | |
else if (typeof exports === 'object') | |
factory(require('../p5')); | |
else | |
factory(root['p5']); | |
}(this, function (p5) { | |
// ============================================================================= | |
// p5.Gen | |
// ============================================================================= | |
/** | |
* Base class for a function generator | |
* | |
* @class p5.Gen | |
* @constructor | |
*/ | |
p5.Gen = function() { | |
// | |
// this object implements GEN table-style | |
// algorithms adapted from MUSICN languages | |
// (e.g. Csound, RTcmix, ChucK, Supercollider, Max). | |
// these algorithms are solved by direct (0.-1.) | |
// evaluation with utility functions to compute arrays. | |
// | |
// some code below is adapted from RTcmix : | |
// https://github.com/RTcmix/RTcmix | |
// Copyright (C) 2005 The RTcmix Development Team, released under the Apache 2.0 License | |
// | |
this.version = 0.01; // just some crap for constructor | |
var that = this; // some bullshit | |
}; // end p5.Gen constructor | |
// harmonic / periodic wave from a list of partial strengths. | |
// Csound / RTcmix GEN10, ported from RTcmix. | |
p5.Gen.prototype.harmonics = function(_x, _args) { | |
var u = true; // single value? | |
if(!Array.isArray(_x) && _x.constructor !== Float32Array && _x.constructor !== Float64Array) { | |
_x = [_x]; // process all values as arrays | |
u = false; | |
} | |
if(!Array.isArray(_args)) _args = [_args]; // catch single harmonic | |
var _sum; // match type: | |
if(Array.isArray(_x)) _sum = new Array(_x.length); | |
else if(_x.constructor === Float32Array) _sum = new Float32Array(_x.length); | |
else if(_x.constructor === Float64Array) _sum = new Float64Array(_x.length); | |
for(var i in _x) { | |
var j = _args.length; | |
_sum[i] = 0.0; | |
while (j--) { | |
if (_args[j] != 0) { | |
var _val = TWO_PI * _x[i] * (j + 1); | |
_sum[i] += (sin(_val) * _args[j]); | |
} | |
} | |
} | |
return(u ? _sum : _sum[0]); | |
}; | |
// wave from triples (ratio, amp, phase). | |
// Csound / RTcmix GEN09, ported from RTcmix. | |
p5.Gen.prototype.triples = function(_x, _args) { | |
var u = true; // single value? | |
if(!Array.isArray(_x) && _x.constructor !== Float32Array && _x.constructor !== Float64Array) { | |
_x = [_x]; // process all values as arrays | |
u = false; | |
} | |
if(_args.length<2) { | |
console.log("p5.Gen : we need at least 3 arguments!") | |
return(0.); | |
} | |
else if(_args.length%3!=0) { | |
console.log("p5.Gen : incomplete <partial, amp, phase> triplet!"); | |
} | |
var _sum; // match type: | |
if(Array.isArray(_x)) _sum = new Array(_x.length); | |
else if(_x.constructor === Float32Array) _sum = new Float32Array(_x.length); | |
else if(_x.constructor === Float64Array) _sum = new Float64Array(_x.length); | |
for(i in _x) { | |
_sum[i] = 0.0; | |
for (var j = _args.length - 1; j > 0; j -= 3) { | |
if (_args[j - 1] != 0.0) { | |
var val; | |
if (_args[j - 2] == 0.0) val = 1.0; // BGG: harmonic 0 (DC) | |
else { | |
val = sin(TWO_PI * (_x[i] / (1.0 / _args[j - 2]) + _args[j] / 360.0)); | |
} | |
_sum[i] += (val * _args[j - 1]); | |
} | |
} | |
} | |
return(u ? _sum : _sum[0]); | |
}; | |
// transfer function from chebyshev polynomials. | |
// Csound / RTcmix GEN17, ported from RTcmix. | |
p5.Gen.prototype.chebyshev = function(_x, _args) { | |
var u = true; // single value? | |
if(!Array.isArray(_x) && _x.constructor !== Float32Array && _x.constructor !== Float64Array) { | |
_x = [_x]; // process all values as arrays | |
u = false; | |
} | |
if(!Array.isArray(_args)) _args = [_args]; // catch single value | |
// compute the transfer function using the chebyshev equation... | |
var _sum // match type: | |
if(Array.isArray(_x)) _sum = new Array(_x.length); | |
else if(_x.constructor === Float32Array) _sum = new Float32Array(_x.length); | |
else if(_x.constructor === Float64Array) _sum = new Float64Array(_x.length); | |
for(var i in _x) { | |
var v=_x[i]*2.-1.; | |
_sum[i]=0.; | |
var Tn1=1; | |
var Tn=v; | |
for(var j=0; j<_args.length;j++) { | |
_sum[i]+=_args[j]*Tn; | |
Tn2=Tn1; | |
Tn1=Tn; | |
Tn=2*v*Tn1-Tn2; | |
} | |
} | |
return(u ? _sum : _sum[0]); | |
} | |
// linear breakpoint function (time, value pairs with y normalization). | |
// Csound GEN27 / RTcmix GEN24, rewritten by rld. | |
p5.Gen.prototype.bpf = function(_x, _args) { | |
var u = true; // single value? | |
if(!Array.isArray(_x) && _x.constructor !== Float32Array && _x.constructor !== Float64Array) { | |
_x = [_x]; // process all values as arrays | |
u = false; | |
} | |
if(_args.length%2!=0) { | |
console.log("p5.Gen : incomplete <time, value> pair!") | |
return(0.); | |
} | |
var endtime = _args[_args.length - 2]; | |
var starttime = _args[0]; | |
if (endtime - starttime <= 0.0) { | |
console.log("p5.Gen : bpf times must be in ascending order!"); | |
return(0.); | |
} | |
var scaler = 1.0 / (endtime - starttime); | |
var thistime = 0; | |
var nexttime = 0; | |
var thisval = 0; | |
var nextval = 0; | |
var _y // match type: | |
if(Array.isArray(_x)) _y = new Array(_x.length); | |
else if(_x.constructor === Float32Array) _y = new Float32Array(_x.length); | |
else if(_x.constructor === Float64Array) _y = new Float64Array(_x.length); | |
for(i in _x) | |
{ | |
for (var k = 1; k < _args.length; k += 2) { | |
thistime = _args[k-1]*scaler; | |
thisval = _args[k]; | |
if(k<_args.length-1) { | |
nexttime = _args[k+1]*scaler; | |
nextval = _args[k+2]; | |
} | |
else { | |
nexttime = thistime; | |
nextval = thisval; | |
} | |
if(nexttime - thistime < 0.0) { // okay for them to be the same | |
console.log("p5.Gen : bpf times music be in ascending order!"); | |
return(0.); | |
} | |
if(_x[i]>=thistime && _x[i]<=nexttime) // this point in bpf | |
{ | |
var _thisval = _args[k+1]; | |
_y[i] = map(_x[i], thistime, nexttime, thisval, nextval); | |
break; | |
} | |
} | |
} | |
return(u ? _y : _y[0]); | |
} | |
// common random number distributions. | |
// Csound GEN21 / RTcmix GEN20, written by rld. | |
// if no seed, auto-generated from millis(). | |
// algorithms adapted from dodge and jerse (1985). | |
// inspired by denis lorrain's | |
// 'a panoply of stochastic cannons' (1980): | |
// http://www.jstor.org/stable/3679442 | |
p5.Gen.prototype.random = function(_x, _type) { | |
// distributions based on RTcmix GEN20: | |
// even distribution ["even" or "linear"] | |
// low weighted linear distribution ["low"] | |
// high weighted linear distribution ["high"] | |
// triangle linear distribution ["triangle"] | |
// gaussian distribution ["gaussian"] | |
// cauchy distribution ["cauchy"] | |
// | |
// a -1 in the seed parameter (or missing) will | |
// instruct the algorithm to use the millisecond | |
// clock for a random seed. | |
var u = true; // single value? | |
var _s = 0.; | |
if(!_x) // no arguments, so do linear with random seed | |
{ | |
_type = 'linear'; | |
_x = [millis()]; | |
u = false; | |
} | |
else if(typeof(_x)!='string') // first argument is a number, so seed | |
{ | |
if(!Array.isArray(arguments[0]) && arguments[0].constructor !== Float32Array && arguments[0].constructor !== Float64Array) { | |
_x = [_x]; // process all values as arrays | |
u = false; | |
} | |
} | |
else // argument is a string, so type | |
{ | |
_type=_x; // it's the type, not the seed | |
_x = [millis()]; // random seed | |
u = false; | |
} | |
var _v; // match type: | |
if(Array.isArray(_x)) _v= new Array(_x.length); | |
else if(_x.constructor === Float32Array) _v = new Float32Array(_x.length); | |
else if(_x.constructor === Float64Array) _v = new Float64Array(_x.length); | |
if(_x[0]===-1) randomSeed(millis()*100000.); | |
for(var i in _x) | |
{ | |
if(_x[i]!=-1) randomSeed(_x[i]*100000.); | |
switch(_type) { | |
case "linear": | |
case "even": | |
_v[i] = random(); | |
break; | |
case "low": | |
_v[i] = min(random(), random()); | |
break; | |
case "high": | |
_v[i] = max(random(), random()); | |
break; | |
case "triangle": | |
_v[i] = (random()+random())/2.0; | |
break; | |
case "gaussian": | |
var n = 12; | |
var sigma = 0.166666; | |
var randnum = 0.0; | |
for (var j = 0; j < n; j++) { | |
randnum += random(); | |
} | |
_v[i] = sigma * (randnum - n/2) + 0.5; | |
break; | |
case "cauchy": | |
var alpha = 0.00628338; | |
do { | |
do { | |
_v[i] = random(); | |
} while (_v[i] == 0.5); | |
_v[i] = (alpha * tan(_v[i] * PI)) + 0.5; | |
} while (_v[i] < 0.0 || _v[i] > 1.0); | |
break; | |
default: | |
_v[i] = random(); | |
break; | |
} | |
} | |
return(u ? _v : _v[0]); | |
} | |
// common window functions for signal processing. | |
// Csound GEN20 / RTcmix GEN25 (paris smaragdis / dave topper) | |
// and Pattern Master by @golanlevin . | |
// rewritten / ported from C and Java by rld. | |
// equations from Wikipedia: https://en.wikipedia.org/wiki/Window_function | |
p5.Gen.prototype.window = function(_x, _type, _args) { | |
// flag order based on CSOUND GEN20: | |
// 1 = hamming | |
// 2 = hanning | |
// 3 = bartlett (triangle) | |
// 4 = blackman (3-term) | |
// 5 = blackman-harris (4-term) | |
// 6 = gaussian | |
// 7 = kaiser | |
// 8 = rectangle | |
// 9 = sinc | |
// these and others are addressible by name. | |
var u = true; // single value? | |
if(!Array.isArray(_args)) _args = [_args]; // catch single value | |
if(!Array.isArray(_x) && _x.constructor !== Float32Array && _x.constructor !== Float64Array) { | |
_x = [_x]; // process all values as arrays | |
u = false; | |
} | |
var _y; // match type: | |
if(Array.isArray(_x)) _y= new Array(_x.length); | |
else if(_x.constructor === Float32Array) _y = new Float32Array(_x.length); | |
else if(_x.constructor === Float64Array) _y = new Float64Array(_x.length); | |
var i; | |
switch(_type) { | |
// proposed by richard wesley hamming (1915-1998). | |
// optimized to quash the nearest sidelobe. | |
case 1: | |
case "hamming": | |
var alpha = 0.54; | |
var beta = 0.46; | |
for(i in _x) _y[i] = alpha - beta * cos(TWO_PI * _x[i]); | |
break; | |
// named for julius von hann (1839-1921). | |
// sidelobes fall at 18db/oct. | |
case 2: | |
case "hanning": // not the guy's actual name | |
case "vonhann": // the guy's actual name | |
case "hann": // sort of the guy's actual name | |
case "hannsolo": // no | |
case "hanningvonhannmeister": // very much no | |
for(i in _x) _y[i] = 0.5 * (1-cos(TWO_PI*_x[i])); | |
break; | |
// proposed by m.s. bartlett (1910-2002). | |
// also by lipót (leopold) fejér (1880-1959). | |
// triangular (2nd order b-spline) window. | |
case 3: | |
case "bartlett": | |
case "fejer": | |
case "fejér": // get the accent right | |
case "triangle": | |
for(i in _x) _y[i] = 1.0 - abs((_x[i]-0.5)/0.5); | |
break; | |
// bartlett-hann window. | |
case "bartlett-hann": | |
var a0 = 0.62; | |
var a1 = 0.48; | |
var a2 = 0.38; | |
for(i in _x) _y[i] = a0 - a1*abs(_x[i] - 0.5) - a2*cos(2*PI*_x[i]); | |
break; | |
case 4: | |
// proposed by ralph beebe blackman (1904-1990). | |
// 'exact' blackman kills sidelobes 3 and 4, but only 6db/oct damping. | |
// 'unqualified' blackman still has the sidelobes, but an 18db/oct damping. | |
case "blackman": | |
// 'exact' blackman: | |
var a0 = 7938/18608; | |
var a1 = 9240/18608; | |
var a2 = 1430/18608; | |
// 'unqualified' blackman: | |
// var a0 = 0.42; | |
// var a1 = 0.5; | |
// var a2 = 0.08; | |
for(i in _x) _y[i] = a0 - a1 * cos(2.*PI*_x[i]) + a2 * cos(4.*PI*_x[i]); | |
break; | |
// generalized (variable center) blackman. | |
case "generalizedblackman": | |
if(!_args[0]) _args[0] = 0.5; // default center | |
var _a = _args[0]; | |
var a0 = (1.0 - _a)/2.0; | |
var a1 = 0.5; | |
var a2 = _a / 2.0; | |
for(i in _x) | |
{ | |
var pix = PI*_x[i]; | |
_y[i] = a0 - a1*cos(2*pix) + a2*cos(4*pix); | |
} | |
break; | |
// blackman window improved by fred harris (brooklyn poly '61): | |
// http://web.mit.edu/xiphmont/Public/windows.pdf | |
// 4-term sinc functions to minimize sidelobes. | |
case 5: | |
case "blackman-harris": | |
var a0 = 0.35875; | |
var a1 = 0.48829; | |
var a2 = 0.14128; | |
var a3 = 0.01168; | |
for(i in _x) _y[i] = a0 - a1 * cos(2.*PI*_x[i]) + a2 * cos(4.*PI*_x[i]) + a3 * cos(6.*PI*_x[i]); | |
break; | |
// 4-term blackman-nuttal (same as BH with different coefficients). | |
case "blackman-nuttal": | |
var a0 = 0.3635819; | |
var a1 = 0.4891775; | |
var a2 = 0.1365995; | |
var a3 = 0.0106411; | |
for(i in _x) _y[i] = a0 - a1 * cos(2.*PI*_x[i]) + a2 * cos(4.*PI*_x[i]) + a3 * cos(6.*PI*_x[i]); | |
break; | |
// 4-term nuttal (same as BH with different coefficients). | |
case "nuttal": | |
var a0 = 0.355768; | |
var a1 = 0.487396; | |
var a2 = 0.144232; | |
var a3 = 0.012604; | |
for(i in _x) _y[i] = a0 - a1 * cos(2.*PI*_x[i]) + a2 * cos(4.*PI*_x[i]) + a3 * cos(6.*PI*_x[i]); | |
break; | |
// gaussians are eigenfunctions of fourier transforms. | |
// gaussian window needs to be zeroed at the ends. | |
// parabolic. | |
case 6: | |
case "gaussian": | |
if(!_args[0]) _args[0] = 0.4; // default sigma | |
var sigma = _args[0]; | |
for(i in _x) _y[i] = exp(-0.5 * ((_x[i]-0.5) / (sigma*0.5)) * ((_x[i]-0.5) / (sigma*0.5))); | |
break; | |
// jim kaiser's 1980 approximation of a DPSS / | |
// slepian window using bessel functions, | |
// developed at bell labs for audio coding. | |
// concentrates the energy in the main lobe. | |
case 7: | |
case "kaiser": | |
var alpha = 3.; | |
for(i in _x) { | |
var above = PI * alpha * sqrt(1.0 - (2. * _x[i] - 1.0) * (2. * _x[i] - 1.0)); | |
var below = PI * alpha; | |
_y[i] = besselI0(above) / besselI0(below); | |
} | |
break; | |
// an 'unwindow'. all samples within window envelope are at unity. | |
// bad signal-to-noise ratio. lots of scalloping loss. | |
// sometimes named for peter gustav lejeune dirichlet (1805-1859). | |
case 8: | |
case "rectangle": | |
case "boxcar": | |
case "dirichlet": | |
for(i in _x) _y[i] = 1.; // nothing to it | |
break; | |
// cosine window | |
case "cosine": | |
for(i in _x) _y[i] = sin(PI*_x[i]); | |
break; | |
// named for cornelius lanczos (1906-1974). | |
// a normalized (double-window) sinc function is often | |
// used as a kernel for interpolation / low-pass filtering. | |
case 9: | |
case "sinc": | |
case "sync": // learn to spell | |
case "lanczos": | |
for(i in _x) _y[i] = sinc(2*_x[i]-1.0); | |
break; | |
// flat top window. | |
case "flattop": | |
var a0 = 1.000; | |
var a1 = 1.930; | |
var a2 = 1.290; | |
var a3 = 0.388; | |
var a4 = 0.032; | |
for(i in _x) { | |
_y[i] = a0 - a1*cos(2*PI*_x[i]) + a2*cos(4*PI*_x[i]) - a3*cos(6*PI*_x[i]) + a4*cos(8*PI*_x[i]); | |
_y[i] /= (a0 + a1 + a2 + a3 + a4); | |
} | |
break; | |
// tukey window courtesy of @golanlevin : | |
// The Tukey window, also known as the tapered cosine window, | |
// can be regarded as a cosine lobe of width \tfrac{\alpha N}{2} | |
// that is convolved with a rectangle window of width \left(1 -\tfrac{\alpha}{2}\right)N. | |
// At alpha=0 it becomes rectangular, and at alpha=1 it becomes a Hann window. | |
case "tukey": | |
if(!_args[0]) _args[0] = 0.5; // default center | |
var _a = _args[0]; | |
var ah = _a/2.0; | |
var omah = 1.0 - ah; | |
for(i in _x) | |
{ | |
_y[i] = 1.0; | |
if (_x[i] <= ah) { | |
_y[i] = 0.5 * (1.0 + cos(PI*((2*_x[i]/_a)-1.0))); | |
} | |
else if (_x[i] > omah) { | |
_y[i] = 0.5 * (1.0 + cos(PI*((2*_x[i]/_a)-(2/_a)+1.0))); | |
} | |
} | |
break; | |
// adjustable sliding gaussian courtesy of @golanlevin . | |
case "slidinggaussian": | |
if(!_args[0]) _args[0] = 0.5; // default center | |
if(!_args[1]) _args[1] = 0.4; // default sigma | |
var dx = 2.0*(_args[0] - 0.5); | |
var sigma = _args[1] * 2.; | |
for(var i in _x) _y[i] = exp(0.0 - (sq(_x[i]*2.-1.0-dx) / (2.0*sigma*sigma))); | |
break; | |
// adjustable center cosine window courtesy of @golanlevin . | |
case "adjustablecosine": | |
if(!_args[0]) _args[0] = 0.5; // default center | |
var _a = _args[0]; | |
var ah = _a/2.0; | |
var omah = 1.0 - ah; | |
for(i in _x) | |
{ | |
_y[i] = 1.0; | |
if (_x[i] <= _a) { | |
_y[i] = 0.5 * (1.0 + cos(PI*((_x[i]/_a)-1.0))); | |
} | |
else { | |
_y[i] = 0.5 * (1.0 + cos(PI*(((_x[i]-_a)/(1.0-_a))))); | |
} | |
} | |
break; | |
// adjustable center elliptic window courtesy of @golanlevin . | |
case "elliptic": | |
if(!_args[0]) _args[0] = 0.5; // default center | |
var _a = _args[0]; | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
for(i in _x) | |
{ | |
_y[i] = 0; | |
if (_x[i]<=_a){ | |
_y[i] = (1.0/_a) * sqrt(sq(_a) - sq(_x[i]-_a)); | |
} | |
else { | |
_y[i] = (1.0/(1-_a)) * sqrt(sq(1.0-_a) - sq(_x[i]-_a)); | |
} | |
} | |
break; | |
// adjustable center hyperelliptic window courtesy of @golanlevin . | |
case "hyperelliptic": | |
if(!_args[0]) _args[0] = 0.5; // default center | |
if(!_args[1]) _args[1] = 3; // default order | |
var _a = _args[0]; | |
var _n = _args[1]; | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
for(i in _x) | |
{ | |
_y[i] = 0; | |
var pwn = _n * 2.0; | |
if (_x[i]<=_a){ | |
_y[i] = (1.0/_a) * pow( pow(_a, pwn) - pow(_x[i]-_a, pwn), 1.0/pwn); | |
} | |
else { | |
_y[i] = ((1.0/ (1-_a))) * pow( pow(1.0-_a, pwn) - pow(_x[i]-_a, pwn), 1.0/pwn); | |
} | |
} | |
break; | |
// adjustable center squircular window courtesy of @golanlevin . | |
case "squircular": | |
if(!_args[0]) _args[0] = 0.5; // default center | |
if(!_args[1]) _args[1] = 3; // default order | |
var _a = _args[0]; | |
var _n = _args[1]; | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
for(i in _x) | |
{ | |
_y[i] = 0; | |
var pwn = max(2, _n * 2.0); | |
if (_x[i]<=_a){ | |
_y[i] = (1-_a) + pow( pow(_a, pwn) - pow(_x[i]-_a, pwn), 1.0/pwn); | |
} | |
else { | |
_y[i] = _a + pow( pow(1.0-_a, pwn) - pow(_x[i]-_a, pwn), 1.0/pwn); | |
} | |
} | |
break; | |
// poisson window functions courtesy of @golanlevin . | |
case "poisson": | |
if(!_args[0]) _args[0] = 0.5; // default center | |
var tau = max(_args[0], Number.EPSILON); | |
for(var i in _x) _y[i] = exp (0.0 - (abs(_x[i] - 0.5))*(1.0/tau)); | |
break; | |
case "hann-poisson": | |
case "poisson-hann": | |
case "hannpoisson": | |
case "poissonhann": | |
if(!_args[0]) _args[0] = 0.5; // default center | |
var tau = 25.0 * max(_args[0]*_args[0]*_args[0]*_args[0], Number.EPSILON); // nice control | |
for(i in _x) { | |
var hy = 0.5 * (1.0 - cos(TWO_PI*_x[i])); | |
var py = exp (0.0 - (abs(_x[i] - 0.5))*(1.0/tau)); | |
_y[i] = hy * py; | |
} | |
break; | |
case "slidinghann-poisson": | |
case "slidingpoisson-hann": | |
case "slidinghannpoisson": | |
case "slidingpoissonhann": | |
if(!_args[0]) _args[0] = 0.5; // default center | |
if(!_args[1]) _args[1] = 0.5; // default sigma | |
var tau = 25.0 * max(_args[1]*_args[1]*_args[1]*_args[1], Number.EPSILON); // nice control | |
for(i in _x) { | |
var newx = constrain(_x[i] + (0.5 - _args[0]), 0, 1); | |
var hy = 0.5 * (1.0 - cos(TWO_PI*newx)); | |
var py = exp (0.0 - (abs(newx - 0.5))*(1.0/tau)); | |
_y[i] = hy * py; | |
} | |
break; | |
for(i in _x) _y[i] = _x[i]; | |
default: | |
} | |
return(u ? _y : _y[0]); | |
} | |
// common waveform functions (0-1 evaluation). | |
p5.Gen.prototype.waveform = function(_x, _type) { | |
// algorithms: | |
// sine | |
// cosine | |
// saw / sawup | |
// sawdown | |
// phasor (ramp 0.-1.) | |
// square | |
// rect / rectangle | |
// pulse | |
// tri / triangle | |
// buzz | |
var u = true; // single value? | |
if(!Array.isArray(_x) && _x.constructor !== Float32Array && _x.constructor !== Float64Array) { | |
_x = [_x]; // process all values as arrays | |
u = false; | |
} | |
var _y // match type: | |
if(Array.isArray(_x)) _y = new Array(_x.length); | |
else if(_x.constructor === Float32Array) _y = new Float32Array(_x.length); | |
else if(_x.constructor === Float64Array) _y = new Float64Array(_x.length); | |
var i; | |
switch(_type) { | |
// sine wave 0. to 1. to -1. to 0. | |
case "sine": | |
case "sin": | |
_y = this.harmonics(_x, [1.]); | |
break; | |
// cosine wave 1. to -1. to 1. | |
case "cosine": | |
case "cos": | |
_y = this.triples(_x, [1., 1., 90]); | |
break; | |
// rising saw -1. to 1. | |
case "saw": | |
case "sawtooth": | |
case "sawup": | |
_y = this.bpf(_x, [0, -1., 1, 1.]); | |
break; | |
// falling saw 1. to -1. | |
case "sawdown": | |
_y = this.bpf(_x, [0, 1., 1, -1.]); | |
break; | |
// phasor ramp 0. to 1. | |
case "phasor": | |
_y = this.bpf(_x, [0, 0., 1, 1.]); | |
break; | |
// square wave 1. to -1. (equal duty cycle) | |
case "square": | |
_y = this.bpf(_x, [0, 1., 1, 1., 1, -1., 2, -1]); | |
break; | |
// rectangle wave 1. to -1. (10% duty cycle) | |
case "rect": | |
case "rectangle": | |
_y = this.bpf(_x, [0, 1., 1, 1., 1, -1., 10, -1]); | |
break; | |
// pulse wave 1. to -1. (1% duty cycle) | |
case "pulse": | |
_y = this.bpf(_x, [0, 1., 1, 1., 1, -1., 100, -1]); | |
break; | |
// triangle wave 0. to 1. to -1. to 0. | |
case "tri": | |
case "triangle": | |
_y = this.bpf(_x, [0, 0, 1, 1, 2, 0, 3, -1, 4, 0]); | |
break; | |
// buzz wave (10 harmonics at equal amplitude) 0. to 1. to -1. to 0. | |
case "buzz": | |
_y = this.harmonics(_x, [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]); | |
break; | |
default: | |
} | |
return(u ? _y : _y[0]); | |
} | |
// list algorithms | |
p5.Gen.prototype.listAlgos = function() { | |
var _styles = new Array(); | |
for(var i in this.__proto__) { | |
var _t = true; | |
if(i=="listAlgos") _t=false; | |
else if(i=="fillArray") _t=false; | |
else if(i=="fillFloat32Array") _t=false; | |
else if(i=="fillFloat64Array") _t=false; | |
if(_t) _styles.push(i); | |
} | |
return(_styles); | |
} | |
// array xfer (for pre-rendered use) | |
p5.Gen.prototype.fillArray = function(_algo, _len, _args, _seed) { | |
var _p = new Array(_len); | |
var _dest = new Array(_len); | |
if(_algo==='random') { // 4th argument is seed | |
for(var i = 0;i<_len;i++) | |
{ | |
if(_seed) _p[i] = i/(_len-1)*10000.+_seed; | |
else _p[i] = '-1'; | |
} | |
} | |
else { | |
for(var i = 0;i<_len;i++) | |
{ | |
_p[i] = i/(_len-1); // 0.-1. | |
} | |
} | |
_dest = this[_algo](_p, _args, _seed); | |
return(_dest); | |
} | |
// array xfer (for pre-rendered use) | |
p5.Gen.prototype.fillFloat32Array = function(_algo, _len, _args, _seed) { | |
var _p = new Float32Array(_len); | |
var _dest = new Float32Array(_len); | |
if(_algo==='random') { // 4th argument is seed | |
for(var i = 0;i<_len;i++) | |
{ | |
if(_seed) _p[i] = i/(_len-1)*10000.+_seed; | |
else _p[i] = '-1'; | |
} | |
} | |
else { | |
for(var i = 0;i<_len;i++) | |
{ | |
_p[i] = i/(_len-1); // 0.-1. | |
} | |
} | |
_dest = this[_algo](_p, _args, _seed); | |
return(_dest); | |
} | |
// array xfer (for pre-rendered use) | |
p5.Gen.prototype.fillFloat64Array = function(_algo, _len, _args, _seed) { | |
var _p = new Float64Array(_len); | |
var _dest = new Float64Array(_len); | |
if(_algo==='random') { // 4th argument is seed | |
for(var i = 0;i<_len;i++) | |
{ | |
if(_seed) _p[i] = i/(_len-1)*10000.+_seed; | |
else _p[i] = '-1'; | |
} | |
} | |
else { | |
for(var i = 0;i<_len;i++) | |
{ | |
_p[i] = i/(_len-1); // 0.-1. | |
} | |
} | |
_dest = this[_algo](_p, _args, _seed); | |
return(_dest); | |
} | |
// ============================================================================= | |
// p5.Ease | |
// ============================================================================= | |
/** | |
* Base class for an easing function | |
* | |
* @class p5.Ease | |
* @constructor | |
*/ | |
p5.Ease = function() { | |
// | |
// this object generates easing / tweening functions | |
// through direct (0.-1.) evaluation, with utilities | |
// to pre-evaluate functions into lookup tables. | |
// | |
// algorithms based on: | |
// | |
// robert penner's algorithms discussed in | |
// 'programming macromedia flash mx' (2002). | |
// Copyright (C) 2001 Robert Penner, released under the BSD License | |
// | |
// golan levin's Pattern_Master functions: | |
// https://github.com/golanlevin/Pattern_Master | |
// Copyright (C) 2006 Golan Levin | |
// | |
// some functions have additional parameters, | |
// such as an order of interpolation (n) or up to four | |
// coefficients (a, b, c, d) which will change the | |
// behavior of the easing function. | |
// | |
this.version = 0.01; // just some crap for constructor | |
var that = this; // some bullshit | |
}; // end p5.Ease constructor | |
// Penner's Easing Functions: | |
// line y = x | |
p5.Ease.prototype.linear = function(_x) { | |
return(_x); | |
}; | |
// parabola y = x^2 | |
p5.Ease.prototype.quadraticIn = function(_x) { | |
return(_x * _x); | |
}; | |
// parabola y = -x^2 + 2x | |
p5.Ease.prototype.quadraticOut = function(_x) { | |
return(-(_x * (_x - 2))); | |
} | |
// piecewise quadratic | |
// y = (1/2)((2x)^2) ; [0, 0.5) | |
// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1] | |
p5.Ease.prototype.quadraticInOut = function(_x) { | |
if(_x < 0.5) | |
{ | |
return(2 * _x * _x); | |
} | |
else | |
{ | |
return((-2 * _x * _x) + (4 * _x) - 1); | |
} | |
} | |
// cubic y = x^3 | |
p5.Ease.prototype.cubicIn = function(_x) { | |
return(_x * _x * _x); | |
} | |
// cubic y = (x - 1)^3 + 1 | |
p5.Ease.prototype.cubicOut = function(_x) { | |
var _v = (_x - 1); | |
return(_v * _v * _v + 1); | |
} | |
// piecewise cubic | |
// y = (1/2)((2x)^3) ; [0, 0.5) | |
// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1] | |
p5.Ease.prototype.cubicInOut = function(_x) { | |
if(_x < 0.5) | |
{ | |
return(4 * _x * _x * _x); | |
} | |
else | |
{ | |
var _v = ((2 * _x) - 2); | |
return(0.5 * _v * _v * _v + 1); | |
} | |
} | |
// quartic x^4 | |
p5.Ease.prototype.quarticIn = function(_x) { | |
return(_x * _x * _x * _x); | |
} | |
// quartic y = 1 - (x - 1)^4 | |
p5.Ease.prototype.quarticOut = function(_x) { | |
var _v = (_x - 1); | |
return(_v * _v * _v * (1 - _x) + 1); | |
} | |
// piecewise quartic | |
// y = (1/2)((2x)^4) ; [0, 0.5) | |
// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1] | |
p5.Ease.prototype.quarticInOut = function(_x) { | |
if(_x < 0.5) | |
{ | |
return(8 * _x * _x * _x * _x); | |
} | |
else | |
{ | |
var _v = (_x - 1); | |
return(-8 * _v * _v * _v * _v + 1); | |
} | |
} | |
// quintic y = x^5 | |
p5.Ease.prototype.quinticIn = function(_x) { | |
return(_x * _x * _x * _x * _x); | |
} | |
// quintic y = (x - 1)^5 + 1 | |
p5.Ease.prototype.quinticOut = function(_x) { | |
var _v = (_x - 1); | |
return(_v * _v * _v * _v * _v + 1); | |
} | |
// piecewise quintic | |
// y = (1/2)((2x)^5) ; [0, 0.5) | |
// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1] | |
p5.Ease.prototype.quinticInOut = function(_x) { | |
if(_x < 0.5) | |
{ | |
return(16 * _x * _x * _x * _x * _x); | |
} | |
else | |
{ | |
var _v = ((2 * _x) - 2); | |
return(0.5 * _v * _v * _v * _v * _v + 1); | |
} | |
} | |
// quarter-cycle sine | |
p5.Ease.prototype.sineIn = function(_x) { | |
return(sin((_x - 1) * HALF_PI) + 1); | |
} | |
// quarter-cycle cosine | |
p5.Ease.prototype.sineOut = function(_x) { | |
return(sin(_x * HALF_PI)); | |
} | |
// half sine | |
p5.Ease.prototype.sineInOut = function(_x) { | |
return(0.5 * (1 - cos(_x * PI))); | |
} | |
// shifted quadrant IV of unit circle | |
p5.Ease.prototype.circularIn = function(_x) { | |
return(1 - sqrt(1 - (_x * _x))); | |
} | |
// shifted quadrant II of unit circle | |
p5.Ease.prototype.circularOut = function(_x) { | |
return(sqrt((2 - _x) * _x)); | |
} | |
// piecewise circular function | |
// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5) | |
// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1] | |
p5.Ease.prototype.circularInOut = function(_x) { | |
if(_x < 0.5) | |
{ | |
return(0.5 * (1 - sqrt(1 - 4 * (_x * _x)))); | |
} | |
else | |
{ | |
return(0.5 * (sqrt(-((2 * _x) - 3) * ((2 * _x) - 1)) + 1)); | |
} | |
} | |
// exponential function y = 2^(10(x - 1)) | |
p5.Ease.prototype.exponentialIn = function(_x) { | |
return((_x == 0.0) ? _x : pow(2, 10 * (_x - 1))); | |
} | |
// exponential function y = -2^(-10x) + 1 | |
p5.Ease.prototype.exponentialOut = function(_x) { | |
return((_x == 1.0) ? _x : 1 - pow(2, -10 * _x)); | |
} | |
// piecewise exponential | |
// y = (1/2)2^(10(2x - 1)) ; [0,0.5) | |
// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1] | |
p5.Ease.prototype.exponentialInOut = function(_x) { | |
if(_x == 0.0 || _x == 1.0) return _x; | |
if(_x < 0.5) | |
{ | |
return(0.5 * pow(2, (20 * _x) - 10)); | |
} | |
else | |
{ | |
return(-0.5 * pow(2, (-20 * _x) + 10) + 1); | |
} | |
} | |
// damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1)) | |
p5.Ease.prototype.elasticIn = function(_x) { | |
return(sin(13 * HALF_PI * _x) * pow(2, 10 * (_x - 1))); | |
} | |
// damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1 | |
p5.Ease.prototype.elasticOut = function(_x) { | |
return(sin(-13 * HALF_PI * (_x + 1)) * pow(2, -10 * _x) + 1); | |
} | |
// piecewise damped sine wave: | |
// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5) | |
// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1] | |
p5.Ease.prototype.elasticInOut = function(_x) { | |
if(_x < 0.5) | |
{ | |
return(0.5 * sin(13 * HALF_PI * (2 * _x)) * pow(2, 10 * ((2 * _x) - 1))); | |
} | |
else | |
{ | |
return(0.5 * (sin(-13 * HALF_PI * ((2 * _x - 1) + 1)) * pow(2, -10 * (2 * _x - 1)) + 2)); | |
} | |
} | |
// overshooting cubic y = x^3-x*sin(x*pi) | |
p5.Ease.prototype.backIn = function(_x) { | |
return(_x * _x * _x - _x * sin(_x * PI)); | |
} | |
// overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi)) | |
p5.Ease.prototype.backOut = function(_x) { | |
var f = (1 - _x); | |
return(1 - (f * f * f - f * sin(f * PI))); | |
} | |
// piecewise overshooting cubic function: | |
// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5) | |
// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1] | |
p5.Ease.prototype.backInOut = function(_x) { | |
if(_x < 0.5) | |
{ | |
var f = 2 * _x; | |
return(0.5 * (f * f * f - f * sin(f * PI))); | |
} | |
else | |
{ | |
var f = (1 - (2*_x - 1)); | |
return(0.5 * (1 - (f * f * f - f * sin(f * PI))) + 0.5); | |
} | |
} | |
// penner's four-part bounce algorithm | |
p5.Ease.prototype.bounceIn = function(_x) { | |
return(1 - this.bounceOut(1 - _x)); | |
} | |
// penner's four-part bounce algorithm | |
p5.Ease.prototype.bounceOut = function(_x) { | |
if(_x < 4/11.0) | |
{ | |
return((121 * _x * _x)/16.0); | |
} | |
else if(_x < 8/11.0) | |
{ | |
return((363/40.0 * _x * _x) - (99/10.0 * _x) + 17/5.0); | |
} | |
else if(_x < 9/10.0) | |
{ | |
return((4356/361.0 * _x * _x) - (35442/1805.0 * _x) + 16061/1805.0); | |
} | |
else | |
{ | |
return((54/5.0 * _x * _x) - (513/25.0 * _x) + 268/25.0); | |
} | |
} | |
// piecewise penner's four-part bounce algorithm | |
p5.Ease.prototype.bounceInOut = function(_x) { | |
if(_x < 0.5) | |
{ | |
return(0.5 * this.bounceIn(_x*2)); | |
} | |
else | |
{ | |
return(0.5 * this.bounceOut(_x * 2 - 1) + 0.5); | |
} | |
} | |
// Golan's Pattern Master Functions: | |
// bryce summers' cubic easing function (@Bryce-Summers) | |
p5.Ease.prototype.brycesCubic = function(_x, _n) | |
{ | |
if(!_n) _n = 3; // default | |
var p = pow(_x, _n-1); | |
var xn = p * _x; | |
return(_n*p - (_n-1)*xn); | |
} | |
// staircase function - n is # of steps | |
p5.Ease.prototype.staircase = function(_x, _n) | |
{ | |
if(!_n) _n = 3; // default | |
var _y = floor(_x*_n) / (_n-1); | |
if(_x>=1.) _y=1.; | |
return(_y); | |
} | |
// staircase function with smoothing - a is smoothing, n is # of steps | |
p5.Ease.prototype.exponentialSmoothedStaircase = function(_x, _a, _n) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_n) _n = 3; // default | |
// See http://web.mit.edu/fnl/volume/204/winston.html | |
var fa = sq (map(_a, 0,1, 5,30)); | |
var _y = 0; | |
for (var i=0; i<_n; i++){ | |
_y += (1.0/(_n-1.0))/ (1.0 + exp(fa*(((i+1.0)/_n) - _x))); | |
} | |
_y = constrain(_y, 0,1); | |
return(_y); | |
} | |
// gompertz curve | |
// http://en.wikipedia.org/wiki/Gompertz_curve | |
p5.Ease.prototype.gompertz = function(_x, _a) | |
{ | |
if(!_a) _a = 0.25; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
_a = max(_a, min_param_a); | |
var b = -8.0; | |
var c = 0 - _a*16.0; | |
var _y = exp( b * exp(c * _x)); | |
var maxVal = exp(b * exp(c)); | |
var minVal = exp(b); | |
_y = map(_y, minVal, maxVal, 0, 1); | |
return(_y); | |
} | |
// clamped processing map function with terms reordered: | |
// min in, max in, min out, max out | |
p5.Ease.prototype.generalizedLinearMap = function(_x, _a, _b, _c, _d) | |
{ | |
if(!_a) _a = 0.; // default | |
if(!_b) _b = 0.; // default | |
if(!_c) _c = 1.; // default | |
if(!_d) _d = 1.; // default | |
var _y = 0; | |
if (_a < _c) { | |
if (_x <= _a) { | |
_y = _b; | |
} | |
else if (_x >= _c) { | |
_y = _d; | |
} | |
else { | |
_y = map(_x, _a, _c, _b, _d); | |
} | |
} | |
else { | |
if (_x <= _c) { | |
_y = _d; | |
} | |
else if (_x >= _a) { | |
_y = _b; | |
} | |
else { | |
_y = map(_x, _c, _a, _d, _b); | |
} | |
} | |
return(_y); | |
} | |
// double-(odd) polynomial ogee | |
// what the hell is an ogee, you may ask? | |
// https://en.wikipedia.org/wiki/Ogee | |
p5.Ease.prototype.doubleOddPolynomialOgee = function(_x, _a, _b, _n) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
if(!_n) _n = 3; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
var min_param_b = 0.0; | |
var max_param_b = 1.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_b = constrain(_b, min_param_b, max_param_b); | |
var p = 2*_n + 1; | |
var _y = 0; | |
if (_x <= _a) { | |
_y = _b - _b*pow(1-_x/_a, p); | |
} | |
else { | |
_y = _b + (1-_b)*pow((_x-_a)/(1-_a), p); | |
} | |
return(_y); | |
} | |
// double-linear interpolator | |
p5.Ease.prototype.doubleLinear = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
var _y = 0; | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
var min_param_b = 0.0; | |
var max_param_b = 1.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_b = constrain(_b, min_param_b, max_param_b); | |
if (_x<=_a) { | |
_y = (_b/_a) * _x; | |
} | |
else { | |
_y = _b + ((1-_b)/(1-_a))*(_x-_a); | |
} | |
return(_y); | |
} | |
// triple-linear interpolator | |
p5.Ease.prototype.tripleLinear = function(_x, _a, _b, _c, _d) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
if(!_c) _c = 0.75; // default | |
if(!_d) _d = 0.25; // default | |
var _y = 0; | |
if (_a < _c) { | |
if (_x <= _a) { | |
_y = map(_x, 0, _a, 0, _b); | |
} | |
else if (_x >= _c) { | |
_y = map(_x, _c, 1, _d, 1); | |
} | |
else { | |
_y = map(_x, _a, _c, _b, _d); | |
} | |
} | |
else { | |
if (_x <= _c) { | |
_y = map(_x, 0, _c, 0, _d); | |
} | |
else if (_x >= _a) { | |
_y = map(_x, _a, 1, _b, 1); | |
} | |
else { | |
_y = map(_x, _c, _a, _d, _b); | |
} | |
} | |
return(_y); | |
} | |
// variable staircase interpolator | |
p5.Ease.prototype.variableStaircase = function(_x, _a, _n) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_n) _n = 3; // default | |
var aa = (_a - 0.5); | |
if (aa == 0) { | |
return(_x); | |
} | |
var x0 = (floor (_x*_n))/ _n; | |
var x1 = (ceil (_x*_n))/ _n; | |
var y0 = x0; | |
var y1 = x1; | |
var px = 0.5*(x0+x1) + aa/_n; | |
var py = 0.5*(x0+x1) - aa/_n; | |
var _y = 0; | |
if ((_x < px) && (_x > x0)) { | |
_y = map(_x, x0, px, y0, py); | |
} | |
else { | |
_y = map(_x, px, x1, py, y1); | |
} | |
return(_y); | |
} | |
// quadratic bezier staircase function | |
p5.Ease.prototype.quadraticBezierStaircase = function(_x, _a, _n) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_n) _n = 3; // default | |
var aa = (_a - 0.5); | |
if (aa == 0) { | |
return(_x); | |
} | |
var x0 = (floor (_x*_n))/ _n; | |
var x1 = (ceil (_x*_n))/ _n; | |
var y0 = x0; | |
var y1 = x1; | |
var px = 0.5*(x0+x1) + aa/_n; | |
var py = 0.5*(x0+x1) - aa/_n; | |
var p0x = (x0 + px)/2.0; | |
var p0y = (y0 + py)/2.0; | |
var p1x = (x1 + px)/2.0; | |
var p1y = (y1 + py)/2.0; | |
var _y = 0; | |
var denom = (1.0/_n)*0.5; | |
if ((_x <= p0x) && (_x >= x0)) { | |
// left side | |
if (floor (_x*_n) <= 0){ | |
_y = map(_x, x0, px, y0, py); | |
} else { | |
if (abs(_x - x0) < Number.EPSILON){ | |
// problem when x == x0 ! | |
} | |
var za = (x0 - (p1x - 1.0/_n))/denom; | |
var zb = (y0 - (p1y - 1.0/_n))/denom; | |
var zx = (_x - (p1x - 1.0/_n))/denom; | |
var om2a = 1.0 - 2.0*za; | |
var interior = max (0, za*za + om2a*zx); | |
var t = (sqrt(interior) - za)/om2a; | |
var zy = (1.0-2.0*zb)*(t*t) + (2*zb)*t; | |
zy *= (p1y - p0y); | |
zy += p1y; //(p1y - 1.0/n); | |
if (_x > x0){ | |
zy -= 1.0/_n; | |
} | |
_y = zy; | |
} | |
} | |
else if ((_x >= p1x) && (_x <= x1)) { | |
// right side | |
if (ceil(_x*_n) >= _n) { | |
_y = map(_x, px, x1, py, y1); | |
} | |
else { | |
if (abs(_x - x1) < Number.EPSILON){ | |
// problem when x == x1 ! | |
} | |
var za = (x1 - p1x)/denom; | |
var zb = (y1 - p1y)/denom; | |
var zx = (_x - p1x)/denom; | |
if (za == 0.5) { | |
za += Number.EPSILON; | |
} | |
var om2a = 1.0 - 2.0*za; | |
if (abs(om2a) < Number.EPSILON) { | |
om2a = ((om2a < 0) ? -1:1) * Number.EPSILON; | |
} | |
var interior = max (0, za*za + om2a*zx); | |
var t = (sqrt(interior) - za)/om2a; | |
var zy = (1.0-2.0*zb)*(t*t) + (2*zb)*t; | |
zy *= (p1y - p0y); | |
zy += p1y; | |
_y = zy; | |
} | |
} | |
else { | |
// center | |
var za = (px - p0x)/denom; | |
var zb = (py - p0y)/denom; | |
var zx = (_x - p0x)/denom; | |
if (za == 0.5) { | |
za += Number.EPSILON; | |
} | |
var om2a = 1.0 - 2.0*za; | |
var t = (sqrt(za*za + om2a*zx) - za)/om2a; | |
var zy = (1.0-2.0*zb)*(t*t) + (2*zb)*t; | |
zy *= (p1y - p0y); | |
zy += p0y; | |
_y = zy; | |
} | |
return(_y); | |
} | |
// symmetric double-element sigmoid function (a is slope) | |
// https://en.wikipedia.org/wiki/Sigmoid_function | |
p5.Ease.prototype.doubleExponentialSigmoid = function(_x, _a) | |
{ | |
if(!_a) _a = 0.75; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_a = 1-_a; | |
var _y = 0; | |
if (_x<=0.5){ | |
_y = (pow(2.0*_x, 1.0/_a))/2.0; | |
} | |
else { | |
_y = 1.0 - (pow(2.0*(1.0-_x), 1.0/_a))/2.0; | |
} | |
return(_y); | |
} | |
// double-element sigmoid function with an adjustable center (b is center) | |
p5.Ease.prototype.adjustableCenterDoubleExponentialSigmoid = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.75; // default | |
if(!_b) _b = 0.5; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_a = 1-_a; | |
var _y = 0; | |
var w = max(0, min(1, _x-(_b-0.5))); | |
if (w<=0.5){ | |
_y = (pow(2.0*w, 1.0/_a))/2.0; | |
} | |
else { | |
_y = 1.0 - (pow(2.0*(1.0-w), 1.0/_a))/2.0; | |
} | |
return(_y); | |
} | |
// quadratic sigmoid function | |
p5.Ease.prototype.doubleQuadraticSigmoid = function(_x) | |
{ | |
var _y = 0; | |
if (_x<=0.5){ | |
_y = sq(2.0*_x)/2.0; | |
} | |
else { | |
_y = 1.0 - sq(2.0*(_x-1.0))/2.0; | |
} | |
return(_y); | |
} | |
// double polynomial sigmoid function | |
p5.Ease.prototype.doublePolynomialSigmoid = function(_x, _n) | |
{ | |
if(!_n) _n = 3; // default | |
var _y = 0; | |
if (_n%2 == 0){ | |
// even polynomial | |
if (_x<=0.5){ | |
_y = pow(2.0*_x, _n)/2.0; | |
} | |
else { | |
_y = 1.0 - pow(2*(_x-1.0), _n)/2.0; | |
} | |
} | |
else { | |
// odd polynomial | |
if (_x<=0.5){ | |
_y = pow(2.0*_x, _n)/2.0; | |
} | |
else { | |
_y = 1.0 + pow(2.0*(_x-1.0), _n)/2.0; | |
} | |
} | |
return(_y); | |
} | |
// double elliptic ogee | |
// http://www.flong.com/texts/code/shapers_circ/ | |
p5.Ease.prototype.doubleEllipticOgee = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
var _y = 0; | |
if (_x<=_a){ | |
_y = (_b/_a) * sqrt(sq(_a) - sq(_x-_a)); | |
} | |
else { | |
_y = 1.0 - ((1.0-_b)/(1.0-_a))*sqrt(sq(1.0-_a) - sq(_x-_a)); | |
} | |
return(_y); | |
} | |
// double-cubic ogee | |
p5.Ease.prototype.doubleCubicOgee = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
var min_param_b = 0.0; | |
var max_param_b = 1.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_b = constrain(_b, min_param_b, max_param_b); | |
var _y = 0; | |
if (_x <= _a){ | |
_y = _b - _b*pow(1.0-_x/_a, 3.0); | |
} | |
else { | |
_y = _b + (1.0-_b)*pow((_x-_a)/(1.0-_a), 3.0); | |
} | |
return(_y); | |
} | |
// double circular sigmoid function | |
p5.Ease.prototype.doubleCircularSigmoid = function(_x, _a) | |
{ | |
if(!_a) _a = 0.25; // default | |
var _y = 0; | |
if (_x<=_a) { | |
_y = _a - sqrt(_a*_a - _x*_x); | |
} | |
else { | |
_y = _a + sqrt(sq(1.0-_a) - sq(_x-1.0)); | |
} | |
return(_y); | |
} | |
// double squircular sigmoid function | |
p5.Ease.prototype.doubleSquircularSigmoid = function(_x, _a, _n) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_n) _n = 3; // default | |
var pwn = max(2, _n * 2.0); | |
var _y = 0; | |
if (_x<=_a) { | |
_y = _a - pow( pow(_a,pwn) - pow(_x,pwn), 1.0/pwn); | |
} | |
else { | |
_y = _a + pow(pow(1.0-_a, pwn) - pow(_x-1.0, pwn), 1.0/pwn); | |
} | |
return(_y); | |
} | |
// double quadratic bezier curve | |
// http://engineeringtraining.tpub.com/14069/css/14069_150.htm | |
p5.Ease.prototype.doubleQuadraticBezier = function(_x, _a, _b, _c, _d) | |
{ | |
// produces mysterious values when a=0,b=1,c=0.667,d=0.417 | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
if(!_c) _c = 0.75; // default | |
if(!_d) _d = 0.25; // default | |
var xmid = (_a + _c)/2.0; | |
var ymid = (_b + _d)/2.0; | |
xmid = constrain (xmid, Number.EPSILON, 1.0-Number.EPSILON); | |
ymid = constrain (ymid, Number.EPSILON, 1.0-Number.EPSILON); | |
var _y = 0; | |
var om2a; | |
var t; | |
var xx; | |
var aa; | |
var bb; | |
if (_x <= xmid){ | |
xx = _x / xmid; | |
aa = _a / xmid; | |
bb = _b / ymid; | |
om2a = 1.0 - 2.0*aa; | |
if (om2a == 0) { | |
om2a = Number.EPSILON; | |
} | |
t = (sqrt(aa*aa + om2a*xx) - aa)/om2a; | |
_y = (1.0-2.0*bb)*(t*t) + (2*bb)*t; | |
_y *= ymid; | |
} | |
else { | |
xx = (_x - xmid)/(1.0-xmid); | |
aa = (_c - xmid)/(1.0-xmid); | |
bb = (_d - ymid)/(1.0-ymid); | |
om2a = 1.0 - 2.0*aa; | |
if (om2a == 0) { | |
om2a = Number.EPSILON; | |
} | |
t = (sqrt(aa*aa + om2a*xx) - aa)/om2a; | |
_y = (1.0-2.0*bb)*(t*t) + (2*bb)*t; | |
_y *= (1.0 - ymid); | |
_y += ymid; | |
} | |
return(_y); | |
} | |
// double-elliptic sigmoid function | |
p5.Ease.prototype.doubleEllipticSigmoid = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
var _y = 0; | |
if (_x<=_a){ | |
if (_a <= 0){ | |
_y = 0; | |
} else { | |
_y = _b * (1.0 - (sqrt(sq(_a) - sq(_x))/_a)); | |
} | |
} | |
else { | |
if (_a >= 1){ | |
_y = 1.0; | |
} else { | |
_y = _b + ((1.0-_b)/(1.0-_a))*sqrt(sq(1.0-_a) - sq(_x-1.0)); | |
} | |
} | |
return(_y); | |
} | |
// simplified double-cubic ogee | |
p5.Ease.prototype.doubleCubicOgeeSimplified = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
_b = 1 - _b; //reverse, for intelligibility. | |
var _y = 0; | |
if (_x<=_a){ | |
if (_a <= 0){ | |
_y = 0; | |
} else { | |
var val = 1 - _x/_a; | |
_y = _b*_x + (1-_b)*_a*(1.0- val*val*val); | |
} | |
} | |
else { | |
if (_a >= 1){ | |
_y = 1; | |
} else { | |
var val = (_x-_a)/(1-_a); | |
_y = _b*_x + (1-_b)*(_a + (1-_a)* val*val*val); | |
} | |
} | |
return(_y); | |
} | |
// raised inverted cosine function | |
p5.Ease.prototype.raisedInvertedCosine = function(_x) | |
{ | |
var _y = (1.0 - cos(PI*_x))/2.0; | |
return(_y); | |
} | |
// blinn / wyvill's cosine approximation | |
// http://www.flong.com/texts/code/shapers_poly/ | |
p5.Ease.prototype.cosineApproximation = function(_x) | |
{ | |
var x2 = _x*_x; | |
var x4 = x2*x2; | |
var x6 = x4*x2; | |
var fa = (4.0/9.0); | |
var fb = (17.0/9.0); | |
var fc = (22.0/9.0); | |
var _y = fa*x6 - fb*x4 + fc*x2; | |
return(_y); | |
} | |
// smoothstep function | |
// https://en.wikipedia.org/wiki/Smoothstep | |
p5.Ease.prototype.smoothStep = function(_x) | |
{ | |
return(_x*_x*(3.0 - 2.0*_x)); | |
} | |
// ken perlin's 'smoother step' smoothstep function | |
// https://www.amazon.com/Texturing-Modeling-Third-Procedural-Approach/dp/1558608486 | |
p5.Ease.prototype.smootherStep = function(_x) | |
{ | |
return(_x*_x*_x*(_x*(_x*6.0 - 15.0) + 10.0)); | |
} | |
// maclaurin cosine approximation | |
// http://blogs.ubc.ca/infiniteseriesmodule/units/unit-3-power-series/taylor-series/the-maclaurin-expansion-of-cosx/ | |
p5.Ease.prototype.maclaurinCosine = function(_x) | |
{ | |
var nTerms = 6; // anything less is fouled | |
_x *= PI; | |
var xp = 1.0; | |
var x2 = _x*_x; | |
var sig = 1.0; | |
var fact = 1.0; | |
var _y = xp; | |
for (var i=0; i<nTerms; i++) { | |
xp *= x2; | |
sig = 0-sig; | |
fact *= (i*2+1); | |
fact *= (i*2+2); | |
_y += sig * (xp / fact); | |
} | |
_y = (1.0 - _y)/2.0; | |
return(_y); | |
} | |
// paul bourke's catmull rom spline | |
// https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline | |
// from http://paulbourke.net/miscellaneous/interpolation/ | |
p5.Ease.prototype.catmullRomInterpolate = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
var y0 = _a; | |
var y3 = _b; | |
var x2 = _x*_x; | |
var a0 = -0.5*y0 + 0.5*y3 - 1.5 ; | |
var a1 = y0 - 0.5*y3 + 2.0 ; | |
var a2 = -0.5*y0 + 0.5 ; | |
var _y = a0*_x*x2 + a1*x2 + a2*_x; | |
return(constrain(_y, 0, 1)); | |
} | |
// hermite polynomial function | |
// https://en.wikipedia.org/wiki/Hermite_polynomials | |
// from http://musicdsp.org/showArchiveComment.php?ArchiveID=93 | |
// by Laurent de Soras | |
p5.Ease.prototype.hermite = function(_x, _a, _b, _c, _d) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.; // default - ??? | |
if(!_c) _c = 1.; // default - ??? | |
if(!_d) _d = 0.25; // default | |
_a = map(_a, 0,1, -1,1); | |
_c = map(_c, 0,1, -1,1); | |
var hC = (_c - _a) * 0.5; | |
var hV = (_b - _c); | |
var hW = hC + hV; | |
var hA = hW + hV + (_d - _b) * 0.5; | |
var hB = hW + hA; | |
var _y = (((hA * _x) - hB) * _x + hC) * _x + _b; | |
return(_y); | |
} | |
// hermite polynomial function | |
// from http://paulbourke.net/miscellaneous/interpolation/ | |
p5.Ease.prototype.hermite2 = function(_x, _a, _b, _c, _d) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
if(!_c) _c = 0.75; // default | |
if(!_d) _d = 0.25; // default | |
// | |
// Tension: 1 is high, 0 normal, -1 is low | |
// Bias: 0 is even, positive is towards first segment, negative towards the other | |
// | |
var tension = map (_c, 0,1, -1,1); | |
var bias = map (_d, 0,1, -1,1); | |
var y0 = 2.0 * (_a - 0.5); //? a | |
var y1 = 0.0; | |
var y2 = 1.0; | |
var y3 = _b; | |
var x2 = _x * _x; | |
var x3 = x2 * _x; | |
var m0, m1; | |
m0 = (y1-y0)*(1.0+bias)*(1.0-tension)/2.0; | |
m0 += (y2-y1)*(1.0-bias)*(1.0-tension)/2.0; | |
m1 = (y2-y1)*(1.0+bias)*(1.0-tension)/2.0; | |
m1 += (y3-y2)*(1.0-bias)*(1.0-tension)/2.0; | |
var a0 = 2.0*x3 - 3.0*x2 + 1.0; | |
var a1 = x3 - 2.0*x2 + _x; | |
var a2 = x3 - x2; | |
var a3 = -2.0*x3 + 3.0*x2; | |
var _y = a0*y1 + a1*m0 + a2*m1 + a3*y2; | |
return(_y); | |
} | |
// error function | |
// http://en.wikipedia.org/wiki/Error_function | |
// Note that this implementation is a shifted, scaled and normalized error function! | |
p5.Ease.prototype.normalizedErf = function(_x) | |
{ | |
var erfBound = 2.0; // set bounds for artificial "normalization" | |
var erfBoundNorm = 0.99532226501; // this = erf(2.0), i.e., erf(erfBound) | |
var z = map(_x, 0.0, 1.0, 0-erfBound, erfBound); | |
var z2 = z*z; | |
var a = (8.0*(PI-3.0)) / ((3*PI)*(4.0-PI)); | |
var _y = sqrt (1.0 - exp(0 - z2*( (a*z2 + 4.0/PI) / (a*z2 + 1.0)))); | |
if (z < 0.0) _y = 0-_y; | |
_y /= erfBoundNorm; | |
_y = (_y+1.0) / 2.0; | |
return(_y); | |
} | |
// inverse error function | |
p5.Ease.prototype.normalizedInverseErf = function(_x) | |
{ | |
var erfBound = 2.0; | |
var erfBoundNorm = 0.99532226501; // this = erf(2.0), i.e., erf(erfBound) | |
var z = map(_x, 0, 1, -erfBoundNorm, erfBoundNorm); | |
var z2 = z*z; | |
var a = (8.0*(PI-3.0)) / ((3*PI)*(4.0-PI)); | |
var A = (2.0 / (PI *a)) + (log(1.0-z2) / 2.0); | |
var B = (log(1.0-z2) / a); | |
var _y = sqrt( sqrt(A*A - B) - A ); | |
if (z < 0.0) _y = 0-_y; | |
_y /= erfBound; | |
_y = (_y+1.0); | |
_y /= 2.0; | |
_y = constrain(_y, 0, 1); // necessary | |
return(_y); | |
} | |
// exponential emphasis function | |
p5.Ease.prototype.exponentialEmphasis = function(_x, _a) | |
{ | |
if(!_a) _a = 0.25; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
if (_a < 0.5) { | |
// emphasis | |
_a = 2*(_a); | |
var _y = pow(_x, _a); | |
return(_y); | |
} | |
else { | |
// de-emphasis | |
_a = 2*(_a-0.5); | |
var _y = pow(_x, 1.0/(1-_a)); | |
return(_y); | |
} | |
} | |
// iterative square root | |
// http://en.wikipedia.org/wiki/Methods_of_computing_square_roots | |
// ancient babylonian technology | |
p5.Ease.prototype.iterativeSquareRoot = function(_x) | |
{ | |
var _y = 0.5; | |
var n = 6; | |
for (var i=0; i<n; i++) { | |
_y = (_y + _x/_y)/2.0; | |
} | |
return(_y); | |
} | |
// fast inverse square root | |
// http://en.wikipedia.org/wiki/Fast_inverse_square_root | |
// http://stackoverflow.com/questions/11513344/how-to-implement-the-fast-inverse-square-root-in-java | |
p5.Ease.prototype.fastSquareRoot = function(_x) | |
{ | |
var xhalf = 0.5 * _x; | |
var i = f2ib(_x); | |
i = 0x5f3759df - (i>>1); | |
_x = ib2f(i); | |
_x = _x*(1.5 - xhalf*_x*_x); | |
return(1.0/_x); | |
} | |
// symmetric double-exponential ogee | |
p5.Ease.prototype.doubleExponentialOgee = function(_x, _a) | |
{ | |
if(!_a) _a = 0.25; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
var _y = 0; | |
if (_x<=0.5){ | |
_y = (pow(2.0*_x, 1.0-_a))/2.0; | |
} | |
else { | |
_y = 1.0 - (pow(2.0*(1.0-_x), 1.0-_a))/2.0; | |
} | |
return(_y); | |
} | |
// joining two lines with a circular arc fillet | |
// Adapted from robert d. miller / graphics gems | |
// http://www.realtimerendering.com/resources/GraphicsGems/gemsiii/fillet.c | |
p5.Ease.prototype.circularFillet = function(_x, _a, _b, _c) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
if(!_c) _c = 0.75; // default | |
var arcStartAngle = 0; | |
var arcEndAngle = 0; | |
var arcStartX = 0; | |
var arcStartY = 0; | |
var arcEndX = 0; | |
var arcEndY = 0; | |
var arcCenterX = 0; | |
var arcCenterY = 0; | |
var arcRadius = 0; | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
var min_param_b = 0.0 + Number.EPSILON; | |
var max_param_b = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_b = constrain(_b, min_param_b, max_param_b); | |
var R = _c; | |
// helper function: | |
// return signed distance from line Ax + By + C = 0 to point P. | |
function linetopoint(a, b, c, ptx, pty) { | |
var lp = 0.0; | |
var d = sqrt((a*a)+(b*b)); | |
if (d != 0.0) { | |
lp = (a*ptx + b*pty + c)/d; | |
} | |
return(lp); | |
} | |
// compute fillet parameters: | |
computefillet: { | |
var p1x = 0; | |
var p1y = 0; | |
var p2x = _a; | |
var p2y = _b; | |
var p3x = _a; | |
var p3y = _b; | |
var p4x = 1; | |
var p4y = 1; | |
var r = R; | |
var c1 = p2x*p1y - p1x*p2y; | |
var a1 = p2y-p1y; | |
var b1 = p1x-p2x; | |
var c2 = p4x*p3y - p3x*p4y; | |
var a2 = p4y-p3y; | |
var b2 = p3x-p4x; | |
if ((a1*b2) == (a2*b1)) { /* Parallel or coincident lines */ | |
break computefillet; | |
} | |
var d1, d2; | |
var mPx, mPy; | |
mPx= (p3x + p4x)/2.0; | |
mPy= (p3y + p4y)/2.0; | |
d1 = linetopoint(a1, b1, c1, mPx, mPy); /* Find distance p1p2 to p3 */ | |
if (d1 == 0.0) { | |
break computefillet; | |
} | |
mPx= (p1x + p2x)/2.0; | |
mPy= (p1y + p2y)/2.0; | |
d2 = linetopoint(a2, b2, c2, mPx, mPy); /* Find distance p3p4 to p2 */ | |
if (d2 == 0.0) { | |
break computefillet; | |
} | |
var c1p, c2p, d; | |
var rr = r; | |
if (d1 <= 0.0) { | |
rr= -rr; | |
} | |
c1p = c1 - rr*sqrt((a1*a1)+(b1*b1)); /* Line parallel l1 at d */ | |
rr = r; | |
if (d2 <= 0.0) { | |
rr = -rr; | |
} | |
c2p = c2 - rr*sqrt((a2*a2)+(b2*b2)); /* Line parallel l2 at d */ | |
d = (a1*b2)-(a2*b1); | |
var pCx = (c2p*b1-c1p*b2)/d; /* Intersect constructed lines */ | |
var pCy = (c1p*a2-c2p*a1)/d; /* to find center of arc */ | |
var pAx = 0; | |
var pAy = 0; | |
var pBx = 0; | |
var pBy = 0; | |
var dP, cP; | |
dP = (a1*a1) + (b1*b1); /* Clip or extend lines as required */ | |
if (dP != 0.0) { | |
cP = a1*pCy - b1*pCx; | |
pAx = (-a1*c1 - b1*cP)/dP; | |
pAy = ( a1*cP - b1*c1)/dP; | |
} | |
dP = (a2*a2) + (b2*b2); | |
if (dP != 0.0) { | |
cP = a2*pCy - b2*pCx; | |
pBx = (-a2*c2 - b2*cP)/dP; | |
pBy = ( a2*cP - b2*c2)/dP; | |
} | |
var gv1x = pAx-pCx; | |
var gv1y = pAy-pCy; | |
var gv2x = pBx-pCx; | |
var gv2y = pBy-pCy; | |
var arcStart = atan2(gv1y, gv1x); | |
var arcAngle = 0.0; | |
var dd = sqrt(((gv1x*gv1x)+(gv1y*gv1y)) * ((gv2x*gv2x)+(gv2y*gv2y))); | |
if (dd != 0.0) { | |
arcAngle = (acos((gv1x*gv2x + gv1y*gv2y)/dd)); | |
} | |
var crossProduct = (gv1x*gv2y - gv2x*gv1y); | |
if (crossProduct < 0.0) { | |
arcStart -= arcAngle; | |
} | |
var arc1 = arcStart; | |
var arc2 = arcStart + arcAngle; | |
if (crossProduct < 0.0) { | |
arc1 = arcStart + arcAngle; | |
arc2 = arcStart; | |
} | |
arcCenterX = pCx; | |
arcCenterY = pCy; | |
arcStartAngle = arc1; | |
arcEndAngle = arc2; | |
arcRadius = r; | |
arcStartX = arcCenterX + arcRadius*cos(arcStartAngle); | |
arcStartY = arcCenterY + arcRadius*sin(arcStartAngle); | |
arcEndX = arcCenterX + arcRadius*cos(arcEndAngle); | |
arcEndY = arcCenterY + arcRadius*sin(arcEndAngle); | |
} | |
// end compute | |
var t = 0; | |
var y = 0; | |
_x = constrain(_x, 0, 1); | |
if (_x <= arcStartX) { | |
if (arcStartX < Math.EPSILON){ | |
_y = 0; | |
} else { | |
t = _x / arcStartX; | |
_y = t * arcStartY; | |
} | |
} | |
else if (_x >= arcEndX) { | |
t = (_x - arcEndX)/(1 - arcEndX); | |
_y = arcEndY + t*(1 - arcEndY); | |
} | |
else { | |
if (_x >= arcCenterX) { | |
_y = arcCenterY - sqrt(sq(arcRadius) - sq(_x-arcCenterX)); | |
} | |
else { | |
_y = arcCenterY + sqrt(sq(arcRadius) - sq(_x-arcCenterX)); | |
} | |
} | |
return(_y); | |
} | |
// circular arc through a point | |
// adapted from paul bourke | |
// http://paulbourke.net/geometry/circlesphere/Circle.cpp | |
p5.Ease.prototype.circularArcThroughAPoint = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
var m = {}; | |
m.centerX = 0; | |
m.centerY = 0; | |
m.dRadius = 0; | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
var min_param_b = 0.0 + Number.EPSILON; | |
var max_param_b = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_b = constrain(_b, min_param_b, max_param_b); | |
_x = constrain(_x, 0+Number.EPSILON,1-Number.EPSILON); | |
var pt1x = 0; | |
var pt1y = 0; | |
var pt2x = _a; | |
var pt2y = _b; | |
var pt3x = 1; | |
var pt3y = 1; | |
// helper functions: | |
// check if the lines defined by the given points are perpendicular | |
// to the x or y axis - used as a check before calcCircleFrom3Points() | |
// from paul bourke's Circle.cpp | |
function isPerpendicular(pt1x, pt1y, pt2x, pt2y, pt3x, pt3y) | |
{ | |
var yDelta_a = pt2y - pt1y; | |
var xDelta_a = pt2x - pt1x; | |
var yDelta_b = pt3y - pt2y; | |
var xDelta_b = pt3x - pt2x; | |
// checking whether the line of the two pts are vertical | |
if (abs(xDelta_a) <= Number.EPSILON && abs(yDelta_b) <= Number.EPSILON){ | |
return(false); | |
} | |
if (abs(yDelta_a) <= Number.EPSILON){ | |
return(true); | |
} | |
else if (abs(yDelta_b) <= Number.EPSILON){ | |
return(true); | |
} | |
else if (abs(xDelta_a)<= Number.EPSILON){ | |
return(true); | |
} | |
else if (abs(xDelta_b)<= Number.EPSILON){ | |
return(true); | |
} | |
else return(false); | |
} | |
// from paul bourke's Circle.cpp | |
function calcCircleFrom3Points(pt1x, pt1y, pt2x, pt2y, pt3x, pt3y, m) | |
{ | |
var yDelta_a = pt2y - pt1y; | |
var xDelta_a = pt2x - pt1x; | |
var yDelta_b = pt3y - pt2y; | |
var xDelta_b = pt3x - pt2x; | |
if (abs(xDelta_a) <= Number.EPSILON && abs(yDelta_b) <= Number.EPSILON){ | |
m.centerX = 0.5*(pt2x + pt3x); | |
m.centerY = 0.5*(pt1y + pt2y); | |
m.dRadius = sqrt(sq(m.centerX-pt1x) + sq(m.centerY-pt1y)); | |
return; | |
} | |
// isPerpendicular() assures that xDelta(s) are not zero | |
var aSlope = yDelta_a / xDelta_a; | |
var bSlope = yDelta_b / xDelta_b; | |
if (abs(aSlope-bSlope) <= Number.EPSILON){ // checking whether the given points are colinear. | |
return; | |
} | |
// calc center | |
m.centerX = (aSlope*bSlope*(pt1y - pt3y) + bSlope*(pt1x + pt2x)- aSlope*(pt2x+pt3x) )/(2* (bSlope-aSlope) ); | |
m.centerY = -1*(m.centerX - (pt1x+pt2x)/2)/aSlope + (pt1y+pt2y)/2; | |
m.dRadius = sqrt(sq(m.centerX-pt1x) + sq(m.centerY-pt1y)); | |
} | |
if (!isPerpendicular(pt1x,pt1y, pt2x,pt2y, pt3x,pt3y) ) calcCircleFrom3Points (pt1x,pt1y, pt2x,pt2y, pt3x,pt3y, m); | |
else if (!isPerpendicular(pt1x,pt1y, pt3x,pt3y, pt2x,pt2y) ) calcCircleFrom3Points (pt1x,pt1y, pt3x,pt3y, pt2x,pt2y, m); | |
else if (!isPerpendicular(pt2x,pt2y, pt1x,pt1y, pt3x,pt3y) ) calcCircleFrom3Points (pt2x,pt2y, pt1x,pt1y, pt3x,pt3y, m); | |
else if (!isPerpendicular(pt2x,pt2y, pt3x,pt3y, pt1x,pt1y) ) calcCircleFrom3Points (pt2x,pt2y, pt3x,pt3y, pt1x,pt1y, m); | |
else if (!isPerpendicular(pt3x,pt3y, pt2x,pt2y, pt1x,pt1y) ) calcCircleFrom3Points (pt3x,pt3y, pt2x,pt2y, pt1x,pt1y, m); | |
else if (!isPerpendicular(pt3x,pt3y, pt1x,pt1y, pt2x,pt2y) ) calcCircleFrom3Points (pt3x,pt3y, pt1x,pt1y, pt2x,pt2y, m); | |
else { | |
return 0; | |
} | |
// constrain | |
if ((m.centerX > 0) && (m.centerX < 1)){ | |
if (_a < m.centerX){ | |
m.centerX = 1; | |
m.centerY = 0; | |
m.dRadius = 1; | |
} else { | |
m.centerX = 0; | |
m.centerY = 1; | |
m.dRadius = 1; | |
} | |
} | |
//------------------ | |
var _y = 0; | |
if (_x >= m.centerX){ | |
_y = m.centerY - sqrt(sq(m.dRadius) - sq(_x-m.centerX)); | |
} | |
else{ | |
_y = m.centerY + sqrt(sq(m.dRadius) - sq(_x-m.centerX)); | |
} | |
return(_y); | |
} | |
// bezier shapers | |
// adapted from BEZMATH.PS (1993) | |
// by don lancaster, SYNERGETICS inc. | |
// http://www.tinaja.com/text/bezmath.html | |
p5.Ease.prototype.quadraticBezier = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
var min_param_a = 0.0; | |
var max_param_a = 1.0; | |
var min_param_b = 0.0; | |
var max_param_b = 1.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_b = constrain(_b, min_param_b, max_param_b); | |
if (_a == 0.5){ | |
_a += Number.EPSILON; | |
} | |
// solve t from x (an inverse operation) | |
var om2a = 1.0 - 2.0*_a; | |
var t = (sqrt(_a*_a + om2a*_x) - _a)/om2a; | |
var _y = (1.0-2.0*_b)*(t*t) + (2*_b)*t; | |
return(_y); | |
} | |
// cubic bezier shaper | |
p5.Ease.prototype.cubicBezier = function(_x, _a, _b, _c, _d) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
if(!_c) _c = 0.75; // default | |
if(!_d) _d = 0.25; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
var min_param_b = 0.0; | |
var max_param_b = 1.0; | |
var min_param_c = 0.0 + Number.EPSILON; | |
var max_param_c = 1.0 - Number.EPSILON; | |
var min_param_d = 0.0; | |
var max_param_d = 1.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_b = constrain(_b, min_param_b, max_param_b); | |
_c = constrain(_c, min_param_c, max_param_c); | |
_d = constrain(_d, min_param_d, max_param_d); | |
//------------------------------------------- | |
var y0a = 0.00; // initial y | |
var x0a = 0.00; // initial x | |
var y1a = _b; // 1st influence y | |
var x1a = _a; // 1st influence x | |
var y2a = _d; // 2nd influence y | |
var x2a = _c; // 2nd influence x | |
var y3a = 1.00; // final y | |
var x3a = 1.00; // final x | |
var A = x3a - 3*x2a + 3*x1a - x0a; | |
var B = 3*x2a - 6*x1a + 3*x0a; | |
var C = 3*x1a - 3*x0a; | |
var D = x0a; | |
var E = y3a - 3*y2a + 3*y1a - y0a; | |
var F = 3*y2a - 6*y1a + 3*y0a; | |
var G = 3*y1a - 3*y0a; | |
var H = y0a; | |
// Solve for t given x (using Newton-Raphelson), then solve for y given t. | |
// Assume for the first guess that t = x. | |
var currentt = _x; | |
var nRefinementIterations = 5; | |
for (var i=0; i<nRefinementIterations; i++){ | |
var currentx = A*(currentt*currentt*currentt) + B*(currentt*currentt) + C*currentt + D; | |
var currentslope = 1.0/(3.0*A*currentt*currentt + 2.0*B*currentt + C); | |
currentt -= (currentx - _x)*(currentslope); | |
currentt = constrain(currentt, 0,1.0); | |
} | |
//------------ | |
var _y = E*(currentt*currentt*currentt) + F*(currentt*currentt) + G*currentt + H; | |
return(_y); | |
} | |
// parabola through a point | |
p5.Ease.prototype.parabolaThroughAPoint = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
var min_param_b = 0.0; | |
var max_param_b = 1.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_b = constrain(_b, min_param_b, max_param_b); | |
var A = (1-_b)/(1-_a) - (_b/_a); | |
var B = (A*(_a*_a)-_b)/_a; | |
var _y = A*(_x*_x) - B*(_x); | |
_y = constrain(_y, 0,1); | |
return(_y); | |
} | |
// damped sine wave | |
// n.b. decays to 0 at x=1 | |
// http://en.wikipedia.org/wiki/Damped_sine_wave | |
p5.Ease.prototype.dampedSinusoid = function(_x, _a) | |
{ | |
if(!_a) _a = 0.25; // default | |
var omega = 100*_a; | |
var lambda = -6.90775527; // ln(lambda) = 0.001 // decay constant | |
var phi = 0; | |
var e = 2.718281828459045; | |
var t = _x; | |
var _y = pow(e, lambda*t) * cos(omega*t + phi); | |
return(_y); | |
} | |
// damped sine wave (reversed) | |
p5.Ease.prototype.dampedSinusoidReverse = function(_x, _a) | |
{ | |
if(!_a) _a = 0.25; // default | |
var omega = 100*_a; | |
var lambda = -6.90775527; // ln(lambda) = 0.001 | |
var phi = 0; | |
var e = 2.718281828459045; | |
var t = 1.0-_x; | |
var _y = pow(e, lambda*t) * cos(omega*t + phi); | |
return(_y); | |
} | |
// cubic bezier through two points | |
p5.Ease.prototype.cubicBezierThrough2Points = function(_x, _a, _b, _c, _d) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
if(!_c) _c = 0.75; // default | |
if(!_d) _d = 0.25; // default | |
var _y = 0; | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
var min_param_b = 0.0 + Number.EPSILON; | |
var max_param_b = 1.0 - Number.EPSILON; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_b = constrain(_b, min_param_b, max_param_b); | |
var x0 = 0; | |
var y0 = 0; | |
var x4 = _a; | |
var y4 = _b; | |
var x5 = _c; | |
var y5 = _d; | |
var x3 = 1; | |
var y3 = 1; | |
var x1,y1,x2,y2; // to be solved. | |
var t1 = 0.3; | |
var t2 = 0.7; | |
var B0t1 = (1-t1)*(1-t1)*(1-t1); | |
var B1t1 = 3*t1*(1-t1)*(1-t1); | |
var B2t1 = 3*t1*t1*(1-t1); | |
var B3t1 = t1*t1*t1; | |
var B0t2 = (1-t2)*(1-t2)*(1-t2); | |
var B1t2 = 3*t2*(1-t2)*(1-t2);; | |
var B2t2 = 3*t2*t2*(1-t2);; | |
var B3t2 = t2*t2*t2; | |
var ccx = x4 - x0*B0t1 - x3*B3t1; | |
var ccy = y4 - y0*B0t1 - y3*B3t1; | |
var ffx = x5 - x0*B0t2 - x3*B3t2; | |
var ffy = y5 - y0*B0t2 - y3*B3t2; | |
x2 = (ccx - (ffx*B1t1)/B1t2) / (B2t1 - (B1t1*B2t2)/B1t2); | |
y2 = (ccy - (ffy*B1t1)/B1t2) / (B2t1 - (B1t1*B2t2)/B1t2); | |
x1 = (ccx - x2*B2t1) / B1t1; | |
y1 = (ccy - y2*B2t1) / B1t1; | |
x1 = constrain(x1, 0+Number.EPSILON,1-Number.EPSILON); | |
x2 = constrain(x2, 0+Number.EPSILON,1-Number.EPSILON); | |
_y = this.cubicBezier (_x, x1,y1, x2,y2); | |
_y = constrain(_y,0,1); | |
return(_y); | |
} | |
// double circular ogee | |
p5.Ease.prototype.doubleCircularOgee = function(_x, _a) | |
{ | |
if(!_a) _a = 0.25; // default | |
var min_param_a = 0.0; | |
var max_param_a = 1.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
var _y = 0; | |
if (_x<=_a){ | |
_y = sqrt(sq(_a) - sq(_x-_a)); | |
} | |
else { | |
_y = 1 - sqrt(sq(1-_a) - sq(_x-_a)); | |
} | |
return(_y); | |
} | |
// double squircular ogee | |
p5.Ease.prototype.doubleSquircularOgee = function(_x, _a, _n) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_n) _n = 3; // default | |
var min_param_a = 0.0; | |
var max_param_a = 1.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
var pown = 2.0 * _n; | |
var _y = 0; | |
if (_x<=_a){ | |
_y = pow( pow(_a,pown) - pow(_x-_a, pown), 1.0/pown); | |
} | |
else { | |
_y = 1.0 - pow( pow(1-_a,pown) - pow(_x-_a, pown), 1.0/pown); | |
} | |
return(_y); | |
} | |
// generalized combo sigmoid / logit function | |
p5.Ease.prototype.generalSigmoidLogitCombo = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
var _y = 0; | |
if (_a < 0.5){ | |
// Logit | |
var dy = _b - 0.5; | |
_y = dy + this.normalizedLogit (_x, 1.0-(2.0*_a)); | |
} else { | |
// Sigmoid | |
var dx = _b - 0.5; | |
_y = this.normalizedLogitSigmoid (_x+dx, (2.0*(_a-0.5))); | |
} | |
_y = constrain(_y, 0, 1); | |
return(_y); | |
} | |
// normalized logistic sigmoid function | |
p5.Ease.prototype.normalizedLogitSigmoid = function(_x, _a) | |
{ | |
if(!_a) _a = 0.25; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
var emph = 5.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_a = (1.0/(1.0-_a) - 1.0); | |
_a = emph * _a; | |
var _y = 1.0 / (1.0 + exp(0 - (_x-0.5)*_a )); | |
var miny = 1.0 / (1.0 + exp( 0.5*_a )); | |
var maxy = 1.0 / (1.0 + exp( -0.5*_a )); | |
_y = map(_y, miny, maxy, 0, 1); | |
return(_y); | |
} | |
// logit function | |
// https://en.wikipedia.org/wiki/Logit | |
p5.Ease.prototype.normalizedLogit = function(_x, _a) | |
{ | |
if(!_a) _a = 0.25; // default | |
var min_param_a = 0.0 + Number.EPSILON; | |
var max_param_a = 1.0 - Number.EPSILON; | |
var emph = 5.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_a = (1/(1-_a) - 1); | |
_a = emph * _a; | |
var minx = 1.0 / (1.0 + exp( 0.5*_a )); | |
var maxx = 1.0 / (1.0 + exp( -0.5*_a )); | |
_x = map(_x, 0,1, minx, maxx); | |
var _y = log (_x / (1.0 - _x)) ; | |
_y *= 1.0/_a; | |
_y += 0.5; | |
_y = constrain (_y, 0, 1); | |
return(_y); | |
} | |
// quartic easing function | |
p5.Ease.prototype.generalizedQuartic = function(_x, _a, _b) | |
{ | |
if(!_a) _a = 0.25; // default | |
if(!_b) _b = 0.75; // default | |
var min_param_a = 0.0; | |
var max_param_a = 1.0; | |
var min_param_b = 0.0; | |
var max_param_b = 1.0; | |
_a = constrain(_a, min_param_a, max_param_a); | |
_b = constrain(_b, min_param_b, max_param_b); | |
_a = 1.0-_a; | |
var _w = (1-2*_a)*(_x*_x) + (2*_a)*_x; | |
var _y = (1-2*_b)*(_w*_w) + (2*_b)*_w; | |
return(_y); | |
} | |
// boxcar function (normalized heaviside step function) | |
// http://mathworld.wolfram.com/BoxcarFunction.html | |
// https://en.wikipedia.org/wiki/Heaviside_step_function | |
p5.Ease.prototype.boxcar = function(_x) | |
{ | |
return(_x>=0.5); | |
} | |
// list algorithms | |
p5.Ease.prototype.listAlgos = function() { | |
var _styles = new Array(); | |
for(var i in this.__proto__) { | |
var _t = true; | |
if(i=="listAlgos") _t=false; | |
else if(i=="fillArray") _t=false; | |
else if(i=="fillFloat32Array") _t=false; | |
else if(i=="fillFloat64Array") _t=false; | |
if(_t) _styles.push(i); | |
} | |
return(_styles); | |
} | |
// array xfer (for pre-rendered use) | |
p5.Ease.prototype.fillArray = function(_algo, _len, _args) { | |
var _dest = new Array(_len); | |
for(var i = 0;i<_len;i++) | |
{ | |
var _p = i/(_len-1); // 0.-1. | |
_dest[i] = this[_algo](_p, _args); | |
} | |
return(_dest); | |
} | |
// array xfer (for pre-rendered use) | |
p5.Ease.prototype.fillFloat32Array = function(_algo, _len, _args) { | |
var _dest = new Float32Array(_len); | |
for(var i = 0;i<_len;i++) | |
{ | |
var _p = i/(_len-1); // 0.-1. | |
_dest[i] = this[_algo](_p, _args); | |
} | |
return(_dest); | |
} | |
// array xfer (for pre-rendered use) | |
p5.Ease.prototype.fillFloat64Array = function(_algo, _len, _args) { | |
var _dest = new Float64Array(_len); | |
for(var i = 0;i<_len;i++) | |
{ | |
var _p = i/(_len-1); // 0.-1. | |
_dest[i] = this[_algo](_p, _args); | |
} | |
return(_dest); | |
} | |
// ============================================================================= | |
// p5.ArrayEval | |
// ============================================================================= | |
/** | |
* Base class for an array evaluator | |
* | |
* @class p5.Gen | |
* @constructor | |
*/ | |
p5.ArrayEval = function() { | |
// | |
// this object implements an 'eval'-style | |
// equation evaluator across n-dimensional arrays. | |
// insired by the 'exprfill' / [jit.expr] functionality | |
// in Max/MSP. | |
// | |
// note that the javascript eval() function is *not* | |
// considered to be particularly secure, as it can | |
// easily be used to execute arbitrary code. | |
// | |
// see here for an exhausting discussion of the issue: | |
// https://stackoverflow.com/questions/86513/why-is-using-the-javascript-eval-function-a-bad-idea | |
// | |
// the p5.ArrayEval object has three methods to create | |
// 1-, 2-, and 3- dimensional javascript arrays based on | |
// a formula encoded in a string. | |
// the letters u, v, and w will be replaced with various | |
// 'normal' maps based on their cell positions. | |
// * u, v, w will unwrap to 0. to 1. across dimension 1, 2, and 3 | |
// * su, sv, sw will unwrap to -1. to 1. | |
// * cu, cv, cw will unwrap to their integer position in the array | |
// * du, dv, dw gets replaced with the length of the array in that dimension | |
// | |
// the object returns an array that solves the equation, so... | |
// if you instantiate an object... | |
// var e = new p5.ArrayEval(); | |
// then... | |
// e.eval('u', 40); | |
// will return a one-dimensional array with 40 values from 0. to 1. | |
// and... | |
// e.eval2d('su*sv', 20, 20); | |
// will return a two-dimensional array with 20x20 values from -1. to 1. multiplied together. | |
// | |
// because the eval() is run in the browser, any code included will add | |
// functionality to the p5.ArrayEval object, e.g. p5.js math functions | |
// (sin(), cos(), etc.) will work correctly. | |
// | |
this.version = 0.01; // just some crap for constructor | |
var that = this; // some bullshit | |
// global dims | |
var l1 = 0; | |
var l2 = 0; | |
var l3 = 0; | |
}; // end p5.ArrayEval constructor | |
// array return - 1d | |
p5.ArrayEval.prototype.eval = function(_evalstr, _l1) { | |
this.l1 = _l1; // make global | |
var multi = 0; // default one output | |
var e; | |
if(Array.isArray(_evalstr)) multi = 1; // array per result | |
var _dest; | |
if(multi) _dest = createArray(_l1, _evalstr.length); | |
else _dest = createArray(_l1); | |
// string expansion: | |
// u unwraps to 0-1 | |
// su unwraps to -1 to 1 | |
// cu unwraps to cell value (0 to dim-1) | |
// du unwraps to a constant representing the dimension (size) | |
if(multi) | |
{ | |
for(e in _evalstr) | |
{ | |
_evalstr[e] = _evalstr[e].replace(/cu/g, "i"); | |
_evalstr[e] = _evalstr[e].replace(/du/g, "l1"); | |
_evalstr[e] = _evalstr[e].replace(/su/g, "(i/(l1-1)*2.-1.)"); | |
_evalstr[e] = _evalstr[e].replace(/u/g, "(i/(l1-1))"); | |
} | |
} | |
else { | |
_evalstr = _evalstr.replace(/cu/g, "i"); | |
_evalstr = _evalstr.replace(/du/g, "l1"); | |
_evalstr = _evalstr.replace(/su/g, "(i/(l1-1)*2.-1.)"); | |
_evalstr = _evalstr.replace(/u/g, "(i/(l1-1))"); | |
} | |
//console.log(_evalstr); | |
for(var i = 0;i<this.l1;i++) | |
{ | |
if(multi) | |
{ | |
for(e = 0;e<_evalstr.length;e++) | |
{ | |
_dest[i][e] = eval('with(this) { ' + _evalstr[e] + ' }'); | |
} | |
} | |
else _dest[i] = eval('with(this) { ' + _evalstr + ' }'); | |
} | |
return(_dest); | |
} | |
// function synonym | |
p5.ArrayEval.prototype.eval1d = function(_evalstr, _l1) | |
{ | |
return(this.eval(_evalstr, _l1)); | |
} | |
// array return - 2d | |
p5.ArrayEval.prototype.eval2d = function(_evalstr, _l1, _l2) { | |
this.l1 = _l1; // make global | |
this.l2 = _l2; // make global | |
var multi = 0; // default one output | |
var e; | |
if(Array.isArray(_evalstr)) multi = 1; // array per result | |
var _dest; | |
if(multi) _dest = createArray(_l1, _l2, _evalstr.length); | |
else _dest = createArray(_l1, _l2); | |
// string expansion: | |
// u unwraps to 0-1 | |
// su unwraps to -1 to 1 | |
// cu unwraps to cell value (0 to dim-1) | |
// du unwraps to a constant representing the dimension (size) | |
// v unwraps to 0-1 | |
// sv unwraps to -1 to 1 | |
// cv unwraps to cell value (0 to dim-1) | |
// dv unwraps to a constant representing the dimension (size) | |
if(multi) | |
{ | |
for(e in _evalstr) | |
{ | |
_evalstr[e] = _evalstr[e].replace(/cu/g, "i"); | |
_evalstr[e] = _evalstr[e].replace(/du/g, "l1"); | |
_evalstr[e] = _evalstr[e].replace(/su/g, "(i/(l1-1)*2.-1.)"); | |
_evalstr[e] = _evalstr[e].replace(/u/g, "(i/(l1-1))"); | |
_evalstr[e] = _evalstr[e].replace(/cv/g, "j"); | |
_evalstr[e] = _evalstr[e].replace(/dv/g, "l2"); | |
_evalstr[e] = _evalstr[e].replace(/sv/g, "(j/(l2-1)*2.-1.)"); | |
_evalstr[e] = _evalstr[e].replace(/v/g, "(j/(l2-1))"); | |
} | |
} | |
else { | |
_evalstr = _evalstr.replace(/cu/g, "i"); | |
_evalstr = _evalstr.replace(/du/g, "l1"); | |
_evalstr = _evalstr.replace(/su/g, "(i/(l1-1)*2.-1.)"); | |
_evalstr = _evalstr.replace(/u/g, "(i/(l1-1))"); | |
_evalstr = _evalstr.replace(/cv/g, "j"); | |
_evalstr = _evalstr.replace(/dv/g, "l2"); | |
_evalstr = _evalstr.replace(/sv/g, "(j/(l2-1)*2.-1.)"); | |
_evalstr = _evalstr.replace(/v/g, "(j/(l2-1))"); | |
} | |
//console.log(_evalstr); | |
for(var i = 0;i<this.l1;i++) | |
{ | |
for(var j = 0;j<this.l2;j++) | |
{ | |
if(multi) | |
{ | |
for(e = 0;e<_evalstr.length;e++) | |
{ | |
_dest[i][j][e] = eval('with(this) { ' + _evalstr[e] + ' }'); | |
} | |
} | |
else _dest[i][j] = eval('with(this) { ' + _evalstr + ' }'); | |
} | |
} | |
return(_dest); | |
} | |
// array return - 3d | |
p5.ArrayEval.prototype.eval3d = function(_evalstr, _l1, _l2, _l3) { | |
this.l1 = _l1; // make global | |
this.l2 = _l2; // make global | |
this.l3 = _l3; // make global | |
var multi = 0; // default one output | |
var e; | |
if(Array.isArray(_evalstr)) multi = 1; // array per result | |
var _dest; | |
if(multi) _dest = createArray(_l1, _l2, _l3, _evalstr.length); | |
else _dest = createArray(_l1, _l2, _l3); | |
// string expansion: | |
// u unwraps to 0-1 | |
// su unwraps to -1 to 1 | |
// cu unwraps to cell value (0 to dim-1) | |
// du unwraps to a constant representing the dimension (size) | |
// v unwraps to 0-1 | |
// sv unwraps to -1 to 1 | |
// cv unwraps to cell value (0 to dim-1) | |
// dv unwraps to a constant representing the dimension (size) | |
// w unwraps to 0-1 | |
// sw unwraps to -1 to 1 | |
// cw unwraps to cell value (0 to dim-1) | |
// dw unwraps to a constant representing the dimension (size) | |
if(multi) | |
{ | |
for(e in _evalstr) | |
{ | |
_evalstr[e] = _evalstr[e].replace(/cu/g, "i"); | |
_evalstr[e] = _evalstr[e].replace(/du/g, "l1"); | |
_evalstr[e] = _evalstr[e].replace(/su/g, "(i/(l1-1)*2.-1.)"); | |
_evalstr[e] = _evalstr[e].replace(/u/g, "(i/(l1-1))"); | |
_evalstr[e] = _evalstr[e].replace(/cv/g, "j"); | |
_evalstr[e] = _evalstr[e].replace(/dv/g, "l2"); | |
_evalstr[e] = _evalstr[e].replace(/sv/g, "(j/(l2-1)*2.-1.)"); | |
_evalstr[e] = _evalstr[e].replace(/v/g, "(j/(l2-1))"); | |
_evalstr[e] = _evalstr[e].replace(/cw/g, "k"); | |
_evalstr[e] = _evalstr[e].replace(/dw/g, "l3"); | |
_evalstr[e] = _evalstr[e].replace(/sw/g, "(k/(l3-1)*2.-1.)"); | |
_evalstr[e] = _evalstr[e].replace(/w/g, "(k/(l3-1))"); | |
} | |
} | |
else { | |
_evalstr = _evalstr.replace(/cu/g, "i"); | |
_evalstr = _evalstr.replace(/du/g, "l1"); | |
_evalstr = _evalstr.replace(/su/g, "(i/(l1-1)*2.-1.)"); | |
_evalstr = _evalstr.replace(/u/g, "(i/(l1-1))"); | |
_evalstr = _evalstr.replace(/cv/g, "j"); | |
_evalstr = _evalstr.replace(/dv/g, "l2"); | |
_evalstr = _evalstr.replace(/sv/g, "(j/(l2-1)*2.-1.)"); | |
_evalstr = _evalstr.replace(/v/g, "(j/(l2-1))"); | |
_evalstr = _evalstr.replace(/cw/g, "k"); | |
_evalstr = _evalstr.replace(/dw/g, "l3"); | |
_evalstr = _evalstr.replace(/sw/g, "(k/(l3-1)*2.-1.)"); | |
_evalstr = _evalstr.replace(/w/g, "(k/(l3-1))"); | |
} | |
//console.log(_evalstr); | |
for(var i = 0;i<this.l1;i++) | |
{ | |
for(var j = 0;j<this.l2;j++) | |
{ | |
for(var k = 0;k<this.l3;k++) | |
{ | |
if(multi) | |
{ | |
for(e = 0;e<_evalstr.length;e++) | |
{ | |
_dest[i][j][k][e] = eval('with(this) { ' + _evalstr[e] + ' }'); | |
} | |
} | |
else _dest[i][j][k] = eval('with(this) { ' + _evalstr + ' }'); | |
} | |
} | |
} | |
return(_dest); | |
} | |
// ============================================================================= | |
// p5.Filt | |
// ============================================================================= | |
/** | |
* Base class for a time-domain filter | |
* | |
* @class p5.Filt | |
* @constructor | |
*/ | |
p5.Filt = function(_fs) { | |
// | |
// this object implements time-domain filtering based on | |
// robert bristow-johnson's cookbook formulae for | |
// biquadratic (2 pole, 2 zero) filters : | |
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt | |
// Copyright (C) 2003 Robert Bristow-Johnson | |
// | |
// we are using his general 2p2z / biquad formula: | |
// y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2] - (a1/a0)*y[n-1] - (a2/a0)*y[n-2] | |
// | |
// this is the same algorithm used in the [biquad~] object in PureData and Max/MSP, | |
// the [2p2z~] object in Max/FTS, the BiQuad.cpp ugen in the STK (ChucK, etc.), | |
// SOS in SuperCollider, the biquada opcode in CSOUND, etc. etc. etc. | |
// | |
// the biquadratic filter equation is pretty standard, insofar as you can | |
// construct any 'simple' filter (lowpass, highpass, bandpass, bandstop, | |
// allpass, etc.) with independent gain controls (coefficients) for the | |
// input (x[n]), (usually) the overall output (y[n]), and four samples | |
// of memory - the two previous input samples (x[n-1], x[n-2]), and the | |
// two previous output samples (y[n-1], y[n-2]). | |
// | |
// so this filter has both 2nd-order FIR (feedforward) and 2nd-order IIR | |
// (feedback) capabilities. you set the coefficients by running some | |
// trig against your desired filter characteristics... | |
// * type | |
// * cutoff / center frequency | |
// * Q / resonance | |
// * gain (for some filters) | |
// ...and the known sample rate (Fs). | |
// | |
// more complex filters can be constructed by 'cascading' biquad filters | |
// in series, e.g. for butterworth / chebyshev filters that require a | |
// more flat frequency response than a simple biquad can offer. | |
// | |
// you can find lots of info about this filter by searching for | |
// 'biquad' on your internet machine. however... | |
// | |
// some people, they will flip their 'a' and 'b' coefficients. | |
// some people, they will skip a0 and use 5 coefficients for biquad formulae. | |
// some people, they like to go out dancing. | |
// | |
// p5.Filt is offered here as a module to allow for filter design | |
// independent of the Web Audio framework used by p5.sound / Tone.js. | |
// there are lots of other things one might want to 'filter' besides sound. | |
// the defaults provided here are against the nominal graphics frame rate | |
// for p5.js (60Hz), giving an effective nyquist (upper frequency limit) | |
// of 30Hz. try running a random() generator through it, or using it | |
// to smooth out a noisy signal coming from a sensor or a network API. | |
// it wil process input sample-by-sample through the tick() method or | |
// process arrays vector-by-vector through the process() method. | |
// | |
if(!_fs) _fs = 60.; // nominal p5 default framerate | |
this.version = 0.01; // just some crap for constructor | |
var that = this; // some bullshit | |
// biquad / 2p2z coefficients | |
this.a0 = 1.; // denominator gain for filter output (y[n] term) | |
this.b0 = 1.; // gain for current input (x[n] term) | |
this.a1 = 0.; // gain for previous input (x[n-1] term) | |
this.b1 = 0.; // gain for previous previous input (x[n-2] term) | |
this.a2 = 0.; // gain for previous output (y[n-1] term) | |
this.b2 = 0.; // gain for previous previous output (y[n-2] term) | |
// sample memory | |
this.xn = 0.; // x[n] (input) | |
this.yn = 0.; // y[n] (output) | |
this.xpn = 0.; // x[n-1] (previous input) | |
this.ypn = 0.; // y[n-1] (previous output) | |
this.xppn = 0.; // x[n-2] (previous previous input) | |
this.yppn = 0.; // y[n-2] (previous previous output) | |
// parameters | |
this.fs = _fs; // sampling rate | |
this.type = "LPF"; // default to lowpass filter | |
this.f0 = this.fs/4.; // center/cutoff frequency; default to fs/4 (half nyquist) | |
this.dB = 0.; // gain for peaking / shelving | |
this.Q = 1.; // width / resonance of filter | |
// intermediates | |
this.A; // amplitude | |
this.w0; // filter increment in radians | |
this.cw0; // cosine of w0 - precompute | |
this.sw0; // sine of w0 - precompute | |
this.alpha; // alpha term - precompute | |
this.soff; // shelving offset - precompute | |
// compute coefficients based on default parameters | |
this.precalc(); | |
}; // end p5.Filt constructor | |
// define the filter characteristics all at once. | |
p5.Filt.prototype.set = function(_type, _f0, _Q, _dB) | |
{ | |
if(_type) this.type = _type; | |
if(_f0) this.f0 = _f0; | |
if(_Q) this.Q = _Q; | |
if(_dB) this.dB = _dB; // only matters for shelving filters | |
this.precalc(); | |
} | |
// set the sampling rate (fs) of the filter. | |
p5.Filt.prototype.setFs = function(_fs) | |
{ | |
if(_fs) this.fs = _fs; | |
this.precalc(); | |
} | |
// set the type of the filter. | |
p5.Filt.prototype.setType = function(_type) | |
{ | |
if(_type) this.type = _type; | |
this.precalc(); | |
} | |
// set the cutoff/center frequency. | |
p5.Filt.prototype.setFreq = function(_f0) | |
{ | |
if(_f0) this.f0 = _f0; | |
this.precalc(); | |
} | |
// set the Q(uality) of the filter. | |
p5.Filt.prototype.setQ = function(_Q) | |
{ | |
if(_Q) this.Q = _Q; | |
this.precalc(); | |
} | |
// set the gain (in decibels). | |
p5.Filt.prototype.setGain = function(_dB) | |
{ | |
if(_dB) this.dB = _dB; | |
this.precalc(); | |
} | |
// set the bandwidth in Hz (inverse of Q). | |
p5.Filt.prototype.setBW = function(_bw) | |
{ | |
// technically... | |
// this.Q = 1.0/(2*Math.sinh(log(2)/2*_bw*this.w0/this.sw0)); | |
// but YOLO... | |
if(_bw) this.Q = this.f0/_bw; | |
this.precalc(); | |
} | |
// process multiple values as a single vector; | |
// n.b. filter will retain memory... use a clear() | |
// beforehand if you want the filter zeroed. | |
p5.Filt.prototype.process = function(_x) | |
{ | |
var _y; // output | |
// figure out input type | |
if(Array.isArray(_x)) _y = new Array(_x.length); | |
if(_x.constructor == Float32Array) _y = new Float32Array(_x.length); | |
if(_x.constructor == Float64Array) _y = new Float64Array(_x.length); | |
for(var i in _x) | |
{ | |
_y[i] = this.tick(_x[i]); | |
} | |
// output | |
return(_y); | |
} | |
// process sample-by-sample, STK style. | |
p5.Filt.prototype.tick = function(_x) | |
{ | |
// input | |
this.xn = _x; | |
// biquad - the only line in this whole situation that really matters: | |
this.yn = (this.b0/this.a0)*this.xn + (this.b1/this.a0)*this.xpn + (this.b2/this.a0)*this.xppn - (this.a1/this.a0)*this.ypn - (this.a2/this.a0)*this.yppn; | |
// shift | |
this.xppn = this.xpn; | |
this.xpn = this.xn; | |
this.yppn = this.ypn; | |
this.ypn = this.yn; | |
// output | |
return(this.yn); | |
} | |
// clear biquad memory - | |
// useful if the filter becomes unstable and you start getting NaNs as output. | |
p5.Filt.prototype.clear = function() | |
{ | |
// clear samples | |
this.xn = 0; // x[n] (input) | |
this.yn = 0; // y[n] (output) | |
this.xpn = 0; // x[n-1] | |
this.ypn = 0; // y[n-1] | |
this.xppn = 0; // x[n-2] | |
this.yppn = 0; // y[n-2] | |
} | |
// set coefficients 'by hand' - | |
// useful for cascade (butterworth / chebyshev) filtering | |
p5.Filt.prototype.coeffs = function(_a0, _b0, _b1, _b2, _a1, _a2) | |
{ | |
if(arguments.length!=6) | |
{ | |
console.log("p5.Filt needs six coefficients for raw biquad:"); | |
console.log(" a0 -> denominator gain for filter (y[n] term)."); | |
console.log(" b0 -> gain for current input (x[n] term)."); | |
console.log(" b1 -> gain for previous input (x[n-1] term)."); | |
console.log(" b2 -> gain for previous previous input (x[n-2] term)."); | |
console.log(" a1 -> gain for previous output (y[n-1] term)."); | |
console.log(" a2 -> gain for previous previous output (y[n-2] term)."); | |
console.log("when working with 5-coefficient biquad formulae set a0 to 1.0."); | |
console.log("n.b. some systems will refer to y terms as 'b' and x terms as 'a'."); | |
} | |
else | |
{ | |
this.a0 = _a0; // y[n] (overall gain) | |
this.b0 = _b0; // x[n] | |
this.b1 = _b1; // x[n-1] | |
this.b2 = _b2; // x[n-2] | |
this.a1 = _a1; // y[n-1] | |
this.a2 = _a2; // y[n-2] | |
} | |
} | |
// calculate filter coefficients from parameters | |
p5.Filt.prototype.precalc = function() | |
{ | |
// intermediates | |
this.A = sqrt(pow(10, this.dB/20)); // amplitude | |
this.w0 = 2*PI*this.f0/this.fs; // filter increment in radians | |
this.cw0 = cos(this.w0); // cosine of w0 - precompute | |
this.sw0 = sin(this.w0); // sine of w0 - precompute | |
this.alpha = sin(this.w0)/(2*this.Q); // alpha term - precompute | |
this.soff = 2*sqrt(this.A)*this.alpha; // shelving offset - precompute | |
switch(this.type) { | |
case "LPF": | |
case "lowpass": | |
this.b0 = (1 - this.cw0)/2; | |
this.b1 = 1 - this.cw0; | |
this.b2 = (1 - this.cw0)/2; | |
this.a0 = 1 + this.alpha; | |
this.a1 = -2 * this.cw0; | |
this.a2 = 1 - this.alpha; | |
break; | |
case "HPF": | |
case "highpass": | |
this.b0 = (1 + this.cw0)/2; | |
this.b1 = -(1 + this.cw0); | |
this.b2 = (1 + this.cw0)/2; | |
this.a0 = 1 + this.alpha; | |
this.a1 = -2 * this.cw0; | |
this.a2 = 1 - this.alpha; | |
break; | |
case "BPF": | |
case "bandpass": | |
this.b0 = this.sw0/2; | |
this.b1 = 0; | |
this.b2 = -this.sw0/2; | |
this.a0 = 1 + this.alpha; | |
this.a1 = -2 * this.cw0; | |
this.a2 = 1 - this.alpha; | |
break; | |
case "BPF0": // constant 0 peak gain | |
case "resonant": | |
this.b0 = this.alpha; | |
this.b1 = 0; | |
this.b2 = -this.alpha; | |
this.a0 = 1 + this.alpha; | |
this.a1 = -2 * this.cw0; | |
this.a2 = 1 - this.alpha; | |
break; | |
case "notch": | |
case "bandreject": | |
case "bandstop": | |
this.b0 = 1; | |
this.b1 = -2 * this.cw0; | |
this.b2 = 1; | |
this.a0 = 1 + this.alpha; | |
this.a1 = -2 * this.cw0; | |
this.a2 = 1 - this.alpha; | |
break; | |
case "APF": | |
case "allpass": | |
this.b0 = 1 - this.alpha; | |
this.b1 = -2 * this.cw0; | |
this.b2 = 1 + this.alpha; | |
this.a0 = 1 + this.alpha; | |
this.a1 = -2 * this.cw0; | |
this.a2 = 1 - this.alpha; | |
break; | |
case "peakingEQ": | |
case "peaknotch": | |
this.b0 = 1 + this.alpha*this.A; | |
this.b1 = -2 * this.cw0; | |
this.b2 = 1 - this.alpha*this.A; | |
this.a0 = 1 + this.alpha/this.A; | |
this.a1 = -2 * this.cw0; | |
this.a2 = 1 - this.alpha/this.A; | |
break; | |
case "lowShelf": | |
case "lowshelf": | |
this.b0 = this.A*((this.A+1) - (this.A-1)*this.cw0 + this.soff); | |
this.b1 = 2*this.A*((this.A-1) - (this.A+1)*this.cw0); | |
this.b2 = this.A*((this.A+1) - (this.A-1)*this.cw0 - this.soff); | |
this.a0 = (this.A+1) + (this.A-1)*this.cw0 + this.soff; | |
this.a1 = -2*((this.A-1) + (this.A+1)*this.cw0); | |
this.a2 = (this.A+1) + (this.A-1)*this.cw0 - this.soff; | |
break; | |
case "highShelf": | |
case "highshelf": | |
this.b0 = this.A*((this.A+1) + (this.A-1)*this.cw0 + this.soff); | |
this.b1 = -2*this.A*((this.A-1) + (this.A+1)*this.cw0); | |
this.b2 = this.A*((this.A+1) + (this.A-1)*this.cw0 - this.soff); | |
this.a0 = (this.A+1) - (this.A-1)*this.cw0 + this.soff; | |
this.a1 = 2*((this.A-1) - (this.A+1)*this.cw0); | |
this.a2 = (this.A+1) - (this.A-1)*this.cw0 - this.soff; | |
break; | |
default: // pass through | |
this.b0 = 1.; | |
this.b1 = 0.; | |
this.b2 = 0.; | |
this.a0 = 1.; | |
this.a1 = 0.; | |
this.a2 = 0.; | |
break; | |
} | |
} | |
// ============================================================================= | |
// p5.FastFourierTransform | |
// ============================================================================= | |
/** | |
* Base class for an FFT (non-signal) module | |
* | |
* @class p5.FastFourierTranform | |
* @constructor | |
*/ | |
p5.FastFourierTransform = function(_bufsize, _fs, _hopsize) { | |
// | |
// this object implements a simple FFT (fast fourier transform) | |
// module using the 1965 cooley-tukey FFT algorithm: | |
// http://www.ams.org/journals/mcom/1965-19-090/S0025-5718-1965-0178586-1/S0025-5718-1965-0178586-1.pdf | |
// | |
// there's a great breakdown of how the FFT algorithm works here: | |
// https://www.cs.cmu.edu/afs/andrew/scs/cs/15-463/2001/pub/www/notes/fourier/fourier.pdf | |
// | |
// the code below is adapted from the FFT module in dsp.js written by @corbanbrook : | |
// https://github.com/corbanbrook/dsp.js/ | |
// Copyright (c) 2010 Corban Brook, released under the MIT license | |
// | |
// p5.FastFourierTransform runs completely independent of the Web Audio | |
// framework used by p5.sound / Tone.js, which allows you to analyze | |
// and synthesize frequency-domain data regardless of whether it counts | |
// as 'sound' or runs at audio rate. | |
// | |
// how many samples are we analyzing? | |
// should be a power of 2. | |
this.bufferSize = _bufsize ? _bufsize : 512; | |
// what's our hopsize? | |
// if nothing's there, set it to the FFT size. | |
this.hopSize = _hopsize ? _hopsize : this.bufferSize; | |
// fourier transforms are 'rate' agnostic... | |
// the algorithm doesn't care how fast the signal is, so | |
// the sampling rate here is simply to calculate | |
// getBandFrequency() as a utility function so we can | |
// find out, e.g. the center frequency of a specific FFT bin. | |
this.sampleRate = _fs ? _fs : 60; | |
// FFT fundamental / bandwidth: used for | |
// getBandFrequency() and calculateFrequency() | |
this.bandwidth = this.sampleRate / this.bufferSize; | |
// data arrays for the raw real/imaginary FFT data. | |
this.real = new Float64Array(this.bufferSize); | |
this.imag = new Float64Array(this.bufferSize); | |
// spectrum compute flag and data arrays for the magnitude / phase. | |
// note that the spectrum (correctly) only needs a half frame of | |
// data, as the FFT output is mirrored for bins above nyquist (SR/2). | |
this.doSpectrum = true; | |
this.magnitude = new Float64Array(this.bufferSize/2); | |
this.phase = new Float64Array(this.bufferSize/2); | |
// compute flag and data arrays for instantaneous frequency estimation | |
this.doFrequency = false; | |
this.runningphase = new Float64Array(this.bufferSize/2); | |
this.previousphase = new Float64Array(this.bufferSize/2); | |
this.frequency = new Float64Array(this.bufferSize/2); | |
// the calculateSpectrum() method will stash the 'loudest' | |
// bin, as well as its value. | |
this.peakBand = 0; // peak band (FFT bin) | |
this.peak = 0.; // peak value. | |
// calculate the FFT 'reverse table'. | |
// the reverse table is a super groovy hack that helps put the | |
// 'fast' in fast fourier transform. | |
// | |
// to quote jim noxon at texas instruments : | |
// "The purpose of having this table is due to a quirk of the FFT algorithm. | |
// As it turns out, the indexing done in the FFT algorithm (and the IFFT too) | |
// relates the input index to the output index in a manner where reversing | |
// the bits of the input index creates the appropriate index for the output. | |
// As you can see the inner for() loop would have to be run for every | |
// input to output index operation of the FFT algorithm so by creating a table | |
// up front, the FFT algorithm can be run multiple times and only needs to | |
// be calculated once. Further, if this table is calculated at compile time, | |
// then there is no dynamic initialization necessary at all. Now, it is | |
// simply a look up into the table using the input index to generate the | |
// output index. Some DSPs have a special addressing mode that does this | |
// automatically eliminating the need for the table all together." | |
// | |
this.reverseTable = new Uint32Array(this.bufferSize); | |
var limit = 1; | |
var bit = this.bufferSize >> 1; | |
var i; | |
while (limit < this.bufferSize) { | |
for (i = 0; i < limit; i++) { | |
this.reverseTable[i + limit] = this.reverseTable[i] + bit; | |
} | |
limit = limit << 1; | |
bit = bit >> 1; | |
} | |
// precompute sine and cosine sampling increment (SI) arrays. | |
this.sinTable = new Float64Array(this.bufferSize); | |
this.cosTable = new Float64Array(this.bufferSize); | |
for (i = 0; i < this.bufferSize; i++) { | |
// we never call index 0, which is NaN | |
this.sinTable[i] = sin(-PI/i); | |
this.cosTable[i] = cos(-PI/i); | |
} | |
}; // end p5.FastFourierTransform constructor | |
// query the center frequency of a specific FFT band (e.g. the peakBand). | |
// remember that this is the frequency of the *filter*, not necessarily | |
// the signal that is actuating the filter. to find that out you would | |
// compute running phase off of sequential frames of data to see whether | |
// the phase in the bin is rising or falling, and use that to compute | |
// the frequency differential between the band and the actuating signal | |
// (see below... calculateFrequency(), for an implementation). | |
p5.FastFourierTransform.prototype.getBandFrequency = function(_index) { | |
return this.bandwidth * _index; | |
} | |
// calculate the spectrum (magnitude / phase) from the cartesian | |
// (real / imaginary - x / y) raw FFT output. this is basically a | |
// pythagorean transformation, with the magnitudes scaled to ranges | |
// based on the FFT size. | |
p5.FastFourierTransform.prototype.calculateSpectrum = function() { | |
var rval, ival, mag, phase; | |
var bSi = 2 / this.bufferSize; // scaling factor for magnitudes | |
this.peakBand = 0; // reset each spectral frame | |
this.peak = 0; // reset each spectral frame | |
for (var i = 0, N = this.bufferSize/2; i < N; i++) { | |
rval = this.real[i]; // x | |
ival = this.imag[i]; // y | |
mag = bSi * sqrt(rval * rval + ival * ival); | |
phase = atan2(ival, rval); | |
if (mag > this.peak) { | |
this.peakBand = i; | |
this.peak = mag; | |
} | |
this.magnitude[i] = mag; | |
this.phase[i] = phase; | |
} | |
if(this.doFrequency) this.calculateFrequency(); | |
} | |
// computes instantaneous frequency based on running phase. | |
p5.FastFourierTransform.prototype.calculateFrequency = function() { | |
var temp = Array.from(this.phase); | |
this.runningphase = subtractArray(temp, this.previousphase); // compute running phase | |
this.runningphase = addArray(this.runningphase,TWO_PI+PI); | |
this.runningphase = moduloArray(this.runningphase, TWO_PI); | |
this.runningphase = subtractArray(this.runningphase, PI); | |
this.previousphase = temp; | |
// compute frequencies: | |
for(i in this.frequency) | |
{ | |
this.frequency[i] = this.bandwidth*i + this.runningphase[i]*this.sampleRate/(TWO_PI*this.hopSize); | |
} | |
} | |
// performs a forward FFT transform on a sample buffer. | |
// this converts the data from the time domain into the frequency domain. | |
// fills up the real and imag buffers in the object's data structure, | |
// and also runs calculateSpectrum() to get the magnitude and phase. | |
p5.FastFourierTransform.prototype.forward = function(_buffer) { | |
var k = floor(log(this.bufferSize) / Math.LN2); | |
if (pow(2, k) !== this.bufferSize) { throw "buffer size must be a power of 2."; } | |
if (this.bufferSize !== _buffer.length) { throw "buffer is not the same size as defined FFT. FFT: " + bufferSize + " buffer: " + buffer.length; } | |
var halfSize = 1; | |
var phaseShiftStepReal, phaseShiftStepImag; | |
var currentPhaseShiftReal, currentPhaseShiftImag; | |
var tr, ti; | |
var tmpReal; | |
var i, o; | |
// STEP 1 - fill up the 'real' array with the signal according to the reverseTable ordering | |
// makes it faster for memory access later as it's already copied in there and we can adjustable | |
// iterate through it. | |
for (i = 0; i < this.bufferSize; i++) { | |
this.real[i] = _buffer[this.reverseTable[i]]; | |
this.imag[i] = 0; | |
} | |
// STEP 2 - do the actual discrete fourier transform (DFT). | |
// halfSize will increment in powers of two up to half of the FFT size (nyquist) | |
// so for a 1024-sample FFT, we're doing 10 outer loops (1, 2, 4, 8, 16, 32, 64, 128, 256, 512). | |
while (halfSize < this.bufferSize) { | |
// figure out the phase increment necessary for each sample size | |
phaseShiftStepReal = this.cosTable[halfSize]; | |
phaseShiftStepImag = this.sinTable[halfSize]; | |
// starting x,y positions | |
currentPhaseShiftReal = 1; | |
currentPhaseShiftImag = 0; | |
// intermediate loop - fftStep will increment from 0 to halfSize-1, | |
// i.e. number of fftStep loops equals to halfSize each time. | |
// each one of these passes is a DFT. | |
for (var fftStep = 0; fftStep < halfSize; fftStep++) { | |
i = fftStep; | |
// inner loop - i and o will integrate the convolution of the input signal | |
// with cosine and sine functions at a period equal to the fftStep | |
while (i < this.bufferSize) { | |
o = i + halfSize; | |
// uncomment the line below to see what's going on: | |
// console.log('halfSize : ' + halfSize + ', fftStep : ' + fftStep + ', i : ' + i + ', o : ' + o + ', psr : ' + currentPhaseShiftReal + ', psi : ' + currentPhaseShiftImag); | |
tr = (currentPhaseShiftReal * this.real[o]) - (currentPhaseShiftImag * this.imag[o]); | |
ti = (currentPhaseShiftReal * this.imag[o]) + (currentPhaseShiftImag * this.real[o]); | |
this.real[o] = this.real[i] - tr; | |
this.imag[o] = this.imag[i] - ti; | |
this.real[i] += tr; | |
this.imag[i] += ti; | |
i += halfSize << 1; | |
} | |
// increment the sampling interval for the next DFT in the loop: | |
tmpReal = currentPhaseShiftReal; | |
currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag); | |
currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal); | |
} | |
// shift up halfSize for next outer loop | |
halfSize = halfSize << 1; | |
} | |
if(this.doSpectrum) this.calculateSpectrum(); // calculate mag/phase from real/imag | |
} | |
// performs an inverse FFT transform (an IFFT) on either real/imag | |
// data passed into the function or, if called without arguments, | |
// the current spectral frame stored in the object. | |
// this converts the data from the frequency domain into the time domain. | |
// the function returns a buffer containing the time-domain signal. | |
p5.FastFourierTransform.prototype.inverse = function(_real, _imag) { | |
_real = _real || this.real; | |
_imag = _imag || this.imag; | |
var halfSize = 1; | |
var phaseShiftStepReal, phaseShiftStepImag; | |
var currentPhaseShiftReal, currentPhaseShiftImag; | |
var off; | |
var tr, ti; | |
var tmpReal; | |
var i; | |
for (i = 0; i < this.bufferSize; i++) { | |
_imag[i] *= -1; | |
} | |
var revReal = new Float64Array(this.bufferSize); | |
var revImag = new Float64Array(this.bufferSize); | |
for (i = 0; i < _real.length; i++) { | |
revReal[i] = _real[this.reverseTable[i]]; | |
revImag[i] = _imag[this.reverseTable[i]]; | |
} | |
_real = revReal; | |
_imag = revImag; | |
while (halfSize < this.bufferSize) { | |
phaseShiftStepReal = this.cosTable[halfSize]; | |
phaseShiftStepImag = this.sinTable[halfSize]; | |
currentPhaseShiftReal = 1; | |
currentPhaseShiftImag = 0; | |
for (var fftStep = 0; fftStep < halfSize; fftStep++) { | |
i = fftStep; | |
while (i < this.bufferSize) { | |
off = i + halfSize; | |
tr = (currentPhaseShiftReal * _real[off]) - (currentPhaseShiftImag * _imag[off]); | |
ti = (currentPhaseShiftReal * _imag[off]) + (currentPhaseShiftImag * _real[off]); | |
_real[off] = _real[i] - tr; | |
_imag[off] = _imag[i] - ti; | |
_real[i] += tr; | |
_imag[i] += ti; | |
i += halfSize << 1; | |
} | |
tmpReal = currentPhaseShiftReal; | |
currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag); | |
currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal); | |
} | |
halfSize = halfSize << 1; | |
} | |
var buffer = new Float64Array(this.bufferSize); // this should be reused instead | |
for (i = 0; i < this.bufferSize; i++) { | |
buffer[i] = _real[i] / this.bufferSize; | |
} | |
return buffer; | |
} | |
// ============================================================================= | |
// Luke's Misc. Utilities (extends p5.js) | |
// ============================================================================= | |
// constrained integer mapping function (useful for array lookup). | |
// similar to the Max [zmap] object when used with integers. | |
// syntactically equivalent to the p5.js / processing map() function. | |
p5.prototype.imap = function(_x, _a, _b, _c, _d) { | |
return(constrain(floor(map(_x, _a, _b, _c, _d)), min(_c,_d), max(_c, _d)-1)); | |
} | |
// number wrapping (courtesy of @pd-l2ork puredata [pong]) | |
p5.prototype.wrap = function(_x, _min, _max) { | |
_a = min(_min, _max); | |
_b = max(_min, _max); | |
var _y; | |
var _r = _b-_a; // range | |
if(_x < _b && _x >= _a) { // normal | |
return(_x); | |
} | |
else if(_a==_b) { // catch | |
return(_a); | |
} | |
else { | |
if(_x < _a) { | |
_y = _x; | |
while(_y < _a){ | |
_y += _r; | |
}; | |
} | |
else { | |
_y = ((_x-_a)%_r) + _a; | |
} | |
} | |
return(_y); | |
} | |
// number folding (courtesy of @pd-l2ork puredata [pong]) | |
p5.prototype.fold = function(_x, _min, _max) { | |
_a = min(_min, _max); | |
_b = max(_min, _max); | |
var _y; | |
var _r = _b-_a; // range | |
if(_x < _b && _x >= _a) { // normal | |
return(_x); | |
} | |
else if(_a==_b) { // catch | |
return(_a); | |
} | |
else { | |
if(_x < _a) { | |
var _d = _a - _x; // diff between input and minimum (positive) | |
var _m = floor(_d/_r); // case where input is more than a range away from minval | |
if(_m % 2 == 0) { // even number of ranges away = counting up from min | |
_d = _d - _m*_r; | |
_y = _d + _a; | |
} | |
else { // odd number of ranges away = counting down from max | |
_d = _d - _m*_r; | |
_y = _b - _d; | |
} | |
} | |
else { // input > maxval | |
var _d = _x - _b; // diff between input and max (positive) | |
var _m = floor(_d/_r); // case where input is more than a range away from maxval | |
if(_m % 2 == 0) { // even number of ranges away = counting down from max | |
_d = _d - _m*_r; | |
_y = _b - _d; | |
} | |
else { //odd number of ranges away = counting up from min | |
_d = _d - _m*_r; | |
_y = _d + _a; | |
} | |
} | |
} | |
return(_y); | |
} | |
// create n-dimensional arrays | |
p5.prototype.createArray = function(_len) | |
{ | |
var _arr = new Array(_len || 0).fill(0), | |
i = _len; | |
if (arguments.length > 1) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
while(i--) _arr[_len-1 - i] = this.createArray.apply(this, args); | |
} | |
return _arr; | |
} | |
// normalize a numerical array to absmax=1.0 without shifting DC | |
p5.prototype.normalizeArray = function(_array) { | |
var _max = max(max(_array), abs(min(_array))); | |
return(_array.map(function(_v) { return _v/_max; })); | |
} | |
// resize a numerical array to a new size with linear interpolation | |
p5.prototype.resizeArray = function(_array, _newlen) { | |
var _out = []; | |
for(var i = 0;i<_newlen;i++) | |
{ | |
var aptr = map(i, 0, _newlen-1, 0, _array.length-1); | |
var a = floor(aptr); | |
var b = ceil(aptr); | |
var l = aptr%1.0; | |
_out[i] = lerp(_array[a], _array[b], l); | |
} | |
return(_out); | |
} | |
// multiply two arrays | |
p5.prototype.multiplyArray = function(_a1, _a2) { | |
if(!Array.isArray(_a2)) _a2 = [_a2]; // allow scalars | |
var _out = []; | |
if(_a1.length!=_a2.length) _a2 = resizeArray(_a2, _a1.length); | |
for(var i = 0;i<_a1.length;i++) | |
{ | |
_out[i] = _a1[i] * _a2[i]; | |
} | |
return(_out); | |
} | |
// add two arrays | |
p5.prototype.addArray = function(_a1, _a2) { | |
if(!Array.isArray(_a2)) _a2 = [_a2]; // allow scalars | |
var _out = []; | |
if(_a1.length!=_a2.length) _a2 = resizeArray(_a2, _a1.length); | |
for(var i = 0;i<_a1.length;i++) | |
{ | |
_out[i] = _a1[i] + _a2[i]; | |
} | |
return(_out); | |
} | |
// subtract two arrays | |
p5.prototype.subtractArray = function(_a1, _a2) { | |
if(!Array.isArray(_a2)) _a2 = [_a2]; // allow scalars | |
var _out = []; | |
if(_a1.length!=_a2.length) _a2 = resizeArray(_a2, _a1.length); | |
for(var i = 0;i<_a1.length;i++) | |
{ | |
_out[i] = _a1[i] - _a2[i]; | |
} | |
return(_out); | |
} | |
// divide two arrays | |
p5.prototype.divideArray = function(_a1, _a2) { | |
if(!Array.isArray(_a2)) _a2 = [_a2]; // allow scalars | |
var _out = []; | |
if(_a1.length!=_a2.length) _a2 = resizeArray(_a2, _a1.length); | |
for(var i = 0;i<_a1.length;i++) | |
{ | |
_out[i] = _a1[i] / _a2[i]; | |
} | |
return(_out); | |
} | |
// modulo two arrays | |
p5.prototype.moduloArray = function(_a1, _a2) { | |
if(!Array.isArray(_a2)) _a2 = [_a2]; // allow scalars | |
var _out = []; | |
if(_a1.length!=_a2.length) _a2 = resizeArray(_a2, _a1.length); | |
for(var i = 0;i<_a1.length;i++) | |
{ | |
_out[i] = _a1[i] % _a2[i]; | |
} | |
return(_out); | |
} | |
// return the sum of an array | |
p5.prototype.sumArray = function(_a) { | |
var _s = _a.reduce(function(_acc, _val) { | |
return(_acc+_val); | |
}); | |
return(_s); | |
} | |
// Java Float.floatToIntBits() IEEE 754 / 32-bit (h/t @mattdesl): | |
p5.prototype.f2ib = function(_x) | |
{ | |
var int8 = new Int8Array(4); | |
var int32 = new Int32Array(int8.buffer, 0, 1); | |
var float32 = new Float32Array(int8.buffer, 0, 1); | |
float32[0] = _x; | |
return(int32[0]); | |
} | |
// Java Float.intBitstoFloat() IEEE 754 / 32-bit (h/t @mattdesl): | |
p5.prototype.ib2f = function(_x) | |
{ | |
var int8 = new Int8Array(4); | |
var int32 = new Int32Array(int8.buffer, 0, 1); | |
var float32 = new Float32Array(int8.buffer, 0, 1); | |
int32[0] = _x; | |
return(float32[0]); | |
} | |
// normalized sinc function (integral equals 1, not PI) | |
// the normalized sinc is the FT of the rectangular function. | |
// 'sinc' is short for sinus cardinalis (woodward '52): | |
// http://www.norbertwiener.umd.edu/crowds/documents/Woodward52.pdf | |
p5.prototype.sinc = function(_x) { | |
return(sin(PI*_x)/(PI*_x)); | |
} | |
// besselI0 - regular modified cylindrical Bessel function (Bessel I) | |
// https://en.wikipedia.org/wiki/Bessel_function | |
// ported from kbdwindow.cpp by craig stuart sapp @ CCRMA ca. 2001 | |
p5.prototype.besselI0 = function(_x) { | |
var denominator; | |
var numerator; | |
var z; | |
if (_x == 0.0) { | |
return(1.0); | |
} else { | |
z = _x * _x; | |
numerator = (z* (z* (z* (z* (z* (z* (z* (z* (z* (z* (z* (z* (z* | |
(z* 0.210580722890567e-22 + 0.380715242345326e-19 ) + | |
0.479440257548300e-16) + 0.435125971262668e-13 ) + | |
0.300931127112960e-10) + 0.160224679395361e-7 ) + | |
0.654858370096785e-5) + 0.202591084143397e-2 ) + | |
0.463076284721000e0) + 0.754337328948189e2 ) + | |
0.830792541809429e4) + 0.571661130563785e6 ) + | |
0.216415572361227e8) + 0.356644482244025e9 ) + | |
0.144048298227235e10); | |
denominator = (z*(z*(z-0.307646912682801e4)+ | |
0.347626332405882e7)-0.144048298227235e10); | |
} | |
return(-numerator/denominator); | |
} | |
// plot an array to the console as if it were a VT100 terminal. | |
// ported from a copy of fplot.c found on luke's NeXTstation. | |
// fplot.c seems rewritten from FORTRAN, presumably by | |
// paul lansky, while porting MIX to C in 83/84. | |
// man page last troff'ed january 31, 1987, 6:56PM by paul lansky. | |
// c code last modified october 18, 1990, 2:26PM by brad garton. | |
p5.prototype.fplot = function(_array, _css) { | |
var a; | |
if(!Array.isArray(_array)) a = [_array]; // single value | |
if(Array.isArray(_array)) a = _array; // normal array | |
if(_array.constructor == Float32Array) a = Array.prototype.slice.call(_array); | |
if(_array.constructor == Float64Array) a = Array.prototype.slice.call(_array); | |
var TERMWIDTH = 80; // columns (1 additional will be used for left margin) | |
var TERMHEIGHT = 23; // VT100 is 24 rows ('take back one kadam...') | |
var out = []; | |
var si, phase; | |
var i, j, k, len, wave; | |
var line = ''; | |
len = _array.length; | |
// decimate or interpolate array to TERMWIDTH values, scale to absmax=1. | |
out = resizeArray(a, TERMWIDTH); | |
out = normalizeArray(out); | |
// plot the fucker | |
si = 1./((TERMHEIGHT-1)/2.); | |
phase = 1.; | |
for(i = 0; i < TERMHEIGHT; i++) { | |
k = 0; | |
// add alternator to left margin of line - prevents browser consoles | |
// from interpreting duplicate lines and only printing them once: | |
if(i%2) line= '~'; else line='|'; | |
// format line based on function being within phase boundary | |
for(j = 0; j < TERMWIDTH-1; j++) { | |
if(isNaN(out[j])) out[j] = 0; // filter out garbage | |
if((out[j]>=phase) && (out[j]<phase+si)) { | |
line+= (out[j+1] > phase+si) ? '/' : '-'; | |
if(out[j+1] < phase) line+= '\\'; | |
k = j; | |
} | |
else if (((out[j]<phase)&&(out[j+1]>phase+si)) || | |
((out[j]>phase+si)&&(out[j+1]<phase))) { | |
line+= '|'; | |
k = j; | |
} | |
else line+= ' '; // function isn't within range... fill with space | |
} | |
// center line | |
if ((0>=phase) && (0<phase+si)) { | |
line = '~'; // alternator | |
for(j = 0; j < TERMWIDTH; j++) line+= '-'; | |
k = TERMWIDTH-2; | |
} | |
console.log('%c'+line, _css); // print, including CSS | |
phase-= si; // decrement phase | |
} | |
return(0); | |
} | |
})); | |
/* | |
todo: | |
*/ | |
// EOF |
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)