add function box initial component
This commit is contained in:
parent
6a3ce850c5
commit
93dd6c53f1
290
src/components/function_box.tsx
Normal file
290
src/components/function_box.tsx
Normal file
@ -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 }[];
|
||||||
|
|
||||||
|
/*
|
||||||
|
<Node ref={this.node} opacity={0}>
|
||||||
|
<CodeBlock
|
||||||
|
fontFamily={theme.font}
|
||||||
|
language="typescript"
|
||||||
|
ref={this.block}
|
||||||
|
fontSize={1}
|
||||||
|
code={this.source}
|
||||||
|
></CodeBlock>
|
||||||
|
</Node>
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<CodeBlock>();
|
||||||
|
private readonly node = createRef<Node>();
|
||||||
|
private readonly rect = createRef<Rect>();
|
||||||
|
|
||||||
|
private readonly boxMoji = createRef<Txt>();
|
||||||
|
private readonly inputSegments: Line[] = [];
|
||||||
|
private readonly inputs: Rect[] = [];
|
||||||
|
private readonly outputSegment = createRef<Line>();
|
||||||
|
private readonly output = createRef<Node>();
|
||||||
|
private readonly child = createRef<FunctionBox>();
|
||||||
|
|
||||||
|
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(
|
||||||
|
<Rect
|
||||||
|
opacity={this.opacity}
|
||||||
|
direction={"row"}
|
||||||
|
alignItems={"center"}
|
||||||
|
layout
|
||||||
|
>
|
||||||
|
<Rect direction={"column"} alignItems={"end"}>
|
||||||
|
{range(this.arity).map((i) => (
|
||||||
|
<Rect direction={"row"} alignItems={"center"} gap={10}>
|
||||||
|
<Rect
|
||||||
|
direction={"row"}
|
||||||
|
ref={makeRef(this.inputs, i)}
|
||||||
|
justifyContent={"end"}
|
||||||
|
opacity={1}
|
||||||
|
></Rect>
|
||||||
|
|
||||||
|
<Line
|
||||||
|
points={[]}
|
||||||
|
stroke={theme.green.hex}
|
||||||
|
ref={makeRef(this.inputSegments, i)}
|
||||||
|
lineWidth={5}
|
||||||
|
arrowSize={10}
|
||||||
|
endArrow
|
||||||
|
></Line>
|
||||||
|
</Rect>
|
||||||
|
))}
|
||||||
|
</Rect>
|
||||||
|
|
||||||
|
<Rect
|
||||||
|
ref={this.rect}
|
||||||
|
radius={4}
|
||||||
|
stroke={theme.overlay0.hex}
|
||||||
|
fill={theme.crust.hex}
|
||||||
|
lineWidth={4}
|
||||||
|
padding={60}
|
||||||
|
direction={"row"}
|
||||||
|
height={"100%"}
|
||||||
|
gap={40}
|
||||||
|
>
|
||||||
|
<Txt fontFamily={theme.font} fill={theme.text.hex} ref={this.boxMoji}>
|
||||||
|
{this.idlingText}
|
||||||
|
</Txt>
|
||||||
|
</Rect>
|
||||||
|
|
||||||
|
<Rect direction={"column"} height={"100%"} alignItems={"end"}>
|
||||||
|
<Rect direction={"row"} alignItems={"center"} gap={10}>
|
||||||
|
<Line
|
||||||
|
points={[]}
|
||||||
|
stroke={theme.red.hex}
|
||||||
|
lineWidth={5}
|
||||||
|
arrowSize={10}
|
||||||
|
ref={this.outputSegment}
|
||||||
|
endArrow
|
||||||
|
></Line>
|
||||||
|
<Rect
|
||||||
|
direction={"row"}
|
||||||
|
ref={this.output}
|
||||||
|
justifyContent={"end"}
|
||||||
|
opacity={1}
|
||||||
|
></Rect>
|
||||||
|
</Rect>
|
||||||
|
</Rect>
|
||||||
|
</Rect>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ?? (
|
||||||
|
<Txt fontFamily={theme.font} fontSize={30} fill={theme.text.hex}>
|
||||||
|
{args[i].val.toString()}
|
||||||
|
</Txt>
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
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(
|
||||||
|
<FunctionBox
|
||||||
|
opacity={0}
|
||||||
|
isChild={true}
|
||||||
|
ref={this.child}
|
||||||
|
fn={output}
|
||||||
|
></FunctionBox>,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
yield this.output().add(
|
||||||
|
<Txt fontFamily={theme.font} fontSize={30} fill={theme.text.hex}>
|
||||||
|
{output.toString()}
|
||||||
|
</Txt>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
import { Txt, makeScene2D } from "@motion-canvas/2d";
|
import { makeScene2D } from "@motion-canvas/2d";
|
||||||
import {
|
import {
|
||||||
Direction,
|
Direction,
|
||||||
beginSlide,
|
beginSlide,
|
||||||
createRef,
|
createRef,
|
||||||
slideTransition,
|
slideTransition,
|
||||||
waitFor,
|
|
||||||
} from "@motion-canvas/core";
|
} from "@motion-canvas/core";
|
||||||
import { CodeBlock, insert } from "@motion-canvas/2d/lib/components/CodeBlock";
|
import { CodeBlock, insert } from "@motion-canvas/2d/lib/components/CodeBlock";
|
||||||
import { theme } from "../theme";
|
import { theme } from "../theme";
|
||||||
|
@ -1,156 +1,45 @@
|
|||||||
import {
|
import { makeScene2D } from "@motion-canvas/2d";
|
||||||
Img,
|
|
||||||
Rect,
|
|
||||||
Node,
|
|
||||||
Video,
|
|
||||||
makeScene2D,
|
|
||||||
Txt,
|
|
||||||
Line,
|
|
||||||
LineSegment,
|
|
||||||
} from "@motion-canvas/2d";
|
|
||||||
import {
|
import {
|
||||||
Direction,
|
Direction,
|
||||||
beginSlide,
|
beginSlide,
|
||||||
createRef,
|
createRef,
|
||||||
map,
|
|
||||||
slideTransition,
|
slideTransition,
|
||||||
tween,
|
|
||||||
all,
|
all,
|
||||||
waitFor,
|
waitFor,
|
||||||
} from "@motion-canvas/core";
|
} from "@motion-canvas/core";
|
||||||
|
|
||||||
import { CodeBlock } from "@motion-canvas/2d/lib/components/CodeBlock";
|
import { FunctionBox } from "../components/function_box";
|
||||||
import { theme } from "../theme";
|
|
||||||
|
|
||||||
const fibonacciFn = (n: number): number => {
|
const add = `(a: number, b: number) => {
|
||||||
if (n <= 2) {
|
return a + b;
|
||||||
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");
|
|
||||||
|
|
||||||
export default makeScene2D(function* (view) {
|
export default makeScene2D(function* (view) {
|
||||||
const block = createRef<CodeBlock>();
|
const functionBox = createRef<FunctionBox>();
|
||||||
const node = createRef<Node>();
|
|
||||||
const rect = createRef<Rect>();
|
|
||||||
|
|
||||||
const boxMoji = createRef<Txt>();
|
view.add(<FunctionBox arity={2} source={add} ref={functionBox} />);
|
||||||
const inSegment = createRef<Line>();
|
|
||||||
const outSegment = createRef<Line>();
|
|
||||||
const input = createRef<Txt>();
|
|
||||||
const output = createRef<Txt>();
|
|
||||||
|
|
||||||
yield* view.add(
|
|
||||||
<>
|
|
||||||
<Rect
|
|
||||||
ref={rect}
|
|
||||||
radius={4}
|
|
||||||
stroke={theme.overlay0.hex}
|
|
||||||
fill={theme.crust.hex}
|
|
||||||
lineWidth={4}
|
|
||||||
padding={60}
|
|
||||||
layout
|
|
||||||
>
|
|
||||||
<Node ref={node} opacity={0}>
|
|
||||||
<CodeBlock
|
|
||||||
fontFamily={theme.font}
|
|
||||||
language="typescript"
|
|
||||||
ref={block}
|
|
||||||
fontSize={1}
|
|
||||||
code={fibonacci}
|
|
||||||
></CodeBlock>
|
|
||||||
</Node>
|
|
||||||
</Rect>
|
|
||||||
<Txt fontFamily={theme.font} fill={theme.text.hex} ref={boxMoji}>
|
|
||||||
😴
|
|
||||||
</Txt>
|
|
||||||
<Line
|
|
||||||
points={[]}
|
|
||||||
ref={inSegment}
|
|
||||||
stroke={theme.green.hex}
|
|
||||||
lineWidth={8}
|
|
||||||
radius={40}
|
|
||||||
endArrow
|
|
||||||
></Line>
|
|
||||||
<Line
|
|
||||||
points={[]}
|
|
||||||
ref={outSegment}
|
|
||||||
stroke={theme.red.hex}
|
|
||||||
lineWidth={8}
|
|
||||||
radius={40}
|
|
||||||
endArrow
|
|
||||||
></Line>
|
|
||||||
<Txt
|
|
||||||
opacity={0}
|
|
||||||
fontFamily={theme.font}
|
|
||||||
fill={theme.text.hex}
|
|
||||||
ref={input}
|
|
||||||
></Txt>
|
|
||||||
<Txt
|
|
||||||
opacity={0}
|
|
||||||
fontFamily={theme.font}
|
|
||||||
fill={theme.text.hex}
|
|
||||||
ref={output}
|
|
||||||
></Txt>
|
|
||||||
</>,
|
|
||||||
);
|
|
||||||
|
|
||||||
yield boxMoji().position(rect().middle());
|
|
||||||
yield* slideTransition(Direction.Left);
|
yield* slideTransition(Direction.Left);
|
||||||
|
|
||||||
yield* beginSlide("Black Box");
|
yield* beginSlide("Black Box");
|
||||||
|
|
||||||
const padding = 100;
|
for (const [a, b] of [
|
||||||
const left = rect().left();
|
[-1, 2],
|
||||||
const right = rect().right();
|
[3, 4],
|
||||||
yield* all(
|
[5, 6],
|
||||||
inSegment().points([left.addX(-padding), left], 0.5),
|
] as [number, number][]) {
|
||||||
outSegment().points([right, right.addX(padding)], 0.5),
|
const inputId = "(" + [a, b].join(",") + ")";
|
||||||
);
|
|
||||||
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),
|
|
||||||
);
|
|
||||||
|
|
||||||
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* beginSlide("Add Inputs " + inputId);
|
||||||
yield* all(input().opacity(0, 0.2), boxMoji().text("👷♀️⚙️", 0.2));
|
|
||||||
yield* waitFor(0.5);
|
|
||||||
|
|
||||||
const result = fibonacciFn(i);
|
yield* functionBox().propogateInput(0.5);
|
||||||
yield* all(
|
yield* waitFor(0.3);
|
||||||
output().opacity(1, 0.5),
|
yield* functionBox().propogateOutput(0.5);
|
||||||
output().text(result.toString(), 0.5),
|
|
||||||
output().position(right, 0.5),
|
|
||||||
);
|
|
||||||
|
|
||||||
yield* all(boxMoji().text("😴", 0.2));
|
yield* beginSlide("Propogate Outputs of " + inputId);
|
||||||
|
yield* beginSlide("Propogate Outputs of 1" + inputId);
|
||||||
yield* beginSlide("Output " + i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
yield* all(
|
|
||||||
boxMoji().opacity(0, 0.2),
|
|
||||||
block().fontSize(30, 1),
|
|
||||||
node().opacity(1, 1),
|
|
||||||
);
|
|
||||||
|
|
||||||
yield* beginSlide("Revealing");
|
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user