createCanvas(windowWidth, windowHeight, WEBGL);
colorMode(HSB, 360, 100, 100, 255);
_minW = min(width, height) * 1;
_maxW = max(width, height) * 1;
strokeWeight(_minW / 800 * pixelDensity() / 2);
let regionClearanceRatio = 0.3;
let minRegionX = -_minW / 2 * (1 - regionClearanceRatio);
let maxRegionX = _minW / 2 * (1 - regionClearanceRatio);
let minRegionY = -_minW / 2 * (1 - regionClearanceRatio);
let maxRegionY = _minW / 2 * (1 - regionClearanceRatio);
for (let i = 0; i < numRegionRect; i++) {
aryRegionRect.push(new RegionRect(minRegionX, maxRegionX, minRegionY, maxRegionY));
constructor(minRegionX, maxRegionX, minRegionY, maxRegionY) {
this.minRegionX = minRegionX;
this.maxRegionX = maxRegionX;
this.minRegionY = minRegionY;
this.maxRegionY = maxRegionY;
let maxPolygonR = min(this.maxRegionX - this.minRegionX, this.maxRegionY - this.minRegionY) / 3
let shrink = min(this.maxRegionX - this.minRegionX, this.maxRegionY - this.minRegionY) / 400;
this.aryAryCornerXy = [];
for (let i = 0; i < numPolygon; i++) {
let areaXy = setAreaXy(this.minRegionX, this.maxRegionX, this.minRegionY, this.maxRegionY);
let rotateAng = random(2*PI);
let numCorner = int(random(3, 9));
let numInner = int(random(3, 10));
let hi = random(_minW / 200, _minW / 200 * 10);
let isInside = checkInside(this.aryAryCornerXy, areaXy);
while (isInside == true) {
areaXy = setAreaXy(this.minRegionX, this.maxRegionX, this.minRegionY, this.maxRegionY);
isInside = checkInside(this.aryAryCornerXy, areaXy);
if (countTrial > maxTrial) { break; }
if(countTrial > maxTrial) { break; }
let aryTemp = growPolygon(numCorner, this.aryAryCornerXy, areaXy, rotateAng, maxPolygonR, stepR,
this.minRegionX, this.maxRegionX, this.minRegionY, this.maxRegionY);
let aryCornerXy = aryTemp[0];
this.aryPolygon.push(new AreaPolygon(areaXy, areaR, rotateAng, shrink, palette, numInner, numCorner, hi));
this.aryAryCornerXy.push(aryCornerXy);
for (let i = 0; i < this.aryPolygon.length; i++) {
this.aryPolygon[i].draw();
function setAreaXy(minRegionX, maxRegionX, minRegionY, maxRegionY) {
let areaXy = createVector(random(minRegionX, maxRegionX), random() * (maxRegionY - minRegionY) + minRegionY);
function growPolygon(numCorner, aryAryXyPolygon, areaXy, rotateAng, maxPolygonR, stepR, minRegionX, maxRegionX, minRegionY, maxRegionY) {
let stepAng = 2*PI / numCorner;
while (isCross == false) {
for (let i = 0; i < numCorner; i++) {
aryCornerXy[i] = p5.Vector.add(areaXy, createVector(0, -areaR).rotate(stepAng * (i - 0.5)).rotate(rotateAng));
for (let i = 0; i < aryAryXyPolygon.length; i++) {
for (let j = 0; j < aryAryXyPolygon[i].length; j++) {
let next_j = (j + 1) % aryAryXyPolygon[i].length;
for (let k = 0; k < numCorner; k++) {
let next_k = (k + 1) % numCorner;
if (checkCrossLineSegment(aryCornerXy[k], aryCornerXy[next_k], aryAryXyPolygon[i][j], aryAryXyPolygon[i][next_j]) == true) {
if (isCross == true) { break; }
if (isCross == true) { break; }
for (let i = 0; i < numCorner; i++) {
if (aryCornerXy[i].x < minRegionX || aryCornerXy[i].x > maxRegionX || aryCornerXy[i].y < minRegionY || aryCornerXy[i].y > maxRegionY) {
if (isCross == true) { areaR -= stepR; }
if (areaR > maxPolygonR) {
for (let i = 0; i < numCorner; i++) {
aryCornerXy[i] = p5.Vector.add(areaXy, createVector(0, -areaR).rotate(stepAng * (i - 0.5)).rotate(rotateAng));
return [aryCornerXy, areaR];
function checkCrossLineSegment(xy_a, xy_b, xy_c, xy_d) {
let vec_a_b = p5.Vector.sub(xy_b, xy_a);
let vec_a_c = p5.Vector.sub(xy_c, xy_a);
let vec_a_d = p5.Vector.sub(xy_d, xy_a);
let vec_c_d = p5.Vector.sub(xy_d, xy_c);
let vec_c_a = p5.Vector.sub(xy_a, xy_c);
let vec_c_b = p5.Vector.sub(xy_b, xy_c);
let cr_ab_ac = p5.Vector.cross(vec_a_b, vec_a_c);
let cr_ab_ad = p5.Vector.cross(vec_a_b, vec_a_d);
let cr_cd_ca = p5.Vector.cross(vec_c_d, vec_c_a);
let cr_cd_cb = p5.Vector.cross(vec_c_d, vec_c_b);
if (cr_ab_ac.z * cr_ab_ad.z <= 0 && cr_cd_ca.z * cr_cd_cb.z <= 0) {
function checkInside(aryAryXy, areaXy) {
for (let i = 0; i < aryAryXy.length; i++) {
for (let j = 0; j < aryAryXy[i].length; j++) {
let next_j = (j + 1) % aryAryXy[i].length;
let vec_a_b = p5.Vector.sub(aryAryXy[i][next_j], aryAryXy[i][j]);
let vec_a_c = p5.Vector.sub(areaXy, aryAryXy[i][j]);
let cr_ab_ac = p5.Vector.cross(vec_a_b, vec_a_c);
} else if (j == aryAryXy[i].length - 1) {
if (isInside == true) { break; }
constructor(areaXy, areaR, rotateAng, shrink, palette, numInner, numCorner, hi) {
this.rotateAng = rotateAng;
this.r = this.areaR - this.shrink;
this.numCorner = numCorner;
this.stepAng = 2*PI / this.numCorner;
this.hiStep = this.hi / 2;
this.numInner = numInner;
let stepR = this.r / ((this.numInner + 1) * 2 - 1) * 2;
for (let i = 0; i < this.numInner; i++) {
this.aryInnerR[i] = this.r - stepR * (i + 1);
this.aryInnerAng[i] = 2*PI / this.numCorner * int(random(this.numCorner));
translate(this.areaXy.x, this.areaXy.y);
drawCylinder(this.numCorner, this.r, this.hi, this.rotateAng, 16);
for (let i = 0; i < this.numInner; i++) {
translate(this.areaXy.x, this.areaXy.y, this.hi + this.hiStep * i);
drawCylinder(this.numCorner, this.aryInnerR[i], this.hiStep, this.rotateAng, Math.round(1 + (15 * (1 - 1 / this.numInner * (i + 1))) / 2));
function drawCylinder(numCorner, r, hi, rotateAng, detailY) {
if(numCorner % 2 == 0) { rotateY(-2*PI / numCorner / 2); }
cylinder(r, hi, numCorner + 1, detailY, true, true);
ortho(-width/2, width/2, -height/2, height/2, 0, width*2);
translate(0, _minW / 10, 0);
rotateZ((frameCount - 1) * 0.003);
for (let i = 0; i < aryRegionRect.length; i++) {