Public
Edited
Apr 9, 2024
Insert cell
Insert cell
Insert cell
/**
return the region code where the endpoint lies in
@param {Vector} endpoint {x,y}
@param {Vector} upperleftPoint {x,y} upperleft corner of the clip window
@param {number} w width of the clip window
@param {number} h height of the clip window
@returns the region code for the endpoint
**/
function endpointRegionCode(endpoint, upperleftPoint, w, h){
let code = 0;
let xmin = upperleftPoint.x, xmax = upperleftPoint.x + w, ymin = upperleftPoint.y, ymax = upperleftPoint.y + h;

if (endpoint.x < xmin) code |= (1 << 0); // endpoint falls on left region of the window
else if (endpoint.x > xmax) code |= (1 << 1); // endpoint falls on right region of the window
if (endpoint.y < ymin) code |= (1 << 2); // endpoint falls bellow the window
else if (endpoint.y > ymax) code |= (1 << 3); // endpoint falls above the window
return code;
}
Insert cell
Insert cell
/**
@param {Vector} p0 {x,y} first endpoint of the line
@param {Vector} p1 {x,y} second endpoint of the line
@param {Vector} p {x,y} upperleft corner of the clip window
@param {number} w width of the clip window
@param {number} h height of the clip window
@returns [p0,p1] the line to draw inside the clip window
**/
function clipLine(p0, p1, p, w, h) {
let code0, code1;
let p0c = {x:p0.x, y:p0.y};
let p1c = {x:p1.x, y:p1.y};
let xmin = p.x, xmax = p.x + w, ymin = p.y, ymax = p.y + h;
while (true) {
code0 = endpointRegionCode(p0c, p, w, h);
code1 = endpointRegionCode(p1c, p, w, h);
if (code0 === 0 && code1 ===0){
// entire line inside the window
return [p0c, p1c];
} else if ((code0 & code1) !== 0){
//entire line outside the window
return null;
} else {
let code = code0 !== 0 ? code0 : code1;
let newX=0, newY=0, x0 = p0c.x, y0 = p0c.y, x1 = p1c.x, y1 = p1c.y;
if ((code & (1 << 0)) !== 0) {
newX = xmin;
newY = (y1 - y0) * (xmin - x0) / (x1- x0) + y0;
//endpoint on the left region
} else if ((code & (1 << 1)) !== 0) {
newX = xmax;
newY = (y1 - y0) * (xmax - x0) / (x1- x0) + y0;
//endpoint on the right region
} else if ((code & (1 << 2)) !== 0) {
newY = ymin;
newX = (x1 - x0) * (ymin - y0) / (y1 - y0) + x0;
//endpoint below the window
} else if ((code & (1 << 3)) !== 0) {
newY = ymax;
newX = (x1 - x0) * (ymax - y0) / (y1 - y0) + x0;
//endpoint above the window
}
// update new endpoint
if (code === code0) {
p0c.x = newX;
p0c.y = newY;
} else {
p1c.x = newX;
p1c.y = newY;
}
}
}
}
Insert cell
Insert cell
Insert cell
p5(s => {
let gridSize = 100;
s.setup = function () {
s.createCanvas(400,400);
s.background(255);
s.stroke(0);
s.strokeWeight(2);
for (let x = 0; x < s.width; x += gridSize) {
for (let y = 0; y < s.height; y += gridSize) {
drawCell(x, y, gridSize, gridSize, s.random(5,15), s.random(Math.PI * 2));
}
}
}

function drawCell(x, y, w, h, gap, a) {
let xStart = x + s.random(w), yStart = y + s.random(h);
let slope = Math.tan(a);
let c = yStart - slope * xStart;
let l1, l2;
let i = 0;
while( l1 !== null || l2 !== null){
let x0 = x - w/2, x1 = x + w + w/2;
let y0 = slope * x0 + c + i * gap / Math.cos(a), y1 = slope * x1 + c + i * gap / Math.cos(a);
l1 = clipLine({x:x0,y:y0},{x:x1,y:y1}, {x:x, y:y}, w, h);
if (l1 !== null) s.line(l1[0].x, l1[0].y, l1[1].x, l1[1].y);

x0 = x - w/2, x1 = x + w + w/2;
y0 = slope * x0 + c - i * gap / Math.cos(a), y1 = slope * x1 + c - i * gap / Math.cos(a);
l2 = clipLine({x:x0,y:y0},{x:x1,y:y1}, {x:x, y:y}, w, h);
if (l2 !== null) s.line(l2[0].x, l2[0].y, l2[1].x, l2[1].y);
i++;
}
}
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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