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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more