├── .gitignore ├── .prettierrc.yaml ├── .vscode └── extensions.json ├── README.md ├── login_website ├── login.html ├── main.css ├── polylogo.png └── register.html ├── package-lock.json ├── package.json ├── prepare_facebook_dataset.py ├── public └── fonts │ └── Serif │ ├── OFL-FAQ.txt │ ├── OFL.txt │ ├── README.txt │ ├── cmun-serif.css │ ├── cmunbi.eot │ ├── cmunbi.svg │ ├── cmunbi.ttf │ ├── cmunbi.woff │ ├── cmunbx.eot │ ├── cmunbx.svg │ ├── cmunbx.ttf │ ├── cmunbx.woff │ ├── cmunrm.eot │ ├── cmunrm.svg │ ├── cmunrm.ttf │ ├── cmunrm.woff │ ├── cmunti.eot │ ├── cmunti.svg │ ├── cmunti.ttf │ └── cmunti.woff ├── src ├── assets │ ├── SMB │ │ ├── Untitled.xcf │ │ ├── Untitled2.xcf │ │ ├── Untitled3.xcf │ │ ├── Untitled4.xcf │ │ ├── Untitled5.xcf │ │ ├── block.png │ │ ├── block.xcf │ │ ├── bush.png │ │ ├── bush.xcf │ │ ├── cloud_big.png │ │ ├── cloud_big.xcf │ │ ├── cloud_small.png │ │ ├── cloud_small.xcf │ │ ├── jump.png │ │ ├── mario-2.gif │ │ ├── misc-3.gif │ │ ├── run1.png │ │ ├── run2.png │ │ ├── run3.png │ │ ├── run4.png │ │ └── smb_tiles.png │ ├── facebook_layout.json │ ├── facebook_raw.txt │ ├── icons │ │ ├── android-brands-solid.svg │ │ ├── check-solid.svg │ │ ├── child-solid.svg │ │ ├── gear-solid.svg │ │ ├── person-chalkboard-solid.svg │ │ ├── thumbs-up-solid.svg │ │ └── user-tie-solid.svg │ ├── images │ │ ├── add_border.py │ │ ├── circuit_screenshot_simple.png │ │ ├── garbage.svg │ │ ├── goldwasser.jpg │ │ ├── key.png │ │ ├── key.svg │ │ ├── micali.jpg │ │ ├── minesweeper.png │ │ ├── pointing-hand-raw.png │ │ ├── prover.png │ │ ├── prover_alarmed.png │ │ ├── prover_embarrassed.png │ │ ├── prover_evil.png │ │ ├── prover_happy.png │ │ ├── prover_looking.png │ │ ├── prover_thinking.png │ │ ├── rackoff.jpg │ │ ├── sat_screenshot.png │ │ ├── student.png │ │ ├── super_mario_bros_logo.svg │ │ ├── teacher.png │ │ ├── tux.png │ │ ├── tux_hacked.png │ │ ├── vasek.png │ │ ├── vasek2.png │ │ ├── verifier.png │ │ ├── verifier_alarmed.png │ │ ├── verifier_embarrassed.png │ │ ├── verifier_evil.png │ │ ├── verifier_happy.png │ │ ├── verifier_looking.png │ │ ├── verifier_thinking.png │ │ ├── vr │ │ │ ├── add_borders.sh │ │ │ ├── no_border │ │ │ │ ├── alarmed.png │ │ │ │ ├── evil.png │ │ │ │ ├── happy.png │ │ │ │ ├── looking.png │ │ │ │ ├── neutral.png │ │ │ │ └── thinking.png │ │ │ ├── orig │ │ │ │ ├── alarmed.png │ │ │ │ ├── evil.png │ │ │ │ ├── happy.png │ │ │ │ ├── looking.png │ │ │ │ ├── neutral.png │ │ │ │ └── thinking.png │ │ │ └── with_border │ │ │ │ ├── alarmed.png │ │ │ │ ├── embarrassed.png │ │ │ │ ├── evil.png │ │ │ │ ├── happy.png │ │ │ │ ├── looking.png │ │ │ │ ├── neutral.png │ │ │ │ └── thinking.png │ │ ├── vv │ │ │ ├── .gitignore │ │ │ ├── add_borders.sh │ │ │ ├── no_border │ │ │ │ ├── alarmed.png │ │ │ │ ├── embarrassed.png │ │ │ │ ├── evil.png │ │ │ │ ├── happy.png │ │ │ │ ├── looking.png │ │ │ │ ├── neutral.png │ │ │ │ ├── neutral2.png │ │ │ │ └── thinking.png │ │ │ ├── orig │ │ │ │ ├── alarmed.png │ │ │ │ ├── embarrassed.png │ │ │ │ ├── evil.png │ │ │ │ ├── happy.png │ │ │ │ ├── looking.png │ │ │ │ ├── neutral.png │ │ │ │ ├── neutral2.png │ │ │ │ └── thinking.png │ │ │ └── with_border │ │ │ │ ├── alarmed.png │ │ │ │ ├── embarrassed.png │ │ │ │ ├── evil.png │ │ │ │ ├── happy.png │ │ │ │ ├── looking.png │ │ │ │ ├── neutral.png │ │ │ │ ├── neutral2.png │ │ │ │ └── thinking.png │ │ └── wigderson.jpg │ └── polylogo.svg ├── components │ ├── cross.tsx │ ├── mario_algorithm.tsx │ ├── participant.tsx │ └── tick.tsx ├── global.css ├── motion-canvas.d.ts ├── project.ts ├── scenes │ ├── building_the_protocol_1.tsx │ ├── building_the_protocol_2.tsx │ ├── commitments_1.tsx │ ├── commitments_2.tsx │ ├── discussion_1.tsx │ ├── discussion_1_fix.tsx │ ├── discussion_2.tsx │ ├── discussion_3.tsx │ ├── full_protocol.tsx │ ├── graph_coloring.tsx │ ├── lock.tsx │ ├── mario_big_graph.tsx │ ├── np_to_coloring.tsx │ ├── np_to_coloring_algorithm.tsx │ ├── np_to_coloring_big_graph.tsx │ ├── np_to_coloring_conversion.tsx │ ├── phone.tsx │ ├── phone_trust.tsx │ ├── polylogo.tsx │ ├── reductions.tsx │ ├── sudoku_coloring.tsx │ ├── sudoku_intro.tsx │ ├── sudoku_reduction.tsx │ ├── teacher.tsx │ ├── teacher_failing.tsx │ ├── teacher_fixup.tsx │ ├── teacher_many_times.tsx │ └── timeline.tsx ├── shaders │ ├── gradient.glsl │ ├── gradient2.glsl │ └── timedgradient.glsl ├── utilities.tsx ├── utilities_finger.tsx ├── utilities_fix_view_scaling.tsx ├── utilities_graph.tsx ├── utilities_lock.tsx ├── utilities_lockable_graph.tsx ├── utilities_moving.tsx ├── utilities_pressed_key.tsx ├── utilities_protocol.tsx ├── utilities_sudoku.tsx └── utilities_text.tsx ├── tsconfig.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated files 2 | node_modules 3 | output 4 | dist 5 | 6 | # Editor directories and files 7 | .vscode/* 8 | !.vscode/extensions.json 9 | .idea 10 | .DS_Store 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | *.sw? 16 | 17 | *.meta 18 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | tabWidth: 2 2 | useTabs: false 3 | printWidth: 88 # Like Python's Black 4 | singleQuote: true 5 | trailingComma: all 6 | plugins: ['@ianvs/prettier-plugin-sort-imports'] 7 | importOrder: ['', '', '', '', '^[.]'] 8 | importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'] 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "naumovs.color-highlight" 5 | ] 6 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zero-knowledge proofs 2 | 3 | Animations for our video, "[I can prove I’ve solved this Sudoku without revealing it](https://www.youtube.com/watch?v=Otvcbw6k4eo)". 4 | 5 | ## Motion Canvas 6 | 7 | To run the animations, run 8 | 9 | ``` 10 | npm install 11 | ``` 12 | 13 | to install dependencies and 14 | 15 | ``` 16 | npm start 17 | ``` 18 | 19 | to launch the Motion Canvas editor. 20 | 21 | ## Code formatter 22 | 23 | We use Prettier to format code. If you use VSCode, you can install the Prettier extension (`esbenp.prettier-vscode`) and then the correct formatting should get applied on save automatically. The formatting style is defined in `.prettierrc`, which is respected by the VSCode extension. 24 | 25 | If you use a different editor, set up Prettier manually there. 26 | 27 | If you need to run Prettier manually, you can do so using 28 | ``` 29 | npx prettier --write src/ 30 | ``` 31 | 32 | ## Sources 33 | 34 | Facebook dataset (used as an example graph): https://snap.stanford.edu/data/egonets-Facebook.html 35 | 36 | Pointing hand: https://www.rawpixel.com/image/6261834/png-sticker-vintage 37 | -------------------------------------------------------------------------------- /login_website/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Polylog 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /login_website/main.css: -------------------------------------------------------------------------------- 1 | #container { 2 | margin-top: 6%; 3 | } 4 | body { 5 | background: #eee8d5; 6 | font-size: 2rem; 7 | color: #002b36; 8 | font-family: 'Cantarell'; 9 | } 10 | 11 | #polylogo { 12 | display: block; 13 | margin: 0.5em auto; 14 | width: 45%; 15 | height: auto; 16 | padding: 2em; 17 | } 18 | input, 19 | button { 20 | display: block; 21 | margin: 0.5em auto; 22 | font-size: 1em; 23 | width: 20%; 24 | box-sizing: content-box; 25 | padding: 0.1em; 26 | border-radius: 0.3em; 27 | border: 0.1em solid #657b83; 28 | -moz-transition: all 0.3s ease-in-out; 29 | } 30 | 31 | input:focus, 32 | button:focus, 33 | button:active { 34 | outline: 0.1em solid #268bd2; 35 | box-shadow: none; 36 | border-color: #268bd2; 37 | } 38 | 39 | button { 40 | background: #073642; 41 | color: #fdf6e3; 42 | border-color: #002b36; 43 | } 44 | 45 | #modal { 46 | display: none; 47 | position: absolute; 48 | top: 50%; 49 | left: 50%; 50 | transform: translate(-50%, -50%); 51 | width: 20%; 52 | padding: 1em; 53 | background: #fdf6e3; 54 | border: 0.3em solid #002b36; 55 | border-radius: 0.6em; 56 | } 57 | 58 | #modal button { 59 | margin: 0.8em auto 0 auto; 60 | } 61 | 62 | #hide { 63 | display: none; 64 | position: absolute; 65 | top: 0; 66 | left: 0; 67 | width: 100%; 68 | height: 100%; 69 | background: rgba(0, 0, 0, 0.1); 70 | } 71 | -------------------------------------------------------------------------------- /login_website/polylogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/login_website/polylogo.png -------------------------------------------------------------------------------- /login_website/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Polylog 7 | 8 | 9 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polylog", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "start": "vite", 7 | "serve": "vite", 8 | "build": "tsc && vite build" 9 | }, 10 | "dependencies": { 11 | "@motion-canvas/2d": "^3.17.1", 12 | "@motion-canvas/core": "^3.17.0", 13 | "@motion-canvas/ffmpeg": "^1.1.0", 14 | "diff": "^7.0.0", 15 | "prettier": "^3.4.2" 16 | }, 17 | "prettier": { 18 | "tabWidth": 2, 19 | "useTabs": false, 20 | "printWidth": 88, 21 | "singleQuote": true, 22 | "trailingComma": "all", 23 | "plugins": [ 24 | "@ianvs/prettier-plugin-sort-imports" 25 | ], 26 | "importOrder": [ 27 | "", 28 | "", 29 | "", 30 | "", 31 | "^[.]" 32 | ], 33 | "importOrderParserPlugins": [ 34 | "typescript", 35 | "jsx", 36 | "decorators-legacy" 37 | ] 38 | }, 39 | "devDependencies": { 40 | "@ianvs/prettier-plugin-sort-imports": "^4.4.0", 41 | "@motion-canvas/ui": "^3.17.0", 42 | "@motion-canvas/vite-plugin": "^3.17.0", 43 | "@types/diff": "^6.0.0", 44 | "typescript": "^5.2.2", 45 | "vite": "^4.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /prepare_facebook_dataset.py: -------------------------------------------------------------------------------- 1 | """Prepare the data to be loaded in np_to_coloring_big_graph.tsx. 2 | 3 | It's this dataset: https://snap.stanford.edu/data/egonets-Facebook.html 4 | filtered a bit for clarity and performance. 5 | """ 6 | 7 | import json 8 | from pathlib import Path 9 | 10 | import networkx as nx 11 | 12 | 13 | def read_edge_list( 14 | file_path: Path, node_filtering: int, edge_filtering: int 15 | ) -> nx.Graph: 16 | if not file_path.exists(): 17 | raise FileNotFoundError(f"Edge list file not found: {file_path}") 18 | 19 | G = nx.Graph() 20 | 21 | with open(file_path, "r") as f: 22 | for edge_index, line in enumerate(f): 23 | # Split line and convert to integers, skip empty lines 24 | if line.strip(): 25 | source, target = map(int, line.strip().split()) 26 | if ( 27 | source % node_filtering == 0 28 | and target % node_filtering == 0 29 | and edge_index % edge_filtering == 0 30 | ): 31 | G.add_edge(source, target) 32 | 33 | return G 34 | 35 | 36 | def compute_layout(G: nx.Graph) -> dict: 37 | """ 38 | Compute Kamada-Kawai layout for the graph. 39 | 40 | Args: 41 | G (nx.Graph): NetworkX graph object 42 | 43 | Returns: 44 | dict: Node positions dictionary 45 | """ 46 | pos = nx.kamada_kawai_layout(G) 47 | # pos = nx.random_layout(G) # For testing 48 | pos_dict = {str(node): pos[node].tolist() for node in G.nodes()} 49 | 50 | return {"positions": pos_dict, "edges": list(G.edges())} 51 | 52 | 53 | if __name__ == "__main__": 54 | edge_list_path = Path(__file__).parent / "src" / "assets" / "facebook_raw.txt" 55 | output_path = Path(__file__).parent / "src" / "assets" / "facebook_layout.json" 56 | node_filtering = 3 57 | edge_filtering = 5 58 | 59 | # Process the graph and save layout 60 | G = read_edge_list(edge_list_path, node_filtering, edge_filtering) 61 | pos_dict = compute_layout(G) 62 | 63 | with open(output_path, "w") as f: 64 | json.dump(pos_dict, f) 65 | 66 | print("Layout saved successfully!") 67 | print(f"Number of nodes: {len(pos_dict['positions'])}") 68 | -------------------------------------------------------------------------------- /public/fonts/Serif/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) Authors of original metafont fonts: 2 | Donald Ervin Knuth (cm, concrete fonts) 3 | 1995, 1996, 1997 J"org Knappen, 1990, 1992 Norbert Schwarz (ec fonts) 4 | 1992-2006 A.Khodulev, O.Lapko, A.Berdnikov, V.Volovich (lh fonts) 5 | 1997-2005 Claudio Beccari (cb greek fonts) 6 | 2002 FUKUI Rei (tipa fonts) 7 | 2003-2005 Han The Thanh (Vietnamese fonts) 8 | 1996-2005 Walter Schmidt (cmbright fonts) 9 | 10 | Copyright (C) 2003-2009, Andrey V. Panov (panov@canopus.iacp.dvo.ru), 11 | with Reserved Font Family Name "Computer Modern Unicode fonts". 12 | 13 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 14 | This license is copied below, and is also available with a FAQ at: 15 | http://scripts.sil.org/OFL 16 | 17 | 18 | ----------------------------------------------------------- 19 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 20 | ----------------------------------------------------------- 21 | 22 | PREAMBLE 23 | The goals of the Open Font License (OFL) are to stimulate worldwide 24 | development of collaborative font projects, to support the font creation 25 | efforts of academic and linguistic communities, and to provide a free and 26 | open framework in which fonts may be shared and improved in partnership 27 | with others. 28 | 29 | The OFL allows the licensed fonts to be used, studied, modified and 30 | redistributed freely as long as they are not sold by themselves. The 31 | fonts, including any derivative works, can be bundled, embedded, 32 | redistributed and/or sold with any software provided that any reserved 33 | names are not used by derivative works. The fonts and derivatives, 34 | however, cannot be released under any other type of license. The 35 | requirement for fonts to remain under this license does not apply 36 | to any document created using the fonts or their derivatives. 37 | 38 | DEFINITIONS 39 | "Font Software" refers to the set of files released by the Copyright 40 | Holder(s) under this license and clearly marked as such. This may 41 | include source files, build scripts and documentation. 42 | 43 | "Reserved Font Name" refers to any names specified as such after the 44 | copyright statement(s). 45 | 46 | "Original Version" refers to the collection of Font Software components as 47 | distributed by the Copyright Holder(s). 48 | 49 | "Modified Version" refers to any derivative made by adding to, deleting, 50 | or substituting -- in part or in whole -- any of the components of the 51 | Original Version, by changing formats or by porting the Font Software to a 52 | new environment. 53 | 54 | "Author" refers to any designer, engineer, programmer, technical 55 | writer or other person who contributed to the Font Software. 56 | 57 | PERMISSION & CONDITIONS 58 | Permission is hereby granted, free of charge, to any person obtaining 59 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 60 | redistribute, and sell modified and unmodified copies of the Font 61 | Software, subject to the following conditions: 62 | 63 | 1) Neither the Font Software nor any of its individual components, 64 | in Original or Modified Versions, may be sold by itself. 65 | 66 | 2) Original or Modified Versions of the Font Software may be bundled, 67 | redistributed and/or sold with any software, provided that each copy 68 | contains the above copyright notice and this license. These can be 69 | included either as stand-alone text files, human-readable headers or 70 | in the appropriate machine-readable metadata fields within text or 71 | binary files as long as those fields can be easily viewed by the user. 72 | 73 | 3) No Modified Version of the Font Software may use the Reserved Font 74 | Name(s) unless explicit written permission is granted by the corresponding 75 | Copyright Holder. This restriction only applies to the primary font name as 76 | presented to the users. 77 | 78 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 79 | Software shall not be used to promote, endorse or advertise any 80 | Modified Version, except to acknowledge the contribution(s) of the 81 | Copyright Holder(s) and the Author(s) or with their explicit written 82 | permission. 83 | 84 | 5) The Font Software, modified or unmodified, in part or in whole, 85 | must be distributed entirely under this license, and must not be 86 | distributed under any other license. The requirement for fonts to 87 | remain under this license does not apply to any document created 88 | using the Font Software. 89 | 90 | TERMINATION 91 | This license becomes null and void if any of the above conditions are 92 | not met. 93 | 94 | DISCLAIMER 95 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 96 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 97 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 98 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 99 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 100 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 101 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 102 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 103 | OTHER DEALINGS IN THE FONT SOFTWARE. 104 | -------------------------------------------------------------------------------- /public/fonts/Serif/README.txt: -------------------------------------------------------------------------------- 1 | This package was compiled by Christian Perfect (http://checkmyworking.com) from the Computer Modern Unicode fonts created by Andrey V. Panov (http://cm-unicode.sourceforge.net/) 2 | 3 | They're released under the SIL Open Font License. See OFL.txt and OFL-FAQ.txt for the terms. 4 | 5 | A demo page for these fonts was at http://www.checkmyworking.com/cm-web-fonts/ when I released them. I can only apologise, citizen of the future, if that address doesn't exist any more. 6 | -------------------------------------------------------------------------------- /public/fonts/Serif/cmun-serif.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Computer Modern Serif'; 3 | src: url('cmunrm.eot'); 4 | src: url('cmunrm.eot?#iefix') format('embedded-opentype'), 5 | url('cmunrm.woff') format('woff'), 6 | url('cmunrm.ttf') format('truetype'), 7 | url('cmunrm.svg#cmunrm') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | 13 | @font-face { 14 | font-family: 'Computer Modern Serif'; 15 | src: url('cmunbx.eot'); 16 | src: url('cmunbx.eot?#iefix') format('embedded-opentype'), 17 | url('cmunbx.woff') format('woff'), 18 | url('cmunbx.ttf') format('truetype'), 19 | url('cmunbx.svg#cmunbx') format('svg'); 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | 25 | @font-face { 26 | font-family: 'Computer Modern Serif'; 27 | src: url('cmunti.eot'); 28 | src: url('cmunti.eot?#iefix') format('embedded-opentype'), 29 | url('cmunti.woff') format('woff'), 30 | url('cmunti.ttf') format('truetype'), 31 | url('cmunti.svg#cmunti') format('svg'); 32 | font-weight: normal; 33 | font-style: italic; 34 | } 35 | 36 | 37 | @font-face { 38 | font-family: 'Computer Modern Serif'; 39 | src: url('cmunbi.eot'); 40 | src: url('cmunbi.eot?#iefix') format('embedded-opentype'), 41 | url('cmunbi.woff') format('woff'), 42 | url('cmunbi.ttf') format('truetype'), 43 | url('cmunbi.svg#cmunbi') format('svg'); 44 | font-weight: bold; 45 | font-style: italic; 46 | } 47 | 48 | 49 | -------------------------------------------------------------------------------- /public/fonts/Serif/cmunbi.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunbi.eot -------------------------------------------------------------------------------- /public/fonts/Serif/cmunbi.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunbi.ttf -------------------------------------------------------------------------------- /public/fonts/Serif/cmunbi.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunbi.woff -------------------------------------------------------------------------------- /public/fonts/Serif/cmunbx.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunbx.eot -------------------------------------------------------------------------------- /public/fonts/Serif/cmunbx.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunbx.ttf -------------------------------------------------------------------------------- /public/fonts/Serif/cmunbx.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunbx.woff -------------------------------------------------------------------------------- /public/fonts/Serif/cmunrm.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunrm.eot -------------------------------------------------------------------------------- /public/fonts/Serif/cmunrm.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunrm.ttf -------------------------------------------------------------------------------- /public/fonts/Serif/cmunrm.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunrm.woff -------------------------------------------------------------------------------- /public/fonts/Serif/cmunti.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunti.eot -------------------------------------------------------------------------------- /public/fonts/Serif/cmunti.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunti.ttf -------------------------------------------------------------------------------- /public/fonts/Serif/cmunti.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/public/fonts/Serif/cmunti.woff -------------------------------------------------------------------------------- /src/assets/SMB/Untitled.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/Untitled.xcf -------------------------------------------------------------------------------- /src/assets/SMB/Untitled2.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/Untitled2.xcf -------------------------------------------------------------------------------- /src/assets/SMB/Untitled3.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/Untitled3.xcf -------------------------------------------------------------------------------- /src/assets/SMB/Untitled4.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/Untitled4.xcf -------------------------------------------------------------------------------- /src/assets/SMB/Untitled5.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/Untitled5.xcf -------------------------------------------------------------------------------- /src/assets/SMB/block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/block.png -------------------------------------------------------------------------------- /src/assets/SMB/block.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/block.xcf -------------------------------------------------------------------------------- /src/assets/SMB/bush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/bush.png -------------------------------------------------------------------------------- /src/assets/SMB/bush.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/bush.xcf -------------------------------------------------------------------------------- /src/assets/SMB/cloud_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/cloud_big.png -------------------------------------------------------------------------------- /src/assets/SMB/cloud_big.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/cloud_big.xcf -------------------------------------------------------------------------------- /src/assets/SMB/cloud_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/cloud_small.png -------------------------------------------------------------------------------- /src/assets/SMB/cloud_small.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/cloud_small.xcf -------------------------------------------------------------------------------- /src/assets/SMB/jump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/jump.png -------------------------------------------------------------------------------- /src/assets/SMB/mario-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/mario-2.gif -------------------------------------------------------------------------------- /src/assets/SMB/misc-3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/misc-3.gif -------------------------------------------------------------------------------- /src/assets/SMB/run1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/run1.png -------------------------------------------------------------------------------- /src/assets/SMB/run2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/run2.png -------------------------------------------------------------------------------- /src/assets/SMB/run3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/run3.png -------------------------------------------------------------------------------- /src/assets/SMB/run4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/run4.png -------------------------------------------------------------------------------- /src/assets/SMB/smb_tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/SMB/smb_tiles.png -------------------------------------------------------------------------------- /src/assets/icons/android-brands-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/check-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/child-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/gear-solid.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 32 | 33 | 37 | 38 | -------------------------------------------------------------------------------- /src/assets/icons/person-chalkboard-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/thumbs-up-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/user-tie-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/add_border.py: -------------------------------------------------------------------------------- 1 | try: 2 | from PIL import Image, ImageFilter 3 | except ImportError: 4 | print( 5 | "Pillow library is not installed. You can install it using 'pip install pillow'" 6 | ) 7 | raise 8 | 9 | 10 | IMAGE_SIZE = 900 11 | # The radius of the Gaussian blur used to expand the mask. 12 | # Not sure this corresponds to pixels. 13 | BORDER_RADIUS = 14 14 | FEATHERING = 1 15 | 16 | 17 | def add_feathered_border( 18 | image_path: str, 19 | output_path: str, 20 | border_color: tuple, 21 | ) -> None: 22 | """ 23 | Add a feathered border to an image with alpha channel. 24 | 25 | Args: 26 | image_path: Path to input image 27 | output_path: Path to save output image 28 | border_color: RGB tuple for border color (default: black) 29 | border_size: Border thickness in pixels (default: 15) 30 | feather_radius: Feathering amount in pixels (default: 5) 31 | """ 32 | img = Image.open(image_path) 33 | img = img.resize((IMAGE_SIZE, IMAGE_SIZE)) 34 | 35 | # Create mask for feathering based on alpha channel 36 | mask = Image.new("L", img.size, 0) 37 | alpha = img.split()[3] 38 | mask.paste(alpha, (0, 0)) 39 | 40 | # Expand the mask using blur + binarization 41 | mask = mask.filter(ImageFilter.GaussianBlur(radius=BORDER_RADIUS)) 42 | mask = mask.point(lambda x: 255 if x > 1 else 0) 43 | # Then feather the mask 44 | mask = mask.filter(ImageFilter.GaussianBlur(radius=FEATHERING)) 45 | 46 | result = Image.new("RGBA", img.size, (*border_color, 255)) 47 | result = Image.composite(img, result, img) 48 | result.putalpha(mask) 49 | 50 | result.save(output_path, "PNG") 51 | 52 | 53 | def main(): 54 | import argparse 55 | 56 | parser = argparse.ArgumentParser(description="Add a feathered border to an image") 57 | parser.add_argument("input", help="Path to input image") 58 | parser.add_argument("output", help="Path to save output image") 59 | parser.add_argument( 60 | "--role", 61 | type=str, 62 | required=True, 63 | choices=["prover", "verifier"], 64 | help="Role determining border color (prover: #b58900, verifier: #073642)", 65 | ) 66 | 67 | args = parser.parse_args() 68 | 69 | # Convert hex colors to RGB tuples 70 | colors = { 71 | "prover": tuple(int(x, 16) for x in ("b5", "89", "00")), 72 | "verifier": tuple(int(x, 16) for x in ("07", "36", "42")), 73 | } 74 | 75 | add_feathered_border(args.input, args.output, colors[args.role]) 76 | 77 | 78 | if __name__ == "__main__": 79 | main() 80 | -------------------------------------------------------------------------------- /src/assets/images/circuit_screenshot_simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/circuit_screenshot_simple.png -------------------------------------------------------------------------------- /src/assets/images/garbage.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 10 | 12 | 13 | 16 | 17 | 18 | 20 | 21 | 22 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/assets/images/goldwasser.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/goldwasser.jpg -------------------------------------------------------------------------------- /src/assets/images/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/key.png -------------------------------------------------------------------------------- /src/assets/images/key.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 35 | 37 | 40 | 44 | 45 | 48 | 52 | 53 | 56 | 60 | 61 | 64 | 68 | 69 | 70 | 75 | 82 | 89 | 96 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/assets/images/micali.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/micali.jpg -------------------------------------------------------------------------------- /src/assets/images/minesweeper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/minesweeper.png -------------------------------------------------------------------------------- /src/assets/images/pointing-hand-raw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/pointing-hand-raw.png -------------------------------------------------------------------------------- /src/assets/images/prover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/prover.png -------------------------------------------------------------------------------- /src/assets/images/prover_alarmed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/prover_alarmed.png -------------------------------------------------------------------------------- /src/assets/images/prover_embarrassed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/prover_embarrassed.png -------------------------------------------------------------------------------- /src/assets/images/prover_evil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/prover_evil.png -------------------------------------------------------------------------------- /src/assets/images/prover_happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/prover_happy.png -------------------------------------------------------------------------------- /src/assets/images/prover_looking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/prover_looking.png -------------------------------------------------------------------------------- /src/assets/images/prover_thinking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/prover_thinking.png -------------------------------------------------------------------------------- /src/assets/images/rackoff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/rackoff.jpg -------------------------------------------------------------------------------- /src/assets/images/sat_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/sat_screenshot.png -------------------------------------------------------------------------------- /src/assets/images/student.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/student.png -------------------------------------------------------------------------------- /src/assets/images/teacher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/teacher.png -------------------------------------------------------------------------------- /src/assets/images/tux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/tux.png -------------------------------------------------------------------------------- /src/assets/images/tux_hacked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/tux_hacked.png -------------------------------------------------------------------------------- /src/assets/images/vasek.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vasek.png -------------------------------------------------------------------------------- /src/assets/images/vasek2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vasek2.png -------------------------------------------------------------------------------- /src/assets/images/verifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/verifier.png -------------------------------------------------------------------------------- /src/assets/images/verifier_alarmed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/verifier_alarmed.png -------------------------------------------------------------------------------- /src/assets/images/verifier_embarrassed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/verifier_embarrassed.png -------------------------------------------------------------------------------- /src/assets/images/verifier_evil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/verifier_evil.png -------------------------------------------------------------------------------- /src/assets/images/verifier_happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/verifier_happy.png -------------------------------------------------------------------------------- /src/assets/images/verifier_looking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/verifier_looking.png -------------------------------------------------------------------------------- /src/assets/images/verifier_thinking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/verifier_thinking.png -------------------------------------------------------------------------------- /src/assets/images/vr/add_borders.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # cd to script dir 4 | cd "$(dirname "$0")" 5 | 6 | cd orig 7 | for i in *.png; do 8 | magick $i -resize $(if [ $i = thinking.png ]; then echo '500x'; else echo x600; fi) -background none -gravity north -extent 800x800 -gravity center -extent 900x900 ../no_border/$i 9 | done 10 | cd .. 11 | 12 | for f in no_border/*.png; do 13 | python ../add_border.py --role verifier $f with_border/$(basename $f) 14 | echo "Added border to $f" 15 | done 16 | -------------------------------------------------------------------------------- /src/assets/images/vr/no_border/alarmed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/no_border/alarmed.png -------------------------------------------------------------------------------- /src/assets/images/vr/no_border/evil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/no_border/evil.png -------------------------------------------------------------------------------- /src/assets/images/vr/no_border/happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/no_border/happy.png -------------------------------------------------------------------------------- /src/assets/images/vr/no_border/looking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/no_border/looking.png -------------------------------------------------------------------------------- /src/assets/images/vr/no_border/neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/no_border/neutral.png -------------------------------------------------------------------------------- /src/assets/images/vr/no_border/thinking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/no_border/thinking.png -------------------------------------------------------------------------------- /src/assets/images/vr/orig/alarmed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/orig/alarmed.png -------------------------------------------------------------------------------- /src/assets/images/vr/orig/evil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/orig/evil.png -------------------------------------------------------------------------------- /src/assets/images/vr/orig/happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/orig/happy.png -------------------------------------------------------------------------------- /src/assets/images/vr/orig/looking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/orig/looking.png -------------------------------------------------------------------------------- /src/assets/images/vr/orig/neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/orig/neutral.png -------------------------------------------------------------------------------- /src/assets/images/vr/orig/thinking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/orig/thinking.png -------------------------------------------------------------------------------- /src/assets/images/vr/with_border/alarmed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/with_border/alarmed.png -------------------------------------------------------------------------------- /src/assets/images/vr/with_border/embarrassed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/with_border/embarrassed.png -------------------------------------------------------------------------------- /src/assets/images/vr/with_border/evil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/with_border/evil.png -------------------------------------------------------------------------------- /src/assets/images/vr/with_border/happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/with_border/happy.png -------------------------------------------------------------------------------- /src/assets/images/vr/with_border/looking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/with_border/looking.png -------------------------------------------------------------------------------- /src/assets/images/vr/with_border/neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/with_border/neutral.png -------------------------------------------------------------------------------- /src/assets/images/vr/with_border/thinking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vr/with_border/thinking.png -------------------------------------------------------------------------------- /src/assets/images/vv/.gitignore: -------------------------------------------------------------------------------- 1 | raw/ 2 | -------------------------------------------------------------------------------- /src/assets/images/vv/add_borders.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # cd to script dir 4 | cd "$(dirname "$0")" 5 | 6 | cd orig 7 | for i in *.png; do 8 | magick $i -resize 700x -background none -gravity north -extent 900x900 ../no_border/$i 9 | done 10 | cd .. 11 | 12 | for f in no_border/*.png; do 13 | python ../add_border.py --role prover $f with_border/$(basename $f) 14 | echo "Added border to $f" 15 | done 16 | -------------------------------------------------------------------------------- /src/assets/images/vv/no_border/alarmed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/no_border/alarmed.png -------------------------------------------------------------------------------- /src/assets/images/vv/no_border/embarrassed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/no_border/embarrassed.png -------------------------------------------------------------------------------- /src/assets/images/vv/no_border/evil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/no_border/evil.png -------------------------------------------------------------------------------- /src/assets/images/vv/no_border/happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/no_border/happy.png -------------------------------------------------------------------------------- /src/assets/images/vv/no_border/looking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/no_border/looking.png -------------------------------------------------------------------------------- /src/assets/images/vv/no_border/neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/no_border/neutral.png -------------------------------------------------------------------------------- /src/assets/images/vv/no_border/neutral2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/no_border/neutral2.png -------------------------------------------------------------------------------- /src/assets/images/vv/no_border/thinking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/no_border/thinking.png -------------------------------------------------------------------------------- /src/assets/images/vv/orig/alarmed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/orig/alarmed.png -------------------------------------------------------------------------------- /src/assets/images/vv/orig/embarrassed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/orig/embarrassed.png -------------------------------------------------------------------------------- /src/assets/images/vv/orig/evil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/orig/evil.png -------------------------------------------------------------------------------- /src/assets/images/vv/orig/happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/orig/happy.png -------------------------------------------------------------------------------- /src/assets/images/vv/orig/looking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/orig/looking.png -------------------------------------------------------------------------------- /src/assets/images/vv/orig/neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/orig/neutral.png -------------------------------------------------------------------------------- /src/assets/images/vv/orig/neutral2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/orig/neutral2.png -------------------------------------------------------------------------------- /src/assets/images/vv/orig/thinking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/orig/thinking.png -------------------------------------------------------------------------------- /src/assets/images/vv/with_border/alarmed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/with_border/alarmed.png -------------------------------------------------------------------------------- /src/assets/images/vv/with_border/embarrassed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/with_border/embarrassed.png -------------------------------------------------------------------------------- /src/assets/images/vv/with_border/evil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/with_border/evil.png -------------------------------------------------------------------------------- /src/assets/images/vv/with_border/happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/with_border/happy.png -------------------------------------------------------------------------------- /src/assets/images/vv/with_border/looking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/with_border/looking.png -------------------------------------------------------------------------------- /src/assets/images/vv/with_border/neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/with_border/neutral.png -------------------------------------------------------------------------------- /src/assets/images/vv/with_border/neutral2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/with_border/neutral2.png -------------------------------------------------------------------------------- /src/assets/images/vv/with_border/thinking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/vv/with_border/thinking.png -------------------------------------------------------------------------------- /src/assets/images/wigderson.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polylog-cs/zero-knowledge-proofs/7b91833589be2e1f0754814b7c462a63c309a97e/src/assets/images/wigderson.jpg -------------------------------------------------------------------------------- /src/components/cross.tsx: -------------------------------------------------------------------------------- 1 | import { Line, LineSegment, Node, NodeProps } from '@motion-canvas/2d'; 2 | 3 | import { Solarized } from '../utilities'; 4 | 5 | export class Cross extends Node { 6 | public constructor(props?: NodeProps) { 7 | super({ ...props }); 8 | 9 | this.add( 10 | <> 11 | 20 | 29 | , 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/mario_algorithm.tsx: -------------------------------------------------------------------------------- 1 | import { Img, Rect, RectProps } from '@motion-canvas/2d'; 2 | 3 | import super_mario_bros_logo_alpha from '../assets/images/super_mario_bros_logo.svg'; 4 | import { Solarized } from '../utilities'; 5 | import { MyTxt } from '../utilities_text'; 6 | 7 | export class MarioAlgorithm extends Rect { 8 | public constructor(props?: RectProps) { 9 | super({ 10 | width: 550, 11 | height: 550, 12 | fill: Solarized.orange, 13 | layout: true, 14 | direction: 'column', 15 | alignItems: 'center', 16 | justifyContent: 'center', 17 | gap: 25, 18 | ...props, 19 | }); 20 | 21 | this.add( 22 | <> 23 | 24 | 25 | World Record{'\n'} 26 | Checker 27 | 28 | , 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/participant.tsx: -------------------------------------------------------------------------------- 1 | import { Img, initial, Node, NodeProps, signal } from '@motion-canvas/2d'; 2 | import { SignalValue, SimpleSignal, Vector2 } from '@motion-canvas/core'; 3 | 4 | import verifierImageAlarmed from '../assets/images/vr/with_border/alarmed.png'; 5 | import verifierImageEmbarrassed from '../assets/images/vr/with_border/embarrassed.png'; 6 | import verifierImageEvil from '../assets/images/vr/with_border/evil.png'; 7 | import verifierImageHappy from '../assets/images/vr/with_border/happy.png'; 8 | import verifierImageLooking from '../assets/images/vr/with_border/looking.png'; 9 | import verifierImageNeutral from '../assets/images/vr/with_border/neutral.png'; 10 | import verifierImageThinking from '../assets/images/vr/with_border/thinking.png'; 11 | import proverImageAlarmed from '../assets/images/vv/with_border/alarmed.png'; 12 | import proverImageEmbarrassed from '../assets/images/vv/with_border/embarrassed.png'; 13 | import proverImageEvil from '../assets/images/vv/with_border/evil.png'; 14 | import proverImageHappy from '../assets/images/vv/with_border/happy.png'; 15 | import proverImageLooking from '../assets/images/vv/with_border/looking.png'; 16 | import proverImageNeutral from '../assets/images/vv/with_border/neutral2.png'; 17 | import proverImageThinking from '../assets/images/vv/with_border/thinking.png'; 18 | 19 | export type ParticipantKind = 'prover' | 'verifier'; 20 | export type Expression = 21 | | 'neutral' 22 | | 'thinking' 23 | | 'looking' 24 | | 'embarrassed' 25 | | 'alarmed' 26 | | 'evil' 27 | | 'happy'; 28 | 29 | export const PROVER_POSITION = new Vector2(-600, 0); 30 | export const VERIFIER_POSITION = new Vector2(600, 0); 31 | 32 | const EXPRESSION_TO_IMAGE: Record> = { 33 | prover: { 34 | neutral: proverImageNeutral, 35 | thinking: proverImageThinking, 36 | looking: proverImageLooking, 37 | embarrassed: proverImageEmbarrassed, 38 | alarmed: proverImageAlarmed, 39 | evil: proverImageEvil, 40 | happy: proverImageHappy, 41 | }, 42 | verifier: { 43 | neutral: verifierImageNeutral, 44 | thinking: verifierImageThinking, 45 | looking: verifierImageLooking, 46 | embarrassed: verifierImageEmbarrassed, 47 | alarmed: verifierImageAlarmed, 48 | evil: verifierImageEvil, 49 | happy: verifierImageHappy, 50 | }, 51 | }; 52 | 53 | export interface ParticipantProps extends NodeProps { 54 | kind: ParticipantKind; 55 | expression?: SignalValue; 56 | } 57 | 58 | export class Participant extends Node { 59 | @signal() 60 | declare public readonly kind: SimpleSignal; 61 | 62 | @initial('neutral') 63 | @signal() 64 | declare public readonly expression: SimpleSignal; 65 | 66 | public constructor(props?: ParticipantProps) { 67 | super({ ...props }); 68 | this.add( 69 | EXPRESSION_TO_IMAGE[this.kind()][this.expression()]} 71 | width={450} 72 | >, 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/components/tick.tsx: -------------------------------------------------------------------------------- 1 | import { Line, LineSegment, Node, NodeProps } from '@motion-canvas/2d'; 2 | 3 | import { Solarized } from '../utilities'; 4 | 5 | export class Tick extends Node { 6 | public constructor(props?: NodeProps) { 7 | super({ ...props }); 8 | 9 | this.add( 10 | <> 11 | 20 | , 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/global.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap'); 2 | 3 | @import url(public/fonts/Serif/cmun-serif.css); 4 | -------------------------------------------------------------------------------- /src/motion-canvas.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/project.ts: -------------------------------------------------------------------------------- 1 | import { makeProject } from '@motion-canvas/core'; 2 | 3 | import building_the_protocol_1 from './scenes/building_the_protocol_1?scene'; 4 | import building_the_protocol_2 from './scenes/building_the_protocol_2?scene'; 5 | import commitments_1 from './scenes/commitments_1?scene'; 6 | import commitments_2 from './scenes/commitments_2?scene'; 7 | import discussion_1_fix from './scenes/discussion_1_fix?scene'; 8 | import discussion_1 from './scenes/discussion_1?scene'; 9 | import discussion_2 from './scenes/discussion_2?scene'; 10 | import discussion_3 from './scenes/discussion_3?scene'; 11 | import graph_coloring from './scenes/graph_coloring?scene'; 12 | import mario_big_graph from './scenes/mario_big_graph?scene'; 13 | import np_to_coloring_algorithm from './scenes/np_to_coloring_algorithm?scene'; 14 | import np_to_coloring_big_graph from './scenes/np_to_coloring_big_graph?scene'; 15 | import np_to_coloring_conversion from './scenes/np_to_coloring_conversion?scene'; 16 | import np_to_coloring from './scenes/np_to_coloring?scene'; 17 | import phone from './scenes/phone?scene'; 18 | import polylogo from './scenes/polylogo?scene'; 19 | import reductions from './scenes/reductions?scene'; 20 | import sudoku_reduction from './scenes/sudoku_reduction?scene'; 21 | import teacher_failing from './scenes/teacher_failing?scene'; 22 | import teacher_fixup from './scenes/teacher_fixup?scene'; 23 | import teacher_many_times from './scenes/teacher_many_times?scene'; 24 | import teacher from './scenes/teacher?scene'; 25 | import timeline from './scenes/timeline?scene'; 26 | 27 | import './global.css'; 28 | 29 | import full_protocol from './scenes/full_protocol?scene'; 30 | 31 | export default makeProject({ 32 | experimentalFeatures: true, 33 | scenes: [ 34 | polylogo, 35 | // teacher_failing, // TODO: malý crf 36 | // teacher, // TODO: malý crf 37 | // teacher_fixup, // TODO: malý crf 38 | // teacher_many_times, // TODO: malý crf 39 | // graph_coloring, // TODO: malý crf 40 | // building_the_protocol_1, // TODO: malý crf 41 | // building_the_protocol_2, // TODO: malý crf 42 | // discussion_1, 43 | discussion_1_fix, // TODO: render po pushi 44 | // discussion_2, // TODO: tohle je broken?? 45 | // discussion_3, 46 | // commitments_1, 47 | // commitments_2, 48 | // sudoku_reduction, 49 | // np_to_coloring, 50 | // full_protocol, 51 | // np_to_coloring_algorithm, 52 | // np_to_coloring_conversion, 53 | // np_to_coloring_big_graph, 54 | // reductions, 55 | // phone, 56 | // mario_big_graph, 57 | ], 58 | }); 59 | -------------------------------------------------------------------------------- /src/scenes/building_the_protocol_1.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | chain, 5 | Color, 6 | sequence, 7 | useLogger, 8 | useRandom, 9 | Vector2, 10 | waitFor, 11 | } from '@motion-canvas/core'; 12 | 13 | import { Solarized, solarizedPalette } from '../utilities'; 14 | import { exampleGraphData } from '../utilities_graph'; 15 | import { ProtocolScene } from '../utilities_protocol'; 16 | 17 | export default makeScene2D(function* (view) { 18 | view.fill(Solarized.base2); 19 | const logger = useLogger(); 20 | 21 | const scene = new ProtocolScene(view); 22 | yield* scene.setup('center', false, false); 23 | 24 | yield* scene.addText('prover', 'I can color this'); 25 | yield* scene.addText('verifier', 'Oh yeah?'); 26 | yield* scene.removeText('both'); 27 | yield* waitFor(1); 28 | 29 | yield* scene.sendGraph('prover'); 30 | yield* waitFor(1); 31 | yield* scene.graphRef().applyColors(); 32 | 33 | yield* waitFor(3); 34 | 35 | let revealedEdge = ['E', 'F']; 36 | let nonRevealedVertices = exampleGraphData.labels.filter( 37 | (label) => !revealedEdge.includes(label), 38 | ); 39 | 40 | yield* scene.graphRef().lockVertices(nonRevealedVertices); 41 | yield* waitFor(1); 42 | 43 | yield* all( 44 | scene.graphRef().pointAtVertex(revealedEdge[0], 1.5), 45 | scene.graphRef().pointAtVertex(revealedEdge[1], 1.5), 46 | ); 47 | 48 | yield* scene.sendGraph('verifier'); 49 | yield* waitFor(1); 50 | 51 | scene.verifierRef().expression('thinking'); 52 | 53 | yield* waitFor(2); 54 | 55 | yield* all( 56 | scene.graphRef().pointAtVertex(revealedEdge[0], 1, true), 57 | scene.graphRef().pointAtVertex(revealedEdge[1], 1, true), 58 | ); 59 | yield* scene.addText('verifier', 'Different\ncolors'); 60 | yield* waitFor(3); 61 | 62 | yield* all( 63 | scene.graphRef().unlockVertices(undefined, 1), 64 | scene.removeText('verifier'), 65 | scene.sendGraph('center'), 66 | scene.verifierRef().expression('neutral', 0), 67 | ); 68 | 69 | let i = 0; 70 | const customPalette = solarizedPalette.filter( 71 | (c) => 72 | [Solarized.red, Solarized.blue, Solarized.green].find((x) => x == c) === 73 | undefined, 74 | ); 75 | yield* all( 76 | chain( 77 | ...Array.from({ length: 7 }, () => { 78 | i++; 79 | return all( 80 | ...exampleGraphData.labels.map((v) => 81 | scene 82 | .graphRef() 83 | .changeVertexColor( 84 | v, 85 | customPalette[ 86 | (i + 87 | 2 * exampleGraphData.colors[exampleGraphData.labels.indexOf(v)]) % 88 | customPalette.length 89 | ], 90 | 0.1, 91 | ), 92 | ), 93 | waitFor(0.8), 94 | ); 95 | }), 96 | ), 97 | ); 98 | yield* scene.graphRef().applyColors(0.1, 0); 99 | 100 | yield* all(scene.graphRef().removeArrows(), scene.removeText('verifier')); 101 | scene.verifierRef().expression('neutral'); 102 | 103 | yield* scene.sendGraph('prover'); 104 | yield* scene.graphRef().unlockVertices(); 105 | 106 | revealedEdge = ['D', 'E']; 107 | nonRevealedVertices = exampleGraphData.labels.filter( 108 | (label) => !revealedEdge.includes(label), 109 | ); 110 | 111 | yield* scene.graphRef().lockVertices(nonRevealedVertices); 112 | yield* scene.sendGraph('verifier'); 113 | 114 | yield* waitFor(2); 115 | yield* all( 116 | scene.graphRef().pointAtVertex('C', 1, true), 117 | scene.graphRef().pointAtVertex('F', 1, true), 118 | ); 119 | 120 | scene.verifierRef().expression('looking'); 121 | scene.proverRef().expression('alarmed'); 122 | yield* waitFor(5); 123 | 124 | yield* waitFor(10); 125 | }); 126 | -------------------------------------------------------------------------------- /src/scenes/building_the_protocol_2.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D } from '@motion-canvas/2d'; 2 | import { all, delay, useLogger, Vector2, waitFor } from '@motion-canvas/core'; 3 | 4 | import { Solarized } from '../utilities'; 5 | import { exampleGraphData } from '../utilities_graph'; 6 | import { ProtocolScene } from '../utilities_protocol'; 7 | 8 | export default makeScene2D(function* (view) { 9 | view.fill(Solarized.base2); 10 | const logger = useLogger(); 11 | 12 | const scene = new ProtocolScene(view); 13 | 14 | yield* scene.setup('prover'); 15 | 16 | yield* all(scene.addText('prover', '1. Lock'), scene.graphRef().lockVertices()); 17 | 18 | yield* waitFor(3); 19 | 20 | yield* scene.sendGraph('verifier'); 21 | 22 | const challengeEdge: [string, string] = exampleGraphData.edges[0]; 23 | yield* all(scene.addText('verifier', '2. Challenge')); 24 | scene.verifierRef().expression('thinking'); 25 | yield* waitFor(1); 26 | scene.verifierRef().expression('neutral'); 27 | yield* scene.graphRef().pointAtEdge(challengeEdge); 28 | 29 | yield* all( 30 | scene.addText('prover', '3. Reveal'), 31 | scene.graphRef().unlockVertices(challengeEdge), 32 | scene.graphRef().removeArrows(), 33 | ); 34 | 35 | yield* waitFor(3); 36 | 37 | yield* all( 38 | scene.addText('verifier', '4. Check'), 39 | delay( 40 | 1, 41 | (function* () { 42 | scene.verifierRef().expression('thinking'); 43 | })(), 44 | ), 45 | ); 46 | 47 | yield* waitFor(1); 48 | scene.verifierRef().expression('happy'); 49 | 50 | yield* waitFor(5); 51 | }); 52 | -------------------------------------------------------------------------------- /src/scenes/commitments_1.tsx: -------------------------------------------------------------------------------- 1 | import { Circle, Img, makeScene2D } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | createRef, 5 | delay, 6 | easeInQuad, 7 | easeOutQuad, 8 | sequence, 9 | waitFor, 10 | } from '@motion-canvas/core'; 11 | 12 | import keyImage from '../assets/images/key.svg'; 13 | import { 14 | Participant, 15 | PROVER_POSITION, 16 | VERIFIER_POSITION, 17 | } from '../components/participant'; 18 | import { FONT_FAMILY, Solarized } from '../utilities'; 19 | import { Lock } from '../utilities_lock'; 20 | import { nextTo } from '../utilities_moving'; 21 | import { MyTxt } from '../utilities_text'; 22 | 23 | export default makeScene2D(function* (view) { 24 | view.fill(Solarized.base2); 25 | 26 | const prover = createRef(); 27 | const verifier = createRef(); 28 | const lock = createRef(); 29 | const circle = createRef(); 30 | const commitText = createRef(); 31 | const revealText = createRef(); 32 | const key = createRef(); 33 | 34 | view.add( 35 | <> 36 | 41 | , 42 | 47 | 55 | 56 | 64 | 72 | 73 | , 74 | ); 75 | 76 | nextTo(commitText(), circle(), 'down', 100); 77 | 78 | //// Lock 79 | yield* waitFor(1); 80 | yield* all(lock().lock(2), commitText().opacity(1, 1)); 81 | yield* circle().position(VERIFIER_POSITION.addX(-350), 1); 82 | 83 | yield* waitFor(2); 84 | //// Unlock 85 | nextTo(revealText(), circle(), 'down', 100); 86 | 87 | const passKey = function* () { 88 | nextTo(key(), prover(), 'right', 20); 89 | key().rotation(0); 90 | yield* all( 91 | key().opacity(1, 0.5), 92 | key().rotation(180, 1), 93 | key().position.x(verifier().position().x - 150, 1), 94 | key().position.y(-200, 0.5, easeInQuad).to(0, 0.5, easeOutQuad), 95 | ); 96 | }; 97 | 98 | const unlockUsingKey = function* () { 99 | yield* all(key().position.add([-50, 0], 1), key().opacity(0, 1), lock().unlock()); 100 | }; 101 | 102 | yield* all(revealText().opacity(1, 1), delay(0.5, passKey())); 103 | yield* unlockUsingKey(); 104 | prover().expression('happy'); 105 | verifier().expression('happy'); 106 | yield* waitFor(2); 107 | prover().expression('neutral'); 108 | verifier().expression('neutral'); 109 | 110 | //// Peeking is not allowed 111 | yield* all( 112 | circle().position([0, 0], 1), 113 | lock().lock(), 114 | commitText().opacity(0, 1), 115 | revealText().opacity(0, 1), 116 | ); 117 | yield* waitFor(2); 118 | 119 | yield* all(lock().seethrough(1, 0.5)); 120 | verifier().expression('looking'); 121 | prover().expression('alarmed'); 122 | yield* waitFor(1); 123 | verifier().expression('neutral'); 124 | prover().expression('neutral'); 125 | yield* all(lock().unseethrough()); 126 | 127 | //// The color cannot be changed 128 | yield* all(lock().unlock(1), circle().position(PROVER_POSITION.addX(350), 1)); 129 | lock().opacity(1); 130 | yield* waitFor(1); 131 | 132 | // Pass to verifier 133 | prover().expression('evil'); 134 | yield* lock().lock(); 135 | circle().fill(Solarized.green); 136 | yield* sequence(0.5, circle().position(VERIFIER_POSITION.addX(-350), 1), passKey()); 137 | 138 | // Open with a different color 139 | yield* unlockUsingKey(); 140 | verifier().expression('happy'); 141 | 142 | // prover().expression('embarrassed'); 143 | // verifier().expression('alarmed'); 144 | yield* waitFor(1); 145 | }); 146 | -------------------------------------------------------------------------------- /src/scenes/commitments_2.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D, Rect, Txt } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | createRef, 5 | delay, 6 | easeInOutQuad, 7 | Reference, 8 | Vector2, 9 | waitFor, 10 | } from '@motion-canvas/core'; 11 | 12 | import { 13 | Participant, 14 | PROVER_POSITION, 15 | VERIFIER_POSITION, 16 | } from '../components/participant'; 17 | import { Solarized } from '../utilities'; 18 | import { MyTxt, customTextLerp as textLerpWithDiff } from '../utilities_text'; 19 | 20 | export default makeScene2D(function* (view) { 21 | view.fill(Solarized.base2); 22 | 23 | const prover = createRef(); 24 | const verifier = createRef(); 25 | 26 | const color = 'red'; 27 | const otherColors = ['green', 'blue']; 28 | const salt = '10101111'; 29 | const collisionSalt = '00100000'; 30 | const hash = '0x72ab83de'; 31 | const otherHashes = ['0x0deadbeef', '0x2cf79693a', '0x00dfd9b2b']; 32 | 33 | const textsConfig = [ 34 | { 35 | participant: 'prover', 36 | texts: [ 37 | `${color}`, 38 | `${color}${salt}`, 39 | `hash(${color}${salt})`, 40 | `hash(${color}${salt}) = ${hash}`, 41 | ], 42 | }, 43 | { 44 | participant: 'prover', 45 | texts: [`“My box is ${hash}.”`], 46 | }, 47 | { 48 | participant: 'verifier', 49 | texts: ['“Ok, could you open it?”'], 50 | }, 51 | { 52 | participant: 'prover', 53 | texts: [`“It's ${color}. You can check using ${salt}.”`], 54 | }, 55 | { 56 | participant: 'verifier', 57 | texts: [ 58 | `hash(${color}${salt}) = ${hash}`, // first state: the hash is shown… 59 | ' ', // then (optionally) erase it (an empty string or whitespace) 60 | '“Looks good.”', // and finally, show the final message at the same position. 61 | ], 62 | }, 63 | // { 64 | // participant: 'verifier', 65 | // texts: [`hash(${color}${salt}) = ${hash}`], 66 | // }, 67 | // { 68 | // participant: 'verifier', 69 | // texts: ['“Looks good.”'], 70 | // }, 71 | 72 | // { 73 | // participant: 'verifier', 74 | // texts: ['“This matches the value you committed to.”', '“.”', '“Looks ok.”'], 75 | // }, 76 | ]; 77 | 78 | view.add( 79 | <> 80 | 85 | 90 | , 91 | ); 92 | 93 | const textRefs: Reference[] = textsConfig.map( 94 | ({ participant, texts }, index) => { 95 | const ref = createRef(); 96 | 97 | view.add( 98 | , 109 | ); 110 | 111 | return ref; 112 | }, 113 | ); 114 | 115 | view.width(view.width() * 2); 116 | view.height(view.height() * 2); 117 | 118 | const shiftY = -300; 119 | yield* all( 120 | prover().position.add([0, shiftY], 1), 121 | verifier().position.add([0, shiftY], 1), 122 | view.scale(view.scale().mul(0.9), 1), 123 | view.position(view.position().add(new Vector2(0, 20)), 1), 124 | ); 125 | 126 | const changeText = function* (textRef: Reference, changeTo: string) { 127 | const lengthDelta = Math.abs(changeTo.length - textRef().text().length); 128 | const time = clamp(lengthDelta * 0.05, 0.5, 1.8); 129 | 130 | yield* textRef().text(changeTo, time, easeInOutQuad, textLerpWithDiff); 131 | }; 132 | 133 | const highlights = [createRef(), createRef()]; 134 | 135 | function* showWhySalt() { 136 | // Show why we need the salt 137 | yield* all( 138 | changeText(textRefs[0], `hash(${color}) = ${otherHashes[0]}`), 139 | changeText(textRefs[1], `“My box is ${otherHashes[0]}.”`), 140 | ); 141 | verifier().expression('evil'); 142 | yield* waitFor(1); 143 | const allColorHashesText = 144 | `hash(${otherColors[1]}) = ${otherHashes[2]}\n` + 145 | `hash(${color}) = ${otherHashes[0]}\n` + 146 | `hash(${otherColors[0]}) = ${otherHashes[1]}\n`; 147 | yield* changeText(textRefs[2], allColorHashesText); 148 | yield* waitFor(1); 149 | 150 | const width = 768; 151 | // textRefs[0]().add( 152 | view.add( 153 | <> 154 | 163 | , 164 | 173 | , 174 | ); 175 | 176 | yield* all( 177 | highlights[0]().position( 178 | highlights[0]() 179 | .position() 180 | .addX(width / 2), 181 | 1, 182 | ), 183 | highlights[1]().position( 184 | highlights[1]() 185 | .position() 186 | .addX(width / 2), 187 | 1, 188 | ), 189 | highlights[0]().width(width + 30, 1), 190 | highlights[1]().width(width + 30, 1), 191 | ); 192 | 193 | yield* waitFor(1); 194 | // Reset to previous state 195 | yield* all( 196 | highlights[0]().width(0, 1), 197 | highlights[1]().width(0, 1), 198 | highlights[0]().position(highlights[0]().left(), 1), 199 | highlights[1]().position(highlights[1]().left(), 1), 200 | ); 201 | yield* waitFor(1); 202 | yield* all( 203 | textRefs[2]().opacity(0, 1), 204 | changeText(textRefs[0], textsConfig[0].texts[textsConfig[0].texts.length - 1]), 205 | changeText(textRefs[1], textsConfig[1].texts[textsConfig[1].texts.length - 1]), 206 | delay(0.5, verifier().expression('neutral', 0)), 207 | ); 208 | yield* waitFor(1); 209 | 210 | textRefs[2]().opacity(1); 211 | textRefs[2]().text(''); 212 | } 213 | 214 | for (let i = 0; i < textsConfig.length; i++) { 215 | const textConfig = textsConfig[i]; 216 | const textRef = textRefs[i]; 217 | 218 | if (i === 2) { 219 | yield* showWhySalt(); 220 | } 221 | 222 | for (let j = 0; j < textConfig.texts.length; j++) { 223 | const text = textConfig.texts[j]; 224 | yield* changeText(textRef, text); 225 | if (i != textsConfig.length - 1 || j != 1) yield* waitFor(1); 226 | } 227 | } 228 | 229 | yield* all(...textRefs.map((r) => changeText(r, ' '))); 230 | yield* waitFor(0.5); 231 | prover().expression('evil'); 232 | yield* waitFor(0.5); 233 | 234 | yield* all( 235 | changeText(textRefs[0], `hash(${color}${salt}) = ${hash}`), 236 | changeText(textRefs[1], `hash(${otherColors[0]}${collisionSalt}) = ${hash}`), 237 | ); 238 | yield* waitFor(1); 239 | 240 | const hashHighlightWidth = 375; 241 | highlights[0]().position(textRefs[0]().position().addX(100)); 242 | highlights[1]().position(textRefs[1]().position().addX(165)); 243 | yield* all( 244 | highlights[0]().width(hashHighlightWidth, 1), 245 | highlights[1]().width(hashHighlightWidth, 1), 246 | ); 247 | yield* waitFor(1); 248 | 249 | const hardText = createRef(); 250 | view.add( 251 | , 258 | ); 259 | yield* changeText(hardText, 'Hard!'); 260 | yield* waitFor(5); 261 | }); 262 | 263 | function clamp(value: number, min: number, max: number): number { 264 | return Math.min(Math.max(value, min), max); 265 | } 266 | -------------------------------------------------------------------------------- /src/scenes/discussion_1.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | createRef, 5 | delay, 6 | Reference, 7 | sequence, 8 | useLogger, 9 | Vector2, 10 | waitFor, 11 | } from '@motion-canvas/core'; 12 | 13 | import { Solarized } from '../utilities'; 14 | import { exampleGraphData } from '../utilities_graph'; 15 | import { alignTo, nextTo, shift } from '../utilities_moving'; 16 | import { ProtocolScene } from '../utilities_protocol'; 17 | import { MyLatex, MyTxt } from '../utilities_text'; 18 | 19 | export default makeScene2D(function* (view) { 20 | view.fill(Solarized.base2); 21 | const logger = useLogger(); 22 | 23 | const scene = new ProtocolScene(view); 24 | 25 | yield* scene.setup('verifier', true); 26 | 27 | yield* scene.graphRef().lockVertices(); 28 | yield* scene.fadeInGraph(1); 29 | 30 | yield* scene.graphRef().pointAtRandomEdges(['A', 'B'], 1); 31 | yield* scene.graphRef().unlockVertices(scene.graphRef().challengeEdge); 32 | yield* waitFor(1); 33 | 34 | yield* scene.fadeOutGraph(1); 35 | 36 | // improper coloring example 37 | const improperColoring = new Map([ 38 | ['A', 0], 39 | ['B', 1], 40 | ['C', 2], 41 | ['D', 1], 42 | ['E', 0], 43 | ['F', 0], 44 | ]); 45 | yield* all( 46 | scene.graphRef().applyColors(0, 0, improperColoring), 47 | scene.sendGraph('prover', 0), 48 | ); 49 | 50 | yield* scene.fadeInGraph(1); 51 | 52 | // vv: reverted back to pointAtEdge here for consistency with the other scenes 53 | // const e = scene.graphRef().getEdge(['E', 'F']).ref(); 54 | // const v1 = scene.graphRef().getVertex('E'); 55 | // const v2 = scene.graphRef().getVertex('F'); 56 | // yield* sequence( 57 | // 0, 58 | // all(v1.scale(1.42, 1), v2.scale(1.42, 1), e.lineWidth(2 * e.lineWidth(), 1)), 59 | // delay(1, all(v1.scale(1, 1), v2.scale(1, 1), e.lineWidth(e.lineWidth(), 1))), 60 | // ); 61 | yield* scene.graphRef().pointAtEdge(['E', 'F'], true, 2, false); 62 | 63 | yield* scene.graphRef().lockVertices(); 64 | yield* scene.sendGraph('verifier', 1); 65 | 66 | yield* scene.graphRef().pointAtRandomEdges(['E', 'F']); 67 | 68 | yield* scene.graphRef().unlockVertices(scene.graphRef().challengeEdge); 69 | 70 | scene.proverRef().expression('embarrassed'); 71 | scene.verifierRef().expression('alarmed'); 72 | yield* waitFor(2); 73 | yield* all(scene.fadeOutGraph(1), scene.removeText('both')); 74 | scene.proverRef().expression('neutral'); 75 | scene.verifierRef().expression('neutral'); 76 | 77 | // evidence from seeing different colors 78 | 79 | const properColors = new Map( 80 | exampleGraphData.labels.map((label, index) => [ 81 | label, 82 | exampleGraphData.colors[index], 83 | ]), 84 | ); 85 | 86 | yield* all( 87 | scene.graphRef().applyColors(0, 0, properColors), 88 | scene.sendGraph('prover', 0), 89 | scene.graphRef().lockVertices(), 90 | ); 91 | 92 | yield* scene.fadeInGraph(1); 93 | yield* scene.sendGraph('verifier', 1); 94 | 95 | yield* scene.challenge(); 96 | yield* scene.fadeOutGraph(1); 97 | 98 | yield* all(scene.sendGraph('prover', 0), scene.graphRef().lockVertices()); 99 | 100 | // getting evidence in each step 101 | 102 | const probabilityRef = createRef(); 103 | view.add( 104 | , 112 | ); 113 | yield* all( 114 | shift(scene.containerRef(), new Vector2(0, 150), 1), 115 | probabilityRef().opacity(1, 1), 116 | ); 117 | const productContainerRef = createRef(); 118 | 119 | view.add( 120 | , 128 | ); 129 | nextTo(productContainerRef(), probabilityRef(), 'down', 30); 130 | alignTo(productContainerRef(), probabilityRef(), 'left'); 131 | yield* productContainerRef().opacity(1, 1); 132 | const flyingTextRefs: Array> = []; 133 | flyingTextRefs.push(productContainerRef); 134 | 135 | yield* scene.fadeInGraph(1); 136 | for (let i = 0; i < 5; i++) { 137 | let duration = 1; 138 | let shortened = false; 139 | if (i >= 1) { 140 | duration = 0.3; 141 | shortened = true; 142 | } 143 | 144 | yield* scene.sendGraph('verifier', duration); 145 | yield* scene.challenge(true, shortened); 146 | 147 | const flyingRef = createRef(); 148 | 149 | view.add( 150 | , 156 | ); 157 | flyingRef() 158 | .absolutePosition(scene.verifierRef().absolutePosition()) 159 | .add(new Vector2(-100, -200)); 160 | yield* all( 161 | flyingRef().opacity(1, 0.5), 162 | nextTo( 163 | flyingRef(), 164 | flyingTextRefs[flyingTextRefs.length - 1](), 165 | 'right', 166 | 0, 167 | shortened ? 0.5 : 1, 168 | ), 169 | scene.sendGraph('prover', duration), 170 | ); 171 | flyingTextRefs.push(flyingRef); 172 | 173 | yield* scene.graphRef().lockVertices(undefined, shortened ? 0.5 : undefined); 174 | if (!shortened) yield* waitFor(0.5); 175 | } 176 | 177 | const finalTextRef = createRef(); 178 | view.add( 179 | , 189 | ); 190 | nextTo(finalTextRef(), flyingTextRefs[flyingTextRefs.length - 1](), 'right', 0, 0); 191 | yield* finalTextRef().opacity(1, 1); 192 | yield* waitFor(1); 193 | scene.verifierRef().expression('happy'); 194 | 195 | // learning the coloring 196 | 197 | yield* waitFor(2); 198 | }); 199 | -------------------------------------------------------------------------------- /src/scenes/discussion_1_fix.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | createEaseInElastic, 5 | createEaseInOutElastic, 6 | createEaseOutElastic, 7 | createRef, 8 | delay, 9 | easeInCubic, 10 | easeInExpo, 11 | easeInQuad, 12 | easeOutExpo, 13 | Reference, 14 | sequence, 15 | useLogger, 16 | Vector2, 17 | waitFor, 18 | } from '@motion-canvas/core'; 19 | 20 | import { Solarized } from '../utilities'; 21 | import { exampleGraphData } from '../utilities_graph'; 22 | import { alignTo, nextTo, shift } from '../utilities_moving'; 23 | import { ProtocolScene } from '../utilities_protocol'; 24 | import { MyLatex, MyTxt } from '../utilities_text'; 25 | 26 | export default makeScene2D(function* (view) { 27 | view.fill(Solarized.base2); 28 | const logger = useLogger(); 29 | 30 | const scene = new ProtocolScene(view); 31 | 32 | yield* scene.setup('verifier', true); 33 | 34 | yield* scene.graphRef().lockVertices(); 35 | yield* scene.fadeInGraph(1); 36 | 37 | yield* scene.graphRef().pointAtRandomEdges(['A', 'B'], 1); 38 | yield* scene.graphRef().unlockVertices(scene.graphRef().challengeEdge); 39 | yield* waitFor(1); 40 | 41 | yield* scene.fadeOutGraph(1); 42 | 43 | // improper coloring example 44 | const improperColoring = new Map([ 45 | ['A', 0], 46 | ['B', 1], 47 | ['C', 2], 48 | ['D', 1], 49 | ['E', 0], 50 | ['F', 0], 51 | ]); 52 | yield* all( 53 | scene.graphRef().applyColors(0, 0, improperColoring), 54 | scene.sendGraph('prover', 0), 55 | ); 56 | 57 | yield* scene.fadeInGraph(1); 58 | 59 | // vv: reverted back to pointAtEdge here for consistency with the other scenes 60 | // const e = scene.graphRef().getEdge(['E', 'F']).ref(); 61 | // const v1 = scene.graphRef().getVertex('E'); 62 | // const v2 = scene.graphRef().getVertex('F'); 63 | // yield* sequence( 64 | // 0, 65 | // all(v1.scale(1.42, 1), v2.scale(1.42, 1), e.lineWidth(2 * e.lineWidth(), 1)), 66 | // delay(1, all(v1.scale(1, 1), v2.scale(1, 1), e.lineWidth(e.lineWidth(), 1))), 67 | // ); 68 | yield* scene.graphRef().pointAtEdge(['E', 'F'], true, 2, false); 69 | 70 | yield* scene.graphRef().lockVertices(); 71 | yield* scene.sendGraph('verifier', 1); 72 | 73 | yield* scene.graphRef().pointAtRandomEdges(['E', 'F']); 74 | 75 | yield* scene.graphRef().unlockVertices(scene.graphRef().challengeEdge); 76 | 77 | scene.proverRef().expression('embarrassed'); 78 | scene.verifierRef().expression('alarmed'); 79 | 80 | yield* waitFor(2); 81 | 82 | let chance_ref = createRef(); 83 | 84 | view.add( 85 | , 92 | ); 93 | 94 | yield* all(shift(chance_ref(), new Vector2(50, 50), 1), chance_ref().opacity(1, 1)); 95 | 96 | yield* all( 97 | ...[...improperColoring.keys()].map((v, i) => 98 | delay(0, scene.graphRef().getVertex(v).size(new Vector2(60, 60), 0.5)), 99 | ), 100 | sequence( 101 | 0, 102 | ...exampleGraphData.edges.map((e) => 103 | all( 104 | scene.graphRef().getEdge(e).ref().stroke(Solarized.yellow2, 0.5), 105 | scene.graphRef().getEdge(e).ref().lineWidth(30, 0.5), 106 | ), 107 | ), 108 | ), 109 | // scene.graphRef().fadeEdgesSequential(0, 1, null, 0.25), 110 | ); 111 | 112 | yield* waitFor(2); 113 | 114 | yield* all( 115 | chance_ref().opacity(0, 1), 116 | scene.graphRef().fadeEdgesSequential(0, 1, null, 1), 117 | ...[...improperColoring.keys()].map((v, i) => 118 | scene.graphRef().getVertex(v).size(new Vector2(75, 75), 1), 119 | ), 120 | ); 121 | }); 122 | -------------------------------------------------------------------------------- /src/scenes/discussion_2.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D, Node } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | createRef, 5 | delay, 6 | sequence, 7 | useLogger, 8 | Vector2, 9 | waitFor, 10 | } from '@motion-canvas/core'; 11 | 12 | import { Cross } from '../components/cross'; 13 | import { Solarized } from '../utilities'; 14 | import { exampleGraphData, Graph } from '../utilities_graph'; 15 | import { nextTo, shift } from '../utilities_moving'; 16 | import { ProtocolScene } from '../utilities_protocol'; 17 | import { MyTxt } from '../utilities_text'; 18 | 19 | export default makeScene2D(function* (view) { 20 | view.fill(Solarized.base2); 21 | const logger = useLogger(); 22 | 23 | const scene = new ProtocolScene(view); 24 | 25 | yield* scene.setup('center', true); 26 | 27 | const g = new Graph(75); 28 | g.initialize(exampleGraphData); 29 | const graphLayout = g.getGraphLayout(); 30 | graphLayout.scale(0.6); 31 | view.add(graphLayout); 32 | nextTo(graphLayout, scene.verifierRef(), 'up', -100); 33 | yield* all(shift(scene.containerRef(), new Vector2(0, 150), 1), g.fadeIn(1)); 34 | 35 | exampleGraphData; 36 | yield* scene.fadeInGraph(1); 37 | let fast = false; 38 | yield* waitFor(5); 39 | scene.verifierRef().expression('evil'); 40 | const overlaidEdges = createRef(); 41 | view.add(); 42 | for (const e of [ 43 | ['B', 'A'], 44 | // ['C', 'B'], 45 | // ['D', 'C'], 46 | ['C', 'E'], 47 | ['F', 'D'], 48 | ]) { 49 | yield* sequence( 50 | fast ? 0.2 : 0.3, 51 | scene.graphRef().pointAtEdge(e as [string, string], undefined, fast ? 0.7 : 1), 52 | scene.graphRef().unlockVertices(e, fast ? 0.6 : 1), 53 | ); 54 | const copy = createRef(); 55 | overlaidEdges().add( 56 | 60 | {scene.graphRef().getVertex(e[0]).clone()} 61 | {scene.graphRef().getVertex(e[1]).clone()} 62 | {scene 63 | .graphRef() 64 | .getEdge(e as [string, string]) 65 | .ref() 66 | .clone()} 67 | , 68 | ); 69 | yield* all( 70 | copy().scale(graphLayout.scale, fast ? 0.7 : 1), 71 | copy().absolutePosition(graphLayout.absolutePosition, fast ? 0.7 : 1), 72 | delay(fast ? 0.2 : 0.6, scene.graphRef().lockVertices(e, fast ? 0.6 : 1)), 73 | ); 74 | fast = true; 75 | } 76 | 77 | scene.proverRef().expression('alarmed'); 78 | yield* waitFor(3); 79 | yield* all( 80 | overlaidEdges().opacity(0, 1), 81 | graphLayout.opacity(0, 1), 82 | scene.containerRef().opacity(0, 1), 83 | ); 84 | overlaidEdges().opacity(1); 85 | overlaidEdges().removeChildren(); 86 | scene.proverRef().expression('neutral'); 87 | scene.verifierRef().expression('neutral'); 88 | yield* waitFor(1); 89 | yield* scene.sendGraph('prover', 0); 90 | yield* all(scene.containerRef().opacity(1, 1)); 91 | yield* scene.graphRef().unlockVertices(); 92 | 93 | yield* waitFor(3); 94 | 95 | scene.proverRef().expression('thinking'); 96 | for (let i = 0; i < 10; i++) { 97 | yield* scene.graphRef().shuffleColors(0.05); 98 | yield* waitFor(0.15); 99 | } 100 | scene.proverRef().expression('neutral'); 101 | 102 | yield* waitFor(3); 103 | 104 | // Highlight the vertices that are originally red when shuffling. 105 | const v1 = scene.graphRef().getVertex('B'); 106 | const v2 = scene.graphRef().getVertex('D'); 107 | yield* all(v1.scale(1.42, 1), v2.scale(1.42, 1)); 108 | yield* scene.shufflingColors(false, false, 10, 0.4, false); 109 | yield* all(v1.scale(1, 1), v2.scale(1, 1)); 110 | 111 | fast = false; 112 | for (const [i, _e] of [ 113 | ['B', 'A'], 114 | ['A', 'C'], 115 | ].entries()) { 116 | const e = _e as [string, string]; 117 | yield* waitFor(3); 118 | yield* scene.shufflingColors(true, fast, 5); 119 | yield* scene.sendGraph('verifier', fast ? 0.5 : 1); 120 | 121 | if (i == 0) { 122 | yield* waitFor(2); 123 | yield* sequence( 124 | 0.1, 125 | graphLayout.opacity(1, 1), 126 | scene.verifierRef().expression('evil', 0), 127 | ); 128 | } 129 | yield* sequence( 130 | fast ? 0.2 : 0.3, 131 | scene.graphRef().pointAtEdge(e as [string, string], undefined, fast ? 0.7 : 1), 132 | scene.graphRef().unlockVertices(e, fast ? 0.6 : 1), 133 | ); 134 | const copy = createRef(); 135 | overlaidEdges().add( 136 | 140 | {scene.graphRef().getVertex(e[0]).clone()} 141 | {scene.graphRef().getVertex(e[1]).clone()} 142 | {scene 143 | .graphRef() 144 | .getEdge(e as [string, string]) 145 | .ref() 146 | .clone()} 147 | , 148 | ); 149 | if (i == 1) { 150 | const cross = createRef(); 151 | view.add( 152 | g.containerRef().position().add([-90, -30])} 155 | scale={10} 156 | opacity={0} 157 | />, 158 | ); 159 | 160 | yield* all( 161 | copy().scale(graphLayout.scale, 1), 162 | copy().absolutePosition( 163 | graphLayout.absolutePosition().addX(-70 * view.absoluteScale().magnitude), 164 | 1, 165 | ), 166 | ); 167 | scene.verifierRef().expression('alarmed'); 168 | scene.proverRef().expression('happy'); 169 | yield* all( 170 | cross().scale(5, 1), 171 | cross().opacity(1, 1), 172 | delay(0.8, cross().opacity(0, 1)), 173 | ); 174 | // scene.verifierRef().expression('neutral'); 175 | // scene.proverRef().expression('neutral'); 176 | break; 177 | } 178 | yield* all( 179 | copy().scale(graphLayout.scale, fast ? 0.7 : 1), 180 | copy().absolutePosition(graphLayout.absolutePosition, fast ? 0.7 : 1), 181 | delay(fast ? 0.4 : 2, scene.graphRef().lockVertices(e, fast ? 0.6 : 1)), 182 | delay(fast ? 0.2 : 2, scene.sendGraph('prover', fast ? 0.5 : 1)), 183 | ); 184 | } 185 | 186 | yield* waitFor(3); 187 | }); 188 | -------------------------------------------------------------------------------- /src/scenes/discussion_3.tsx: -------------------------------------------------------------------------------- 1 | import { Img, Line, makeScene2D, Txt } from '@motion-canvas/2d'; 2 | import { all, createRef, delay, sequence, Vector2, waitFor } from '@motion-canvas/core'; 3 | 4 | import { Solarized } from '../utilities'; 5 | import { exampleGraphData } from '../utilities_graph'; 6 | import { ProtocolScene } from '../utilities_protocol'; 7 | import { MyTxt } from '../utilities_text'; 8 | 9 | export default makeScene2D(function* (view) { 10 | view.fill(Solarized.base2); 11 | const scene = new ProtocolScene(view); 12 | 13 | yield* scene.setup('prover', false, false); 14 | for (const v of exampleGraphData.labels) { 15 | scene 16 | .graphRef() 17 | .getVertex(v) 18 | .add( 19 | 20 | 💩 21 | , 22 | ); 23 | } 24 | 25 | const edges = [ 26 | ['C', 'D'], 27 | ['E', 'F'], 28 | ]; 29 | for (let i = 0; i < 2; i++) { 30 | const edge = edges[i]; 31 | const arrowRef = createRef(); 32 | scene.containerRef().add( 33 | { 35 | return [ 36 | scene 37 | .proverRef() 38 | .cacheBBox() 39 | .topLeft.add(scene.proverRef().cacheBBox().topRight) 40 | .mul(0.5) 41 | .add(scene.proverRef().position()) 42 | .addY(-30) 43 | .addX(80), 44 | scene 45 | .graphRef() 46 | .getVertex(edge[0]) 47 | .position() 48 | .add(scene.graphRef().getVertex(edge[1]).position()) 49 | .mul(0.5) 50 | .add(scene.graphRef().containerRef().position()), 51 | ]; 52 | }} 53 | lineWidth={8} 54 | stroke={Solarized.proverText} 55 | endArrow 56 | ref={arrowRef} 57 | arrowSize={30} 58 | end={0.95} 59 | />, 60 | ); 61 | 62 | scene.proverRef().expression('thinking'); 63 | yield* all( 64 | scene.addText('prover', 'I know he will\nlook here…'), 65 | arrowRef().opacity(0).opacity(1, 0.5), 66 | ); 67 | yield* waitFor(1); 68 | scene.proverRef().expression('evil'); 69 | 70 | const chosenEdgeAnims = all( 71 | scene.graphRef().changeVertexColor(edge[0], Solarized.red), 72 | scene.graphRef().changeVertexColor(edge[1], Solarized.green), 73 | ); 74 | const otherEdgeAnims = all( 75 | ...['A', 'B', 'C', 'D', 'E', 'F'].map((c) => 76 | scene 77 | .graphRef() 78 | .getVertex(c) 79 | .children()[0] 80 | .opacity(edge[0] == c || edge[1] == c ? 0 : 1, 1), 81 | ), 82 | ); 83 | if (i == 0) { 84 | yield* chosenEdgeAnims; 85 | yield* waitFor(1); 86 | yield* otherEdgeAnims; 87 | } else { 88 | yield* all(chosenEdgeAnims, otherEdgeAnims); 89 | } 90 | 91 | yield* waitFor(1); 92 | 93 | yield* all( 94 | scene.proverTexts[0]().opacity(0, 1.5), 95 | arrowRef().opacity(0, 1.5), 96 | scene.graphRef().lockVertices(), 97 | ); 98 | yield* scene.sendGraph('verifier'); 99 | yield* sequence( 100 | 0.5, 101 | scene.graphRef().pointAtEdge(edge as [string, string], undefined, 1), 102 | scene.graphRef().unlockVertices(edge), 103 | ); 104 | scene.verifierRef().expression('happy'); 105 | //yield* scene.addText('verifier', '✓'); 106 | yield* all(waitFor(1), scene.removeText('prover')); 107 | scene.verifierRef().expression('neutral'); 108 | yield* all(scene.removeText('verifier'), scene.sendGraph('prover')); 109 | yield* scene.graphRef().unlockVertices(undefined, 1); 110 | yield* all( 111 | scene.graphRef().uncolor(1, 0), 112 | ...['A', 'B', 'C', 'D', 'E', 'F'].map((c) => 113 | scene.graphRef().getVertex(c).children()[0].opacity(0, 1), 114 | ), 115 | ); 116 | } 117 | yield* waitFor(3); 118 | }); 119 | -------------------------------------------------------------------------------- /src/scenes/full_protocol.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D } from '@motion-canvas/2d'; 2 | import { all, useLogger, Vector2, waitFor } from '@motion-canvas/core'; 3 | 4 | import { Solarized } from '../utilities'; 5 | import { shift } from '../utilities_moving'; 6 | import { ProtocolScene } from '../utilities_protocol'; 7 | 8 | export default makeScene2D(function* (view) { 9 | view.fill(Solarized.base2); 10 | const logger = useLogger(); 11 | 12 | const scene = new ProtocolScene(view); 13 | shift(scene.containerRef(), new Vector2(0, 50)); 14 | 15 | yield* scene.setup('prover', false); 16 | 17 | yield* scene.basicProtocol(); 18 | yield* waitFor(3); 19 | }); 20 | -------------------------------------------------------------------------------- /src/scenes/graph_coloring.tsx: -------------------------------------------------------------------------------- 1 | import { CubicBezier, Layout, Line, makeScene2D, Rect } from '@motion-canvas/2d'; 2 | import { createRef, Vector2 } from '@motion-canvas/core'; 3 | import { all, delay, sequence, waitFor } from '@motion-canvas/core/lib/flow'; 4 | 5 | import { indicate, Solarized } from '../utilities'; 6 | import { exampleGraphData, Graph } from '../utilities_graph'; 7 | import { absoluteToViewSpace, alignTo, nextTo, shift } from '../utilities_moving'; 8 | import { clues, solution, Sudoku } from '../utilities_sudoku'; 9 | import { MyTxt } from '../utilities_text'; 10 | import { makeWobbly } from './np_to_coloring_conversion'; 11 | 12 | export default makeScene2D(function* (view) { 13 | view.fill(Solarized.base2); 14 | 15 | const g = new Graph(75); 16 | g.initialize(exampleGraphData); 17 | const graph = g.getGraphLayout(); 18 | graph.scale(1.25); 19 | view.add(graph); 20 | yield* g.fadeIn(0); 21 | yield* waitFor(5); 22 | 23 | const swatchRef = createRef(); 24 | view.add( 25 | 26 | {[Solarized.red, Solarized.green, Solarized.blue].map((c, i) => ( 27 | 28 | ))} 29 | , 30 | ); 31 | alignTo(swatchRef(), view, 'left', 220, 0); 32 | const squares = Array.from(swatchRef().children()) as Rect[]; 33 | 34 | // Fade in the swatch 35 | yield* sequence(0.7, ...squares.map((square) => square.opacity(1, 0.5))); 36 | yield* waitFor(1); 37 | 38 | yield* all(swatchRef().opacity(0, 1), g.applyColors()); 39 | yield* waitFor(0.5); 40 | yield* all(...exampleGraphData.labels.map((v) => indicate(g.getVertex(v), 1.2))); 41 | yield* waitFor(0.4); 42 | yield* sequence( 43 | 0.8, 44 | g.pointAtVertexLooping('B', 45, 1.5), 45 | g.applyColors(undefined, undefined, new Map([['B', 2]])), 46 | ); 47 | yield* waitFor(1); 48 | yield* g.pointAtEdgeLooping(['B', 'C'], 45, 1.5); 49 | yield* waitFor(2); 50 | 51 | // 1) Create and show the sample Sudoku. 52 | const sudoku = new Sudoku(9, 55, solution, clues); 53 | const sudokuLayout = sudoku.getLayout(); 54 | yield* sudokuLayout.opacity(0, 0); 55 | view.add(sudokuLayout); 56 | sudokuLayout.scale(1.2); 57 | 58 | yield* sudoku.layoutRef().position.x(-500, 0); 59 | yield* all( 60 | g.applyColors(undefined, undefined, new Map([['B', 1]])), 61 | shift(graph, new Vector2(500, 0), 1), 62 | delay(0.5, sudokuLayout.opacity(1, 1)), 63 | ); 64 | 65 | const nosameSudoku = createRef(), 66 | nosameGraph = createRef(); 67 | 68 | view.add( 69 | <> 70 | 71 | 72 | , 73 | ); 74 | nextTo(nosameSudoku(), sudoku.layoutRef().children()[0].children()[6], 'up', 100); 75 | nextTo(nosameGraph(), g.getEdge(['B', 'C']).ref(), 'up', 0); 76 | nosameGraph().position.y(nosameSudoku().position.y()); 77 | 78 | const mkline = (a: Vector2, b: Vector2, c: number) => { 79 | const ref = createRef(); 80 | view.add( 81 | { 83 | return [a, b.add(a.sub(b).normalized.scale(c))]; 84 | }} 85 | lineWidth={8} 86 | stroke={Solarized.gray} 87 | endArrow 88 | ref={ref} 89 | arrowSize={30} 90 | opacity={0} 91 | />, 92 | ); 93 | return ref; 94 | }; 95 | 96 | const ar1 = mkline( 97 | nosameSudoku().bottom().add([-50, 0]), 98 | absoluteToViewSpace( 99 | view, 100 | sudoku.layoutRef().children()[0].children()[4].absolutePosition(), 101 | ).addY(-50), 102 | 25, 103 | ); 104 | 105 | const ar2 = mkline( 106 | nosameSudoku().bottom().add([50, 0]), 107 | absoluteToViewSpace( 108 | view, 109 | sudoku.layoutRef().children()[0].children()[8].absolutePosition(), 110 | ).addY(-50), 111 | 25, 112 | ); 113 | 114 | const ar3 = mkline( 115 | nosameGraph().bottom().add([-50, 0]), 116 | absoluteToViewSpace(view, g.getVertex('A').absolutePosition()), 117 | 70, 118 | ); 119 | 120 | const ar4 = mkline( 121 | nosameGraph().bottom().add([50, 0]), 122 | absoluteToViewSpace(view, g.getVertex('B').absolutePosition()), 123 | 70, 124 | ); 125 | 126 | // I'm shifting the view, so I hack the background using 127 | view.height(view.height() * 2); 128 | 129 | yield* all( 130 | shift(view, new Vector2(0, 100), 1), 131 | delay(0.5, all(...[nosameGraph, ar3, ar4].map((x) => x().opacity(1, 1)))), 132 | ); 133 | yield* waitFor(3); 134 | yield* all(...[nosameSudoku, ar1, ar2].map((x) => x().opacity(1, 1))); 135 | 136 | yield* waitFor(10); 137 | const line = createRef(); 138 | view.add( 139 | , 151 | ); 152 | 153 | line().scale(new Vector2(1, -1)); 154 | makeWobbly(line); 155 | line().opacity(0); 156 | yield* all(line().end(1, 1), line().opacity(1, 0.5)); 157 | yield* waitFor(10); 158 | yield* all( 159 | ...[sudoku.layoutRef, line, nosameSudoku, nosameGraph, ar1, ar2, ar3, ar4].map( 160 | (x) => x().opacity(0, 1), 161 | ), 162 | graph.position(Vector2.zero, 1.5), 163 | ); 164 | yield* waitFor(10); 165 | }); 166 | -------------------------------------------------------------------------------- /src/scenes/lock.tsx: -------------------------------------------------------------------------------- 1 | import { Circle, makeScene2D } from '@motion-canvas/2d'; 2 | import { createRef, waitFor } from '@motion-canvas/core'; 3 | 4 | import { Solarized } from '../utilities'; 5 | import { Lock } from '../utilities_lock'; 6 | 7 | export default makeScene2D(function* (view) { 8 | view.fill(Solarized.base2); 9 | 10 | const circle = createRef(); 11 | const lock = createRef(); 12 | 13 | view.add( 14 | <> 15 | 16 | 17 | , 18 | ); 19 | 20 | yield* waitFor(1); 21 | 22 | yield* lock().lock(); 23 | 24 | yield* waitFor(1); 25 | 26 | yield* lock().unlock(); 27 | }); 28 | -------------------------------------------------------------------------------- /src/scenes/mario_big_graph.tsx: -------------------------------------------------------------------------------- 1 | import { Circle, Line, makeScene2D, Node } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | clamp, 5 | Color, 6 | createRef, 7 | delay, 8 | easeInCubic, 9 | easeInQuad, 10 | easeOutBounce, 11 | easeOutElastic, 12 | getImageData, 13 | PlaybackState, 14 | sequence, 15 | useRandom, 16 | Vector2, 17 | waitFor, 18 | } from '@motion-canvas/core'; 19 | 20 | import mario_ascii from '../assets/images/mario_ascii.png'; 21 | import { Solarized } from '../utilities'; 22 | import { MyTxt, Write } from '../utilities_text'; 23 | 24 | function invertIncreasing(fn, x) { 25 | let l = 0, 26 | r = 1; 27 | for (let i = 0; i < 20; i++) { 28 | let m = (l + r) / 2; 29 | if (fn(m) < x) l = m; 30 | else r = m; 31 | } 32 | return (l + r) / 2; 33 | } 34 | 35 | const mario = [ 36 | [3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 3], 37 | [3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3], 38 | [3, 3, 0, 0, 0, 1, 1, 0, 1, 3, 3, 3], 39 | [3, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 3], 40 | [3, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1], 41 | [3, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 3], 42 | [3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 3, 3], 43 | [3, 3, 0, 0, 2, 0, 0, 0, 3, 3, 3, 3], 44 | [3, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 3], 45 | [0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0], 46 | [1, 1, 0, 2, 0, 2, 2, 0, 2, 0, 1, 1], 47 | [1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1], 48 | [1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1], 49 | [3, 3, 2, 2, 2, 3, 3, 2, 2, 2, 3, 3], 50 | [3, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 3], 51 | [0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0], 52 | ]; 53 | const colors = ['white', Solarized.red, Solarized.yellow, Solarized.base03]; 54 | const R = 16, 55 | S = 12; 56 | 57 | function gammaFunction(c: number) { 58 | return [0, 2, 5, 10][c]; 59 | } 60 | 61 | function colorAt(r: number, s: number, gamma: boolean = true) { 62 | const raw = 3 - mario[Math.floor(r)][Math.floor(s)]; 63 | return gamma ? gammaFunction(raw) : raw; 64 | } 65 | 66 | const minC = gammaFunction(0); 67 | const maxC = gammaFunction(3); 68 | 69 | export default makeScene2D(function* (view) { 70 | view.fill(Solarized.base2); 71 | const random = useRandom(); 72 | let n = 1000, 73 | m = 300; 74 | // To kill your computer: 75 | /* 76 | if (view.playbackState() == PlaybackState.Rendering) { 77 | n = 5000; 78 | m = 10000; 79 | }*/ 80 | 81 | const cam = ; 82 | const G = ; 83 | view.add(cam); 84 | cam.add(G); 85 | const V = ; 86 | G.add(V); 87 | while (V.children().length < n) { 88 | let r = random.nextFloat(0, R); 89 | let s = random.nextFloat(0, S); 90 | if (V.children().length == 0) { 91 | r = R / 2; 92 | s = S / 2; 93 | } 94 | const c = colorAt(r, s); 95 | let raw = colorAt(r, s, false); 96 | if (random.nextFloat(minC, maxC) > c) continue; 97 | V.add(); 98 | if (random.nextFloat(0, 1) < 0.7) raw = random.nextInt(1, 4); 99 | V.children()[V.children().length - 1].color = colors[raw]; 100 | } 101 | 102 | const its_subsampling = 50; 103 | const its_avg = 20; 104 | 105 | const E = ; 106 | G.add(E); 107 | while (E.children().length < m) { 108 | const i = random.nextInt(0, n - 1); 109 | const u = V.children()[i].position(); 110 | //if (random.nextFloat(minC, maxC) > colorAt(u.y, u.x)) continue; 111 | let minJ: number = undefined; 112 | let minDist = 10000; 113 | for (let _ = 0; _ < its_subsampling; _++) { 114 | const j = random.nextInt(i, n); 115 | if (i == j) continue; 116 | const v = V.children()[j].position(); 117 | const dist = u.sub(v).magnitude; 118 | if (dist < minDist) { 119 | minDist = dist; 120 | minJ = j; 121 | } 122 | } 123 | 124 | const j = minJ; 125 | const v = V.children()[j].position(); 126 | let minColor = maxC; 127 | let avgColor = 0; 128 | for (let i = 0; i < its_avg; i++) { 129 | const p = u.add(v.sub(u).mul(i / (its_avg - 1))); 130 | const c = colorAt(p.y, p.x); 131 | minColor = Math.min(c, minColor); 132 | avgColor += c; 133 | } 134 | avgColor /= its_avg; 135 | //if (random.nextFloat(minC, maxC) > minColor) continue; 136 | E.add( 137 | , 143 | ); 144 | E.children()[E.children().length - 1].color = new Color(V.children()[i].color).mix( 145 | V.children()[j].color, 146 | 0.5, 147 | ); 148 | E.children()[E.children().length - 1].i = i; 149 | E.children()[E.children().length - 1].j = j; 150 | } 151 | 152 | const ithDelay = (i: number) => invertIncreasing(easeInCubic, i / n) * 8; 153 | cam.scale(30); 154 | yield* all( 155 | cam.scale(1, 10), 156 | cam.position(new Vector2(0, -40), 10), 157 | all( 158 | ...V.children().map((v, i) => { 159 | let old = v.scale(); 160 | return delay(ithDelay(i), v.scale(0).scale(old, 1.2, easeOutElastic)); 161 | }), 162 | ), 163 | all( 164 | ...E.children().map((e: Line, i) => { 165 | let old = e.opacity(); 166 | e.end(0).opacity(0); 167 | return delay( 168 | Math.max(ithDelay(e.i) + 0.5, ithDelay(e.j) - 0.5), 169 | all(e.opacity(old, 1.2), e.end(1, 1.7)), 170 | ); 171 | }), 172 | ), 173 | ); 174 | yield* all( 175 | sequence(1 / n, ...V.children().map((v: Circle) => v.fill(v.color, 0.5))), 176 | sequence(1 / m, ...E.children().map((e: Line) => e.stroke(e.color, 0.5))), 177 | ); 178 | yield* waitFor(2); 179 | }); 180 | -------------------------------------------------------------------------------- /src/scenes/np_to_coloring_algorithm.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D, Rect } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | createRef, 5 | delay, 6 | easeOutBounce, 7 | linear, 8 | loop, 9 | useLogger, 10 | Vector2, 11 | waitFor, 12 | } from '@motion-canvas/core'; 13 | 14 | import { MarioAlgorithm } from '../components/mario_algorithm'; 15 | import { Solarized } from '../utilities'; 16 | import { nextTo, shift, toEdge } from '../utilities_moving'; 17 | import { PressedKey } from '../utilities_pressed_key'; 18 | import { MyTxt } from '../utilities_text'; 19 | 20 | const inputs = [ 21 | '🠦🠥A🠦🠦🠦🠥A🠦🠦🠦🠦🠦🠦B🠦🠦🠥🠦🠦🠦', //←A→→→→←', //↑→↑→B→→→→→A→→→↑A→↑→→↑→→→B', 22 | 'AB🠦🠤🠦🠤🠧🠧🠥🠥', // easter egg: Konami cheat sheet 23 | ]; 24 | 25 | export default makeScene2D(function* (view) { 26 | view.fill(Solarized.base2); 27 | 28 | const marioAlgo = createRef(); 29 | const inputsMask = createRef(); 30 | 31 | // 2. Container for our keys 32 | const inputsContainer = createRef(); 33 | 34 | // Output UI references 35 | const outputTextRect = createRef(); 36 | const outputText1 = createRef(); 37 | const outputText2 = createRef(); 38 | 39 | view.add( 40 | <> 41 | 42 | 43 | 55 | 56 | 67 | 68 | {/* "No"/"Yes" text container */} 69 | 77 | 78 | No 79 | 80 | 81 | World record{'\n'} 82 | not broken 83 | 84 | 85 | , 86 | ); 87 | 88 | nextTo(inputsMask(), marioAlgo(), 'right', 0); 89 | 90 | const logger = useLogger(); 91 | 92 | // Loop twice (No/Yes) 93 | for (let it = 0; it < 2; it++) { 94 | // Clear old keys from previous pass 95 | inputsContainer().children([]); 96 | 97 | // Build a PressedKey for each character 98 | const chars = Array.from(inputs[it]); 99 | for (const c of chars) { 100 | inputsContainer().add( 101 | new PressedKey({ 102 | text: c, 103 | fill: Solarized.base3, 104 | textColor: Solarized.base03, 105 | stroke: Solarized.base03, 106 | fontSize: 70, 107 | radius: 25, 108 | }), 109 | ); 110 | } 111 | 112 | // Let layout recalc so inputsContainer().width() is correct 113 | yield; 114 | 115 | // Start fully off-screen to the left: negative X 116 | nextTo(inputsContainer(), view, 'left', 100); 117 | //inputsContainer().x(-inputsContainer().width()); 118 | 119 | yield* waitFor(5); 120 | yield* inputsContainer().x( 121 | inputsContainer().width() / 2 + 100, 122 | it == 0 ? 4 : 3, 123 | linear, 124 | ); 125 | 126 | // Update "No/Yes" text 127 | outputTextRect().x(100); 128 | outputText1().text(['No', 'Yes'][it]); 129 | outputText2().text(['World record\nnot broken', 'World record\nbroken'][it]); 130 | outputText1().fill([Solarized.red, Solarized.green][it]); 131 | outputText2().fill([Solarized.red, Solarized.green][it]); 132 | 133 | yield* all( 134 | outputTextRect().opacity(1, 1), 135 | outputTextRect().position.add([450, 0], 1), 136 | ); 137 | 138 | yield* waitFor(1); 139 | yield* outputTextRect().opacity(0, 1); 140 | } 141 | 142 | yield* waitFor(2); 143 | }); 144 | -------------------------------------------------------------------------------- /src/scenes/np_to_coloring_big_graph.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Camera, 3 | Circle, 4 | Line, 5 | Node, 6 | makeScene2D, 7 | } from '@motion-canvas/2d'; 8 | import { 9 | createRef, 10 | useLogger, 11 | waitFor, 12 | Reference, 13 | all, 14 | } from '@motion-canvas/core'; 15 | 16 | import graphData from '../assets/facebook_layout.json'; 17 | import { Solarized } from '../utilities'; 18 | 19 | export default makeScene2D(function* (view) { 20 | view.fill(Solarized.base2); 21 | const camera = createRef(); 22 | 23 | // Our JSON uses an object for positions, so we cast here: 24 | const positions = (graphData.positions as unknown) as { 25 | [key: string]: [number, number]; 26 | }; 27 | const edges = (graphData.edges as unknown) as [string, string][]; 28 | 29 | const logger = useLogger(); 30 | 31 | const positionScale = 3000; 32 | const totalZoomDuration = 10; // camera zoom runs for 10 seconds 33 | const vertexAnimDuration = 0.5; // each vertex fades in over 0.5 sec 34 | const edgeFadeDuration = 2; // each edge fades in over 2.0 sec 35 | const startingDelay = 0; 36 | const yShift = -18; 37 | 38 | // Store references for each vertex 39 | const vertexRefs: { [id: string]: Reference } = {}; 40 | 41 | // Compute the maximum distance from center for scheduling fade-in delays 42 | let maxDistance = 0; 43 | for (const [_, [px, py]] of Object.entries(positions)) { 44 | const dist = Math.hypot(px * positionScale, py * positionScale); 45 | if (dist > maxDistance) { 46 | maxDistance = dist; 47 | } 48 | } 49 | 50 | // Keep track of each vertex’s delay 51 | const vertexDelays: { [id: string]: number } = {}; 52 | 53 | // Store references for each edge 54 | const edgeRefs: Reference[] = []; 55 | 56 | // Add camera + all vertices & edges to the view 57 | view.add( 58 | 59 | {/* Vertices */} 60 | {Object.entries(positions).map(([id, [px, py]]) => { 61 | const x = px * positionScale; 62 | const y = py * positionScale + yShift; 63 | // Vertex fade-in delay (custom formula, can be whatever you like) 64 | const dist = Math.hypot(x, y); 65 | const delay = ( 66 | (2.001 + Math.log10(Math.max(dist, maxDistance / 100) / maxDistance)) / 2 67 | ) * totalZoomDuration; 68 | vertexDelays[id] = delay + startingDelay; 69 | 70 | const ref = createRef(); 71 | vertexRefs[id] = ref; 72 | 73 | return ( 74 | , 114 | ); 115 | 116 | // Start the "camera" fully scaled in 117 | camera().scale(10); 118 | 119 | // ------------------------- 120 | // 1) Vertex fade-ins 121 | // ------------------------- 122 | const vertexGenerators = Object.entries(vertexDelays).map(([id, delay]) => 123 | (function* () { 124 | yield* waitFor(delay); 125 | yield* vertexRefs[id]().opacity(1, vertexAnimDuration); 126 | })(), 127 | ); 128 | 129 | // ------------------------- 130 | // 2) Edge fade-ins 131 | // after their endpoints appear 132 | // ------------------------- 133 | const edgeGenerators = edges.map(([a, b], i) => 134 | (function* () { 135 | const edgeDelay = 136 | Math.max(vertexDelays[a], vertexDelays[b]) + vertexAnimDuration; 137 | yield* waitFor(edgeDelay); 138 | yield* edgeRefs[i]().opacity(1, edgeFadeDuration); 139 | })(), 140 | ); 141 | 142 | // ------------------------- 143 | // 3) Run camera scale & fade-in concurrency 144 | // ------------------------- 145 | yield* all( 146 | camera().scale(0.7, totalZoomDuration), 147 | ...vertexGenerators, 148 | ...edgeGenerators, 149 | ); 150 | 151 | // ------------------------- 152 | // 4) After main fades, color the vertices in random order 153 | // ------------------------- 154 | // Shuffle vertex IDs 155 | const vertexIDs = Object.keys(vertexRefs); 156 | shuffleArray(vertexIDs); 157 | 158 | // Possible colors (adjust as you like) 159 | const colorChoices = [ 160 | Solarized.red, 161 | Solarized.blue, 162 | Solarized.green, 163 | ]; 164 | 165 | // Animate each vertex picking a random color concurrently using delayed starts. 166 | const colorGenerators = vertexIDs.map((vId, index) => 167 | (function* () { 168 | yield* waitFor(index * 0.005); 169 | const chosenColor = colorChoices[Math.floor(Math.random() * colorChoices.length)]; 170 | yield* all( 171 | vertexRefs[vId]().fill(chosenColor, 0.5), 172 | vertexRefs[vId]().scale(1.5, 0.5), 173 | ); 174 | })() 175 | ); 176 | 177 | yield* all(...colorGenerators); 178 | 179 | // Optionally wait a bit after everything completes 180 | yield* waitFor(3); 181 | }); 182 | 183 | /** 184 | * Utility function to shuffle an array in-place (Fisher-Yates). 185 | */ 186 | function shuffleArray(array: T[]): T[] { 187 | for (let i = array.length - 1; i > 0; i--) { 188 | const j = Math.floor(Math.random() * (i + 1)); 189 | [array[i], array[j]] = [array[j], array[i]]; 190 | } 191 | return array; 192 | } 193 | -------------------------------------------------------------------------------- /src/scenes/np_to_coloring_conversion.tsx: -------------------------------------------------------------------------------- 1 | import { CubicBezier, Img, makeScene2D, Rect } from '@motion-canvas/2d'; 2 | import { all, createRef, Reference, Vector2, waitFor } from '@motion-canvas/core'; 3 | 4 | import circuit_screenshot_simple from '../assets/images/circuit_screenshot_simple.png'; 5 | import sat_screenshot from '../assets/images/sat_screenshot.png'; 6 | import { MarioAlgorithm } from '../components/mario_algorithm'; 7 | import { Solarized } from '../utilities'; 8 | import { exampleGraphData, Graph } from '../utilities_graph'; 9 | import { shift } from '../utilities_moving'; 10 | 11 | export const makeWobbly = (bezier: Reference) => { 12 | const lineDirection = () => bezier().p3().sub(bezier().p0()); 13 | bezier().p1(() => bezier().p0().add(lineDirection().mul(0.4).rotate(30))); 14 | bezier().p2(() => bezier().p3().add(lineDirection().mul(-0.4).rotate(30))); 15 | }; 16 | 17 | export default makeScene2D(function* (view) { 18 | view.fill(Solarized.base2); 19 | 20 | const algorithmStep = createRef(); 21 | const circuitStep = createRef(); 22 | const satStep = createRef(); 23 | const coloringStep = createRef(); 24 | 25 | view.add(); 26 | 27 | const squareSize = algorithmStep().width(); 28 | 29 | const xGap = 400; 30 | const yGap = 250; 31 | const arrowColor = Solarized.gray; 32 | const scale1 = 0.9; 33 | const scale2 = 0.6; 34 | 35 | yield* all(algorithmStep().scale(scale1, 1), algorithmStep().position([-xGap, 0], 1)); 36 | 37 | const line1 = createRef(); 38 | 39 | view.add( 40 | <> 41 | 50 | 51 | 52 | algorithmStep().right().add(new Vector2(50, 0))} 58 | p3={circuitStep().left} 59 | end={0} 60 | endArrow 61 | zIndex={1} 62 | lineCap={'round'} 63 | /> 64 | , 65 | ); 66 | 67 | makeWobbly(line1); 68 | 69 | line1().opacity(0); 70 | yield* waitFor(2); 71 | yield* all(circuitStep().opacity(1, 1), line1().end(1, 1), line1().opacity(1, 0.5)); 72 | yield* waitFor(2); 73 | yield* all( 74 | algorithmStep().scale(scale2, 1), 75 | algorithmStep().position([-xGap, -yGap], 1), 76 | circuitStep().scale(scale2, 1), 77 | circuitStep().position([xGap, -yGap], 1), 78 | ); 79 | 80 | // Step 2: Circuit to SAT 81 | 82 | const line2 = createRef(); 83 | view.add( 84 | <> 85 | 95 | 96 | 97 | circuitStep().bottom().add(new Vector2(0, 40))} 103 | p3={() => satStep().top().add(new Vector2(0, 30))} 104 | end={0} 105 | endArrow 106 | lineCap={'round'} 107 | /> 108 | , 109 | ); 110 | makeWobbly(line2); 111 | 112 | line2().opacity(0); 113 | yield* all(satStep().opacity(1, 1), line2().end(1, 1), line2().opacity(1, 0.5)); 114 | yield* waitFor(1); 115 | 116 | // Step 3: SAT to coloring 117 | 118 | const line3 = createRef(); 119 | 120 | const g = new Graph(50); 121 | g.initialize(exampleGraphData); 122 | const graphLayout = g.getGraphLayout(); 123 | graphLayout.scale(1.2); 124 | shift(graphLayout, new Vector2(0, 0)); // why is this here? makes it not centered... 125 | yield* g.applyColors(); 126 | yield* g.fadeIn(0); 127 | 128 | view.add( 129 | <> 130 | 140 | {/* Hacky to use a screenshot here, but we don't manipulate the graph at all so it's ok. */} 141 | {graphLayout} 142 | 143 | satStep().left().addX(-50)} 149 | p3={coloringStep().right} 150 | end={0} 151 | endArrow 152 | lineCap={'round'} 153 | /> 154 | , 155 | ); 156 | makeWobbly(line3); 157 | 158 | yield* all(coloringStep().opacity(1, 1), line3().end(1, 1)); 159 | 160 | yield* waitFor(2); 161 | 162 | const t = 2; 163 | yield* all( 164 | line1().p3(() => coloringStep().left().add(new Vector2(40, 0)), t), 165 | algorithmStep().scale(scale1, t), 166 | algorithmStep().position([-xGap, 0], t), 167 | coloringStep().scale(scale1, t), 168 | coloringStep().position([xGap, 0], t), 169 | line1().lineWidth(25, t), 170 | line1().arrowSize(50, t), 171 | // fade out 172 | line2().opacity(0, t * 0.5), 173 | line2().end(0, t * 0.5), 174 | line3().opacity(0, t * 0.5), 175 | line3().end(0, t * 0.5), 176 | circuitStep().opacity(0, t * 0.5), 177 | circuitStep().scale(0, t * 0.5), 178 | satStep().opacity(0, t * 0.5), 179 | satStep().scale(0, t * 0.5), 180 | ); 181 | yield* waitFor(5); 182 | }); 183 | -------------------------------------------------------------------------------- /src/scenes/phone_trust.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Circle, 3 | Img, 4 | Layout, 5 | LayoutProps, 6 | Line, 7 | makeScene2D, 8 | Node, 9 | Rect, 10 | Txt, 11 | } from '@motion-canvas/2d'; 12 | import { createRef, waitFor } from '@motion-canvas/core'; 13 | import chroma from 'chroma-js'; 14 | 15 | import android_logo from '../assets/icons/android-brands-solid.svg'; 16 | import vasek1 from '../assets/images/vasek.png'; 17 | import vasek2 from '../assets/images/vasek2.png'; 18 | import gradientShader from '../shaders/gradient2.glsl'; 19 | import { FONT_FAMILY, Solarized } from '../utilities'; 20 | 21 | export interface BadgeProps extends LayoutProps { 22 | image: string; 23 | width?: number; 24 | height?: number; 25 | text?: string; 26 | monochromatic?: boolean; 27 | } 28 | 29 | export class Badge extends Layout { 30 | public readonly circle = createRef(); 31 | private readonly image = createRef(); 32 | private readonly text = createRef(); 33 | public constructor(props?: BadgeProps) { 34 | props.layout = true; 35 | props.gap = 50; 36 | props.alignItems = 'center'; 37 | props.direction = 'column'; 38 | super({ ...props }); 39 | 40 | this.add( 41 | <> 42 | 43 | this.circle().width() - 20} 46 | fill={Solarized.base1} 47 | shaders={{ 48 | fragment: gradientShader, 49 | uniforms: { 50 | mixColor: chroma('white') 51 | .rgba() 52 | .map((i) => i / 256), 53 | mixStrength: 8, 54 | distFactor: 1, 55 | position: () => this.image().position(), 56 | }, 57 | }} 58 | > 59 | 60 | this.circle().width() / 2} 64 | src={props.image} 65 | /> 66 | Math.max(this.image().width(), this.image().height()) * 5} 69 | rotation={this.image().rotation} 70 | fill={Solarized.base02} 71 | compositeOperation={'source-in'} 72 | opacity={(props.monochromatic ?? false) ? 1 : 0} 73 | /> 74 | 75 | 76 | 83 | , 84 | ); 85 | } 86 | } 87 | 88 | export default makeScene2D(function* (view) { 89 | view.fill(Solarized.base2); 90 | 91 | const prover = createRef(); 92 | const authority = createRef(); 93 | const verifier = createRef(); 94 | const lineSilent = createRef(); 95 | const lineHonest = createRef(); 96 | 97 | view.add( 98 | <> 99 | 100 | 101 | 108 | 109 | 110 | { 113 | const b = prover().position().add(prover().circle().right().addX(20)); 114 | const a = authority().position().add(authority().circle().left().addX(-20)); 115 | 116 | return [b, a]; 117 | }} 118 | lineWidth={20} 119 | stroke={Solarized.cyan} 120 | lineDash={[50, 30]} 121 | /> 122 | 131 | { 134 | const b = verifier().position().add(verifier().circle().left().addX(-20)); 135 | const a = authority().position().add(authority().circle().right().addX(20)); 136 | 137 | return [b, a]; 138 | }} 139 | lineWidth={20} 140 | stroke={Solarized.cyan} 141 | lineDash={[50, 30]} 142 | /> 143 | 152 | , 153 | ); 154 | 155 | yield* waitFor(1); 156 | }); 157 | -------------------------------------------------------------------------------- /src/scenes/reductions.tsx: -------------------------------------------------------------------------------- 1 | import { Img, Layout, Line, makeScene2D, Node } from '@motion-canvas/2d'; 2 | import { all, chain, createRef, sequence, Vector2, waitFor } from '@motion-canvas/core'; 3 | 4 | import minePath from '../assets/images/minesweeper.png'; 5 | import tuxPath from '../assets/images/tux_hacked.png'; 6 | import { MarioAlgorithm } from '../components/mario_algorithm'; 7 | import { fontSize, Solarized } from '../utilities'; 8 | import { exampleGraphData, Graph } from '../utilities_graph'; 9 | import { clues, solution, Sudoku } from '../utilities_sudoku'; 10 | import { MyLatex } from '../utilities_text'; 11 | 12 | export default makeScene2D(function* (view) { 13 | // ------------------------------ 14 | // 1) Background + Central Graph 15 | // ------------------------------ 16 | view.fill(Solarized.base2); 17 | 18 | const g = new Graph(75); 19 | g.initialize(exampleGraphData); 20 | const graph = g.getGraphLayout(); 21 | //graph.scale(1.25); 22 | view.add(graph); 23 | 24 | // Animate the graph into the scene 25 | yield* g.fadeIn(0); 26 | yield* all(g.applyColors(0, 0), graph.scale(0.5, 0)); 27 | yield* waitFor(3); 28 | 29 | // We'll consider the center to be at (0,0) 30 | // so item final positions are around it in a circle 31 | const center = new Vector2(0, 0); 32 | 33 | // ------------------------------ 34 | // 2) Circle Layout Parameters 35 | // ------------------------------ 36 | const totalItems = 5; 37 | // Items slide from a large radius inward 38 | const initialRadius = 600; 39 | // to a smaller final radius 40 | const finalRadius = 400; 41 | // Starting angle offset 42 | const alphaAngle = -125; 43 | // Uniform height for all objects 44 | const desiredHeight = 230; 45 | 46 | const arrowParams = [ 47 | { alpha: 0.35, beta: 0.58 }, // Sudoku 48 | { alpha: 0.3, beta: 0.58 }, // Mario 49 | { alpha: 0.25, beta: 0.7 }, // Tux 50 | { alpha: 0.28, beta: 0.62 }, // Zeta 51 | { alpha: 0.35, beta: 0.7 }, // Minesweeper 52 | ]; 53 | 54 | const offsets = [ 55 | [-55, -15], 56 | [60, -30], 57 | [0, 0], 58 | [0, 0], 59 | [-80, 0], 60 | ].map((i) => new Vector2(i[0], i[1])); 61 | 62 | // Utility: angle for item i 63 | function getAngleDeg(i: number): number { 64 | return alphaAngle + (i * 360) / totalItems; 65 | } 66 | 67 | // Utility: get position on a circle of radius r at angleDeg 68 | function getCirclePos(radius: number, angleDeg: number): Vector2 { 69 | const rad = (Math.PI / 180) * angleDeg; 70 | return new Vector2(radius * Math.cos(rad), radius * Math.sin(rad)); 71 | } 72 | 73 | // Utility: scale the node so that it has a certain "height" 74 | function setUniformHeight(node: Layout, targetHeight: number) { 75 | node.cacheBBox(); 76 | const size = node.size(); 77 | if (size.y <= 0) return; 78 | const factor = targetHeight / size.y; 79 | node.scale(factor); 80 | return node; 81 | } 82 | 83 | // Parametric combination: (1 - alpha)*Y + alpha*X 84 | function paramPoint(X: Vector2, Y: Vector2, alpha: number) { 85 | return new Vector2( 86 | (1 - alpha) * X.x + alpha * Y.x, 87 | (1 - alpha) * X.y + alpha * Y.y, 88 | ); 89 | } 90 | 91 | // Create and reveal an arrow from paramPoint(...alpha) to paramPoint(...beta) 92 | function createArrow(finalPos: Vector2, index: number) { 93 | // Extract alpha_i, beta_i for this item 94 | const { alpha, beta } = arrowParams[index]; 95 | 96 | const start = paramPoint(finalPos, center, alpha); 97 | const end = paramPoint(finalPos, center, beta); 98 | 99 | const arrowLineRef = createRef(); 100 | view.add( 101 | , 109 | ); 110 | return arrowLineRef; 111 | } 112 | 113 | const objects: Node[] = []; 114 | const sudoku = new Sudoku(9, 55, solution, clues); 115 | objects.push(setUniformHeight(sudoku.getLayout() as Layout, desiredHeight)); 116 | 117 | objects.push(setUniformHeight(new MarioAlgorithm(), desiredHeight)); 118 | 119 | { 120 | const img = ; 121 | objects.push(setUniformHeight(img as Layout, desiredHeight)); 122 | } 123 | 124 | objects.push( 125 | setUniformHeight( 126 | ( 127 | 131 | ) as Layout, 132 | desiredHeight * 0.7, 133 | ), 134 | ); 135 | 136 | { 137 | const img = ; 138 | objects.push(setUniformHeight(img as Layout, desiredHeight)); 139 | } 140 | 141 | const anims = []; 142 | for (let i = 0; i < 5; i++) { 143 | if (i == 2) anims.push(waitFor(3)); 144 | yield view.add(objects[i]); 145 | objects[i].opacity(0); 146 | const angle = getAngleDeg(i); 147 | const initialPos = getCirclePos(initialRadius, angle); 148 | const finalPos = getCirclePos(finalRadius, angle); 149 | objects[i].position(initialPos); 150 | const arrow = createArrow(finalPos, i); 151 | view.add(arrow); 152 | const origScale = objects[i].scale(); 153 | 154 | anims.push( 155 | sequence( 156 | 0.0, 157 | all( 158 | objects[i].position(finalPos.add(offsets[i]), 0), 159 | objects[i].opacity(1, 1), 160 | objects[i].scale(origScale.mul(0.5)).scale(origScale, 1), 161 | ), 162 | all(arrow().start(1).start(0, 0.8), arrow().end(1).end(1, 0.8)), 163 | ), 164 | ); 165 | } 166 | 167 | yield* chain(...anims); 168 | 169 | // Wait a bit at the end 170 | yield* waitFor(3); 171 | }); 172 | -------------------------------------------------------------------------------- /src/scenes/sudoku_coloring.tsx: -------------------------------------------------------------------------------- 1 | import { Layout, makeScene2D, Node } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | createRef, 5 | sequence, 6 | useLogger, 7 | Vector2, 8 | waitFor, 9 | } from '@motion-canvas/core'; 10 | 11 | import { 12 | cellSize, 13 | clues, 14 | fontSize, 15 | Graph, 16 | gridSize, 17 | Solarized, 18 | solution, 19 | Sudoku, 20 | } from '../utilities'; 21 | import { exampleEdges, exampleLabels } from '../utilities_graph'; 22 | import { MyTxt } from '../utilities_text'; 23 | 24 | export default makeScene2D(function* (view) { 25 | const logger = useLogger(); 26 | const graph = new Graph(); 27 | 28 | view.fill(Solarized.base2); 29 | 30 | // fadein sudoku and shift it 31 | const sudoku = new Sudoku(gridSize, cellSize, solution, clues); 32 | const sudokuTitleRef = createRef(); 33 | const sudokuLayoutRef = createRef(); 34 | view.add( 35 | 42 | 43 | {sudoku.getLayout()} 44 | , 45 | ); 46 | 47 | yield* sudokuLayoutRef().position.x(-view.size().x / 6, 1); 48 | 49 | for (const label of exampleLabels) { 50 | graph.addVertex(label); 51 | } 52 | 53 | exampleEdges.forEach(([from, to]) => graph.addEdge(from, to)); 54 | 55 | // Add graph node to the view with the specified positions 56 | const graphTitleRef = createRef(); 57 | const graphLayoutRef = createRef(); 58 | 59 | view.add( 60 | 61 | 62 | {graph.getNode(examplePositions)} 63 | , 64 | ); 65 | graphTitleRef().position.y(sudokuTitleRef().position.y()); 66 | yield* graph.fadeIn(1); 67 | 68 | // first coloring 69 | const coloring = [0, 1, 2, 0, 1, 2]; 70 | yield* sequence( 71 | 0.3, 72 | ...graph.vertices.map((vertex, i) => 73 | vertex.ref().fill(graph.palette[coloring[i]], 0.5), 74 | ), 75 | ); 76 | 77 | // highlight one vertex, change its color 78 | yield* all( 79 | graph.vertices[0].ref().scale(2, 1), 80 | graph.vertices[0].ref().position( 81 | graph.vertices[0] 82 | .ref() 83 | .position() 84 | .add(new Vector2(-l / 2, -l / 2)), 85 | 1, 86 | ), 87 | ); 88 | yield* graph.vertices[0].ref().fill(graph.palette[coloring[1]], 1); 89 | yield* waitFor(1); 90 | 91 | const sc = 4; 92 | yield* graph.edges[0].ref().lineWidth(graph.edges[0].ref().lineWidth() * sc, 1); 93 | yield* waitFor(1); 94 | yield* all( 95 | graph.edges[0].ref().lineWidth(graph.edges[0].ref().lineWidth() / sc, 1), 96 | graph.vertices[0].ref().scale(1, 1), 97 | graph.vertices[0].ref().position( 98 | graph.vertices[0] 99 | .ref() 100 | .position() 101 | .add(new Vector2(l / 2, l / 2)), 102 | 1, 103 | ), 104 | graph.vertices[0].ref().fill(graph.palette[coloring[0]], 1), 105 | ); 106 | yield* waitFor(1); 107 | 108 | // // highlight two vertices, then two cells of the sudoku 109 | 110 | // const vertexIndices = [0, 1]; 111 | // const sudokuIndices = [[0, 0], [0, 4]]; 112 | 113 | // yield* all( 114 | // ...vertexIndices.map(i => 115 | // all( 116 | // graph.vertices[vertexIndices[i]].ref().scale(2, 1), 117 | // graph.vertices[vertexIndices[i]].ref().position( 118 | // graph.vertices[vertexIndices[i]].ref().position().add(new Vector2(0, -l / 2)), 119 | // 1 120 | // ) 121 | // ) 122 | // ) 123 | // ); 124 | // yield* waitFor(1); 125 | 126 | // yield* all( 127 | // ...sudokuIndices.map(([row, col]) => 128 | // all( 129 | // sudoku.cells[row][col].ref().position( 130 | // sudoku.cells[row][col].ref().position().add(new Vector2(0, -l / 2)), 131 | // 1 132 | // ) 133 | // ) 134 | // ) 135 | // ); 136 | 137 | // Wait to observe the colors and movements before ending the scene 138 | yield* waitFor(3); 139 | }); 140 | -------------------------------------------------------------------------------- /src/scenes/sudoku_intro.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D } from '@motion-canvas/2d'; 2 | import { waitFor } from '@motion-canvas/core'; 3 | 4 | import { cellSize, clues, gridSize, Solarized, solution, Sudoku } from '../utilities'; 5 | 6 | export default makeScene2D(function* (view) { 7 | view.fill(Solarized.base2); 8 | // Create Sudoku instance 9 | const sudoku = new Sudoku(gridSize, cellSize, solution, clues); 10 | 11 | // Add Sudoku layout to the view 12 | view.add(sudoku.getLayout()); 13 | 14 | // Fill in non-clue cells with initial blur effect 15 | yield* sudoku.fillInNonClues(8); 16 | 17 | yield* waitFor(1); 18 | 19 | yield* sudoku.blur_nonClues(0); 20 | 21 | yield* waitFor(1); 22 | 23 | yield* sudoku.blur_nonClues(8); 24 | 25 | yield* waitFor(3); 26 | }); 27 | -------------------------------------------------------------------------------- /src/scenes/sudoku_reduction.tsx: -------------------------------------------------------------------------------- 1 | import { Layout, makeScene2D, Rect, Shape, Spline, Txt } from '@motion-canvas/2d'; 2 | import { Color, createRef, easeInOutBack, linear, Vector2 } from '@motion-canvas/core'; 3 | import { all, chain, sequence, waitFor } from '@motion-canvas/core/lib/flow'; 4 | 5 | import { indicate, Solarized, solarizedPalette } from '../utilities'; 6 | import { generateArcPoints } from '../utilities_graph'; 7 | import { nextTo } from '../utilities_moving'; 8 | import { clues, solution, Sudoku, SudokuGraph } from '../utilities_sudoku'; 9 | import { MyTxt } from '../utilities_text'; 10 | 11 | export default makeScene2D(function* (view) { 12 | view.fill(Solarized.base2); 13 | 14 | // 1) Create and show the sample Sudoku. 15 | const sudoku = new Sudoku(9, 55, solution, clues); 16 | const sudokuLayout = sudoku.getLayout(); 17 | view.add(sudokuLayout); 18 | 19 | // Position the sudoku on the left side eventually, but start it centered 20 | sudoku.layoutRef().position.x(0); 21 | sudokuLayout.scale(1.2); 22 | 23 | // Wait a moment to show the Sudoku 24 | yield* waitFor(2); 25 | 26 | // 2) Slide Sudoku to the left. 27 | // Adjust the x value so it moves nicely to the left half of the screen. 28 | yield* sudoku.layoutRef().position.x(-400, 1); 29 | 30 | const graph = new SudokuGraph(); 31 | 32 | view.add(graph.getGraphLayout()); 33 | yield; 34 | graph.containerRef().position([150, -280]); 35 | 36 | yield* graph.fadeVerticesSequential(0.01, 0.7, graph.gridVertices); 37 | 38 | // 4) On top of the nodes, show a swatch of nine colors. 39 | 40 | const swatchRef = createRef(); 41 | view.add( 42 | 43 | {solarizedPalette.map((c, i) => ( 44 | 45 | ))} 46 | , 47 | ); 48 | const squares = Array.from(swatchRef().children()) as Rect[]; 49 | 50 | yield* waitFor(1); 51 | // Fade in the swatch 52 | yield* sequence(0.2, ...squares.map((square) => square.opacity(1, 0.5))); 53 | yield* waitFor(1); 54 | 55 | // 5) On top of sudoku, show a list of 9 digits. 56 | const digitsRef = createRef(); 57 | view.add( 58 | 66 | {Array.from({ length: 9 }, (_, i) => ( 67 | 74 | ))} 75 | , 76 | ); 77 | nextTo(digitsRef(), sudoku.layoutRef(), 'up', 20); 78 | 79 | const digits = Array.from(digitsRef().children()) as Txt[]; 80 | yield* sequence(0.1, ...digits.map((digit) => digit.opacity(1, 0.3))); 81 | yield* waitFor(1); 82 | 83 | yield* all(sudoku.color(0, 1)); 84 | 85 | yield* waitFor(2); 86 | 87 | // 6) Fade out the swatch and the digits 88 | yield* all( 89 | swatchRef().opacity(0, 1), 90 | digitsRef().opacity(0, 1), 91 | sudoku.color(0, 1, true), 92 | ); 93 | 94 | yield* waitFor(3); 95 | 96 | yield* sudokuLayout.children()[0].zIndex(10, 0); 97 | yield* all( 98 | indicate(graph.getVertex('(0,0)')), 99 | indicate(sudokuLayout.children()[0].children()[0]), 100 | ); 101 | 102 | yield* waitFor(3); 103 | 104 | for (let e of [ 105 | ...graph.boxArcs, 106 | ...graph.rowArcs, 107 | ...graph.crossArcs, 108 | ...graph.cliqueArcs, 109 | ...graph.columnArcs, 110 | ]) { 111 | graph.getEdge(e).ref().stroke(Solarized.base1); 112 | } 113 | 114 | const arr = [ 115 | Array.from({ length: 8 }, (_, i) => [0, i + 1]), 116 | Array.from({ length: 8 }, (_, i) => [i + 1, 0]), 117 | [ 118 | [1, 1], 119 | [1, 2], 120 | [2, 1], 121 | [2, 2], 122 | ], 123 | ]; 124 | 125 | for (let i = 0; i < arr.length; i++) { 126 | yield* graph.fadeEdgesSequential( 127 | i == 0 ? 0.3 : 0.1, 128 | 1, 129 | arr[i].map(([r, c]) => ['(0,0)', `(${r},${c})`]), 130 | ); 131 | yield* waitFor(2); 132 | } 133 | 134 | yield* graph.fadeEdgesSequential( 135 | 0.02, 136 | 1, 137 | arr.flat().map(([r, c]) => ['(0,0)', `(${r},${c})`]), 138 | 0, 139 | ); 140 | 141 | yield* waitFor(1); 142 | 143 | const exampleCell = [0, 4]; 144 | 145 | yield* sudokuLayout.children()[exampleCell[0]].zIndex(10, 0); 146 | const rect = sudokuLayout.children()[exampleCell[0]].children()[ 147 | exampleCell[1] 148 | ] as Rect; 149 | 150 | yield* all( 151 | indicate(rect), 152 | indicate(graph.getVertex(`(${exampleCell[0]},${exampleCell[1]})`)), 153 | ); 154 | yield* waitFor(1); 155 | 156 | yield* sequence( 157 | 0.5, 158 | graph.containerRef().position.y(-150, 1), 159 | graph.fadeVerticesSequential(0.05, 1, graph.cliqueVertices), 160 | ); 161 | 162 | yield* graph.fadeEdgesSequential(0.02, 1, graph.cliqueArcs, 0.5); 163 | yield* waitFor(1); 164 | 165 | yield* graph.colorPalette(0.1, 0.5); 166 | yield* waitFor(1); 167 | 168 | // show edges to one nodes 169 | const exampleEdges = graph.crossArcs.filter( 170 | ([u, v]) => u === `(${exampleCell[0]},${exampleCell[1]})`, 171 | ); 172 | yield* graph.fadeEdgesSequential(0.1, 1, exampleEdges); 173 | yield* waitFor(1); 174 | 175 | const fromVertex = graph.getVertex(`(${exampleCell[0]},${exampleCell[1]})`); 176 | const toVertex = graph.getVertex( 177 | `clique-${solution[exampleCell[0]][exampleCell[1]] - 1}`, 178 | ); 179 | const nonExistingEdge = ( 180 | 193 | ); 194 | graph.containerRef().add(nonExistingEdge); 195 | 196 | yield* nonExistingEdge.opacity(1, 0.5); 197 | yield* waitFor(1); 198 | yield* fromVertex.fill(toVertex.fill(), 0.5); 199 | yield* waitFor(1); 200 | yield* all( 201 | nonExistingEdge.opacity(0, 0.5), 202 | fromVertex.fill(Solarized.gray, 0.5), 203 | graph.colorPalette(0, 1, true), 204 | ); 205 | 206 | const edgeGroups = [graph.rowArcs, graph.columnArcs, graph.boxArcs]; 207 | 208 | yield* graph.fadeEdgesSequential(0.01, 1, graph.crossArcs, 0.7); 209 | yield* graph.fadeEdgesSequential( 210 | 0.001, 211 | 0.5, 212 | [...graph.crossArcs, ...graph.cliqueArcs], 213 | 0.15, 214 | ); 215 | yield* waitFor(1); 216 | 217 | for (let i = 0; i < edgeGroups.length; i++) { 218 | let factor = i < 2 ? 1 / (9 * 4 * 9) : 1 / (9 * 2 * 9); 219 | yield* sequence( 220 | 1.5, 221 | //graph.fadeEdgesSequential(0.3 * factor, 1, edgeGroups[i], 0.7), 222 | graph.fadeEdgesSequential(0.3 * factor, 0.5, edgeGroups[i], i == 2 ? 0.5 : 0.15), 223 | ); 224 | } 225 | 226 | /*yield* indicate(graph.containerRef(), 1.1); 227 | yield* waitFor(1);*/ 228 | yield* all(graph.colorPalette(0, 1), graph.colorSolution(solution, undefined, 0, 1)); 229 | yield* waitFor(3); 230 | yield* all( 231 | ...[...Array(9).keys()].flatMap((i) => 232 | [...Array(9).keys()].map((j) => 233 | (function* () { 234 | let v = graph.getVertex('(' + i + ',' + j + ')'); 235 | let w = v.clone(); 236 | view.add(w); 237 | w.absolutePosition(v.absolutePosition()); 238 | let cell = sudoku.cells[i][j].textRef(); 239 | let sol = sudoku.solution[i][j]; 240 | yield* chain( 241 | all(v.absolutePosition(cell.absolutePosition(), 2), v.scale(1.3, 1)), 242 | waitFor(3), 243 | all( 244 | cell.text('' + sol, 0), 245 | cell.fill(solarizedPalette[sol - 1], 0), 246 | cell.opacity(1, 0), 247 | v.opacity(0.2, 1), 248 | ), 249 | ); 250 | })(), 251 | ), 252 | ), 253 | ); 254 | yield* waitFor(3); 255 | return; 256 | 257 | yield* indicate(sudokuLayout, 1.1); 258 | yield* waitFor(1); 259 | yield* sudoku.fillInSolutionFancy(); 260 | yield* waitFor(1); 261 | 262 | const eachDelay = 0.5, 263 | eachDuration = 0.3; 264 | yield* all( 265 | graph.colorPalette(eachDelay, eachDuration), 266 | sudoku.color(eachDelay, eachDuration), 267 | ); 268 | 269 | yield* graph.colorSolution(solution); 270 | yield* waitFor(1); 271 | const colorOrNot = function* (node: Shape, c: Color) { 272 | if (node.origFill === undefined) { 273 | node.save(); 274 | node.origFill = node.fill(); 275 | } 276 | const keep = (node.origFill as Color).css() == c.css(); 277 | yield* all( 278 | node.fill(keep ? c : Solarized.gray, 1), 279 | node.opacity(keep ? 1 : 0.3, 1), 280 | ); 281 | }; 282 | 283 | const colorOneColor = (c: Color) => 284 | all( 285 | ...graph.gridVertices 286 | .concat(graph.cliqueVertices) 287 | .map((v) => colorOrNot(graph.getVertex(v), c)), 288 | ...sudoku.cells 289 | .flat() 290 | .flat() 291 | .map((v) => colorOrNot(v.textRef(), c)), 292 | ); 293 | 294 | for (let i = 0; i < 5; i++) { 295 | yield* colorOneColor( 296 | sudoku.cells[0][i].textRef().origFill ?? 297 | (sudoku.cells[0][i].textRef().fill() as Color), 298 | ); 299 | yield* waitFor(i == 0 ? 2 : 1); 300 | } 301 | 302 | yield* all( 303 | ...graph.gridVertices 304 | .concat(graph.cliqueVertices) 305 | .map((v) => graph.getVertex(v).restore(1)), 306 | ...sudoku.cells 307 | .flat() 308 | .flat() 309 | .map((v) => v.textRef().restore(1)), 310 | ); 311 | 312 | yield* waitFor(5); 313 | }); 314 | -------------------------------------------------------------------------------- /src/scenes/teacher_failing.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D } from '@motion-canvas/2d'; 2 | 3 | import { terriblehack } from './teacher'; 4 | 5 | export default makeScene2D(function* (view) { 6 | yield* terriblehack(view, true); 7 | }); 8 | -------------------------------------------------------------------------------- /src/scenes/teacher_fixup.tsx: -------------------------------------------------------------------------------- 1 | import { Img, Layout, makeScene2D, View2D } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | createEaseInElastic, 5 | createEaseOutElastic, 6 | createRef, 7 | createSignal, 8 | delay, 9 | easeInOutBounce, 10 | easeOutBounce, 11 | linear, 12 | loop, 13 | ThreadGenerator, 14 | useRandom, 15 | Vector2, 16 | waitFor, 17 | } from '@motion-canvas/core'; 18 | 19 | import gear from '../assets/icons/gear-solid.svg'; 20 | import studentImage from '../assets/images/student.png'; 21 | import teacherImage from '../assets/images/teacher.png'; 22 | import { FONT_FAMILY, Icon, Solarized } from '../utilities'; 23 | import { MyTxt } from '../utilities_text'; 24 | 25 | export function* solve( 26 | view: View2D, 27 | object: MyTxt, 28 | solveAttempts: number = 10, 29 | solveTime: number = 0.1, 30 | answer: string = undefined, 31 | ): ThreadGenerator { 32 | const random = useRandom(); 33 | const [a, b] = object.text().split(' + ').map(Number); 34 | const result = answer === undefined ? a + b : answer; 35 | 36 | const icon = createRef(); 37 | 38 | const opacityScaleSignal = createSignal(0); 39 | 40 | view.add( 41 | opacityScaleSignal() * object.opacity() * 0.25} 47 | scale={() => object.scale().mul(opacityScaleSignal())} 48 | />, 49 | ); 50 | 51 | icon().absolutePosition(object.absolutePosition); 52 | 53 | yield loop(() => icon().rotation(0).rotation(360, 1, linear)); 54 | 55 | const gearDuration = solveTime * solveAttempts; 56 | 57 | yield* all( 58 | opacityScaleSignal(1, gearDuration), 59 | delay( 60 | gearDuration / 2, 61 | all( 62 | ...Array.from({ length: solveAttempts }, (_, i) => { 63 | let number; 64 | if (i == solveAttempts - 1) number = result; 65 | else number = random.nextInt(10, 99); 66 | 67 | return delay(i * solveTime, object.text(`${number}`, (solveTime / 3) * 2)); 68 | }), 69 | delay(gearDuration / 2, opacityScaleSignal(0, gearDuration)), 70 | ), 71 | ), 72 | ); 73 | 74 | icon().remove(); 75 | } 76 | 77 | function* addChallengeAndResponse( 78 | view, 79 | challengeLayout, 80 | responseLayout, 81 | challenge, 82 | teacher, 83 | student, 84 | responseObject, 85 | quick: boolean, 86 | gearColor, 87 | fake: boolean, 88 | ) { 89 | if (fake) quick = true; 90 | const random = useRandom(); 91 | 92 | const num1 = random.nextInt(10, 99); 93 | const num2 = random.nextInt(10, 99); 94 | 95 | const newChallenge = createRef(); 96 | newChallenge( 97 | challenge().clone().top(teacher().top().addY(100)).text(`${num1} + ${num2}`), 98 | ); 99 | 100 | challengeLayout().add(newChallenge().clone().opacity(0)); 101 | view.add(newChallenge()); 102 | 103 | const challengeIndex = challengeLayout().children().length - 1; 104 | 105 | const newResponse = createRef(); 106 | newResponse(newChallenge().clone()); 107 | newResponse().top(student().top().addY(100)); 108 | 109 | view.add(newResponse()); 110 | 111 | responseLayout().add( 112 | responseObject() 113 | .clone() 114 | .text(`${num1 + num2}`) 115 | .opacity(0), 116 | ); 117 | 118 | const responseIndex = responseLayout().children().length - 1; 119 | 120 | yield* all( 121 | all( 122 | newChallenge().absolutePosition( 123 | challengeLayout().children()[challengeIndex].absolutePosition(), 124 | fake ? 0 : 1, 125 | ), 126 | newChallenge() 127 | .opacity(0) 128 | .opacity(fake ? 0 : 1, fake ? 0 : 1), 129 | newChallenge() 130 | .scale(0) 131 | .scale(fake ? 0 : new Vector2(-1, 1), fake ? 0 : 1), 132 | ), 133 | delay( 134 | quick ? 0 : 1, 135 | all( 136 | solve(view, newResponse(), fake ? 0 : quick ? 7 : 10), 137 | delay( 138 | quick ? 0 : 0.25, 139 | all( 140 | newResponse() 141 | .opacity(0) 142 | .opacity(fake ? 0 : 1, fake ? 0 : 0.5), 143 | newResponse() 144 | .scale(0) 145 | .scale(fake ? 0 : new Vector2(-1, 1), fake ? 0 : 1), 146 | newResponse().fill( 147 | gearColor != null ? gearColor : newResponse().fill(), 148 | fake ? 0 : 1, 149 | ), 150 | newResponse().absolutePosition( 151 | responseLayout().children()[responseIndex].absolutePosition(), 152 | fake ? 0 : 1, 153 | ), 154 | ), 155 | ), 156 | ), 157 | ), 158 | ); 159 | 160 | challengeLayout().children()[challengeIndex].remove(); 161 | challengeLayout().add(newChallenge()); 162 | 163 | responseLayout().children()[responseIndex].remove(); 164 | responseLayout().add(newResponse()); 165 | } 166 | 167 | function* animatePercentage( 168 | view: View2D, 169 | responseLayout: Reference, 170 | i, 171 | percentageStr: string = undefined, 172 | ) { 173 | const p = createRef(); 174 | 175 | // Calculate percentage based on the current index 176 | const percentage = Math.pow(0.9, i) * 100; 177 | const text = 178 | percentageStr === undefined 179 | ? `[${percentage.toFixed(i >= 3 ? 1 : 0)}%]` 180 | : `[${percentageStr}%]`; 181 | 182 | view.add( 183 | , 194 | ); 195 | 196 | p().absolutePosition(() => { 197 | const response = responseLayout().children()[i] as MyTxt; 198 | let responseCenter = response.absolutePosition(); 199 | responseCenter.x = 200 | responseLayout().localToWorld().transformPoint(responseLayout().left()).x - 201 | 80 * view.absoluteScale().x; 202 | return responseCenter; 203 | //return responseCenter 204 | // .addX(-response.width() / 2) 205 | // .addX(-p().width() / 2) 206 | // .addX(responseLayout().width() * 1.2); 207 | }); 208 | 209 | yield* all( 210 | p().opacity(0).opacity(1, 1), 211 | p().scale(0).scale(new Vector2(-1, 1), 1), 212 | responseLayout().children()[i].fill(Solarized.cyan, 1), 213 | ); 214 | } 215 | 216 | export function* terriblehack(view: View2D, failing: boolean = false) { 217 | view.fill(Solarized.base2); 218 | view.scale(new Vector2(-view.scale().x, view.scale().y)); 219 | 220 | const student = createRef(); 221 | const teacher = createRef(); 222 | 223 | yield view.add( 224 | <> 225 | view.height() / 2 - student().height() / 2} 230 | layout={false} 231 | src={studentImage} 232 | /> 233 | view.height() / 2 - teacher().height() / 2} 238 | layout={false} 239 | src={teacherImage} 240 | /> 241 | , 242 | ); 243 | 244 | // show teacher and student 245 | yield* all( 246 | student().opacity(0).opacity(1, 1), 247 | student().width(0).width(300, 1), 248 | teacher().opacity(0).opacity(1, 1), 249 | teacher().width(0).width(400, 1), 250 | ); 251 | 252 | const left = createRef(); 253 | const right = createRef(); 254 | const plus = createRef(); 255 | view.add( 256 | <> 257 | 265 | 273 | 281 | , 282 | ); 283 | 284 | yield* all( 285 | left().bottom(teacher().top().addY(-40).addX(80), 1), 286 | left().opacity(0).opacity(1, 1), 287 | left().scale(0).scale(new Vector2(-1, 1), 1), 288 | 289 | delay( 290 | 0.125, 291 | all( 292 | plus().bottom(teacher().top().addY(-40), 1), 293 | plus().opacity(0).opacity(1, 1), 294 | plus().scale(0).scale(new Vector2(-1, 1), 1), 295 | ), 296 | ), 297 | 298 | delay( 299 | 0.25, 300 | all( 301 | right().bottom(teacher().top().addY(-40).addX(-80), 1), 302 | right().opacity(0).opacity(1, 1), 303 | right().scale(0).scale(new Vector2(-1, 1), 1), 304 | ), 305 | ), 306 | ); 307 | 308 | let to = left().position().add(right().position()).div(2); 309 | 310 | const result = createRef(); 311 | view.add( 312 | <> 313 | 322 | , 323 | ); 324 | 325 | yield* all( 326 | left().position(to, 0.75), 327 | left().opacity(0, 0.75), 328 | right().position(to, 0.75), 329 | right().opacity(0, 0.75), 330 | plus().opacity(0, 0.75), 331 | plus().scale(0, 0.75), 332 | result().opacity(1, 1), 333 | result().scale(new Vector2(-1.5, 1.5), 0.75, createEaseOutElastic(1.5)), 334 | ); 335 | 336 | yield* waitFor(1); 337 | } 338 | 339 | export default makeScene2D(function* (view) { 340 | yield* terriblehack(view, false); 341 | }); 342 | -------------------------------------------------------------------------------- /src/scenes/timeline.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D, Layout, Img, Line } from '@motion-canvas/2d'; 2 | import { all, createRef, Vector2, waitFor } from '@motion-canvas/core'; 3 | import { MyTxt } from '../utilities_text'; 4 | import { Solarized } from '../utilities'; 5 | 6 | export default makeScene2D(function* (view) { 7 | // ------------------------------------------------ 8 | // 1. Setup background and timeline container 9 | // ------------------------------------------------ 10 | view.fill(Solarized.base2); 11 | 12 | // Provide an empty props object so TypeScript is happy 13 | const timelineGroup = new Layout({}); 14 | view.add(timelineGroup); 15 | 16 | // ------------------------------------------------ 17 | // 2. Timeline parameters and mapping from year -> x coordinate 18 | // ------------------------------------------------ 19 | const startYear = 1970; 20 | const endYear = 2000; 21 | const spacing = 150; // pixels per year difference 22 | const timelineLength = (endYear - startYear) * spacing; 23 | const margin = 100; // extra margin at both ends 24 | 25 | function getXForYear(year: number): number { 26 | return (year - startYear) * spacing; 27 | } 28 | 29 | // ------------------------------------------------ 30 | // 3. Draw the timeline line 31 | // ------------------------------------------------ 32 | const timelineLine = ( 33 | 41 | ); 42 | timelineGroup.add(timelineLine); 43 | 44 | // ------------------------------------------------ 45 | // 4. Add year markers (text labels above the line) 46 | // ------------------------------------------------ 47 | const yearLabels = [1970, 1975, 1980, 1985, 1990, 1995, 2000]; 48 | for (const year of yearLabels) { 49 | const x = getXForYear(year); 50 | const yearText = ( 51 | 52 | ); 53 | // Position above the line (e.g. y = 50) 54 | yearText.position.x(x); 55 | yearText.position.y(50); 56 | timelineGroup.add(yearText); 57 | } 58 | 59 | // ------------------------------------------------ 60 | // 5. Create paper representations 61 | // ------------------------------------------------ 62 | type Paper = { 63 | year: number; 64 | images: string[]; 65 | description: string; 66 | }; 67 | 68 | const papers: Paper[] = [ 69 | { 70 | year: 1971, 71 | images: ["path1.jpg", "path2.jpg"], 72 | description: "My favorite paper" 73 | }, 74 | { 75 | year: 1978, 76 | images: ["path3.jpg"], 77 | description: "Another influential paper" 78 | }, 79 | // Add more papers as needed 80 | ]; 81 | 82 | const paperImageHeight = 80; 83 | const imageGap = 10; 84 | 85 | for (const paper of papers) { 86 | // Provide an empty props object here as well 87 | const paperGroup = new Layout({}); 88 | const paperX = getXForYear(paper.year); 89 | 90 | // Description text above the timeline 91 | const descTextRef = createRef(); 92 | const descText = ( 93 | 99 | ); 100 | descTextRef().position.x(0); 101 | descTextRef().position.y(60); 102 | paperGroup.add(descText); 103 | 104 | // Author images below the timeline 105 | let currentX = 0; 106 | for (const imgPath of paper.images) { 107 | const authorImg = ( 108 | 109 | ); 110 | authorImg.position.x(currentX); 111 | authorImg.position.y(-60); 112 | paperGroup.add(authorImg); 113 | currentX += paperImageHeight + imageGap; 114 | } 115 | 116 | // Position the paperGroup at the timeline 117 | paperGroup.position.x(paperX); 118 | paperGroup.position.y(0); 119 | 120 | timelineGroup.add(paperGroup); 121 | } 122 | 123 | // ------------------------------------------------ 124 | // 6. Animate the timeline scrolling from right to left 125 | // ------------------------------------------------ 126 | timelineGroup.position.x(800); 127 | const targetX = - (timelineLength + margin); 128 | yield* timelineGroup.position.x(targetX, 20); 129 | 130 | yield* waitFor(2); 131 | }); 132 | -------------------------------------------------------------------------------- /src/shaders/gradient.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | 4 | #include "@motion-canvas/core/shaders/common.glsl" 5 | 6 | uniform vec4 mixColor; 7 | uniform float mixStrength; 8 | uniform float offset; 9 | 10 | void main() { 11 | // Sample the texture 12 | outColor = texture(sourceTexture, sourceUV); 13 | 14 | float x = sourceUV.x; 15 | float y = sourceUV.y * 0.25; 16 | 17 | // Calculate distance from the center (0.5, 0.5) 18 | float dist = distance(vec2(x, y), vec2(0.5, offset)); 19 | 20 | // We want a stronger mix near the center, so we can invert the distance. 21 | // Clamp the distance to 0.0 to 1.0 range for more control. 22 | float mixFactor = 1.0 - clamp(dist * 2.0, 0.0, 1.0); // Multiply distance by 2 to make the effect sharper 23 | 24 | mixFactor *= mixStrength; 25 | 26 | // Mix the sampled texture color with the mixColor, based on the mixFactor 27 | outColor.rgb = mix(outColor.rgb, mixColor.rgb, mixFactor); 28 | } 29 | -------------------------------------------------------------------------------- /src/shaders/gradient2.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | 4 | #include "@motion-canvas/core/shaders/common.glsl" 5 | 6 | uniform vec4 mixColor; 7 | uniform float mixStrength; 8 | uniform float distFactor; 9 | uniform vec2 position; 10 | 11 | void main() { 12 | // Sample the texture 13 | outColor = texture(sourceTexture, sourceUV); 14 | 15 | float px = position.x / resolution.x + 0.5; 16 | float py = position.y / resolution.y + 0.5; 17 | 18 | // Calculate distance from the center (0.5, 0.5) 19 | float dist = distance(sourceUV, vec2(px, py)); 20 | 21 | dist /= distFactor; 22 | 23 | // We want a stronger mix near the center, so we can invert the distance. 24 | // Clamp the distance to 0.0 to 1.0 range for more control. 25 | float mixFactor = 1.0 - clamp(dist * 2.0, 0.0, 1.0); // Multiply distance by 2 to make the effect sharper 26 | 27 | mixFactor *= mixStrength; 28 | 29 | // Mix the sampled texture color with the mixColor, based on the mixFactor 30 | outColor.rgb = mix(outColor.rgb, mixColor.rgb, mixFactor); 31 | } 32 | -------------------------------------------------------------------------------- /src/shaders/timedgradient.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | 4 | #include "@motion-canvas/core/shaders/common.glsl" 5 | 6 | uniform float t; 7 | uniform float gradientWidth; 8 | uniform float strokeWidth; 9 | 10 | float isBorder(float _r) { 11 | float min_a = 1., max_a = 0.; 12 | vec2 resolution = vec2(textureSize(sourceTexture, 0)); 13 | float r = _r; 14 | int ndirs = 64; 15 | for (int it = 0; it < 5; it++) { 16 | for (int i = 0; i < ndirs; i++) { 17 | float angle = radians(float(i) * 360. / float(ndirs)); 18 | vec2 d = vec2(r * cos(angle), r * sin(angle)) / resolution; 19 | float a = texture(sourceTexture, sourceUV + d).a; 20 | min_a = min(min_a, a); 21 | max_a = max(max_a, a); 22 | } 23 | r *= 0.8; 24 | } 25 | return max_a - min_a; 26 | } 27 | 28 | float alphaFromT(float t) { 29 | float _t = t * (1. + gradientWidth); 30 | float alpha = (sourceUV.x - (_t - gradientWidth)) / gradientWidth; 31 | return 1. - clamp(alpha, 0., 1.); 32 | } 33 | 34 | void main() { 35 | float _t = t * (1. + gradientWidth / 2.); 36 | outColor = texture(sourceTexture, sourceUV); 37 | float strokeAmount = isBorder(strokeWidth); 38 | float stroke_a = strokeAmount * alphaFromT(_t); 39 | float fill_a = (1. - strokeAmount) * alphaFromT(_t - gradientWidth / 2.); 40 | outColor.a *= stroke_a + fill_a; 41 | } 42 | -------------------------------------------------------------------------------- /src/utilities.tsx: -------------------------------------------------------------------------------- 1 | import { Img, Layout, LayoutProps, Node, Rect } from '@motion-canvas/2d'; 2 | import { 3 | Color, 4 | createRef, 5 | Reference, 6 | useLogger, 7 | useRandom, 8 | Vector2, 9 | waitFor, 10 | } from '@motion-canvas/core'; 11 | 12 | export const Solarized = { 13 | base03: '#002b36', 14 | base02: '#073642', 15 | base01: '#586e75', 16 | base00: '#657b83', 17 | base0: '#839496', 18 | base1: '#93a1a1', 19 | base2: '#eee8d5', 20 | base3: '#fdf6e3', 21 | yellow: '#d0b700', 22 | yellow2: '#b58900', // The original Solarized yellow 23 | orange: '#c1670c', 24 | orange2: '#cb4b16', // The original Solarized orange - too close to red 25 | red: '#dc322f', 26 | magenta: '#d33682', 27 | violet: '#6c71c4', 28 | blue: '#268bd2', 29 | cyan: '#2aa198', 30 | green: '#859900', 31 | 32 | background: '#eee8d5', // base2 33 | text: '#657b83', // base00 34 | gray: '#657b83', // base00 35 | proverText: '#b58900', // yellow 36 | verifierText: '#073642', // base02 37 | }; 38 | 39 | export const solarizedPalette = [ 40 | Solarized.red, 41 | Solarized.orange, 42 | Solarized.yellow, 43 | Solarized.green, 44 | Solarized.cyan, 45 | Solarized.blue, 46 | Solarized.violet, 47 | Solarized.magenta, 48 | '#3ecea0', // We've run out of solarized :( 49 | ]; 50 | 51 | export const fontSize = 50; 52 | export const FONT_FAMILY = 'Computer Modern Serif, Noto Color Emoji, sans-serif'; 53 | // sans-serif so that it's visible CMU hasn't loaded 54 | // (if this has happened to you, just force a HMR update by touching a file) 55 | 56 | export interface IconProps extends LayoutProps { 57 | path: string; 58 | color: Color | string; 59 | } 60 | 61 | export class Icon extends Layout { 62 | public readonly image: Reference; 63 | 64 | public constructor(props?: IconProps) { 65 | super({ ...props }); 66 | 67 | this.image = createRef(); 68 | 69 | this.add( 70 | 71 | 72 | Math.max(this.image().width(), this.image().height())} 75 | rotation={this.image().rotation} 76 | fill={props.color} 77 | compositeOperation={'source-in'} 78 | /> 79 | , 80 | ); 81 | } 82 | } 83 | 84 | export function addVectors( 85 | v1: [number, number], 86 | v2: [number, number], 87 | ): [number, number] { 88 | return [v1[0] + v2[0], v1[1] + v2[1]]; 89 | } 90 | 91 | export function* indicate(node: Node, newScale: number = 1.5) { 92 | // Save current z-level 93 | const oldZ = node.zIndex(); // get current z-value 94 | 95 | // Move it to the front 96 | yield* node.zIndex(999, 0); // instantly set z to a very large number 97 | 98 | const scale = node.scale(); 99 | // Animate scale up and back down 100 | yield* node.scale(scale.mul(newScale), 0.3); 101 | yield* waitFor(0.5); 102 | yield* node.scale(scale, 0.3); 103 | 104 | // Restore old z 105 | yield* node.zIndex(oldZ, 0); 106 | } 107 | 108 | export function logArray(arr: unknown[], message: string = '') { 109 | const logger = useLogger(); 110 | const prefix = message ? `${message}: ` : ''; 111 | logger.info(`${prefix}${JSON.stringify(arr)}`); 112 | } 113 | 114 | export function logPair(pair: [unknown, unknown], message: string = '') { 115 | const logger = useLogger(); 116 | const prefix = message ? `${message}: ` : ''; 117 | logger.info(`${prefix}(${JSON.stringify(pair[0])}, ${JSON.stringify(pair[1])})`); 118 | } 119 | 120 | export function logPosition(position: Vector2, message: string = '') { 121 | const logger = useLogger(); 122 | const prefix = message ? `${message}: ` : ''; 123 | logger.info( 124 | `${prefix}(${JSON.stringify(position.x)}, ${JSON.stringify(position.y)})`, 125 | ); 126 | } 127 | 128 | export function logValue(value: unknown, message: string = '') { 129 | const logger = useLogger(); 130 | const prefix = message ? `${message}: ` : ''; 131 | logger.info(`${prefix}${JSON.stringify(value)}`); 132 | } 133 | 134 | export function logLabeled(label: string, value: unknown) { 135 | const logger = useLogger(); 136 | logger.info(`${label}: ${JSON.stringify(value)}`); 137 | } 138 | 139 | export function shuffleArray(array: any[]) { 140 | const random = useRandom(); 141 | for (let i = array.length - 1; i > 0; i--) { 142 | const j = random.nextInt(0, i + 1); 143 | [array[i], array[j]] = [array[j], array[i]]; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/utilities_finger.tsx: -------------------------------------------------------------------------------- 1 | import { Img, Node, NodeProps, Rect, Shape, ShapeProps, Txt } from '@motion-canvas/2d'; 2 | import { createRef, Vector2 } from '@motion-canvas/core'; 3 | 4 | import pointingHand from './assets/images/pointing-hand-raw.png'; 5 | import { Solarized } from './utilities'; 6 | 7 | export interface FingerProps extends ShapeProps { 8 | padding?: number; 9 | } 10 | 11 | export class Finger extends Shape { 12 | public constructor(props?: FingerProps) { 13 | props.cache = true; 14 | super({ ...props }); 15 | const img = createRef(); 16 | this.add( 17 | 23 | img()?.width()} 26 | height={() => img()?.height()} 27 | compositeOperation={'source-in'} 28 | /> 29 | , 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/utilities_fix_view_scaling.tsx: -------------------------------------------------------------------------------- 1 | import { makeScene2D as makeScene2DOld, Scene2D, View2D } from '@motion-canvas/2d'; 2 | import { 3 | DescriptionOf, 4 | PlaybackState, 5 | ThreadGeneratorFactory, 6 | Vector2, 7 | } from '@motion-canvas/core'; 8 | 9 | import { alignTo } from './utilities_moving'; 10 | 11 | function fixRenderScaling(_view: View2D) { 12 | const view = _view.clone(); 13 | view.scale(1); 14 | _view.add(view); 15 | view.position(Vector2.zero); 16 | return view; 17 | } 18 | 19 | export function makeScene2D( 20 | runner: ThreadGeneratorFactory, 21 | ): DescriptionOf { 22 | return makeScene2DOld(function* (_view) { 23 | const view = fixRenderScaling(_view); 24 | yield* runner(view); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /src/utilities_lock.tsx: -------------------------------------------------------------------------------- 1 | import { Circle, Layout, Node, NodeProps, Rect } from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | createRef, 5 | createSignal, 6 | delay, 7 | easeInOutExpo, 8 | SimpleSignal, 9 | } from '@motion-canvas/core'; 10 | import chroma from 'chroma-js'; 11 | 12 | import gradientShader from './shaders/gradient.glsl'; 13 | import { Solarized } from './utilities'; 14 | 15 | export interface LockProps extends NodeProps { 16 | object?: Layout; 17 | } 18 | 19 | export class Lock extends Node { 20 | private readonly background = createRef(); 21 | private readonly top = createRef(); 22 | private readonly bottom = createRef(); 23 | private readonly lockTop = createRef(); 24 | private readonly lockBottom = createRef(); 25 | 26 | private readonly object: Layout = null; 27 | 28 | private readonly openScale: SimpleSignal = createSignal(1.5); 29 | 30 | private locked: boolean = false; 31 | 32 | public constructor(props?: LockProps) { 33 | super({ ...props }); 34 | 35 | this.object = props.object; 36 | 37 | const w = () => this.object.size.x() * 1.25; 38 | const h = () => this.object.size.y() * 1.25; 39 | 40 | const r = () => w() / 4; 41 | const lw = () => r() / 2; 42 | 43 | this.add( 44 | <> 45 | h() * this.openScale()} 50 | fill={Solarized.base02} 51 | stroke={Solarized.base01} 52 | scale={0} 53 | lineWidth={lw} 54 | zIndex={-1} 55 | radius={r} 56 | position={this.object.position} 57 | /> 58 | h() / 2} 63 | fill={Solarized.base00} 64 | zIndex={1} 65 | top={this.background().top} 66 | opacity={0} 67 | radius={() => [r(), r(), 0, 0]} 68 | shaders={{ 69 | fragment: gradientShader, 70 | uniforms: { 71 | mixColor: [0, 0, 0, 0], 72 | mixStrength: 0.5, 73 | offset: 0.5, 74 | }, 75 | }} 76 | /> 77 | h() / 2} 82 | fill={Solarized.base00} 83 | zIndex={1} 84 | bottom={this.background().bottom} 85 | opacity={0} 86 | radius={() => [0, 0, r(), r()]} 87 | shaders={{ 88 | fragment: gradientShader, 89 | uniforms: { 90 | mixColor: [0, 0, 0, 0], 91 | mixStrength: 0.5, 92 | offset: 0.0, 93 | }, 94 | }} 95 | /> 96 | lw() / 2} 104 | stroke={() => chroma(this.lockTop().fill().toString()).darken(0.3)} 105 | size={() => w() * 0.5} 106 | opacity={this.top().opacity} 107 | zIndex={2} 108 | /> 109 | lw() / 2} 117 | stroke={() => chroma(this.lockTop().fill().toString()).darken(0.3)} 118 | size={() => w() * 0.5} 119 | opacity={this.bottom().opacity} 120 | zIndex={2} 121 | /> 122 | , 123 | ); 124 | } 125 | 126 | public *lock(duration: number = 1.5) { 127 | const t = duration / 3; 128 | 129 | if (!this.children().includes(this.object)) { 130 | this.add(this.object); 131 | } 132 | 133 | yield* all( 134 | this.background().scale(1, t * 2), 135 | delay( 136 | t, 137 | all( 138 | this.openScale(1, t * 2, easeInOutExpo), 139 | this.top().opacity(1, t * 2), 140 | this.bottom().opacity(1, t * 2), 141 | this.object.opacity(0, t * 2), 142 | ), 143 | ), 144 | ); 145 | this.locked = true; 146 | } 147 | 148 | public *unlock(duration: number = 1.5) { 149 | const t = duration / 3; 150 | 151 | yield* all( 152 | all( 153 | this.openScale(1.5, t * 2, easeInOutExpo), 154 | this.top().opacity(0, t * 2), 155 | this.bottom().opacity(0, t * 2), 156 | this.object.opacity(1, t * 2), 157 | ), 158 | delay(t, this.background().scale(0, t * 2)), 159 | ); 160 | this.locked = false; 161 | } 162 | 163 | public *seethrough(duration: number = 1, opacity: number = 0.7) { 164 | if (!this.locked) return; 165 | yield* all( 166 | this.object.opacity(1, 0), 167 | this.background().opacity(0, duration), 168 | this.top().opacity(opacity, duration), 169 | this.bottom().opacity(opacity, duration), 170 | ); 171 | } 172 | 173 | public *unseethrough(duration: number = 1) { 174 | if (!this.locked) return; 175 | yield* all( 176 | this.background().opacity(1, duration), 177 | this.top().opacity(1, duration), 178 | this.bottom().opacity(1, duration), 179 | delay(duration, this.object.opacity(0, 0)), 180 | ); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/utilities_lockable_graph.tsx: -------------------------------------------------------------------------------- 1 | import { Node } from '@motion-canvas/2d'; 2 | import { all, createRef, Reference, ThreadGenerator } from '@motion-canvas/core'; 3 | 4 | import { Graph } from './utilities_graph'; 5 | import { Lock } from './utilities_lock'; 6 | 7 | export class LockableGraph extends Graph { 8 | public locks = new Map>(); 9 | 10 | override addVertex(label: string, position: [number, number]) { 11 | super.addVertex(label, position); 12 | const lockRef = createRef(); 13 | this.locks.set(label, lockRef); 14 | } 15 | 16 | protected override createVertexNode(label: string) { 17 | const vertexData = this.vertexMap.get(label); 18 | const lock = this.locks.get(label); 19 | 20 | return ( 21 | 22 | {super.createVertexNode(label)} 23 | 24 | 25 | ); 26 | } 27 | 28 | *lockVertices(vertices: string[] = [], duration: number = 1.5) { 29 | const animations: ThreadGenerator[] = []; 30 | if (vertices.length === 0) { 31 | vertices = [...this.vertexMap.keys()]; 32 | } 33 | for (const v of vertices) { 34 | const lock = this.locks.get(v)(); 35 | if (lock) { 36 | animations.push(lock.lock(duration)); 37 | } 38 | } 39 | yield* all(...animations); 40 | } 41 | 42 | *unlockVertices(vertices: string[] = [], duration: number = 1.5) { 43 | const animations: ThreadGenerator[] = []; 44 | if (vertices.length === 0) { 45 | vertices = [...this.vertexMap.keys()]; 46 | } 47 | for (const v of vertices) { 48 | const lock = this.locks.get(v)(); 49 | if (lock) { 50 | animations.push(lock.unlock(duration)); 51 | } 52 | } 53 | yield* all(...animations); 54 | } 55 | 56 | *setSeeThrough(seethrough: boolean) { 57 | yield* all( 58 | ...[...this.locks.entries()].map(([_, lock]) => { 59 | return (function* () { 60 | if (seethrough) yield* lock().seethrough(); 61 | else yield* lock().unseethrough(); 62 | })(); 63 | }), 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/utilities_moving.tsx: -------------------------------------------------------------------------------- 1 | import { Node, View2D } from '@motion-canvas/2d'; 2 | import { all, useLogger, Vector2 } from '@motion-canvas/core'; 3 | 4 | // all functions use absolute positions 5 | 6 | // Assuming a known frame size. Replace these with actual values or computations. 7 | const frameWidth = 1920; 8 | const frameHeight = 1080; 9 | 10 | const frameLeft = -frameWidth / 2; 11 | const frameRight = frameWidth / 2; 12 | const frameTop = -frameHeight / 2; 13 | const frameBottom = frameHeight / 2; 14 | 15 | type Direction = 'left' | 'right' | 'up' | 'down'; 16 | type Corner = 'UL' | 'UR' | 'DL' | 'DR'; 17 | 18 | // Helper to get node edges from the cached bounding box 19 | function getNodeEdges(node: Node) { 20 | // cacheBBox returns {x, y, width, height} 21 | let bbox = node.cacheBBox(); 22 | let p = node.localToWorld().transformPoint(bbox.topLeft); 23 | let q = node.localToWorld().transformPoint(bbox.bottomRight); 24 | const left = p.x; 25 | const right = q.x; 26 | const top = p.y; 27 | const bottom = q.y; 28 | 29 | const centerX = (left + right) / 2; 30 | const centerY = (top + bottom) / 2; 31 | 32 | return { 33 | left, 34 | right, 35 | top, 36 | bottom, 37 | centerX, 38 | centerY, 39 | width: right - left, 40 | height: bottom - top, 41 | }; 42 | } 43 | 44 | /** 45 | * Move the node to a given position or another node's position. 46 | * If duration > 0, animate; otherwise, apply immediately. 47 | */ 48 | export function moveTo(node: Node, newPos: Vector2 | Node, duration: number = 0) { 49 | const finalPos = newPos instanceof Node ? newPos.absolutePosition() : newPos; 50 | 51 | if (duration <= 0) { 52 | node.absolutePosition(finalPos); 53 | return (function* () {})(); 54 | } else { 55 | return (function* () { 56 | yield* node.absolutePosition(finalPos, duration); 57 | })(); 58 | } 59 | } 60 | 61 | /** 62 | * Shift the node by an offset. 63 | * If duration > 0, animate; otherwise, immediate. 64 | */ 65 | export function shift(node: Node, offset: Vector2, duration: number = 0) { 66 | offset.x *= node.view().absoluteScale().x; 67 | offset.y *= node.view().absoluteScale().y; 68 | const currentPos = node.absolutePosition(); 69 | const finalPos = currentPos.add(offset); 70 | 71 | if (duration <= 0) { 72 | node.absolutePosition(finalPos); 73 | return (function* () {})(); 74 | } else { 75 | return (function* () { 76 | yield* node.absolutePosition(finalPos, duration); 77 | })(); 78 | } 79 | } 80 | 81 | /** 82 | * Place node next to another node along a given direction with a buffer. 83 | * If duration > 0, animate; otherwise, immediate. 84 | */ 85 | export function alignTo( 86 | node: Node, 87 | other: Node, 88 | direction: Direction, 89 | buff: number = 0, 90 | duration: number = 0, 91 | ) { 92 | buff *= 93 | direction == 'up' || direction == 'down' 94 | ? node.view().absoluteScale().y 95 | : node.view().absoluteScale().x; 96 | const n = getNodeEdges(node); 97 | const o = getNodeEdges(other); 98 | 99 | let finalPos = node.absolutePosition(); 100 | 101 | switch (direction) { 102 | case 'left': 103 | finalPos = new Vector2(o.left + buff + n.width / 2, finalPos.y); 104 | break; 105 | case 'right': 106 | finalPos = new Vector2(o.right - buff - n.width / 2, finalPos.y); 107 | break; 108 | case 'up': 109 | finalPos = new Vector2(finalPos.x, o.top + buff + n.height / 2); 110 | break; 111 | case 'down': 112 | finalPos = new Vector2(finalPos.x, o.bottom - buff - n.height / 2); 113 | break; 114 | } 115 | 116 | if (duration <= 0) { 117 | node.absolutePosition(finalPos); 118 | return (function* () {})(); 119 | } else { 120 | return (function* () { 121 | yield* node.absolutePosition(finalPos, duration); 122 | })(); 123 | } 124 | } 125 | 126 | export function nextTo( 127 | node: Node, 128 | other: Node, 129 | direction: Direction, 130 | buff: number = 0, 131 | duration: number = 0, 132 | ) { 133 | buff *= 134 | direction == 'up' || direction == 'down' 135 | ? node.view().absoluteScale().y 136 | : node.view().absoluteScale().x; 137 | const n = getNodeEdges(node); 138 | const o = getNodeEdges(other); 139 | 140 | let finalPos = node.absolutePosition(); 141 | const otherPos = other.absolutePosition(); 142 | 143 | switch (direction) { 144 | case 'left': 145 | finalPos = new Vector2(o.left - buff - n.width / 2, otherPos.y); 146 | break; 147 | case 'right': 148 | finalPos = new Vector2(o.right + buff + n.width / 2, otherPos.y); 149 | break; 150 | case 'up': 151 | finalPos = new Vector2(otherPos.x, o.top - buff - n.height / 2); 152 | break; 153 | case 'down': 154 | finalPos = new Vector2(otherPos.x, o.bottom + buff + n.height / 2); 155 | break; 156 | } 157 | 158 | if (duration <= 0) { 159 | node.absolutePosition(finalPos); 160 | return (function* () {})(); 161 | } else { 162 | return (function* () { 163 | yield* node.absolutePosition(finalPos, duration); 164 | })(); 165 | } 166 | } 167 | 168 | /** 169 | * Move the node to the given edge of the frame. 170 | * direction: 'left'|'right'|'up'|'down' 171 | * buff: optional padding from the edge 172 | * duration: if >0, animate 173 | */ 174 | export function toEdge( 175 | node: Node, 176 | direction: 'left' | 'right' | 'up' | 'down', 177 | buff: number = 0, 178 | duration: number = 0, 179 | ) { 180 | buff *= 181 | direction == 'up' || direction == 'down' 182 | ? node.view().absoluteScale().y 183 | : node.view().absoluteScale().x; 184 | const n = getNodeEdges(node); 185 | let finalPos = node.absolutePosition(); 186 | 187 | switch (direction) { 188 | case 'left': 189 | finalPos = new Vector2(frameLeft + n.width / 2 + buff, finalPos.y); 190 | break; 191 | case 'right': 192 | finalPos = new Vector2(frameRight - n.width / 2 - buff, finalPos.y); 193 | break; 194 | case 'up': 195 | finalPos = new Vector2(finalPos.x, frameTop + n.height / 2 + buff); 196 | break; 197 | case 'down': 198 | finalPos = new Vector2(finalPos.x, frameBottom - n.height / 2 - buff); 199 | break; 200 | } 201 | 202 | if (duration <= 0) { 203 | node.absolutePosition(finalPos); 204 | return (function* () {})(); 205 | } else { 206 | return (function* () { 207 | yield* node.absolutePosition(finalPos, duration); 208 | })(); 209 | } 210 | } 211 | 212 | /** 213 | * Move the node to a given corner of the frame. 214 | * corner: 'top_left'|'top_right'|'bottom_left'|'bottom_right' 215 | * buffX, buffY: optional padding from the edges in X and Y directions 216 | * duration: if >0, animate 217 | * 218 | * Implemented by using toEdge() twice. 219 | */ 220 | export function toCorner( 221 | node: Node, 222 | corner: Corner, 223 | buffX: number = 0, 224 | buffY: number = 0, 225 | duration: number = 0, 226 | ) { 227 | buffX *= node.view().absoluteScale().x; 228 | buffY *= node.view().absoluteScale().y; 229 | let horizontalDirection: 'left' | 'right'; 230 | let verticalDirection: 'up' | 'down'; 231 | 232 | switch (corner) { 233 | case 'UL': 234 | horizontalDirection = 'left'; 235 | verticalDirection = 'up'; 236 | break; 237 | case 'UR': 238 | horizontalDirection = 'right'; 239 | verticalDirection = 'up'; 240 | break; 241 | case 'DL': 242 | horizontalDirection = 'left'; 243 | verticalDirection = 'down'; 244 | break; 245 | case 'DR': 246 | horizontalDirection = 'right'; 247 | verticalDirection = 'down'; 248 | break; 249 | } 250 | 251 | if (duration <= 0) { 252 | toEdge(node, horizontalDirection, buffX, 0); 253 | toEdge(node, verticalDirection, buffY, 0); 254 | return (function* () {})(); 255 | } else { 256 | return (function* () { 257 | yield* all( 258 | toEdge(node, horizontalDirection, buffX, duration), 259 | toEdge(node, verticalDirection, buffY, duration), 260 | ); 261 | })(); 262 | } 263 | } 264 | 265 | export function absoluteToViewSpace(view: View2D, p: Vector2) { 266 | const a = view.worldToLocal().transformPoint(p); 267 | return new Vector2(a.x, a.y); 268 | } 269 | -------------------------------------------------------------------------------- /src/utilities_pressed_key.tsx: -------------------------------------------------------------------------------- 1 | import { FONT_FAMILY, Solarized } from './utilities'; 2 | import { Rect, Txt } from '@motion-canvas/2d'; 3 | import { PossibleColor, PossibleVector2 } from '@motion-canvas/core/lib/types'; 4 | interface PressedKeyProps { 5 | text: string; 6 | fill?: PossibleColor; 7 | textColor?: PossibleColor; 8 | fontSize?: number; 9 | radius?: number; 10 | stroke?: PossibleColor; 11 | } 12 | export class PressedKey extends Rect { 13 | public constructor(props: PressedKeyProps) { 14 | super({ 15 | width: 90, // fixed square size 16 | height: 90, 17 | stroke: props.stroke ?? '#000', 18 | lineWidth: 3, 19 | fill: props.fill ?? '#ccc', 20 | radius: props.radius, 21 | layout: true, // layout = true, so that children are centered 22 | alignItems: 'center', // center child horizontally 23 | justifyContent: 'center', // center child vertically 24 | children: [ 25 | new Txt({ 26 | text: props.text, 27 | fontSize: props.fontSize ?? 48, 28 | fill: props.textColor ?? '#000', 29 | }), 30 | ], 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/utilities_text.tsx: -------------------------------------------------------------------------------- 1 | import { Latex, LatexProps, Shape, Txt, TxtProps } from '@motion-canvas/2d'; 2 | import { Color, tween } from '@motion-canvas/core'; 3 | import chroma from 'chroma-js'; 4 | import { diffChars } from 'diff'; 5 | 6 | import timedGradientShader from './shaders/timedgradient.glsl'; 7 | import { FONT_FAMILY, Solarized } from './utilities'; 8 | 9 | const TEXT_SIZE: number = 30; 10 | 11 | export class MyTxt extends Txt { 12 | constructor(props: TxtProps) { 13 | super({ 14 | fontFamily: FONT_FAMILY, 15 | fill: Solarized.text, 16 | fontSize: TEXT_SIZE, 17 | ...props, 18 | }); 19 | } 20 | } 21 | 22 | export class MyLatex extends Latex { 23 | constructor(props: LatexProps) { 24 | super({ 25 | fontFamily: FONT_FAMILY, 26 | fill: Solarized.text, 27 | fontSize: TEXT_SIZE, 28 | ...props, 29 | }); 30 | } 31 | } 32 | 33 | /** 34 | * A smarter interpolation function for strings that takes into account the diff 35 | * and keeps the changes in the same order. 36 | * 37 | * Currently it looks a bit less smooth when inserting characters at the beginning 38 | * because that leads to the whole string being shifted by the centering. 39 | */ 40 | export function customTextLerp(fromString: string, toString: string, value: number) { 41 | const changes = diffChars(fromString, toString); 42 | 43 | const totalChanges = changes 44 | .map((change) => (change.added || change.removed ? change.count : 0)) 45 | .reduce((acc, change) => acc + change); 46 | 47 | let text = ''; 48 | let changesToDo = value * totalChanges; // Note that this is a float 49 | 50 | for (const change of changes) { 51 | if (change.removed) { 52 | for (let i = 0; i < change.count; i++) { 53 | if (changesToDo > 0) { 54 | changesToDo--; 55 | } else { 56 | text += change.value[i]; 57 | } 58 | } 59 | } 60 | 61 | if (change.added) { 62 | for (let i = 0; i < change.count; i++) { 63 | if (changesToDo > 0) { 64 | text += change.value[i]; 65 | changesToDo--; 66 | } 67 | } 68 | } 69 | 70 | if (!change.added && !change.removed) { 71 | text += change.value; 72 | } 73 | } 74 | 75 | return text; 76 | } 77 | 78 | export function* Write( 79 | object: Shape, 80 | duration: number = 1, 81 | gradientWidth: number = 0.1, 82 | strokeWidth: number = 1, 83 | ) { 84 | object.shaders({ 85 | fragment: timedGradientShader, 86 | uniforms: { 87 | gradientWidth: gradientWidth, 88 | strokeWidth: strokeWidth * object.view().absoluteScale().magnitude, 89 | }, 90 | }); 91 | yield* tween(duration, (t) => { 92 | object.shaders()[0].uniforms['t'] = t; 93 | }); 94 | } 95 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@motion-canvas/2d/tsconfig.project.json", 3 | "include": [ 4 | "src" 5 | ], 6 | } -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'vite'; 2 | import motionCanvas from '@motion-canvas/vite-plugin'; 3 | import ffmpeg from '@motion-canvas/ffmpeg'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | motionCanvas(), 8 | ffmpeg(), 9 | ], 10 | }); 11 | --------------------------------------------------------------------------------