xxxxxxxxxx
/*
Zentangle #WCCChallenge 250402
https://openprocessing.org/sketch/2599861
#generativeart #creativecoding #p5js
Dear Raph and creative coding community,
took the opportunity to wok on my drawing function helper.
Have to figure out for the future, how to best separate this functions
to best include it later via unpkg.com, jsdelivr.com or similar,
to not c&p it due the latest skecthes :/
inspiration from several images, eg
https://www.google.com/search?client=firefox-b-d&q=zentangle
Join the Birb's Nest Discord for friendly creative coding community
and future challenges and contributions: https://discord.gg/S8c7qcjw2b
WCCC-Contributions: https://openprocessing.org/curation/78544
*/
let S, MAR;
let svgStr;
let circles;
let pal;
function setup() {
createCanvas(S = min(windowWidth, windowHeight), S);
describe("random lines and circles fitting into areas")
MAR = ~~(S * 0.05);
background(240);
stroke(2, 2, 8);
strokeWeight(2);
fill(255);
loop();
}
function draw() {
svgStr = '';
const zenFc = random([zen1, zen2]);
zen1(MAR, MAR, width - 2 * MAR, height - 2 * MAR);
noLoop();
}
function mousePressed() {
if (mouseButton !== RIGHT && mouseX > 0.1 * width && mouseX < 0.9 * width) setup();
}
function zen1(x, y, w, h) {
const cx = x + ~~(random(0.35, 0.65) * w);
const cy = y + ~~(random(0.35, 0.65) * h);
const boundaryPoints = [
{x: x + w, y: y + h},
{x: x, y: y + h},
{x: x, y: y},
{x: x + w, y: y},
];
rect(x, y, w, h); noFill(); // just white fill, svg lines added in loop below
for (let i = 0; i < boundaryPoints.length; i++) {
const p1 = boundaryPoints[i];
boundaryPoints[i].angle = (atan2(p1.y - cy, p1.x - cx)+TAU)%TAU;
const p2 = boundaryPoints[(i+1)%boundaryPoints.length];
clipLine(p1, p2, boundaryPoints);
}
// console.log(boundaryPoints);
const l = max(w, h) * 1.5;
const n = ~~random(3, 17);
let seperators = [];
const aa = random(TAU); // initial rotation
const da = TAU / n / 3;
for (let i = 0; i < n; i++) {
const a = (aa + i / n * TAU + random(-da, da)) % TAU;
const v = createVector(l, 0).rotate(a);
// line(cx, cy, cx+v.x, cy+v.y);
const p1 = {x: cx, y: cy};
const p2 = {x: cx + v.x, y: cy + v.y};
seperators.push({pts: clipLine(p1, p2, boundaryPoints), angle: a});
}
// console.log(seperators)
const off = ~~random(seperators.length);
const palettes = random([palettes1, palettes2, palettes3]); // allow b/w or colored palette for all cells
for (let j = 0; j < seperators.length; j += 1) {
pal = random(palettes); // randomly choose possible color combinations for each cell
const s1 = seperators[j];
const s2 = seperators[(j+1) % seperators.length];
const areaPts = getArea(s1, s2, boundaryPoints);
svgStr += `<g stroke="#000000" stroke-width="1" fill="none">\n`;
if ((j+off) % 6 === 0) {
fillAreaCircles(areaPts)
} else if ((j+off) % 6 === 1) {
fillAreaGrid(areaPts, random(20, 50));
} else if ((j+off) % 6 === 2) {
fillAreaRadial(areaPts);
} else if ((j+off) % 6 === 3) {
fillAreaWaves(areaPts, random(20, 50));
} else if ((j+off) % 6 === 4) {
fillAreaRandom(areaPts, random(20, 50));
} else if ((j+off) % 6 === 5) {
fillAreaLines(areaPts);
}
svgStr += `</g>\n`;
}
}
// get the area between the two seperators by including
// ERROR: when between
function getArea(s1, s2, boundaryPts) {
const bdPts = [boundaryPts]
const arr = [];
arr.push({x: s1.pts[0].x, y: s1.pts[0].y}); // 1st pt = center
arr.push({x: s1.pts[1].x, y: s1.pts[1].y}); // 2nd pt = end pt 1st seperator
const s1a = s1.angle;
let s2a = s2.angle;
if (s2a < s1a) {
s2a += TAU;
for (let i = 0; i < boundaryPts.length/2; i++) {
// if (s1a < boundaryPts[i].angle) bdPts.push(bdPts.shift());
bdPts.push(bdPts.shift());
}
}
// console.log(bdPts); console.log(`${s1a}, ${s2a}`)
for (const pt of bdPts) {
let ptA = pt.angle; if (s2a > TAU && ptA < s1a) ptA += TAU;
if (ptA > s1a && ptA < s2a) {
arr.push({x: pt.x, y: pt.y}); // add boundaryPts if between s1/s2 angles
}
}
arr.push({x: s2.pts[1].x, y: s2.pts[1].y}); // last pt = end pt 2nd seperator
return arr;
}
function zen2(x, y, w, h) {
const boundaryPoints = [
{x: x, y: y},
{x: x + w, y: y},
{x: x + w, y: y + h},
{x: x, y: y + h},
];
rect(x, y, w, h);
noFill();
const cx = x + ~~(random(0.3, 0.7) * w);
const cy = y + ~~(random(0.3, 0.7) * h);
let n = ~~random(2, 7);
const dia = min(w, h) / n;
n *= 2;
const areas = [];
for (let i = 1; i < n; i++) {
const d = dia * i;
const dx = random(-dia / 6, dia / 6);
const dy = random(-dia / 6, dia / 6);
areas.push(clipArc(cx + dx, cy + dy, d, d, 0, TAU, boundaryPoints)); // circle(cx, cy, d, d)
// areas.push(getCirclePts(cx + dx, cy + dy, d/2, d/2));
}
function getCirclePts(cx, cy, rx, ry=null, l=10, eps = 0.00001) {
if (ry === null) ry = rx;
const u = TAU*(rx+ry)/2;
const da = min(HALF_PI, TAU/(u/l));
const pts = [];
for (let a = 0; a <= TAU + eps; a+=da) {
const x = cos(a) * rx + cx;
const y = sin(a) * ry + cy;
pts.push({x, y});
// point(x, y);
}
return pts;
}
// draw areas
const off = 0; // ~~random(areas.length);
const palettes = random([palettes1, palettes2, palettes3]); // allow b/w or colored palette for all cells
for (let j = 0; j < areas.length; j++) {
pal = random(palettes); // randomly choose possible color combinations for each cell
let areaPts = [];
areaPts = areaPts.concat(areas[j]);
if (j > 0) areaPts = areaPts.concat(areas[j-1].reverse());
// console.log(area);
svgStr += `<g stroke="#000000" stroke-width="1" fill="none">\n`;
if ((j+off) % 5 === 0) {
fillAreaCircles(areaPts)
} else if ((j+off) % 5 === 1) {
fillAreaRadial(areaPts);
} else if ((j+off) % 5 === 2) {
fillAreaWaves(areaPts, random(20, 50));
} else if ((j+off) % 5 === 3) {
fillAreaRandom(areaPts, random(20, 50));
} else if ((j+off) % 5 === 4) {
fillAreaLines(areaPts);
}
svgStr += `</g>\n`;
}
}
function keyPressed() {
if (key == "s") {
saveSvg("epi_zentangle.svg.txt");
}
}
function saveSvg(file_name) {
let svgAll = `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">\n`;
svgAll += svgStr;
svgAll += `</svg>`;
save(svgAll.split("\n"), file_name);
}