/ concept-collection / fastandaccurate
Sign in
concept-collection / fastandaccurate
fastandaccurate / src / app / components / DomainView.tsx
92 lines · 2.8 KBBlameHistoryRaw
1// Draws the problem geometry: boundary curve, evaluation points, and the
2// exact solution's sources.
4import { useEffect, useRef } from "react";
5import type { Laplace2dInstance } from "../../problems/laplace2d/spec";
6import {
7 boundaryPoint,
8 evalPoints,
9 sources,
10} from "../../problems/laplace2d/exact";
12function token(name: string): string {
13 return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
16export function DomainView({ inst }: { inst: Laplace2dInstance }) {
17 const canvasRef = useRef<HTMLCanvasElement>(null);
19 useEffect(() => {
20 const canvas = canvasRef.current;
21 if (!canvas) return;
22 const draw = () => {
23 const size = 360;
24 const dpr = window.devicePixelRatio || 1;
25 canvas.width = size * dpr;
26 canvas.height = size * dpr;
27 canvas.style.width = `${size}px`;
28 canvas.style.height = `${size}px`;
29 const ctx = canvas.getContext("2d");
30 if (!ctx) return;
31 ctx.scale(dpr, dpr);
32 ctx.clearRect(0, 0, size, size);
34 const extent = 1 + Math.abs(inst.a) + inst.d + 0.25;
35 const s = size / (2 * extent);
36 const X = (x: number) => size / 2 + x * s;
37 const Y = (y: number) => size / 2 - y * s;
39 // domain fill + boundary
40 ctx.beginPath();
41 for (let i = 0; i <= 512; i++) {
42 const t = (2 * Math.PI * i) / 512;
43 const p = boundaryPoint(inst, t);
44 if (i === 0) ctx.moveTo(X(p.x), Y(p.y));
45 else ctx.lineTo(X(p.x), Y(p.y));
46 }
47 ctx.closePath();
48 ctx.fillStyle = token("--surface-2");
49 ctx.fill();
50 ctx.strokeStyle = token("--text");
51 ctx.lineWidth = 2;
52 ctx.stroke();
54 // evaluation points
55 ctx.fillStyle = token("--text-2");
56 for (const p of evalPoints(inst)) {
57 ctx.beginPath();
58 ctx.arc(X(p.x), Y(p.y), 2, 0, 2 * Math.PI);
59 ctx.fill();
60 }
62 // sources
63 ctx.strokeStyle = token("--series-2");
64 ctx.lineWidth = 2;
65 for (const src of sources(inst)) {
66 const cx = X(src.x);
67 const cy = Y(src.y);
68 ctx.beginPath();
69 ctx.moveTo(cx - 5, cy - 5);
70 ctx.lineTo(cx + 5, cy + 5);
71 ctx.moveTo(cx - 5, cy + 5);
72 ctx.lineTo(cx + 5, cy - 5);
73 ctx.stroke();
74 }
75 };
76 draw();
77 const mq = window.matchMedia("(prefers-color-scheme: dark)");
78 mq.addEventListener("change", draw);
79 return () => mq.removeEventListener("change", draw);
80 }, [inst]);
82 return (
83 <figure style={{ margin: 0 }}>
84 <canvas ref={canvasRef} />
85 <figcaption className="field-caption" style={{ maxWidth: 360 }}>
86 The domain, the 65 evaluation points where solutions are scored
87 (dots), and the exact solution's sources a distance {inst.d} outside
88 the boundary (crosses).
89 </figcaption>
90 </figure>
91 );
moveopenescclose