Public
Edited
Sep 23, 2023
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function applyRelu(x) {
return x < 0 ? 0 : x;
}
Insert cell
function discret(x, count=2) {
if (count === 2) {
return x >= 0 ? 1 : -1;
} else {
return x < -1/3 ? -1 : (x > 1/3 ? 1 : 0);
}
}
Insert cell
function drawScalarProduct(ctx, vec1Vals, rect1, vec2Vals, rect2, time, resWidth, resLabel) {
const rectDistance = rect2[1] - rect1[1];
const delta = rectDistance / 2 - rect1[3] * 0.5;
if (time <= 1) {
time = smooth(time);
let [x0a, y0a, wa, ha] = rect1;
let [x0b, y0b, wb, hb] = rect2;
y0a += time * 0;
y0b -= time * delta * 2;
ha *= (1 - time);
hb *= (1 - time);
drawVector(ctx, vec1Vals, [x0a, y0a, wa, ha], '', 1);
drawVector(ctx, vec2Vals, [x0b, y0b, wb, hb], '', 1);
const alpha = 1;
ctx.lineWidth = 4;
for (let i=0; i<vec1Vals.length; i++) {
const relI = i / vec1Vals.length;
const scalar = vec1Vals[i] * vec2Vals[i];
if (scalar < 0) continue;
const alpha = clamp(scalar ** 0.5 * (time * 2 + scalar - 1));
const color = getValueColor(mix(vec1Vals[i] + vec2Vals[i] / 2, 0, time));
ctx.strokeStyle = rgba(...color, alpha);
ctx.beginPath();
ctx.moveTo(x0a + wa * relI, y0a + ha);
ctx.lineTo(x0b + wb * relI, y0b);
ctx.stroke();
}
} else {
time = smooth(time - 1);
let [x0a, y0a, wa, ha] = rect1;
let [x0b, y0b, wb, hb] = rect2;
y0a += delta * 0;
y0b -= delta * 2;
const deltaW = wa / 2 * time * (1 - resWidth);
x0a += deltaW * 0;
x0b += deltaW * 0;
wa -= deltaW * 2;
wb -= deltaW * 2;
ha = 0;
hb = 0;
drawVector(ctx, vec1Vals, [x0a, y0a, wa, ha], '', 1);
drawVector(ctx, vec2Vals, [x0b, y0b, wb, hb], resLabel, 1);
ctx.lineWidth = 4;
for (let i=0; i<vec1Vals.length; i++) {
const relI = i / vec1Vals.length;
const scalar = vec1Vals[i] * vec2Vals[i];
const alpha = scalar < 0 ? mix(0, 1, (time - 0.9) * 10) : mix(scalar ** 0.5 * (scalar + 1), 1, time);
const color = getValueColor(0);
ctx.strokeStyle = rgba(...color, alpha);
ctx.beginPath();
ctx.moveTo(x0a + wa * relI, y0a + ha);
ctx.lineTo(x0b + wb * relI, y0b);
ctx.stroke();
}
}
}
Insert cell
function smooth(t) {
return (1 - Math.cos(clamp(t) * Math.PI)) / 2;
}
Insert cell
function drawRect(ctx, [x0, y0, w, h]) {
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
ctx.strokeRect(x0, y0, w, h);
}
Insert cell
function clamp(x) {
if (x < 0) return 0;
if (x > 1) return 1;
return x;
}
Insert cell
function getRandomVector(len=700) {
return new Array(len).fill(0).map(() => Math.random() * 2 - 1);
}
Insert cell
Insert cell
Insert cell
function mix(v0, v1, t) {
return v0 * (1 - t) + v1 * t;
}
Insert cell
function rgba(r, g, b, a=1) {
return `rgba(${r}, ${g}, ${b}, ${a})`;
}
Insert cell
function drawLinearTransform(ctx, [x0a, y0a, wa, ha], [x0b, y0b, wb, hb], time) {
const random = d3.randomUniform.source(d3.randomLcg(0))(0, 1);
ctx.lineWidth = 1;
ctx.globalCompositeOperation = 'screen';
const delta = 0.1;
const t = time % 1;
const localTemp = 0.5 + Math.sin(t * Math.PI * 4) * 0.2;
const jStart = t * (1 - delta);
const jEnd = clamp(jStart + delta);
for (let i=0; i<1; i+=0.0001) {
// const j = jStart + random() * (jEnd - jStart);
const j = random();
const r = random();
if (j < jStart || j > jEnd) continue;
const relJ = (j - jStart) / (jEnd - jStart);
const alpha = (1 - ((relJ - 0.5) * 2) ** 2) ** 1 * 0.5;
if (r < localTemp) {
ctx.strokeStyle = `rgba(255, 127, 0, ${alpha})`;
} else {
ctx.strokeStyle = `rgba(0, 127, 255, ${alpha})`;
}
ctx.beginPath();
ctx.moveTo(x0a + wa * i, y0a + ha);
ctx.lineTo(x0b + wb * j, y0b);
ctx.stroke();
}
ctx.globalCompositeOperation = 'source-over';
}
Insert cell
function mixColors([r1, g1, b1], [r2, g2, b2], t) {
t = clamp(t);
return [mix(r1, r2, t), mix(g1, g2, t), mix(b1, b2, t)];
}
Insert cell
function drawVector(ctx, values, [x0, y0, w, h], label, progress=1) {
progress = clamp(progress);
for (let i=0; i<values.length * progress; i++) {
const endRatio1 = progress === 1 ? 0 : clamp(1 - 10 * (Math.abs(i / values.length - progress) - 0.1));
const endRatio2 = progress === 1 ? 0 : clamp(1 - 10 * (Math.abs(i / values.length - progress) - 0.0));
if (values[i] === null) continue;
const color = getValueColor(values[i]);
const [r, g, b] = mixColors(color, [255, 255, 255], endRatio1);
// const r = mix(mix(255, 0, v), 255, endRatio1);
// const g = mix(127, 255, endRatio1);
// const b = mix(mix(0, 255, v), 255, endRatio1);
ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${1 - endRatio2})`;
ctx.fillRect(x0 + i / values.length * w, y0, w / values.length * 1.3, h);
}
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = '20px JetBrains Mono';
ctx.fillText(label, x0 + w / 2, y0 + h + 20);
ctx.strokeStyle = 'white';
ctx.lineWidth = 1;
ctx.strokeRect(x0, y0, w, h);
}
Insert cell
function getValueColor(v) {
const zeroRatio = (1 - Math.abs(v)) ** 1.5;
const blueOrangeRatio = 1 - (v + 1) / 2;
const r = mix(mix(255, 0, blueOrangeRatio), 255, zeroRatio);
const g = mix(127, 255, zeroRatio);
const b = mix(mix(0, 255, blueOrangeRatio), 255, zeroRatio);
return [r, g, b];
}
Insert cell
Insert cell
d3 = require('d3@6')
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