xxxxxxxxxx
284
let jiggle = 0.03;
let mindist = 0.1;
let tiling;
let edges;
let outline;
let scaling;
let done;
function inv(T) {
const det = T[0] * T[4] - T[1] * T[3];
return [
T[4] / det,
-T[1] / det,
(T[1] * T[5] - T[2] * T[4]) / det,
-T[3] / det,
T[0] / det,
(T[2] * T[3] - T[0] * T[5]) / det,
];
}
function area(pts) {
let total = 0.0;
let p = pts[pts.length - 1];
for (let idx = 0; idx < pts.length; ++idx) {
const q = pts[idx];
total += p.x * q.y - p.y * q.x;
p = q;
}
return abs(total / 2);
}
function getOutline() {
outline = [];
function dofmul(T, p) {
const tp = mul(T, p);
return { x: tp.x, y: tp.y, dofs: p.dofs };
}
for (let i of tiling.parts()) {
const ej = edges[i.id];
let cur = i.rev ? ej.length - 2 : 1;
const inc = i.rev ? -1 : 1;
for (let idx = 0; idx < ej.length - 1; ++idx) {
outline.push(dofmul(i.T, ej[cur]));
cur += inc;
}
}
return outline;
}
function tick() {
// Step 0: Get canonical edge information
const edgeinfo = {};
for (let i of tiling.parts()) {
const T = i.T;
const id = i.id;
edgeinfo[id] = { scale: mag(T[0], T[1]), copied: false };
}
// Step 1: jiggle
for (let p of outline) {
const d = random(-jiggle, jiggle);
const ang = random(TWO_PI);
p.x += d * cos(ang);
p.y += d * sin(ang);
}
// Step 2: repulsion
let coords = [];
for (let p of outline) {
coords.push(p.x);
coords.push(p.y);
}
const delaunay = new Delaunator(coords);
const tris = delaunay.triangles;
for (let idx = 0; idx < tris.length; idx += 3) {
for (let v of [
[0, 1],
[1, 2],
[2, 0],
]) {
let p = outline[tris[idx + v[0]]];
let q = outline[tris[idx + v[1]]];
let d = dist(p.x, p.y, q.x, q.y);
if (d < mindist) {
let dx = (q.x - p.x) / d;
let dy = (q.y - p.y) / d;
q.x += 0.03 * dx;
q.y += 0.03 * dy;
p.x -= 0.03 * dx;
p.y -= 0.03 * dy;
}
}
}
// Step 3: fairing
for (let idx = 0; idx < outline.length; ++idx) {
let a = outline[(idx + outline.length - 1) % outline.length];
let b = outline[idx];
let c = outline[(idx + 1) % outline.length];
let mx = 0.5 * (a.x + c.x);
let my = 0.5 * (a.y + c.y);
b.x = lerp(b.x, mx, 0.005);
b.y = lerp(b.y, my, 0.005);
}
// Step 4: copy back
let vi = 0;
let maxdist = 0.0;
const nejs = [];
for (let e of edges) {
const ne = [];
for (let idx = 0; idx < e.length; ++idx) {
ne.push({ x: 0, y: 0, n: 0 });
}
nejs.push(ne);
}
for (let i of tiling.parts()) {
const id = i.id;
const ej = edges[id];
const T = i.T;
const Ti = inv(T);
let cur = i.rev ? ej.length - 2 : 1;
const inc = i.rev ? -1 : 1;
for (let idx = 0; idx < ej.length - 1; ++idx) {
const ip = mul(Ti, outline[vi]);
nejs[id][cur].x += ip.x;
nejs[id][cur].y += ip.y;
++nejs[id][cur].n;
cur += inc;
++vi;
}
}
for (let idx = 0; idx < edges.length; ++idx) {
for (let vidx = 0; vidx < edges[idx].length; ++vidx) {
const v = nejs[idx][vidx];
const ox = edges[idx][vidx].x;
const oy = edges[idx][vidx].y;
if (edges[idx][vidx].dofs > 0) {
edges[idx][vidx].y = v.y / v.n;
if (edges[idx][vidx].dofs > 1) {
edges[idx][vidx].x = v.x / v.n;
}
}
maxdist = max( maxdist, dist( ox, oy, edges[idx][vidx].x, edges[idx][vidx].y ));
}
}
let added = false;
// Step 5: subdivision + simplification
for (let idx = 0; idx < edges.length; ++idx) {
const ei = edgeinfo[idx];
const sc = ei.scale;
const ej = edges[idx];
const nej = [ej[0]];
for (let vidx = 1; vidx < ej.length; ++vidx) {
const b = ej[vidx - 1];
const c = ej[vidx];
let d = dist(b.x, b.y, c.x, c.y);
if (d * sc > 1.9 * mindist) {
let mx = 0.5 * (b.x + c.x);
let my = 0.5 * (b.y + c.y);
nej.push({ x: mx, y: my, dofs: 2 });
added = true;
}
nej.push(c);
}
edges[idx] = nej;
}
/*
console.log( maxdist );
if( true ) {
return true;
} else {
return false;
} */
return true;
}
function makeTile() {
const goodTypes = [10, 11, 18, 28, 36, 61, 62, 71, 73, 90];
const tp = goodTypes[int(random(goodTypes.length))];
tiling = new IsohedralTiling(tp);
//console.log( tp );
edges = [];
for (let idx = 0; idx < tiling.numEdgeShapes(); ++idx) {
const es = tiling.getEdgeShape(idx);
if (es == EdgeShape.U) {
edges.push([
{ x: 0, y: 0, dofs: 0 },
{ x: 1, y: 0, dofs: 1 },
]);
} else {
edges.push([
{ x: 0, y: 0, dofs: 0 },
{ x: 1, y: 0, dofs: 0 },
]);
}
}
outline = getOutline();
const ar = area(outline);
scaling = sqrt((width * height) / (60 * ar));
const q = sqrt(ar);
// console.log( q );
mindist = 0.1 * q;
jiggle = 0.03 * q;
}
function setup() {
createCanvas(512, 512);
makeTile();
}
function draw() {
background(220);
stroke(1);
strokeWeight(1 / scaling);
if (!done) {
if( !tick() ) {
done = true;
}
}
outline = getOutline();
push();
translate(width / 2, height / 2);
scale(scaling, scaling);
translate(-5, -5);
for (let i of tiling.fillRegionBounds(-2, -4, 12, 12)) {
const T = i.T;
const col = tiling.getColour(i.t1, i.t2, i.aspect);
fill([color("#CE7777"), color("#E8C4C4"), color("#485874")][col]);
beginShape();
for (let p of outline) {
const tp = mul(T, p);
if (p.dofs == 2) {
curveVertex(tp.x, tp.y);
} else {
vertex(tp.x, tp.y);
}
}
endShape(CLOSE);
/*
fill(0);
for (let p of outline) {
const tp = mul(T, p);
ellipse(tp.x, tp.y, 5 / scaling, 5 / scaling);
}*/
}
pop();
}
function keyPressed() {
if (key == " ") {
makeTile();
done = false;
} else if (key == "t") {
// tick();
}
}