Skip to content

Commit 2e7438d

Browse files
refactor
1 parent 45deb4a commit 2e7438d

File tree

1 file changed

+158
-1
lines changed

1 file changed

+158
-1
lines changed

lib/react/GenericSolverDebugger.tsx

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,157 @@ import React, { useEffect, useMemo, useReducer } from "react"
22
import type { BaseSolver } from "../BaseSolver"
33
import { InteractiveGraphics } from "graphics-debug/react"
44
import { GenericSolverToolbar } from "./GenericSolverToolbar"
5+
import type { GraphicsObject } from "graphics-debug"
6+
7+
class ErrorBoundary extends React.Component<
8+
{ fallback: React.ReactNode; children: React.ReactNode },
9+
{ hasError: boolean }
10+
> {
11+
constructor(props: { fallback: React.ReactNode; children: React.ReactNode }) {
12+
super(props)
13+
this.state = { hasError: false }
14+
}
15+
static getDerivedStateFromError() {
16+
return { hasError: true }
17+
}
18+
override componentDidCatch(error: any) {
19+
console.error("InteractiveGraphics render error:", error)
20+
}
21+
override render() {
22+
if (this.state.hasError) {
23+
return this.props.fallback
24+
}
25+
return this.props.children
26+
}
27+
}
28+
29+
function SimpleGraphicsSVG({ graphics }: { graphics: GraphicsObject }) {
30+
const points = graphics.points ?? []
31+
const lines = graphics.lines ?? []
32+
const rects = graphics.rects ?? []
33+
const circles = graphics.circles ?? []
34+
const texts = (graphics as any).texts ?? []
35+
36+
let minX = Number.POSITIVE_INFINITY
37+
let minY = Number.POSITIVE_INFINITY
38+
let maxX = Number.NEGATIVE_INFINITY
39+
let maxY = Number.NEGATIVE_INFINITY
40+
41+
const consider = (x?: number, y?: number) => {
42+
if (typeof x === "number") {
43+
if (x < minX) minX = x
44+
if (x > maxX) maxX = x
45+
}
46+
if (typeof y === "number") {
47+
if (y < minY) minY = y
48+
if (y > maxY) maxY = y
49+
}
50+
}
51+
52+
for (const p of points) consider((p as any).x, (p as any).y)
53+
for (const l of lines) {
54+
const pts = (l as any).points ?? []
55+
for (const p of pts) consider(p.x, p.y)
56+
}
57+
for (const r of rects) {
58+
const x = (r as any).x ?? 0
59+
const y = (r as any).y ?? 0
60+
const w = (r as any).width ?? 0
61+
const h = (r as any).height ?? 0
62+
consider(x, y)
63+
consider(x + w, y + h)
64+
}
65+
for (const c of circles) {
66+
const x = (c as any).x ?? 0
67+
const y = (c as any).y ?? 0
68+
const rad = (c as any).radius ?? 1
69+
consider(x - rad, y - rad)
70+
consider(x + rad, y + rad)
71+
}
72+
for (const t of texts) consider((t as any).x, (t as any).y)
73+
74+
if (
75+
!isFinite(minX) ||
76+
!isFinite(minY) ||
77+
!isFinite(maxX) ||
78+
!isFinite(maxY)
79+
) {
80+
minX = -20
81+
minY = -20
82+
maxX = 20
83+
maxY = 20
84+
}
85+
86+
const pad = 10
87+
const vbX = minX - pad
88+
const vbY = minY - pad
89+
const vbW = Math.max(1, maxX - minX + 2 * pad)
90+
const vbH = Math.max(1, maxY - minY + 2 * pad)
91+
92+
return (
93+
<svg
94+
className="w-full h-[400px] bg-white"
95+
viewBox={`${vbX} ${vbY} ${vbW} ${vbH}`}
96+
role="img"
97+
aria-label="Graphics fallback"
98+
>
99+
{rects.map((r: any, i: number) => (
100+
<rect
101+
key={`rect-${i}`}
102+
x={r.x ?? 0}
103+
y={r.y ?? 0}
104+
width={r.width ?? 0}
105+
height={r.height ?? 0}
106+
fill="none"
107+
stroke={r.strokeColor ?? "black"}
108+
strokeWidth={r.strokeWidth ?? 1}
109+
/>
110+
))}
111+
{lines.map((l: any, i: number) => (
112+
<polyline
113+
key={`line-${i}`}
114+
fill="none"
115+
stroke={l.strokeColor ?? "black"}
116+
strokeWidth={l.strokeWidth ?? 1}
117+
points={(l.points ?? [])
118+
.map((p: any) => `${p.x ?? 0},${p.y ?? 0}`)
119+
.join(" ")}
120+
/>
121+
))}
122+
{circles.map((c: any, i: number) => (
123+
<circle
124+
key={`circle-${i}`}
125+
cx={c.x ?? 0}
126+
cy={c.y ?? 0}
127+
r={c.radius ?? 1.5}
128+
fill={c.fillColor ?? "none"}
129+
stroke={c.strokeColor ?? "black"}
130+
strokeWidth={c.strokeWidth ?? 1}
131+
/>
132+
))}
133+
{points.map((p: any, i: number) => (
134+
<circle
135+
key={`point-${i}`}
136+
cx={p.x ?? 0}
137+
cy={p.y ?? 0}
138+
r={p.radius ?? 1.5}
139+
fill={p.color ?? "black"}
140+
/>
141+
))}
142+
{texts.map((t: any, i: number) => (
143+
<text
144+
key={`text-${i}`}
145+
x={t.x ?? 0}
146+
y={t.y ?? 0}
147+
fontSize={t.fontSize ?? 10}
148+
fill={t.color ?? "black"}
149+
>
150+
{t.text ?? ""}
151+
</text>
152+
))}
153+
</svg>
154+
)
155+
}
5156

6157
export interface GenericSolverDebuggerProps {
7158
solver: BaseSolver
@@ -63,7 +214,13 @@ export const GenericSolverDebugger = ({
63214
{graphicsAreEmpty ? (
64215
<div className="p-4 text-gray-500">No Graphics Yet</div>
65216
) : (
66-
<InteractiveGraphics graphics={visualization} />
217+
<ErrorBoundary
218+
fallback={
219+
<SimpleGraphicsSVG graphics={visualization as GraphicsObject} />
220+
}
221+
>
222+
<InteractiveGraphics graphics={visualization} />
223+
</ErrorBoundary>
67224
)}
68225
</div>
69226
)

0 commit comments

Comments
 (0)