From c55d9d2832a46d9dc265fec36936b1313f5af6f6 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sun, 11 Feb 2024 20:44:17 -0700 Subject: birthdays --- src/components/function_box.tsx | 6 +- src/components/person.tsx | 105 ++++++++++++++++++ src/scenes/birthday_letters.meta | 5 + src/scenes/birthday_letters.tsx | 213 +++++++++++++++++++++++++++++++++++++ src/scenes/index.ts | 10 +- src/scenes/sad_people.meta | 5 + src/scenes/sad_people.tsx | 99 +++++++++++++++++ src/scenes/valentines_letters.meta | 5 + src/scenes/valentines_letters.tsx | 122 +++++++++++++++++++++ 9 files changed, 565 insertions(+), 5 deletions(-) create mode 100644 src/components/person.tsx create mode 100644 src/scenes/birthday_letters.meta create mode 100644 src/scenes/birthday_letters.tsx create mode 100644 src/scenes/sad_people.meta create mode 100644 src/scenes/sad_people.tsx create mode 100644 src/scenes/valentines_letters.meta create mode 100644 src/scenes/valentines_letters.tsx (limited to 'src') 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 { , ); 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) => ` + + + + + + + + + + +`; + +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(); + + public constructor(props?: PersonProps) { + super({ ...props }); + + this.add( + + + + {props.person.name} + + , + ); + } + + public *emit(text: string, duration: number, cleanUp = true) { + const ref = createRef(); + + this.insert( + + {text} + , + 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)[ + 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 + )[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(); + const date = createRef(); + const functionBox = createRef(); + const people: Person[] = []; + const peopleLayout: Layout[] = []; + const peopleText: Txt[] = []; + + view.add( + , + ); + + 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: ( + + {PEOPLE.map((person) => ( + + ))} + + ), + }, + ], + 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( + + + + + {PEOPLE.map((person, i) => ( + + + + + ))} + + , + ); + 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(); + const people: Person[] = []; + const peopleLayout: Layout[] = []; + const peopleText: Txt[] = []; + + view.add( + + + + + {PEOPLE.map((person, i) => ( + + + + + ))} + + , + ); + 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(); + const functionBox = createRef(); + const people: Person[] = []; + const peopleLayout: Layout[] = []; + const peopleText: Txt[] = []; + + view.add( + , + ); + + 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: ( + + {PEOPLE.map((person) => ( + + ))} + + ), + }, + ], + 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( + + {PEOPLE.map((person, i) => ( + + + + + ))} + , + ); + 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))); +}); -- cgit v1.2.3-70-g09d2