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 {
|
||||
Direction,
|
||||
beginSlide,
|
||||
createRef,
|
||||
slideTransition,
|
||||
waitFor,
|
||||
} from "@motion-canvas/core";
|
||||
import { CodeBlock, insert } from "@motion-canvas/2d/lib/components/CodeBlock";
|
||||
import { theme } from "../theme";
|
||||
|
@ -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<CodeBlock>();
|
||||
const node = createRef<Node>();
|
||||
const rect = createRef<Rect>();
|
||||
const functionBox = createRef<FunctionBox>();
|
||||
|
||||
const boxMoji = createRef<Txt>();
|
||||
const inSegment = createRef<Line>();
|
||||
const outSegment = createRef<Line>();
|
||||
const input = createRef<Txt>();
|
||||
const output = createRef<Txt>();
|
||||
view.add(<FunctionBox arity={2} source={add} ref={functionBox} />);
|
||||
|
||||
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* 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");
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user