Published
Edited
Oct 13, 2020
1 star
Insert cell
Insert cell
Insert cell
Insert cell
canvas3 = {
const context = DOM.context2d(width, height);
context.translate(width / 2, height / 2);
context.fillStyle = "rgba(20,20,20,1)";
context.fillRect(-width / 2, -height / 2, width, height);
context.fill();
context.globalCompositeOperation = 'lighter';

for (let line of lines) {
line.color = "rgba(200, 200, 200, 0.9)";
line.draw(context);
}

function shadow(offset) {
const rays = [];
const n = 100;
const r = 1000;
for (let i = 0; i < n; ++i) {
const angle = i * ((360 + offset) / n);
const x = r * Math.cos(angle * (Math.PI / 180));
const y = r * Math.sin(angle * (Math.PI / 180));
const ray = new Line(
[startX, startY],
[x, y],
"rgba(200,10,10,0.9)",
2.8
);

for (let line of lines) {
ray.intersection(line);
}

rays.push(ray);
}

context.fillStyle = "rgba(255, 255, 255, 0.1)";
context.moveTo(...rays[0].intersectionP);
for (let ray of rays.slice(1)) {
context.lineTo(...ray.intersectionP);
}
context.fill();
}

for (let i = 0; i < 10; ++i) {
shadow(offset[i]);
}

return context.canvas;
}
Insert cell
offset = {
const res = [];
for (let i = 0; i < 10; ++i) {
res.push(Math.random() * 2);
}
return res;
}
Insert cell
lines = {
// prettier-ignore
const lines = [
new Line([-300, -50], [-200, -260]),
new Line([-200, -260], [-400, -280]),
new Line([-400, -280], [-300, -50]),
new Line([0, -200], [70, -180]),
new Line([70, -180], [140, -210]),
new Line([140, -210], [100, -250]),
new Line([100, -250], [0, -200]),
new Line([200, 300], [400, 80]),
new Line([400, 80], [450, 320]),
new Line([450, 320], [200, 300]),
new Line([-280, 180], [-350, 100]),
new Line([-350, 100], [-450, 80]),
new Line([-450, 80], [-400, 300]),
new Line([-400, 300], [-280, 180]),
new Line([50, 50], [180, -80]),
new Line([180, -80], [300, -90]),
new Line([300, -90], [250, 10]),
new Line([250, 10], [50,50])
// ...ball([-100,180,60]),
// ...ball([200, -80, 70])
];
return lines;
}
Insert cell
function ball([x, y, r], n = 15) {
const points = [];
for (let i = 0; i < n; ++i) {
const angle = i * (360 / n);
const sx = x + r * Math.cos(angle * (Math.PI / 180));
const sy = y + r * Math.sin(angle * (Math.PI / 180));
points.push([sx, sy]);
}

const res = [];
for (let i = 0; i < points.length; ++i) {
const l = new Line(points[i], points[(i + 1) % points.length]);
res.push(l);
}
return res;
}
Insert cell
mutable startY = 0
Insert cell
mutable startX = 0
Insert cell
canvas1.addEventListener('mousemove', e => {
mutable startX = e.offsetX - width / 2;
mutable startY = e.offsetY - height / 2;
})
Insert cell
canvas2.addEventListener('mousemove', e => {
mutable startX = e.offsetX - width / 2;
mutable startY = e.offsetY - height / 2;
})
Insert cell
canvas3.addEventListener('mousemove', e => {
mutable startX = e.offsetX - width / 2;
mutable startY = e.offsetY - height / 2;
})
Insert cell
height = width * 0.7
Insert cell
class Line {
constructor(start, end, color = "#555", r = 2) {
this.start = start;
this.r = r;
this.t = 1;
this.color = color;
this.direction = [end[0] - start[0], end[1] - start[1]];
this.showIntersection = color !== '#555';
this.intersectionP = [
start[0] + this.t * this.direction[0],
start[1] + this.t * this.direction[1]
];
}

intersection(ray) {
const { start, direction, intersectionP } = this;
const t2 =
(ray.direction[0] * (start[1] - ray.start[1]) +
ray.direction[1] * (ray.start[0] - start[0])) /
(direction[0] * ray.direction[1] - direction[1] * ray.direction[0]);
if (t2 < 0 || t2 > 1) return;

const t1 = (start[0] + direction[0] * t2 - ray.start[0]) / ray.direction[0];
if (t1 < 0 || t1 > 1) return;

const iP = [
ray.start[0] + t1 * ray.direction[0],
ray.start[1] + t1 * ray.direction[1]
];

// make sure to get the closest intersection
const d = Math.hypot(start[0] - iP[0], start[1] - iP[1]);
const oldD = Math.hypot(start[0] - intersectionP[0], start[1] - iP[1]);
if (d <= oldD) {
this.intersectionP = iP;
return;
}
}

draw(context) {
const {
start,
direction,
t,
r,
showIntersection,
intersectionP,
color
} = this;
context.strokeStyle = color;
context.fillStyle = color;

context.beginPath();
context.moveTo(...start);
context.lineTo(...intersectionP);
context.stroke();

if (showIntersection) {
context.beginPath();
context.arc(...intersectionP, r, 0, 2 * Math.PI, false);
context.fill();
}
return;
}
}
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