summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/function_box.tsx6
-rw-r--r--src/components/person.tsx105
-rw-r--r--src/scenes/birthday_letters.meta5
-rw-r--r--src/scenes/birthday_letters.tsx213
-rw-r--r--src/scenes/index.ts10
-rw-r--r--src/scenes/sad_people.meta5
-rw-r--r--src/scenes/sad_people.tsx99
-rw-r--r--src/scenes/valentines_letters.meta5
-rw-r--r--src/scenes/valentines_letters.tsx122
9 files changed, 565 insertions, 5 deletions
diff --git a/src/components/function_box.tsx b/src/components/function_box.tsx
index d116a47..b2221b4 100644
--- a/src/components/function_box.tsx
+++ b/src/components/function_box.tsx
@@ -20,7 +20,7 @@ export interface FunctionBoxProps extends NodeProps {
arity?: number;
- isChild?: boolean;
+ isChild?: boolean; // todo: attach DNA
}
type FunctionArgs = { node?: Node; val: any }[];
@@ -59,7 +59,7 @@ export class FunctionBox extends Node {
this.arity = props?.arity ?? 1;
if (props.fn) {
- this.source = props.fn.toString();
+ this.source = props.source ?? props.fn.toString();
this.function = props.fn;
} else {
this.source = props?.source ?? `(x: number): number => x + 2`;
@@ -273,7 +273,7 @@ export class FunctionBox extends Node {
<CodeBlock
fontFamily={theme.font}
language="typescript"
- fontSize={15}
+ fontSize={this.outputFontSize}
code={JSON.stringify(output, null, 2)}
></CodeBlock>,
);
diff --git a/src/components/person.tsx b/src/components/person.tsx
new file mode 100644
index 0000000..35aa475
--- /dev/null
+++ b/src/components/person.tsx
@@ -0,0 +1,105 @@
+import { Layout, Node, NodeProps, SVG, Txt } from "@motion-canvas/2d";
+import { theme } from "../theme";
+import { all, createRef, waitFor } from "@motion-canvas/core";
+
+export const PEOPLE: PersonI[] = [
+ {
+ name: "Alan Turing",
+ birthday: new Date("06/23/1912"),
+ color: theme.green.hex,
+ },
+ {
+ name: "Grace Hopper",
+ birthday: new Date("12/09/1906"),
+ color: theme.flamingo.hex,
+ },
+ {
+ name: "Edsger Dijkstra",
+ birthday: new Date("07/11/1930"),
+ color: theme.red.hex,
+ },
+ {
+ name: "Alonzo Church",
+ birthday: new Date("06/14/1912"),
+ color: theme.sapphire.hex,
+ },
+ {
+ name: "Margaret Hamilton",
+ birthday: new Date("12/09/1902"),
+ color: theme.yellow.hex,
+ },
+];
+
+const profileSrc = (color: string) => `
+<?xml version="1.0" encoding="utf-8"?>
+<svg width="800px" height="800px" viewBox="0 0 72 72" id="emoji" xmlns="http://www.w3.org/2000/svg">
+ <g id="color">
+<path fill="${color}" stroke="none" d="M58,61c0,0,0-3-1-7c-1.2109-4.8457-4-8-10-8c-5,0-15,0-22,0c-6,0-8.7891,3.1543-10,8c-1,4-1,7-1,7H58z"/>
+<path fill="${color}" stroke="none" d="M26,26c0,3.7246,0.5391,7.8086,2,10c1.8613,2.793,5.0176,4,8,4c3.0957,0,6.1367-1.207,8-4 c1.46-2.1914,2-6.2754,2-10c0-2.7935-1-12-10-12S26,21.3442,26,26z"/>
+ </g>
+ <g id="line">
+ <path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M58,60c0,0,0-2-1-6 c-1.2109-4.8457-4-8-10-8c-5,0-15,0-22,0c-6,0-8.7891,3.1543-10,8c-1,4-1,6-1,6"/>
+ <path fill="none" stroke="#000000" stroke-linejoin="round" stroke-width="2" d="M26,26c0,3.7246,0.5391,7.8086,2,10 c1.8613,2.793,5.0176,4,8,4c3.0957,0,6.1367-1.207,8-4c1.46-2.1914,2-6.2754,2-10c0-2.7935-1-12-10-12S26,21.3442,26,26z"/>
+ </g>
+</svg>`;
+
+export interface PersonI {
+ name: string;
+ birthday: Date;
+ color: string;
+}
+
+export interface PersonProps extends NodeProps {
+ person: PersonI;
+
+ width?: number;
+ height?: number;
+}
+
+export class Person extends Node {
+ private readonly svg = createRef<SVG>();
+
+ public constructor(props?: PersonProps) {
+ super({ ...props });
+
+ this.add(
+ <Layout direction="column" alignItems="center" layout>
+ <SVG
+ ref={this.svg}
+ svg={profileSrc(props.person.color)}
+ fill="green"
+ width={props.width ?? 150}
+ height={props.height ?? 150}
+ ></SVG>
+ <Txt fontSize={20} fontFamily={theme.font} fill={theme.text.hex}>
+ {props.person.name}
+ </Txt>
+ </Layout>,
+ );
+ }
+
+ public *emit(text: string, duration: number, cleanUp = true) {
+ const ref = createRef<Txt>();
+
+ this.insert(
+ <Txt
+ position={this.svg().bottomRight()}
+ ref={ref}
+ fontSize={0}
+ opacity={0}
+ fontFamily={theme.font}
+ fill={theme.text.hex}
+ >
+ {text}
+ </Txt>,
+ 0,
+ );
+
+ yield* all(ref().fontSize(40, duration), ref().opacity(1, duration));
+ if (cleanUp) {
+ yield* waitFor(duration);
+ yield* all(ref().fontSize(0, duration), ref().opacity(0, duration));
+ yield ref().remove();
+ }
+ }
+}
diff --git a/src/scenes/birthday_letters.meta b/src/scenes/birthday_letters.meta
new file mode 100644
index 0000000..628ca8d
--- /dev/null
+++ b/src/scenes/birthday_letters.meta
@@ -0,0 +1,5 @@
+{
+ "version": 0,
+ "timeEvents": [],
+ "seed": 1594477027
+} \ No newline at end of file
diff --git a/src/scenes/birthday_letters.tsx b/src/scenes/birthday_letters.tsx
new file mode 100644
index 0000000..2a63360
--- /dev/null
+++ b/src/scenes/birthday_letters.tsx
@@ -0,0 +1,213 @@
+import { Layout, Txt, makeScene2D } from "@motion-canvas/2d";
+import {
+ Direction,
+ all,
+ beginSlide,
+ createRef,
+ makeRef,
+ slideTransition,
+ waitFor,
+} from "@motion-canvas/core";
+
+import { FunctionBox } from "../components/function_box";
+import { theme } from "../theme";
+import { PEOPLE, Person, PersonI } from "../components/person";
+
+const daysUntilNextBirthday = (birthDate: Date): number => {
+ const today = new Date();
+
+ const nextBirthday = new Date(
+ today.getFullYear(),
+ birthDate.getMonth(),
+ birthDate.getDate(),
+ );
+ if (today > nextBirthday) nextBirthday.setFullYear(today.getFullYear() + 1);
+
+ const timeDiff = nextBirthday.getTime() - today.getTime();
+ return Math.ceil(timeDiff / (1000 * 3600 * 24));
+};
+
+export interface CardI {
+ message: string;
+ deliverInDays: number;
+}
+
+export const birthdayCardsFor = (people: PersonI[]): CardI[] => {
+ const cards: CardI[] = [];
+
+ for (const person of people) {
+ const age = new Date().getFullYear() - person.birthday.getFullYear();
+ const ending =
+ ({ 1: "st", 2: "nd", 3: "rd" } as Record<number, string>)[
+ parseInt(Array.from(age.toString()).at(-1))
+ ] ?? "th";
+ const message =
+ `Happy ${age}${ending} birthday, ${person.name}!\n` +
+ "I can't believe it's already been a year!";
+
+ const deliverInDays = daysUntilNextBirthday(person.birthday);
+ cards.push({ deliverInDays, message });
+ }
+
+ return cards;
+};
+
+const birthdayCardsSrc = `export interface CardI {
+ message: string;
+ deliverInDays: number;
+}
+
+const birthdayCardsFor = (people: PersonI[]): CardI[] => {
+ const cards: CardI[] = [];
+
+ for (const person of people) {
+ const age = new Date().getFullYear() - person.birthday.getFullYear();
+ const ageEnding = (
+ { 1: "st", 2: "nd", 3: "rd" } as Record<number, string>
+ )[parseInt(Array.from(age.toString()).at(-1))] ?? "th";
+
+ const message =
+ \`Happy \${age}\${ageEnding} birthday, \${person.name}!\\n\` +
+ "I can't believe it's already been a year!";
+
+ const deliverInDays = daysUntilNextBirthday(person.birthday);
+ cards.push({ deliverInDays, message });
+ }
+
+ return cards;
+};`;
+
+export default makeScene2D(function* (view) {
+ const layout = createRef<Layout>();
+ const date = createRef<Txt>();
+ const functionBox = createRef<FunctionBox>();
+ const people: Person[] = [];
+ const peopleLayout: Layout[] = [];
+ const peopleText: Txt[] = [];
+
+ view.add(
+ <FunctionBox
+ ref={functionBox}
+ source={birthdayCardsSrc}
+ fn={birthdayCardsFor}
+ workingText="📝⚙"
+ outputFontSize={25}
+ ></FunctionBox>,
+ );
+
+ yield* all(slideTransition(Direction.Left), functionBox().showCode(0.75));
+ yield* functionBox().reset(0.1);
+
+ yield* beginSlide("Show code");
+
+ yield* functionBox().reset(0.1);
+ yield* all(
+ functionBox().hideCode(0.8),
+ functionBox().setInputs(
+ [
+ {
+ val: PEOPLE,
+ node: (
+ <Layout direction="column" gap={5} layout>
+ {PEOPLE.map((person) => (
+ <Person person={person} />
+ ))}
+ </Layout>
+ ),
+ },
+ ],
+ 0.8,
+ ),
+ );
+
+ yield* beginSlide("Show people");
+
+ yield* functionBox().propogateInput(0.6);
+ yield* functionBox().propogateOutput(0.6);
+ yield* beginSlide("Generate birthday cards");
+
+ yield* functionBox().opacity(0, 0.5);
+ functionBox().remove();
+
+ view.add(
+ <Layout opacity={0} ref={layout} direction="column" gap={15} layout>
+ <Txt
+ fontSize={30}
+ fontFamily={theme.font}
+ fill={theme.text.hex}
+ ref={date}
+ />
+
+ <Layout direction="row" gap={15}>
+ {PEOPLE.map((person, i) => (
+ <Layout
+ ref={makeRef(peopleLayout, i)}
+ alignItems="center"
+ direction="column"
+ gap={100}
+ >
+ <Person ref={makeRef(people, i)} person={person} />
+ <Txt
+ textAlign="center"
+ ref={makeRef(peopleText, i)}
+ fontSize={0}
+ fontFamily={theme.font}
+ fill={theme.text.hex}
+ />
+ </Layout>
+ ))}
+ </Layout>
+ </Layout>,
+ );
+ yield* layout().opacity(1, 0.5);
+
+ const cards = birthdayCardsFor(PEOPLE);
+
+ for (let i = 1; i <= 365 + 1; i++) {
+ const day = new Date(Date.now() + i * 24 * 60 * 60 * 1000);
+
+ yield* waitFor(0.02);
+ yield date().text(day.toDateString());
+
+ const peoplesBirthday = cards
+ .map((x, i) => [x, i] as [CardI, number])
+ .filter(([{ deliverInDays }]) => deliverInDays === i)
+ .map(([_, i]) => i);
+
+ yield* all(
+ ...peoplesBirthday.map((p) => {
+ const text = peopleText[p];
+ return all(
+ text.text("🗓️🎂\n" + cards[p].message, 0),
+ text.fontSize(20, 0.5),
+ );
+ }),
+ );
+
+ yield* all(
+ ...peoplesBirthday.map((p) => {
+ const layout = peopleLayout[p];
+ return layout.gap(0, 0.5);
+ }),
+ );
+
+ yield* all(
+ ...peoplesBirthday.map((p) => {
+ const person = people[p];
+ const text = peopleText[p];
+ return all(
+ text.opacity(0, 0.5),
+ text.fontSize(0, 0.5),
+ person.emit("🥳", 0.8, false),
+ );
+ }),
+ );
+ }
+
+ yield* beginSlide("See their reactions");
+ yield* all(
+ date().fontSize(0, 0.5),
+ date().opacity(0, 0.65),
+ ...people.map((person) => person.opacity(0, 0.5)),
+ );
+});
diff --git a/src/scenes/index.ts b/src/scenes/index.ts
index 8eab357..8b53e09 100644
--- a/src/scenes/index.ts
+++ b/src/scenes/index.ts
@@ -6,6 +6,9 @@ import doctor from "./doctor?scene";
import first_box from "./first_box?scene";
import hungry_partner from "./hungry_partner?scene";
import pure_functions from "./pure_functions?scene";
+import valentines_letters from "./valentines_letters?scene";
+import birthday_letters from "./birthday_letters?scene";
+import sad_people from "./sad_people?scene";
export const scenes = [
//title,
@@ -14,6 +17,9 @@ export const scenes = [
//flirtingwithfunctions,
//doctor,
//first_box,
- hungry_partner,
- pure_functions,
+ //hungry_partner,
+ //pure_functions,
+ //valentines_letters,
+ sad_people,
+ birthday_letters,
];
diff --git a/src/scenes/sad_people.meta b/src/scenes/sad_people.meta
new file mode 100644
index 0000000..37c43fe
--- /dev/null
+++ b/src/scenes/sad_people.meta
@@ -0,0 +1,5 @@
+{
+ "version": 0,
+ "timeEvents": [],
+ "seed": 867976792
+} \ No newline at end of file
diff --git a/src/scenes/sad_people.tsx b/src/scenes/sad_people.tsx
new file mode 100644
index 0000000..ccc0479
--- /dev/null
+++ b/src/scenes/sad_people.tsx
@@ -0,0 +1,99 @@
+import { Layout, Txt, makeScene2D } from "@motion-canvas/2d";
+import {
+ Direction,
+ all,
+ beginSlide,
+ createRef,
+ makeRef,
+ slideTransition,
+ waitFor,
+} from "@motion-canvas/core";
+import { theme } from "../theme";
+import { PEOPLE, Person } from "../components/person";
+import { CardI, birthdayCardsFor } from "./birthday_letters";
+
+export default makeScene2D(function* (view) {
+ const date = createRef<Txt>();
+ const people: Person[] = [];
+ const peopleLayout: Layout[] = [];
+ const peopleText: Txt[] = [];
+
+ view.add(
+ <Layout direction="column" gap={15} layout>
+ <Txt
+ fontSize={30}
+ fontFamily={theme.font}
+ fill={theme.text.hex}
+ ref={date}
+ />
+
+ <Layout direction="row" gap={15}>
+ {PEOPLE.map((person, i) => (
+ <Layout
+ ref={makeRef(peopleLayout, i)}
+ alignItems="center"
+ direction="column"
+ gap={100}
+ >
+ <Person ref={makeRef(people, i)} person={person} />
+ <Txt
+ textAlign="center"
+ ref={makeRef(peopleText, i)}
+ fontSize={0}
+ fontFamily={theme.font}
+ fill={theme.text.hex}
+ />
+ </Layout>
+ ))}
+ </Layout>
+ </Layout>,
+ );
+ yield* all(slideTransition(Direction.Right));
+
+ const cards = birthdayCardsFor(PEOPLE);
+
+ for (let i = 1; i <= 365 + 1; i++) {
+ const day = new Date(Date.now() + i * 24 * 60 * 60 * 1000);
+
+ yield* waitFor(0.01);
+ yield date().text(day.toDateString());
+
+ const peoplesBirthday = cards
+ .map((x, i) => [x, i] as [CardI, number])
+ .filter(([{ deliverInDays }]) => deliverInDays === i)
+ .map(([_, i]) => i);
+
+ yield* all(
+ ...peoplesBirthday.map((p) => {
+ const text = peopleText[p];
+ return all(text.text("🗓️🎂", 0), text.fontSize(50, 0.5));
+ }),
+ );
+
+ yield* all(
+ ...peoplesBirthday.map((p) => {
+ const layout = peopleLayout[p];
+ return layout.gap(0, 0.5);
+ }),
+ );
+
+ yield* all(
+ ...peoplesBirthday.map((p) => {
+ const person = people[p];
+ const text = peopleText[p];
+ return all(
+ text.opacity(0, 0.5),
+ text.fontSize(0, 0.5),
+ person.emit(":(", 0.8, false),
+ );
+ }),
+ );
+ }
+
+ yield* beginSlide("See their reactions");
+ yield* all(
+ date().fontSize(0, 0.5),
+ date().opacity(0, 0.65),
+ ...people.map((person) => person.opacity(0, 0.5)),
+ );
+});
diff --git a/src/scenes/valentines_letters.meta b/src/scenes/valentines_letters.meta
new file mode 100644
index 0000000..2eef586
--- /dev/null
+++ b/src/scenes/valentines_letters.meta
@@ -0,0 +1,5 @@
+{
+ "version": 0,
+ "timeEvents": [],
+ "seed": 3850509512
+} \ No newline at end of file
diff --git a/src/scenes/valentines_letters.tsx b/src/scenes/valentines_letters.tsx
new file mode 100644
index 0000000..8a7359d
--- /dev/null
+++ b/src/scenes/valentines_letters.tsx
@@ -0,0 +1,122 @@
+import { Layout, Txt, makeScene2D } from "@motion-canvas/2d";
+import {
+ Direction,
+ all,
+ beginSlide,
+ createRef,
+ makeRef,
+ slideTransition,
+} from "@motion-canvas/core";
+
+import { FunctionBox } from "../components/function_box";
+import { theme } from "../theme";
+import { PEOPLE, Person } from "../components/person";
+
+const valentineLetterGenerator = `
+interface PersonI {
+ name: string;
+ birthday: Date;
+ color: string;
+}
+
+(function valentinesLettersFor(people: PersonI[]) {
+ const letters: string[] = [];
+
+ for (const person of people) {
+ const letter = "Dear, " + person.name + "\\n"
+ + "Your smile lights up my world.\\n"
+ + "Happy Valentine's Day!";
+ letters.push(letter);
+ }
+
+ return letters;
+})`;
+
+export default makeScene2D(function* (view) {
+ const layout = createRef<Layout>();
+ const functionBox = createRef<FunctionBox>();
+ const people: Person[] = [];
+ const peopleLayout: Layout[] = [];
+ const peopleText: Txt[] = [];
+
+ view.add(
+ <FunctionBox
+ ref={functionBox}
+ source={valentineLetterGenerator}
+ workingText="📝⚙"
+ outputFontSize={25}
+ ></FunctionBox>,
+ );
+
+ yield* all(slideTransition(Direction.Left), functionBox().showCode(0.75));
+ yield* functionBox().reset(0.1);
+
+ yield* beginSlide("Show code");
+
+ yield* functionBox().reset(0.1);
+ yield* all(
+ functionBox().hideCode(0.8),
+ functionBox().setInputs(
+ [
+ {
+ val: PEOPLE,
+ node: (
+ <Layout direction="column" gap={5} layout>
+ {PEOPLE.map((person) => (
+ <Person person={person} />
+ ))}
+ </Layout>
+ ),
+ },
+ ],
+ 0.8,
+ ),
+ );
+
+ yield* beginSlide("Show people");
+
+ yield* functionBox().propogateInput(0.6);
+ yield* functionBox().propogateOutput(0.6);
+ yield* beginSlide("Generate valentines letters");
+
+ yield* functionBox().opacity(0, 0.5);
+ functionBox().remove();
+
+ view.add(
+ <Layout opacity={0} ref={layout} direction="row" gap={15} layout>
+ {PEOPLE.map((person, i) => (
+ <Layout
+ ref={makeRef(peopleLayout, i)}
+ alignItems="center"
+ direction="column"
+ gap={100}
+ >
+ <Person ref={makeRef(people, i)} person={person} />
+ <Txt
+ ref={makeRef(peopleText, i)}
+ fontSize={0}
+ fontFamily={theme.font}
+ fill={theme.text.hex}
+ />
+ </Layout>
+ ))}
+ </Layout>,
+ );
+ yield* layout().opacity(1, 0.5);
+
+ yield* all(...peopleText.map((text) => text.text("💌", 0)));
+ yield* all(...peopleText.map((text) => text.fontSize(50, 0.5)));
+ yield* all(...peopleLayout.map((layout) => layout.gap(0, 0.5)));
+ yield* all(
+ ...peopleText.map((text) =>
+ all(text.fontSize(0, 0.5), text.opacity(0, 0.5)),
+ ),
+ );
+
+ yield* beginSlide("Give people valentines letters");
+
+ yield* all(...people.map((person) => person.emit("😊", 0.5)));
+
+ yield* beginSlide("See their reactions");
+ yield* all(...people.map((person) => person.opacity(0, 0.5)));
+});