xxxxxxxxxx
455
/*
really happy with how this turned out
was a lot more effort than i thought it would be but now im so invested i wanna do more with it
maybe other ways to make it interactive would be cool
really the voronoi itself was the only hard part now i can do anything with it that does something to points in a grid
*/
// let colors = [];
// let colors = ["001219","005f73","0a9396","94d2bd","e9d8a6","ee9b00","ca6702","bb3e03","ae2012","9b2226"];
// let colors = ["fe4d55","fe6053","fe8c46","fec34b","a1c9fd"];
// let colors = ["03071e","370617","6a040f","9d0208","d00000","dc2f02","e85d04","f48c06","faa307","ffba08"];
let colors = ["421012","932527","e23b3e","f65a38","b02c07","f86f41","f9844a","f9c74f","43aa8b","277da1"]
let cwidth = 1000
let cheight = 2000
const frate = 30 // frame rate
const numFrames = 600 // num of frames to record
let numberOfPoints = colors.length;
let p = [];
let shift = 20;
let polopacity = 255;
let bw = false;
let drawline = false;
let drawinsec = false;
let pointBool = false;
let autoColors = true;
let play = true;
let polygons = [];
let button
let encoder
let recording = false
let recordedFrames = 0
const markersize = 5;
let off = 0.0;
// make sure encoder is ready before use
function preload() {
HME.createH264MP4Encoder().then(enc => {
encoder = enc
encoder.outputFilename = 'test'
encoder.width = cwidth
encoder.height = cheight
encoder.frameRate = frate
encoder.kbps = 50000 // video quality
encoder.groupOfPictures = 10 // lower if you have fast actions.
encoder.initialize()
})
}
function setup() {
pixelDensity(1);
createCanvas(cwidth, cheight)
frameRate(frate)
button = createButton('record')
button.mousePressed(() => recording = true)
background(255);
if(colors[0]){
autoColors = false;
}
// generate random points
for(let i=0; i<numberOfPoints; i++){
p.push([random(width), random(height)]);
// off.push(0.0);
if(bw){
colors.push(50 + i*(150/numberOfPoints));
}
else if(autoColors){
colors.push([random(255),random(255),random(255)])
}
}
print(p)
for(let i=0; i<numberOfPoints; i++){
for(let j=0; j<numberOfPoints; j++){
if((p[i][0] == p[j][0] || p[i][1] == p[j][1]) && i!=j){
print('DOUBLE VALUE!')
}
}
}
}
function createVoronoi(){
polygons = [];
//draw points
// drawPoints();
for(let a=0;a<p.length;a++){
//draw lines for all other points
let c = [];
let s = [];
let my = []; // on which side of line is point
let mx = []; // where is the middle x of the line
let pdist = sqrt(width**2 + height**2);
let curint;
for(let b=0;b<p.length;b++){
if(b!=a){
c[b] = ((p[b][0]-p[a][0]) / (p[a][1]-p[b][1]));
s[b] = ((p[a][1]+p[b][1])/2 - c[b] * (p[a][0]+p[b][0]) / 2);
//AB = c*x + s
my[b] = (p[a][1] + p[b][1]) / 2; //1 if a is right of b, -1 if a is left
mx[b] = (p[a][0] + p[b][0]) / 2;
if(sqrt((p[a][0]-p[b][0])**2 + (p[a][1]-p[b][1])**2) < pdist){
pdist = sqrt((p[a][0]-p[b][0])**2 + (p[a][1]-p[b][1])**2);
curint = b;
}
}
else{
c[b] = 'n/a';
s[b] = 'n/a';
my[b] = 'n/a';
mx[b] = 'n/a';
}
}
my.push((0+p[a][1])/2, (height+p[a][1])/2, p[a][1], p[a][1]);
mx.push(p[a][0], p[a][0], (0+p[a][0])/2, (width+p[a][0])/2);
//create array to store polygon points
let pol = [];
if(drawline){
drawLines(c,s);
}
// find intersections with current line
let insec = findLineIntersections(curint,c,s);
if(drawinsec){
drawIntersections(insec);
}
//find closest intersection
let closestInt = findHorInt(Math.sign((mx[curint] - p[a][0]) * c[curint] * -1), mx[curint], insec);
pol.push(closestInt[0]);
curint = closestInt[1];
//start looping through the points
findPolygonLoop(a, c, s, mx, my, pol, insec, curint);
polygons.push(pol);
}
}
function findPolygonLoop(a, c, s, mx, my, pol, insec, curint){
if(curint >= c.length){
//its one of the borders
insec = findBorderIntersections(curint - c.length, c, s);
}
else{
//its with another line
insec = findLineIntersections(curint, c, s);
}
//draw intersections
// drawIntersections(insec);
//find closest intersection in the correct direction
let closestInt;
if(curint >= c.length){
if(curint > c.length + 1){
//vertical border
closestInt = findVertInt(1 - 2* (curint-c.length-2), pol[pol.length-1][1], insec, c);
}
else{
//horizontal border
closestInt = findHorInt( 2* (curint-c.length) -1, pol[pol.length-1][0], insec);
}
}
else{
//normal line always works like this?
closestInt = findHorInt(Math.sign((mx[curint] - p[a][0]) * c[curint] * -1), pol[pol.length-1][0], insec);
}
if(pol[0][0] != closestInt[0][0] || pol[0][1] !=closestInt[0][1] ){
pol.push(closestInt[0]);
curint = closestInt[1];
findPolygonLoop(a, c, s, mx, my, pol, insec, curint);
}
else{
}
}
function findLineIntersections(l1,c,s){
let xint = [], yint = [];
for(let l=0;l<c.length;l++){
if(l!=l1 && c[l] != 'n/a'){
xint[l] = ( (s[l] - s[l1]) / (c[l1] - c[l]) );
yint[l] = (c[l1] * xint[l] + s[l1]);
}
else{
xint[l] = 'n/a';
yint[l] = 'n/a'
}
}
//add intersections with borders in order T,B,L,R
xint.push( (0 - s[l1])/c[l1], (height - s[l1])/c[l1], 0, width );
yint.push(0, height, s[l1], c[l1] * width + s[l1]);
let intersections = [];
for(let i=0;i<xint.length;i++){
intersections.push([xint[i], yint[i]])
}
return intersections;
}
function findBorderIntersections(b,c,s){
if(b<2){
//top or bottom
let xint = [];
for(let l=0;l<c.length;l++){
if(c[l] != 'n/a'){
xint[l] = (height*b - s[l])/c[l];
}
else{
xint[l] = 'n/a';
}
}
//add intersections with borders in order T,B,L,R
xint.push( 'n/a', 'n/a', 0, width );
let intersections = [];
for(let i=0;i<xint.length;i++){
if(xint[i] != 'n/a'){
intersections.push([xint[i], height*b]);
}
else{
intersections.push(['n/a','n/a']);
}
}
return intersections;
}
else{
//left or right
let yint = [];
for(let l=0;l<c.length;l++){
if(c[l] != 'n/a'){
yint[l] = width*(b-2)*c[l] + s[l];
}
else{
yint[l] = 'n/a';
}
}
//add intersections with borders in order T,B,L,R
yint.push( 0, height, 'n/a', 'n/a' );
let intersections = [];
for(let i=0;i<yint.length;i++){
if(yint[i] != 'n/a'){
intersections.push([width*(b-2), yint[i]]);
}
else{
intersections.push(['n/a','n/a']);
}
}
return intersections;
}
}
function findHorInt(ms, pnt, insec){
//use this one normally
let curdist = sqrt(width**2 + height**2);
let curint;
for(let i=0; i<insec.length; i++){
if(abs(pnt - insec[i][0]) < curdist &&
Math.sign(insec[i][0] - pnt) == ms ){
curdist = abs(pnt - insec[i][0]);
curint = i;
}
}
return [[insec[curint][0], insec[curint][1]], curint];
}
function findVertInt(ms, pnt, insec, c){
//use this one if on a vertical borderline and/or if previous line was horizontal
let curdist = sqrt(width**2 + height**2);
let curint;
for(let i=0; i<insec.length; i++){
if(abs(pnt - insec[i][1]) < curdist &&
Math.sign(insec[i][1] - pnt) == ms){
curdist = abs(pnt - insec[i][1]);
curint = i;
}
}
return [[insec[curint][0], insec[curint][1]], curint];
}
function drawIntersections(insec){
for(let i=0; i<insec.length; i++){
if(insec[i][0] != 'n/a'){
drawMarker(insec[i][0], insec[i][1], 150);
}}
}
function drawLines(c,s){
stroke(0);
for(let l=0;l<c.length;l++){
if(c[l] != 'n/a'){
line(0,c[l]*0+s[l],width,c[l]*height+s[l]);
}
}
noStroke();
}
function drawPolygons(){
// stroke(255);
for(let i = 0; i<polygons.length; i++){
if(bw){
fill(colors[i], polopacity);
}
else if(autoColors){
stroke(colors[i][0],colors[i][1],colors[i][2]);
fill(colors[i][0],colors[i][1],colors[i][2], polopacity);
}
else{
stroke("#" + colors[i]);
fill("#" + colors[i]);
}
beginShape()
for(let j=0; j<polygons[i].length; j++){
vertex(polygons[i][j][0], polygons[i][j][1])
}
endShape(CLOSE)
}
noStroke();
}
function drawPoints(){
for(let a=0;a<p.length;a++){
drawMarker(p[a][0],p[a][1],0);
}
}
function drawMarker(x, y, col) {
stroke(col);
line(x - markersize, y, x + markersize, y);
line(x, y - markersize, x, y + markersize);
noStroke();
}
function draw(){
background(255);
if(play){
off += 0.01;
for(let i=0; i<p.length; i++){
//perlin noise should be betweeen 0 and 1 but for some reason if i make it 0.5 it has a slight tendency to the bottom right corner so i made it 0.47 (which would make sense if it was more like in between 0 and 0.94)
let shiftx = shift * (0.47 - noise(i*100, off, 0));
if(p[i][0] + shiftx > 0 && p[i][0] + shiftx<width){
p[i][0] += shiftx;
}
let shifty = shift * (0.47 - noise(i*100, 0, off));
if(p[i][1] + shifty > 0 && p[i][1] + shifty<height){
p[i][1] += shifty;
}
}
}
createVoronoi();
drawPolygons();
if(pointBool){
drawPoints();
}
// DOWNLOADER CODE
// keep adding new frame
if (recording) {
// console.log('recording')
encoder.addFrameRgba(drawingContext.getImageData(0, 0, encoder.width, encoder.height).data);
recordedFrames++
}
// finalize encoding and export as mp4
if (recordedFrames === numFrames) {
recording = false
recordedFrames = 0
console.log('recording stopped')
encoder.finalize()
const uint8Array = encoder.FS.readFile(encoder.outputFilename);
const anchor = document.createElement('a')
anchor.href = URL.createObjectURL(new Blob([uint8Array], { type: 'video/mp4' }))
anchor.download = encoder.outputFilename
anchor.click()
encoder.delete()
preload() // reinitialize encoder
}
}
function keyPressed(){
if(keyCode == ENTER){
if(play){
play = false;
} else{
play = true;
}
}
}