diff --git a/src/components/function_box.tsx b/src/components/function_box.tsx
new file mode 100644
index 0000000..3c65062
--- /dev/null
+++ b/src/components/function_box.tsx
@@ -0,0 +1,290 @@
+import {
+ Img,
+ Rect,
+ Node,
+ Video,
+ makeScene2D,
+ Txt,
+ Line,
+ LineSegment,
+ NodeProps,
+} from "@motion-canvas/2d";
+import {
+ Direction,
+ beginSlide,
+ createRef,
+ map,
+ slideTransition,
+ tween,
+ all,
+ waitFor,
+ range,
+ makeRef,
+} from "@motion-canvas/core";
+
+import { CodeBlock } from "@motion-canvas/2d/lib/components/CodeBlock";
+import { theme } from "../theme";
+
+import * as ts from "typescript";
+
+export interface FunctionBoxProps extends NodeProps {
+ source?: string;
+ fn?: Function;
+ padding?: number;
+ delta?: number;
+
+ workingText?: string;
+ idlingText?: string;
+
+ arity?: number;
+
+ isChild?: boolean;
+}
+
+type FunctionArgs = { node?: Node; val: any }[];
+
+/*
+
+
+
+ */
+
+export class FunctionBox extends Node {
+ private readonly source: string;
+ private readonly workingText: string;
+ private readonly idlingText: string;
+ private readonly function: any;
+ private readonly delta: number;
+ private readonly arity: number;
+ private readonly padding: number;
+
+ private readonly block = createRef();
+ private readonly node = createRef();
+ private readonly rect = createRef();
+
+ private readonly boxMoji = createRef();
+ private readonly inputSegments: Line[] = [];
+ private readonly inputs: Rect[] = [];
+ private readonly outputSegment = createRef();
+ private readonly output = createRef();
+ private readonly child = createRef();
+
+ private readonly isChild: boolean;
+
+ private currentArgs: FunctionArgs = [];
+
+ public constructor(props?: FunctionBoxProps) {
+ super({
+ ...props,
+ });
+
+ this.arity = props?.arity ?? 1;
+ if (props.fn) {
+ this.source = props.fn.toString();
+ this.function = props.fn;
+ } else {
+ this.source = props?.source ?? `(x: number): number => x + 2`;
+
+ const functionCode = ts.transpile(this.source);
+ this.function = eval(functionCode);
+ }
+
+ this.delta = props?.delta ?? 20;
+ this.padding = props?.padding ?? 100;
+
+ this.workingText = props?.workingText ?? "👷♀️⚙️";
+ this.idlingText = props?.idlingText ?? "😴";
+
+ this.isChild = props?.isChild ?? false;
+
+ this.add(
+
+
+ {range(this.arity).map((i) => (
+
+
+
+
+
+ ))}
+
+
+
+
+ {this.idlingText}
+
+
+
+
+
+
+
+
+
+ ,
+ );
+ }
+
+ public *resetInput(duration: number) {
+ yield* all(
+ ...this.inputs.map((x) =>
+ all(
+ x.opacity(0, duration),
+ x.height(0, duration),
+ x.width(0, duration),
+ ),
+ ),
+ ...this.inputSegments.map((segment) => segment.points([], duration)),
+ );
+ }
+
+ public *resetOutput(duration: number) {
+ yield* all(
+ this.output().opacity(0, duration),
+ this.outputSegment().points([], duration),
+ );
+ yield this.output().removeChildren();
+ }
+
+ public *reset(duration: number) {
+ yield* all(this.resetInput(duration), this.resetOutput(duration));
+ }
+
+ public *setInputs(args: FunctionArgs, duration: number) {
+ if (args.length != this.arity)
+ throw new Error("input length must equal function arity");
+ this.currentArgs = args;
+
+ this.inputs.forEach((input, i) => {
+ input.removeChildren();
+ input.add(
+ args[i].node ?? (
+
+ {args[i].val.toString()}
+
+ ),
+ );
+ });
+ yield* all(
+ ...this.inputSegments.map((segment) =>
+ segment.points(
+ [
+ { x: -this.padding, y: 0 },
+ { x: -this.delta, y: 0 },
+ ],
+ duration,
+ ),
+ ),
+ ...this.inputs.map((input) =>
+ all(
+ input.height(40, duration),
+ input.width(40, duration),
+ input.opacity(1, duration),
+ ),
+ ),
+ );
+ }
+
+ public *propogateInput(duration: number) {
+ const opacityChangeDuration = 0.1;
+
+ yield* all(
+ ...this.inputSegments.map((segment) =>
+ segment.opacity(0.2, opacityChangeDuration),
+ ),
+ );
+
+ yield* all(
+ ...this.inputSegments.map((segment) => segment.points([], duration)),
+ );
+
+ yield* all(
+ ...this.inputSegments.map((segment) =>
+ segment.opacity(1, opacityChangeDuration),
+ ),
+ ...this.inputs.map((input) => input.opacity(0, opacityChangeDuration)),
+ this.boxMoji().text(this.workingText, duration),
+ );
+ }
+
+ public *propogateOutput(duration: number) {
+ const opacityChangeDuration = 0.1;
+
+ const output = this.function(...this.currentArgs.map((input) => input.val));
+ if (typeof output === "function") {
+ yield this.output().add(
+ ,
+ );
+ } else {
+ yield this.output().add(
+
+ {output.toString()}
+ ,
+ );
+ }
+
+ yield* all(
+ this.boxMoji().text(this.idlingText, duration),
+ this.outputSegment().points(
+ [
+ { x: -this.delta, y: 0 },
+ { x: this.padding, y: 0 },
+ ],
+ duration,
+ ),
+ this.child()?.opacity(1, duration),
+ this.output().opacity(1, duration),
+ this.outputSegment().opacity(1, duration),
+ );
+ }
+}
diff --git a/src/scenes/doctor.tsx b/src/scenes/doctor.tsx
index 642b83e..4b6c483 100644
--- a/src/scenes/doctor.tsx
+++ b/src/scenes/doctor.tsx
@@ -1,10 +1,9 @@
-import { Txt, makeScene2D } from "@motion-canvas/2d";
+import { makeScene2D } from "@motion-canvas/2d";
import {
Direction,
beginSlide,
createRef,
slideTransition,
- waitFor,
} from "@motion-canvas/core";
import { CodeBlock, insert } from "@motion-canvas/2d/lib/components/CodeBlock";
import { theme } from "../theme";
diff --git a/src/scenes/first_box.tsx b/src/scenes/first_box.tsx
index 4927157..5508a41 100644
--- a/src/scenes/first_box.tsx
+++ b/src/scenes/first_box.tsx
@@ -1,156 +1,45 @@
-import {
- Img,
- Rect,
- Node,
- Video,
- makeScene2D,
- Txt,
- Line,
- LineSegment,
-} from "@motion-canvas/2d";
+import { makeScene2D } from "@motion-canvas/2d";
import {
Direction,
beginSlide,
createRef,
- map,
slideTransition,
- tween,
all,
waitFor,
} from "@motion-canvas/core";
-import { CodeBlock } from "@motion-canvas/2d/lib/components/CodeBlock";
-import { theme } from "../theme";
+import { FunctionBox } from "../components/function_box";
-const fibonacciFn = (n: number): number => {
- if (n <= 2) {
- return 1;
- }
- return fibonacciFn(n - 1) + fibonacciFn(n - 2);
-};
-
-const fibonacci = `
-const fibonacci = (n: number): number => {
- if (n <= 2) {
- return 1;
- }
- return fibonacci(n - 1) + fibonacci(n - 2);
-};
-`
- .split("\n")
- .filter((x) => x.trim())
- .join("\n");
+const add = `(a: number, b: number) => {
+ return a + b;
+}`;
export default makeScene2D(function* (view) {
- const block = createRef();
- const node = createRef();
- const rect = createRef();
+ const functionBox = createRef();
- const boxMoji = createRef();
- const inSegment = createRef();
- const outSegment = createRef();
- const input = createRef();
- const output = createRef();
+ view.add();
- yield* view.add(
- <>
-
-
-
-
-
-
- 😴
-
-
-
-
-
- >,
- );
-
- yield boxMoji().position(rect().middle());
yield* slideTransition(Direction.Left);
yield* beginSlide("Black Box");
- const padding = 100;
- const left = rect().left();
- const right = rect().right();
- yield* all(
- inSegment().points([left.addX(-padding), left], 0.5),
- outSegment().points([right, right.addX(padding)], 0.5),
- );
- yield input().position(left.addX(-padding));
- yield output().position(right);
- for (const i of [1, 2, 3]) {
- yield* all(
- input().opacity(1, 0.5),
- input().text(i.toString(), 0.5),
- input().position(left.addX(-padding - 20), 0.5),
- );
+ for (const [a, b] of [
+ [-1, 2],
+ [3, 4],
+ [5, 6],
+ ] as [number, number][]) {
+ const inputId = "(" + [a, b].join(",") + ")";
- yield* beginSlide("Input " + i);
+ yield* all(functionBox().reset(0.25));
+ yield* functionBox().setInputs([{ val: a }, { val: b }], 0.5);
- yield* input().position(left, 0.5);
- yield* all(input().opacity(0, 0.2), boxMoji().text("👷♀️⚙️", 0.2));
- yield* waitFor(0.5);
+ yield* beginSlide("Add Inputs " + inputId);
- const result = fibonacciFn(i);
- yield* all(
- output().opacity(1, 0.5),
- output().text(result.toString(), 0.5),
- output().position(right, 0.5),
- );
+ yield* functionBox().propogateInput(0.5);
+ yield* waitFor(0.3);
+ yield* functionBox().propogateOutput(0.5);
- yield* all(boxMoji().text("😴", 0.2));
-
- yield* beginSlide("Output " + i);
+ yield* beginSlide("Propogate Outputs of " + inputId);
+ yield* beginSlide("Propogate Outputs of 1" + inputId);
}
-
- yield* all(
- boxMoji().opacity(0, 0.2),
- block().fontSize(30, 1),
- node().opacity(1, 1),
- );
-
- yield* beginSlide("Revealing");
});