Public
Edited
Aug 21, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
p5((sketch) => {
let system;

const fc = sketch.color(fillColor),
width = 640,
height = 400;

sketch.setup = function () {
sketch.createCanvas(width, height);
sketch.textAlign(sketch.CENTER);
sketch.textFont("sans-serif");
sketch.textStyle(sketch.BOLD);
sketch.strokeWeight(0.5);
};
sketch.draw = function () {
sketch.background(viewof background.valueAsNumber);

// Convert width -> 100, height -> 100
sketch.scale(height / 100, height / 100);
sketch.translate(0 + ((width / height - 1) * 100) / 2, 0);

let seconds = sketch.millis() / 1000;

let draw = (foo) => {
sketch.push();
foo();
sketch.pop();
};

// Draw the ellipse
draw(() => {
let t = 5,
radians = Math.PI * 2 * ((seconds / t) % 1),
fill = parseInt((0.5 + Math.sin(radians) * 0.5) * 256);

sketch.translate(0, 100);
sketch.rotate(radians);
sketch.stroke(fc).strokeWeight(0.2);
sketch.fill(fill);
sketch.ellipse(0, 0, 10, 20);
});

// Draw the 100x100 framework
draw(() => {
sketch.stroke("black");
sketch.noFill();
sketch.square(0, 0, 100);
});

// Draw the timer
draw(() => {
let text = seconds.toFixed(2) + " | " + computingHull.notUpdatingSteps;
sketch.translate(100, 100);
sketch.fill(fc).textSize(5).textAlign(sketch.LEFT);
sketch.text(text, 0, 0);
});

// Draw the multiple polygon
draw(() => {
sketch.fill(fillColor + "aa");
sketch.beginShape();
hull.map((d) => {
sketch.vertex(d.x, d.y);
});
sketch.endShape(sketch.CLOSE);
});

// Draw the update trace
draw(() => {
sketch.fill("#3335");
sketch.noStroke();

let extent = d3.extent(computingHull.f1f2Trace, (d) => d.area),
scale = d3.scaleLinear().domain(extent).range([0, 255]),
c;
computingHull.f1f2Trace.map(({ f1, f2, area }) => {
c = scale(area);
sketch.fill(c, c, c, 100);
sketch.ellipse(f1.x, f1.y, 1, 1);
sketch.ellipse(f2.x, f2.y, 1, 1);
});
});

// Draw the ellipse
draw(() => {
let ellipse = computingHull.largestEllipse;
let { o, radian, a, b, f1, f2 } = ellipse;
sketch.noFill();
sketch.ellipse(f1.x, f1.y, 3, 3);
sketch.ellipse(f2.x, f2.y, 3, 3);

sketch.translate(o.x, o.y);
sketch.rotate(radian);
sketch.ellipse(0, 0, a * 2, b * 2);
});
};
})
Insert cell
{
now;
updateComputingHull()
}
Insert cell
updateComputingHull()
Insert cell
updateComputingHull = () => {
let ellipse,
flagLongTimeNotUpdate = computingHull.notUpdatingSteps > 100;

if (flagLongTimeNotUpdate) {
ellipse = mkEllipse(rndHullInsidePoints(2, undefined, 100));
Object.assign(computingHull, {
largestEllipse: ellipse,
notUpdatingSteps: 0
});
} else {
// ellipse = mkEllipse(rndHullInsidePoints(2, computingHull.largestEllipse));
ellipse = d3.sort(
new Array(20)
.fill(0)
.map(() =>
mkEllipse(rndHullInsidePoints(2, computingHull.largestEllipse))
),
(a, b) => b.area - a.area
)[0];
}

if (ellipse.area > computingHull.largestEllipse.area) {
computingHull.f1f2Trace.push({
f1: ellipse.f1,
f2: ellipse.f2,
area: ellipse.area
});

// Prevent buffer grows too long, if its length is larger than 2000, shift 1 step.
if (computingHull.f1f2Trace.length > 2000) {
computingHull.f1f2Trace.shift();
}

Object.assign(computingHull, {
largestEllipse: ellipse,
notUpdatingSteps: 0
});
} else {
computingHull.notUpdatingSteps += 1;
}

return ellipse;
}
Insert cell
rndHullInsidePoints(2, computingHull.largestEllipse)
Insert cell
computingHull = {
let ellipse = mkEllipse(rndHullInsidePoints());
return Object.assign(hull, {
largestEllipse: ellipse,
notUpdatingSteps: 0,
f1f2Trace: [{f1:ellipse.f1, f2:ellipse.f2, area:ellipse.area}],
});
}
Insert cell
ellipse = mkEllipse(rndHullInsidePoints())
Insert cell
mkEllipse = (rhip) => {
let f1 = rhip[0],
f2 = rhip[1],
o = getCenter(f1, f2),
radian = getRadian(f1, f2),
c = distance(f1, f2) / 2,
a =
d3.min(
hullSurroundingPoints.map((d) => distance(d, f1) + distance(d, f2))
) / 2,
b = Math.sqrt(a ** 2 - c ** 2),
area = a * b;

return { f1, f2, o, radian, c, a, b, area };
}
Insert cell
rhip = rndHullInsidePoints()
Insert cell
rndHullInsidePoints = (n = 2, f1f2 = undefined, r = undefined) => {
let points = [];

if (r === undefined) {
r = searchRange;
}

// If f1f2 is not given, create points randomly
if (f1f2 === undefined) {
while (points.length < 2) {
points = new Array(100)
.fill(0)
.map(rndPoint)
.filter((d) =>
d3.polygonContains(
hull.map((d) => [d.x, d.y]),
d
)
);
}
} else {
let f1 = f1f2.f1,
f2 = f1f2.f2,
rnd = () => (Math.random() - 0.5) * 2 * r,
ff1,
ff2;

while (true) {
ff1 = [f1.x + rnd(), f1.y + rnd()];
ff2 = [f2.x + rnd(), f2.y + rnd()];
if (
d3.polygonContains(
hull.map((d) => [d.x, d.y]),
ff1
) &&
d3.polygonContains(
hull.map((d) => [d.x, d.y]),
ff2
)
) {
points = [ff1, ff2];
break;
}
}
}

points = points
.slice(0, 2)
.map((d) => Object.assign({}, { x: d[0], y: d[1] }));

return points;
}
Insert cell
hullSurroundingPoints = {
let points = [];
let j,
a,
b,
x,
y,
n = 100;
for (let i = 0; i < hull.length; ++i) {
j = (i + 1) % hull.length;
a = hull[i];
b = hull[j];
for (let k = 0; k < n; ++k) {
x = a.x * (1 - k / n) + b.x * (k / n);
y = a.y * (1 - k / n) + b.y * (k / n);
points.push(Object.assign({}, { x, y, i }));
}
}
return points;
}
Insert cell
hullSurroundingPoints
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
hull = {
let points = new Array(numHullPoints)
.fill(0)
.map(rndPoint);

return d3
.polygonHull(points)
.map((d) => Object.assign({}, { x: d[0], y: d[1] }));
}
Insert cell
hull
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
getRadian = (a, b) => {
return Math.atan2(b.y - a.y, b.x - a.x);
}
Insert cell
getCenter = (a, b) => {
let x = (a.x + b.x) / 2,
y = (a.y + b.y) / 2;

return { x, y };
}
Insert cell
distance = (a, b) => {
return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);
}
Insert cell
rndPoint = () => {
return [Math.random() * 100, Math.random() * 100];
}
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require("d3")
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