import React, { useState, useRef } from "react"; const InteractiveSectorWidget: React.FC = () => { const [angle, setAngle] = useState(60); // degrees const [radius, setRadius] = useState(120); // pixels const isDragging = useRef<"angle" | "radius" | null>(null); const svgRef = useRef(null); const cx = 200; const cy = 200; const maxRadius = 160; // Calculate Handle Position const rad = (angle * Math.PI) / 180; const hx = cx + radius * Math.cos(-rad); // SVG Y is down, so -rad for standard math "up" rotation behavior if we want counter-clockwise from East const hy = cy + radius * Math.sin(-rad); // For the arc path // Start point is (cx + r, cy) [0 degrees] // End point is (hx, hy) const largeArc = angle > 180 ? 1 : 0; // Sweep flag 0 because we are using -rad (counter clockwise visual in SVG) const pathData = ` M ${cx} ${cy} L ${cx + radius} ${cy} A ${radius} ${radius} 0 ${largeArc} 0 ${hx} ${hy} Z `; // Interaction const handleInteraction = (e: React.MouseEvent) => { if (!svgRef.current || !isDragging.current) return; const rect = svgRef.current.getBoundingClientRect(); const mx = e.clientX - rect.left; const my = e.clientY - rect.top; const dx = mx - cx; const dy = my - cy; if (isDragging.current === "angle") { let deg = Math.atan2(-dy, dx) * (180 / Math.PI); // -dy to correct for SVG coords if (deg < 0) deg += 360; setAngle(Math.round(deg)); } else if (isDragging.current === "radius") { const dist = Math.sqrt(dx * dx + dy * dy); setRadius(Math.max(50, Math.min(maxRadius, dist))); } }; return (
(isDragging.current = null)} onMouseLeave={() => (isDragging.current = null)} className="cursor-crosshair" > {/* Full Circle Ghost */} {/* Sector */} {/* Radius Handle Line (Baseline) */} {/* Radius Drag Handle (on baseline) */} (isDragging.current = "radius")} /> {/* Angle Drag Handle (on arc) */} (isDragging.current = "angle")} /> {/* Angle Text */} {angle}° {/* Radius Text */} r = {Math.round(radius / 10)} {/* Center */}

INPUT Parameters

Angle (θ): {angle}°
setAngle(parseInt(e.target.value))} className="w-full h-2 bg-orange-200 rounded-lg appearance-none cursor-pointer accent-orange-600" />
Radius (r): {Math.round(radius / 10)}
setRadius(parseInt(e.target.value))} className="w-full h-2 bg-orange-200 rounded-lg appearance-none cursor-pointer accent-orange-600" />
Fraction of Circle {angle}/360 ≈ {(angle / 360).toFixed(2)}
Arc Length
2π({Math.round(radius / 10)}) ×{" "} {(angle / 360).toFixed(2)}
= {(2 * Math.PI * (radius / 10) * (angle / 360)).toFixed(1)}
Sector Area
π({Math.round(radius / 10)})² ×{" "} {(angle / 360).toFixed(2)}
={" "} {(Math.PI * Math.pow(radius / 10, 2) * (angle / 360)).toFixed( 1, )}
); }; export default InteractiveSectorWidget;