summaryrefslogtreecommitdiff
path: root/src/toys/fourier/js/script.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/toys/fourier/js/script.js')
-rw-r--r--src/toys/fourier/js/script.js294
1 files changed, 0 insertions, 294 deletions
diff --git a/src/toys/fourier/js/script.js b/src/toys/fourier/js/script.js
deleted file mode 100644
index 8d5af31..0000000
--- a/src/toys/fourier/js/script.js
+++ /dev/null
@@ -1,294 +0,0 @@
-const RENDER_TYPE = {
- LATEX: 1,
- FUNC: 2,
-};
-const THRESHOLD = 1e-12;
-const FONT = "Courier New";
-const FONT_HEIGHT_PX = 24;
-const DX = 4;
-
-const canvas = document.getElementById("canvas");
-const ctx = canvas.getContext("2d");
-let state = {};
-const initializeState = () => {
- const xLabels = [
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sept",
- "Oct",
- "Nov",
- "Dec",
- "Jan",
- ];
- const yLabels = ["Great", "Good", "Meh", "Bad", "Horrible"];
- return {
- width: canvas.parentElement.clientWidth,
- height: canvas.parentElement.clientHeight,
- xLabels,
- yLabels,
- yLabelPadding: 12,
- heights: Array(xLabels.length * DX - 1).fill(0),
- };
-};
-
-const dft = (heights, render = RENDER_TYPE.LATEX, threshold = THRESHOLD) => {
- const n = heights.length;
- return Array(n)
- .fill()
- .map((x, w) => {
- const rate = -2 * Math.PI * w;
- const s = heights.reduce(
- (a, x, i) => ({
- re: a.re + x * Math.cos((rate * i) / n),
- im: a.im + x * Math.sin((rate * i) / n),
- }),
- { re: 0, im: 0 }
- );
-
- Object.entries(s).forEach(
- ([key, value]) =>
- (s[key] = ((Math.abs(value) < threshold ? 0 : 1) * value) / n)
- );
-
- const amp = Math.sqrt(s.re * s.re + s.im * s.im);
- const phase = Math.atan2(s.im, s.re);
-
- switch (render) {
- case RENDER_TYPE.LATEX:
- return `${amp}\\cos\\left(${w}\\frac{${
- 2 * DX
- }\\pi}{${n}}x+${phase}\\right)`;
- case RENDER_TYPE.FUNC:
- return (t) => amp * Math.cos(w * t + phase);
- }
- });
-};
-
-const resizeCanvas = ({ width, height }) => {
- canvas.width = width;
- canvas.style.width = width;
- canvas.height = height;
- canvas.style.height = height;
-};
-
-const loop = () => {
- const stateChanges = Object.keys(state.diff);
- if (stateChanges.length > 0) {
- state = { ...state, ...state.diff };
- if (
- state.diff.width ||
- state.diff.height ||
- state.diff.xLabels ||
- state.diff.yLabels
- ) {
- resizeCanvas(state.diff);
- ctx.font = `${FONT_HEIGHT_PX}px ${FONT}`;
- state.maxYLabelWidth = state.yLabels.reduce(
- (a, label) => Math.max(ctx.measureText(label).width, a),
- -Infinity
- );
-
- state.gridBoxWidth =
- state.width - state.maxYLabelWidth - state.yLabelPadding;
- state.gridBoxHeight = state.height - 2.5 * FONT_HEIGHT_PX; // 2.5 to include bottom part of tall letters ("g", "y", etc.)
-
- state.topLeftGridPos = {
- x: state.maxYLabelWidth + state.yLabelPadding,
- y: FONT_HEIGHT_PX,
- };
-
- state.bottomRightGridPos = {
- x: state.topLeftGridPos.x + state.gridBoxWidth,
- y: state.topLeftGridPos.y + state.gridBoxHeight,
- };
- }
- if (state.diff.heights) drawDesmos();
- draw(state);
- state.diff = {};
- }
-
- requestAnimationFrame(loop);
-};
-
-const drawLine = (pos1, pos2) => {
- ctx.beginPath();
- ctx.moveTo(pos1.x, pos1.y);
- ctx.lineTo(pos2.x, pos2.y);
- ctx.stroke();
-};
-
-const drawDividers = (
- xDividers,
- yDividers,
- topLeftGridPos,
- bottomRightGridPos,
- yLabelPadding
-) => {
- ctx.font = `${FONT_HEIGHT_PX}px ${FONT}`;
- xDividers.forEach(({ label, position }) => {
- ctx.fillText(label, position.x - ctx.measureText(label).width, position.y);
- drawLine(
- { ...position, y: topLeftGridPos.y },
- { ...position, y: bottomRightGridPos.y }
- );
- });
- yDividers.forEach(({ label, position }) => {
- ctx.fillText(
- label,
- topLeftGridPos.x - yLabelPadding - ctx.measureText(label).width,
- position.y
- );
- drawLine(
- { ...position, x: topLeftGridPos.x },
- { ...position, x: bottomRightGridPos.x }
- );
- });
-};
-
-const draw = ({
- heights,
- gridBoxWidth,
- gridBoxHeight,
- topLeftGridPos,
- bottomRightGridPos,
- maxYLabelWidth,
- xLabels,
- yLabels,
- width,
- height,
-}) => {
- ctx.clearRect(0, 0, width, height);
-
- const xDividers = xLabels.map((label, i) => ({
- label,
- position: {
- x: topLeftGridPos.x + (gridBoxWidth / (xLabels.length - 1)) * i,
- y: bottomRightGridPos.y + FONT_HEIGHT_PX,
- },
- }));
-
- const yDividers = yLabels.map((label, i) => ({
- label,
- position: {
- x: 0,
- y: topLeftGridPos.y + (gridBoxHeight / (yLabels.length - 1)) * i,
- },
- }));
-
- drawDividers(xDividers, yDividers, topLeftGridPos, bottomRightGridPos, 12);
-
- const dx = gridBoxWidth / (DX * (xLabels.length - 1));
- const prevStrokeStyle = ctx.strokeStyle;
- ctx.strokeStyle = "red";
- for (let i = 0; i < heights.length; ++i) {
- const x = dx * i + topLeftGridPos.x;
- drawLine(
- { x, y: (gridBoxHeight / 2) * (1 - heights[i]) + topLeftGridPos.y },
- {
- x: x + dx,
- y: (gridBoxHeight / 2) * (1 - heights[i + 1]) + topLeftGridPos.y,
- }
- );
- }
- ctx.strokeStyle = prevStrokeStyle;
-};
-
-const calculator = Desmos.GraphingCalculator(
- document.getElementById("calculator"),
- {
- expressionsCollapsed: true,
- autosize: true,
- }
-);
-calculator.setMathBounds({
- left: -0.8,
- right: 12,
- bottom: -3,
- top: 3,
-});
-
-const drawDesmos = () => {
- const equations = dft(state.heights);
-
- calculator.setExpression({
- id: `graph-total`,
- latex: equations.map((_x, i) => `y_{${i}}`).join(" + "),
- });
- equations.forEach((x, i) =>
- calculator.setExpression({
- id: `graph${i}`,
- latex: `y_{${i}}=${x}`,
- hidden: true,
- })
- );
-};
-
-let isDown = false;
-canvas.addEventListener(
- "mousedown",
- (e) => {
- e.preventDefault();
- isDown = true;
- },
- true
-);
-
-canvas.addEventListener(
- "mouseup",
- (e) => {
- e.preventDefault();
- isDown = false;
- },
- true
-);
-
-canvas.addEventListener(
- "mousemove",
- (e) => {
- e.preventDefault();
- if (isDown) {
- const rect = canvas.getBoundingClientRect();
- const [x, y] = [e.clientX - rect.left, e.clientY - rect.top];
-
- const {
- topLeftGridPos,
- bottomRightGridPos,
- gridBoxWidth,
- gridBoxHeight,
- heights,
- xLabels,
- } = state;
- const delta = gridBoxWidth / (DX * (xLabels.length - 1));
-
- const bin = Math.min(
- Math.round(Math.max(x - topLeftGridPos.x, 0) / delta),
- heights.length - 1
- );
- heights[bin] = Math.min(
- Math.max(1 - (2 * (y - topLeftGridPos.y)) / gridBoxHeight, -1),
- 1
- );
- state.diff.heights = heights;
- }
- },
- true
-);
-
-window.addEventListener("resize", () => {
- state.diff = {
- ...state.diff,
- width: canvas.parentElement.clientWidth,
- height: canvas.parentElement.clientHeight,
- };
-});
-
-(() => {
- state.diff = initializeState();
- window.requestAnimationFrame(loop);
-})();