class SquareArrangement {
constructor (center = [0,0], options = {heuristic:"first",
metric:"euclidean",
closeFreq : 1,
closeFactor:0.5}) {
this.center = center;
this.squares = [];
this.polygon = new VList();
Object.assign(this,options);
this.distance = this.metric == "chessboard" ? (
(p,q) => Math.max(Math.abs(p[0]-q[0]),Math.abs(p[1]-q[1]))
) : this.metric == "euclidean" ? (
(p,q) => Math.sqrt((p[0]-q[0])**2+(p[1]-q[1])**2)
): this.metric == "mahalanobis" ? (
mahalanobis
) : (
(p,q) => Math.abs(p[0]-q[0])+Math.abs(p[1]-q[1])
);
}
closePolygon (amount) {
this.polygon = topo(topo(this.polygon,amount),-amount)
}
addSquare (area) {
let [cx,cy] = this.center;
let side = Math.sqrt(area);
let d = side/Math.sqrt(2);
let s = undefined;
let poly;
if (this.squares.length == 0) {
s = square ([cx-d,cy-d],side)
}
else {
let distToCenter = Number.MAX_VALUE;
let smallestPerimeter;
let vtx = [...this.polygon].map(v=>{v.dist = this.distance(v.p,this.center); return v});
vtx.sort ((a,b) => a.dist - b.dist);
let rlist = new RectList(this.polygon)
for (let v of vtx) {
let [x,y] = v.p;
if (v.dist > distToCenter+d) continue;
for (let [sx,sy,sign] of [[x,y,-1],[x-side,y,+1],[x,y-side,+1],[x-side,y-side,-1]]) {
if (Math.sign(v.w) != sign) continue;
let candidate = square ([sx,sy],side)
let [scx,scy] = [sx+d,sy+d];
if (rlist.pointIntersection([scx,scy])) continue;
if (rlist.rectIntersection(candidate)) continue;
let dist = this.distance([scx,scy],[cx,cy]);
if (!s || dist < distToCenter) {
s = candidate;
distToCenter = dist;
}
}
if (this.heuristic == "first" && s) break;
}
}
if (s == undefined) throw "Something went wrong : could not find a place for square";
this.squares.push (s);
this.polygon = union(this.polygon,s);
let factor = d*this.closeFactor;
if (this.squares.length % config.closeFreq == 0) this.closePolygon(factor);
}
}