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),
);
}
}