/ simulated_instruments / cymbal2
Sign in
simulated_instruments / cymbal2
cymbal2 / webapp / src / tutorial / figures / GeometryFig.tsx
142 lines · 4.5 KBCodeBlameHistory
17959acInteractive tutorial on the theory and the solverJeremy Magland 1// Interactive cross-section of the shallow spherical cap: radius, rise, and
2// thickness sliders, with the osculating sphere drawn dashed and the derived
3// quantities (curvature radius Rs, bending stiffness D) reported. Drawn at
4// true aspect ratio, because the point of the figure is how shallow the
5// shell actually is.
7import { useState } from 'react';
8import { useCanvas, drawFrame, PLOT_COLORS } from '../../components/plots/canvasUtils';
9import { Figure, FigControls, FigSlider } from '../figparts';
11const HEIGHT = 250;
13export default function GeometryFig() {
14 const [a, setA] = useState(0.23);
15 const [rise, setRise] = useState(0.04);
16 const [hmm, setHmm] = useState(1.0); // thickness in mm
18 const Rs = (a * a) / (2 * rise);
19 const E = 1.1e11;
20 const nu = 0.33;
21 const h = hmm * 1e-3;
22 const D = (E * h * h * h) / (12 * (1 - nu * nu));
24 const ref = useCanvas(
25 (ctx, w, hpx) => {
26 drawFrame(ctx, w, hpx);
27 const scale = (w - 60) / (2 * 0.36); // fixed scale so resizing the shell reads
28 const cx = w / 2;
29 const baseY = hpx - 70;
30 const px = (x: number) => cx + x * scale;
31 const py = (z: number) => baseY - z * scale;
33 // osculating sphere, dashed: center on the axis at z = rise - Rs
34 ctx.strokeStyle = 'rgba(139, 147, 161, 0.55)';
35 ctx.setLineDash([5, 5]);
36 ctx.beginPath();
37 ctx.arc(px(0), py(rise - Rs), Rs * scale, 0, 2 * Math.PI);
38 ctx.stroke();
39 ctx.setLineDash([]);
41 // the cap itself: z(r) = rise (1 - r^2/a^2), the shallow form the model uses
42 ctx.strokeStyle = '#c9a227';
43 ctx.lineWidth = Math.max(2, h * scale);
44 ctx.beginPath();
45 const N = 90;
46 for (let i = 0; i <= N; i++) {
47 const r = -a + (2 * a * i) / N;
48 const z = rise * (1 - (r * r) / (a * a));
49 if (i === 0) ctx.moveTo(px(r), py(z));
50 else ctx.lineTo(px(r), py(z));
51 }
52 ctx.stroke();
53 ctx.lineWidth = 1;
55 // rise marker
56 ctx.strokeStyle = PLOT_COLORS.trace;
57 ctx.beginPath();
58 ctx.moveTo(px(0), py(0));
59 ctx.lineTo(px(0), py(rise));
60 ctx.stroke();
61 ctx.fillStyle = PLOT_COLORS.trace;
62 ctx.font = '11px system-ui';
63 ctx.textAlign = 'left';
64 ctx.fillText(`rise ${(rise * 100).toFixed(1)} cm`, px(0) + 6, py(rise / 2));
66 // radius marker
67 ctx.strokeStyle = PLOT_COLORS.axis;
68 ctx.beginPath();
69 ctx.moveTo(px(0), baseY + 14);
70 ctx.lineTo(px(a), baseY + 14);
71 ctx.stroke();
72 ctx.fillStyle = PLOT_COLORS.axis;
73 ctx.textAlign = 'center';
74 ctx.fillText(`a = ${(a * 100).toFixed(1)} cm`, px(a / 2), baseY + 28);
76 // readouts
77 ctx.textAlign = 'left';
78 ctx.fillStyle = '#d6dae2';
79 ctx.fillText(
80 `curvature radius Rs = a² / (2·rise) = ${Rs.toFixed(2)} m`,
81 14,
82 20,
83 );
84 ctx.fillText(
85 `bending stiffness D = E h³ / 12(1−ν²) = ${D.toFixed(3)} N·m`,
86 14,
87 38,
88 );
89 ctx.fillStyle = PLOT_COLORS.axis;
90 ctx.fillText('drawn to true scale; dashes: the sphere the cap sits on', 14, 56);
91 },
92 [a, rise, hmm, Rs, D, h],
93 HEIGHT,
94 );
96 return (
97 <Figure
98 caption={
99 <>
100 The whole cymbal, cup and bow together, is replaced by one spherical cap: radius{' '}
101 <i>a</i>, a small rise at the center, uniform thickness <i>h</i>. The rise fixes a
102 single radius of curvature <i>R</i>
103 <sub>s</sub>, and that one number carries all of the shell's &ldquo;shellness&rdquo;:
104 flatten the rise toward zero and the model degenerates to a flat plate, whose low
105 modes are much softer. Note how little curvature is involved; this shallowness is
106 what licenses the von K&aacute;rm&aacute;n equations of the next section.
107 </>
108 }
109 >
110 <canvas ref={ref} />
111 <FigControls>
112 <FigSlider
113 label="radius a"
114 value={a}
115 min={0.12}
116 max={0.35}
117 step={0.005}
118 format={(v) => `${(v * 100).toFixed(1)} cm`}
119 onChange={setA}
120 />
121 <FigSlider
122 label="rise"
123 value={rise}
124 min={0.005}
125 max={0.09}
126 step={0.001}
127 format={(v) => `${(v * 100).toFixed(1)} cm`}
128 onChange={setRise}
129 />
130 <FigSlider
131 label="thickness h"
132 value={hmm}
133 min={0.4}
134 max={3}
135 step={0.05}
136 format={(v) => `${v.toFixed(2)} mm`}
137 onChange={setHmm}
138 />
139 </FigControls>
140 </Figure>
141 );
↑↓ move↵openescclose