├── .eslintrc.json ├── .gitignore ├── app ├── app │ └── page.tsx ├── articles │ ├── layout.tsx │ ├── parabolas │ │ └── page.tsx │ └── riemann-sums │ │ └── page.tsx ├── layout.tsx ├── page.tsx ├── present │ ├── layout.tsx │ └── new │ │ └── page.tsx ├── providers.tsx └── tools │ ├── complex-domain-coloring-image-generator │ └── page.tsx │ ├── complex-sequence │ └── page.tsx │ ├── expression-image-generator │ └── page.tsx │ └── layout.tsx ├── components ├── Graph3D │ ├── Graph3D.tsx │ ├── GraphAxis3D.tsx │ ├── GraphBoundingBox3D.tsx │ ├── GraphComplexExpression3D.tsx │ ├── GraphContours3D.tsx │ ├── GraphCurve3D.tsx │ ├── GraphEquation3D.tsx │ ├── GraphEquation3DInternal.tsx │ ├── GraphEquation3DShader.tsx │ ├── GraphExpression3D.tsx │ ├── GraphGrid3D.tsx │ ├── GraphGridLine3D.tsx │ ├── GraphInfiniteGrid3D.tsx │ ├── GraphPoint3D.tsx │ ├── GraphSurfaceGridMaterial.tsx │ ├── GraphText3D.tsx │ ├── GraphVectorField3D.tsx │ ├── GraphVectorField3DInternal.tsx │ └── display │ │ ├── Area.tsx │ │ ├── Contour.tsx │ │ ├── Point.tsx │ │ └── Vector.tsx ├── Latex.tsx ├── MathLiveInput.tsx ├── MathObjects │ ├── Action │ │ ├── editor.tsx │ │ ├── icon.tsx │ │ ├── index.tsx │ │ ├── output.tsx │ │ └── spec.ts │ ├── ComplexExpression │ │ ├── editor.tsx │ │ ├── icon.tsx │ │ ├── index.tsx │ │ ├── output.tsx │ │ └── spec.ts │ ├── Equation │ │ ├── editor.tsx │ │ ├── icon.tsx │ │ ├── index.tsx │ │ ├── output.tsx │ │ └── spec.ts │ ├── Expression │ │ ├── editor.tsx │ │ ├── icon.tsx │ │ ├── index.tsx │ │ ├── output.tsx │ │ └── spec.ts │ ├── Table │ │ ├── editor.tsx │ │ ├── icon.tsx │ │ ├── index.tsx │ │ ├── output.tsx │ │ └── spec.ts │ ├── VectorField │ │ ├── editor.tsx │ │ ├── icon.tsx │ │ ├── index.tsx │ │ ├── output.tsx │ │ └── spec.ts │ ├── index.tsx │ └── notes.md ├── MathQuillInput.tsx ├── Navigation.tsx ├── Slides │ ├── Box.tsx │ ├── Slide.tsx │ └── Slideshow.tsx ├── ThemeToggle.tsx └── UseGPU │ ├── component.tsx │ └── test.wgsl ├── hooks ├── useApplyUniforms.ts ├── useClippingPlanes.ts ├── useFlatContoursForEquation.ts ├── useResizeObserver.ts ├── useTrianglesForEquation3D.ts ├── useVectorField.tsx ├── useWorkQueue.ts └── useWorldCoordinateTransformation.ts ├── mathquill-0.10.1 ├── .exists--used_by_Makefile ├── fonts │ ├── Symbola-basic.eot │ ├── Symbola-basic.ttf │ ├── Symbola-basic.woff │ ├── Symbola-basic.woff2 │ ├── Symbola.eot │ ├── Symbola.otf │ ├── Symbola.svg │ ├── Symbola.ttf │ ├── Symbola.woff │ └── Symbola.woff2 ├── mathquill.css └── mathquill.js ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public └── mathlive │ ├── fonts │ ├── KaTeX_AMS-Regular.woff2 │ ├── KaTeX_Caligraphic-Bold.woff2 │ ├── KaTeX_Caligraphic-Regular.woff2 │ ├── KaTeX_Fraktur-Bold.woff2 │ ├── KaTeX_Fraktur-Regular.woff2 │ ├── KaTeX_Main-Bold.woff2 │ ├── KaTeX_Main-BoldItalic.woff2 │ ├── KaTeX_Main-Italic.woff2 │ ├── KaTeX_Main-Regular.woff2 │ ├── KaTeX_Math-BoldItalic.woff2 │ ├── KaTeX_Math-Italic.woff2 │ ├── KaTeX_SansSerif-Bold.woff2 │ ├── KaTeX_SansSerif-Italic.woff2 │ ├── KaTeX_SansSerif-Regular.woff2 │ ├── KaTeX_Script-Regular.woff2 │ ├── KaTeX_Size1-Regular.woff2 │ ├── KaTeX_Size2-Regular.woff2 │ ├── KaTeX_Size3-Regular.woff2 │ ├── KaTeX_Size4-Regular.woff2 │ └── KaTeX_Typewriter-Regular.woff2 │ └── sounds │ ├── keypress-delete.wav │ ├── keypress-return.wav │ ├── keypress-spacebar.wav │ ├── keypress-standard.wav │ └── plonk.wav ├── rust └── joshs_graphing_calculator_lib │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── benches │ └── my_benchmark.rs │ ├── notes.md │ ├── pkg │ ├── joshs_graphing_calculator_lib.d.ts │ ├── joshs_graphing_calculator_lib.js │ ├── joshs_graphing_calculator_lib_bg.js │ ├── joshs_graphing_calculator_lib_bg.wasm │ ├── joshs_graphing_calculator_lib_bg.wasm-opt.wasm │ ├── joshs_graphing_calculator_lib_bg.wasm.d.ts │ └── package.json │ └── src │ ├── ast.rs │ ├── equation.rs │ ├── expression.rs │ ├── graphing.rs │ ├── lib.rs │ ├── point.rs │ ├── segment.rs │ ├── triangle.rs │ └── vector.rs ├── styles └── globals.css ├── tailwind.config.js ├── tsconfig.json ├── types.ts ├── utils ├── lerp.ts └── tailwindColors.ts └── workers ├── graphEquation.worker.ts └── graphEquationAPI.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals", 3 | "rules": { 4 | "react/no-unescaped-entities": "off", 5 | 6 | // The following rule causes problems with react-three-fiber, so we disable it 7 | "react/no-unknown-property": "off" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next -------------------------------------------------------------------------------- /app/articles/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Navigation } from "../../components/Navigation"; 2 | 3 | export const metadata = { 4 | title: "Mr. Pullen's Graphing Calculator", 5 | }; 6 | 7 | interface LayoutProps { 8 | children: React.ReactNode; 9 | } 10 | 11 | export default function Layout({ children }: LayoutProps) { 12 | return ( 13 |
14 | 15 | {children} 16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /app/articles/parabolas/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import classNames from "classnames"; 4 | import { ReactNode } from "react"; 5 | import { Euler, Vector3 } from "three"; 6 | import { Graph3D } from "../../../components/Graph3D/Graph3D"; 7 | import { GraphAxis3D } from "../../../components/Graph3D/GraphAxis3D"; 8 | import { GraphBoundingBox3D } from "../../../components/Graph3D/GraphBoundingBox3D"; 9 | import { GraphCurve3D } from "../../../components/Graph3D/GraphCurve3D"; 10 | import { GraphEquation3D } from "../../../components/Graph3D/GraphEquation3D"; 11 | import { GraphGrid3D } from "../../../components/Graph3D/GraphGrid3D"; 12 | import { GraphSurfaceGridMaterial } from "../../../components/Graph3D/GraphSurfaceGridMaterial"; 13 | import { Latex } from "../../../components/Latex"; 14 | 15 | let coneParabolaPoints = new Float32Array(((5 - -5 / 4) / 0.02) * 2 * 3); 16 | let i = 0; 17 | for (let x = 5; x > -5 / 4; x -= 0.02) { 18 | coneParabolaPoints[i++] = (1 / 2) * Math.sqrt(5) * Math.sqrt(4 * x + 5); 19 | coneParabolaPoints[i++] = -2 * x; 20 | coneParabolaPoints[i++] = x; 21 | } 22 | for (let x = -5 / 4; x <= 5; x += 0.02) { 23 | coneParabolaPoints[i++] = (-1 / 2) * Math.sqrt(5) * Math.sqrt(4 * x + 5); 24 | coneParabolaPoints[i++] = -2 * x; 25 | coneParabolaPoints[i++] = x; 26 | } 27 | 28 | export default function Parabolas() { 29 | return ( 30 |
31 |
32 |

Parabolas

33 |

What is a parabola?

34 | 114 |
115 |
116 | ); 117 | } 118 | 119 | interface FunctionTableProps { 120 | labels: string[]; 121 | values: ReactNode[][]; 122 | equalWidths?: boolean; 123 | } 124 | 125 | function FunctionTable({ 126 | labels, 127 | values, 128 | equalWidths = true, 129 | }: FunctionTableProps) { 130 | const valuesTransposed = values[0].map((_, i) => values.map((row) => row[i])); 131 | 132 | return ( 133 | 136 | 137 | 138 | {labels.map((label, i) => ( 139 | 142 | ))} 143 | 144 | 145 | 146 | {valuesTransposed.map((row, i) => ( 147 | 148 | {row.map((value, j) => ( 149 | 152 | ))} 153 | 154 | ))} 155 | 156 |
140 | {label} 141 |
150 | {value} 151 |
157 | ); 158 | } 159 | -------------------------------------------------------------------------------- /app/articles/riemann-sums/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Graph3D } from "../../../components/Graph3D/Graph3D"; 4 | import { GraphAxis3D } from "../../../components/Graph3D/GraphAxis3D"; 5 | import { GraphGrid3D } from "../../../components/Graph3D/GraphGrid3D"; 6 | import { ParametricGeometry } from "three/examples/jsm/geometries/ParametricGeometry"; 7 | import { extend, Object3DNode } from "@react-three/fiber"; 8 | import { Shape, Vector2 } from "three"; 9 | import { Area } from "../../../components/Graph3D/display/Area"; 10 | import { useState } from "react"; 11 | 12 | extend({ ParametricGeometry }); 13 | 14 | declare module "@react-three/fiber" { 15 | interface ThreeElements { 16 | parametricGeometry: Object3DNode< 17 | ParametricGeometry, 18 | typeof ParametricGeometry 19 | >; 20 | } 21 | } 22 | 23 | const smoothShape = getFunctionAreaShape((x) => 2 * (x / 3) ** 2 + 1, -3, 3); 24 | 25 | export default function RiemannSums() { 26 | return ( 27 |
28 |
29 |

Riemann Sums

30 |

What is the area of the following shape?

31 | 32 |

33 | Not so bad, right? Adding up rectangular areas is easy.{" "} 34 | But what if you have a curvey shape, like this? 35 |

36 |
37 | 43 | {() => ( 44 | <> 45 | 46 | 47 | 48 | 49 | 50 | )} 51 | 52 |
53 |
54 |
55 | ); 56 | } 57 | 58 | const sharpShape = new Shape([ 59 | new Vector2(-3, 0), 60 | new Vector2(-3, 3), 61 | new Vector2(-1, 3), 62 | new Vector2(-1, 2), 63 | new Vector2(1, 2), 64 | new Vector2(1, 1), 65 | new Vector2(3, 1), 66 | new Vector2(3, 0), 67 | new Vector2(-3, 0), 68 | ]); 69 | 70 | function SharpAreaGuessGame() { 71 | const [guess, setGuess] = useState(""); 72 | 73 | const guessNumber = Number(guess); 74 | 75 | const { audioContext, gainNode, oscillator } = useWaveformSound(); 76 | 77 | return ( 78 |
79 |
80 | 86 | {() => ( 87 | <> 88 | 89 | 90 | 91 | 92 | 93 | )} 94 | 95 |
96 |
97 | setGuess(event.target.value)} 103 | /> 104 | 119 |
120 |
121 | ); 122 | } 123 | 124 | function getFunctionAreaShape( 125 | f: (x: number) => number, 126 | a: number, 127 | b: number, 128 | divisions = 25 129 | ) { 130 | const shape = new Shape([new Vector2(a, 0)]); 131 | for (let i = 0; i <= divisions; i++) { 132 | const x = a + (b - a) * (i / divisions); 133 | const y = f(x); 134 | shape.lineTo(x, y); 135 | } 136 | shape.lineTo(b, 0); 137 | shape.autoClose = true; 138 | return shape; 139 | } 140 | 141 | function useWaveformSound() { 142 | const [stuff] = useState(() => { 143 | if (typeof AudioContext === "undefined") { 144 | return {}; 145 | } 146 | 147 | const audioContext = new AudioContext(); 148 | const oscillator = audioContext.createOscillator(); 149 | const gainNode = audioContext.createGain(); 150 | 151 | oscillator.connect(gainNode); 152 | gainNode.connect(audioContext.destination); 153 | 154 | oscillator.type = "sine"; 155 | oscillator.frequency.value = 440; 156 | gainNode.gain.value = 0; 157 | 158 | oscillator.start(); 159 | 160 | return { audioContext, oscillator, gainNode }; 161 | }); 162 | 163 | return stuff; 164 | } 165 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Noto_Sans, Noto_Serif } from "next/font/google"; 2 | import "../styles/globals.css"; 3 | import "../mathquill-0.10.1/mathquill.css"; 4 | 5 | import classNames from "classnames"; 6 | import Script from "next/script"; 7 | import { Providers } from "./providers"; 8 | 9 | export const metadata = { 10 | title: "Mr. Pullen's Graphing Calculator", 11 | // description: "Generated by Next.js", 12 | }; 13 | 14 | const noto_sans = Noto_Sans({ 15 | weight: ["400", "700"], 16 | style: ["normal", "italic"], 17 | subsets: ["latin"], 18 | variable: "--font-noto-sans", 19 | display: "swap", 20 | }); 21 | 22 | const noto_serif = Noto_Serif({ 23 | weight: ["400", "700"], 24 | style: ["normal", "italic"], 25 | subsets: ["latin"], 26 | variable: "--font-noto-serif", 27 | display: "swap", 28 | }); 29 | 30 | interface RootLayoutProps { 31 | children: React.ReactNode; 32 | } 33 | 34 | export default function RootLayout({ children }: RootLayoutProps) { 35 | return ( 36 | // suppressHydrationWarning is needed for `next-themes` (the dark/light theme 37 | // toggle library). Note that it only suppresses the warning on this one element, 38 | // not on its children. 39 | 40 | 47 |