xxxxxxxxxx
160
// Shadows (Genuary 22)
// Isometric projection of Suprematism (Genuary 11 prompt)
// https://editor.p5js.org/N-Lebrun/sketches/3S13Je7bK
// Nicolas Lebrun @nclslbrn https://nicolas-lebrun.fr
let theShader, gl;
const palettes = [
'#E9F8F6,#F1FAFD,#246023,#161922,#EA1703,#0C080B,#0173BB,#8B3014,#EEA600',
'#CED3D5,#FAF9E6,#5C4973,#1B0811,#321D19,#F4C808,#6487EE',
'#F2F5EC,#D15436,#FDFCF8,#62858B,#161817,#E6C034,#6C3928,#D1BF8A',
'#E4DCC8,#3C4755,#E5E2DC,#E3B228,#414042,#CC311A,#613A29,#372F50,#302D28',
'#DEDFDB,#AD3F0D,#050003,#8C9F9B,#215707,#1B6D9E,#E2BB0C'
]
let ctr = { x: window.innerWidth / 2, y: window.innerHeight / 2 }
let minD = Math.min(window.innerWidth, window.innerHeight) / 2
function iso (pt) {
return {
x: ctr.x + pt.x - pt.y,
y: ctr.y*1.2 + (pt.x + pt.y) / 1.25 // Normally you divide by 2 (I find this bird's eye view more cool)
}
}
function setup () {
canvas = createCanvas(windowWidth, windowHeight);
noLoop();
pixelDensity(2);
stroke(255);
composition();
fill(255, 200);
rect(width*0.2, height*0.4, width*0.6, height*0.2);
fill("#333")
textSize(24);
textAlign(CENTER);
text("Click to regenerate a composition", width/2, height/2);
}
function composition() {
let palette = random(palettes).split(',');
const compAngle = PI * 0.5 * random();
background(255);
drawGradient('#aaa', '#eee');
const shapes = []
for (let i = 0; i < 18 + random() * 24; i++) {
const shpRot = PI / Math.ceil(random() * 3)
const shpCenter = {
x: (random() - 0.5) * minD * 0.75,
y: (random() - 0.5) * minD * 0.75
}
const shapeSize = createVector(
Math.max(ctr.x * 0.005, minD * random() * 0.7),
Math.max(ctr.y * 0.001, minD * random() * 0.07)
);
// Create two points to define an edge
const shpStart = {
x: shpCenter.x - shapeSize.x / 2 * Math.cos(shpRot + compAngle),
y: shpCenter.y - shapeSize.x / 2 * Math.sin(shpRot + compAngle),
}
const shpEnd = {
x: shpCenter.x + shapeSize.x / 2 * Math.cos(shpRot + compAngle),
y: shpCenter.y + shapeSize.x / 2 * Math.sin(shpRot + compAngle),
}
// Then create a path around it
const shapePts = [{
x: shpStart.x + Math.cos(shpRot + compAngle - HALF_PI) * shapeSize.y,
y: shpStart.y + Math.sin(shpRot + compAngle - HALF_PI) * shapeSize.y
}, {
x: shpEnd.x + Math.cos(shpRot + compAngle - HALF_PI) * shapeSize.y,
y: shpEnd.y + Math.sin(shpRot + compAngle - HALF_PI) * shapeSize.y
}, {
x: shpEnd.x + Math.cos(shpRot + compAngle + HALF_PI) * shapeSize.y,
y: shpEnd.y + Math.sin(shpRot + compAngle + HALF_PI) * shapeSize.y
}, {
x: shpStart.x + Math.cos(shpRot + compAngle + HALF_PI) * shapeSize.y,
y: shpStart.y + Math.sin(shpRot + compAngle + HALF_PI) * shapeSize.y
}];
shapes.push({
center: shpCenter,
pts: shapePts,
rot: shpRot
})
}
// Order shapes from top left to bottom right
shapes.sort((a, b) => a.center.x < b.center.x || a.center.y < b.center.y)
// Build visible side of these shapes
const isoShapes = []
shapes.forEach((shp, i) => {
const shpHeight = minD * random(0.07, 0.3)
// Project these points into an isometric grid
const base = shp.pts.map((pt) => iso(pt))
// Find the highest point of the shape
const highestPt = [base].sort((a, b) => a.y - b.y)[0]
const highestPtId = base.indexOf(highestPt)
// And build sides from it
const bottom = []
for (let i = 0; i < 4; i++) {
const pt = base[(highestPtId + i) % base.length]
bottom.push({ x: pt.x, y: pt.y - shpHeight / 2 })
}
const top = bottom.map((pt) => { return { x: pt.x, y: pt.y - shpHeight / 2 } })
isoShapes.push({
bottom: bottom,
left: [top[3], top[2], bottom[2], bottom[3]],
right: [top[2], top[1], bottom[1], bottom[2]],
top: top,
height: shpHeight
})
})
// Draw drop shadow first
isoShapes.forEach((iso, i) => {
drawingContext.shadowBlur = (iso.height * 200) / minD;
drawingContext.shadowColor = `rgba(0, 0, 0, ${(iso.height * 4) / minD})`;
drawingContext.shadowOffsetX = iso.height / 1.25;
drawingContext.shadowOffsetY = iso.height / 1.25;
drawGradient(palette[i % palette.length], 'rgb(0, 0, 0)', iso.bottom)
})
// Remove the shadow
drawingContext.shadowColor = "rgba(0, 0, 0, 0)";
// Then draw side
isoShapes.forEach((iso, i) => {
const color = palette[i % palette.length]
drawGradient(color, 'rgb(15, 15, 15)', iso.bottom)
drawGradient(color, 'rgb(125, 125, 125)', iso.left)
drawGradient(color, 'rgb(0, 0, 0)', iso.right)
drawGradient('rgb(255, 255, 255)', color, iso.top)
})
}
function drawGradient (c1, c2, mask = false) {
drawingContext.save();
if (mask) {
beginShape();
mask.forEach((pt) => vertex(pt.x, pt.y));
endShape(CLOSE);
drawingContext.clip();
}
const gradient = drawingContext.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, c1)
// Uncomment if you want stongest gradient
// gradient.addColorStop(0.25, c1)
// gradient.addColorStop(0.75, c2)
gradient.addColorStop(1, c2)
drawingContext.fillStyle = gradient
drawingContext.fillRect(0, 0, width, height)
drawingContext.restore();
}
function mouseClicked () {
composition();
}
function keyPressed() {
save('shadow-genuary-22');
}