/ concept-collection / barycentric-rational
Sign in
concept-collection / barycentric-rational
barycentric-rational / src / panels / Controls.tsx
162 lines · 4.6 KBBlameHistoryRaw
1import { useState } from 'react'
2import type { FuncName, NodeKind } from '../engine/types.ts'
4export interface Settings {
5 f: FuncName
6 fexpr: string
7 a: number
8 b: number
9 n: number
10 d: number
11 nodes: NodeKind
12 seed: number
15const FUNCS: { id: FuncName; label: string; hint: string }[] = [
16 { id: 'runge', label: '1 / (1 + x²)', hint: "Runge's example, the paper's Figure 1" },
17 { id: 'sine', label: 'sin x', hint: 'Figure 2, smooth everywhere' },
18 { id: 'abs', label: '|x|', hint: 'Figure 3, a corner at 0' },
19 { id: 'custom', label: 'custom', hint: 'any MATLAB expression in x' },
22const NODES: { id: NodeKind; label: string; hint: string }[] = [
23 { id: 'uniform', label: 'uniform', hint: 'equally spaced, as in every figure of the paper' },
24 { id: 'chebyshev', label: 'Chebyshev', hint: 'clustered at both ends' },
25 { id: 'random', label: 'random', hint: 'sorted uniform draws, endpoints kept' },
26 { id: 'paired', label: 'paired', hint: 'every second node pulled close to its neighbour' },
27 { id: 'graded', label: 'graded', hint: 'quadratically clustered at the left end' },
30interface Props {
31 value: Settings
32 onChange: (patch: Partial<Settings>) => void
33 /** the convergence tab sets n itself */
34 showN: boolean
35 busy: boolean
38export default function Controls({ value: v, onChange, showN, busy }: Props) {
39 const [more, setMore] = useState(false)
40 const dMax = Math.min(v.n, 12)
41 return (
42 <div className="controls">
43 <div className="field">
44 <span className="field-label">function f</span>
45 <div className="chips">
46 {FUNCS.map((fn) => (
47 <button
48 key={fn.id}
49 title={fn.hint}
50 className={`chip ${v.f === fn.id ? 'on' : ''}`}
51 onClick={() => onChange({ f: fn.id })}
52 >
53 {fn.label}
54 </button>
55 ))}
56 </div>
57 </div>
59 {v.f === 'custom' && (
60 <div className="field grow">
61 <span className="field-label">f(x) =</span>
62 <input
63 className="expr"
64 value={v.fexpr}
65 spellCheck={false}
66 placeholder="exp(-x.^2) .* cos(3*x)"
67 onChange={(e) => onChange({ fexpr: e.target.value })}
68 />
69 </div>
70 )}
72 {showN && (
73 <div className="field grow">
74 <span className="field-label">
75 nodes n = <b>{v.n}</b>
76 </span>
77 <input
78 type="range"
79 min={2}
80 max={80}
81 value={v.n}
82 disabled={busy}
83 onChange={(e) => {
84 const n = Number(e.target.value)
85 onChange({ n, d: Math.min(v.d, Math.min(n, 12)) })
86 }}
87 />
88 </div>
89 )}
91 <div className="field grow">
92 <span className="field-label">
93 blend degree d = <b>{v.d}</b>
94 </span>
95 <input
96 type="range"
97 min={0}
98 max={dMax}
99 value={Math.min(v.d, dMax)}
100 disabled={busy}
101 onChange={(e) => onChange({ d: Number(e.target.value) })}
102 />
103 </div>
105 <div className="field">
106 <span className="field-label">node distribution</span>
107 <div className="chips">
108 {NODES.map((nd) => (
109 <button
110 key={nd.id}
111 title={nd.hint}
112 className={`chip ${v.nodes === nd.id ? 'on' : ''}`}
113 onClick={() => onChange({ nodes: nd.id })}
114 >
115 {nd.label}
116 </button>
117 ))}
118 {v.nodes === 'random' && (
119 <button className="chip" onClick={() => onChange({ seed: v.seed + 1 })} title="new draw">
120 reroll
121 </button>
122 )}
123 </div>
124 </div>
126 <button
127 className="ghost more-toggle"
128 onClick={() => setMore((m) => !m)}
129 title="the interval [a, b]"
130 >
131 {more ? 'less ▴' : 'more ▾'}
132 </button>
134 {more && (
135 <div className="field">
136 <span className="field-label">interval</span>
137 <div className="interval">
138 <input
139 type="number"
140 value={v.a}
141 step={1}
142 onChange={(e) => {
143 const a = Number(e.target.value)
144 if (isFinite(a) && a < v.b) onChange({ a })
145 }}
146 />
147 <span>to</span>
148 <input
149 type="number"
150 value={v.b}
151 step={1}
152 onChange={(e) => {
153 const b = Number(e.target.value)
154 if (isFinite(b) && b > v.a) onChange({ b })
155 }}
156 />
157 </div>
158 </div>
159 )}
160 </div>
161 )
moveopenescclose