Public
Edited
Sep 16, 2023
Paused
1 fork
39 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
class RectArrangement {
constructor(
center = [0, 0],
options = {
heuristic: "first",
metric: "euclidean",
closeFreq: 1,
closeFactor: 0.5
}
) {
this.center = center;
this.rects = [];
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)
: (p, q) => Math.abs(p[0] - q[0]) + Math.abs(p[1] - q[1]);
}

closePolygon(amount) {
this.polygon = topo(topo(this.polygon, amount), -amount);
}

addRect(area, aspect) {
let [cx, cy] = this.center;
let sidex = Math.sqrt(area * aspect);
let sidey = area / sidex;
let [dx, dy] = [sidex / 2, sidey / 2];
let d = Math.sqrt(dx * dx + dy * dy);
let s = undefined;
let poly;
if (this.rects.length == 0) {
s = rectangle([cx - dx, cy - dy], [sidex, sidey]);
} else {
let distToCenter = Number.MAX_VALUE;
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; // Worse than the best so far
for (let [sx, sy, sign] of [
[x, y, -1],
[x - sidex, y, +1],
[x, y - sidey, +1],
[x - sidex, y - sidey, -1]
]) {
if (Math.sign(v.w) != sign) continue; // Wrong sign
let candidate = rectangle([sx, sy], [sidex, sidey]);
let [scx, scy] = [sx + dx, sy + dy]; // Center of rectangle
if (rlist.pointIntersection([scx, scy])) continue; // Center inside polygon
if (rlist.rectIntersection(candidate)) continue; // Polygon intersects rectangl
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 rect";
this.rects.push(s);
this.polygon = union(this.polygon, s);
let factor = d * this.closeFactor;
if (this.rects.length % config.closeFreq == 0) this.closePolygon(factor);
}
}
Insert cell
Insert cell
import { Vertex, VList } from "@esperanc/vertex-lists"
Insert cell
import {tabbed,paged,combo} from "@esperanc/aggregated-inputs"
Insert cell
import {select,button,slider} from "@jashkenas/inputs"
Insert cell
import {
drawPolygon,
rectangle,
topo,
union,
RectList
} from "@esperanc/square-packing"
Insert cell
// Used to style one of Jeff Ashkenas input components
function styleInput(form,style) {
let input = form.querySelector("input");
for (let s in style) {
input.style[s] = style[s];
}
}
Insert cell
// Makes a slider with 80px width
function shortSlider (...args) {
let s = slider(...args);
styleInput(s,{width:"80px"});
return s;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more