Files
edbridge-scholars/src/components/lessons/PolygonWidget.tsx
2026-03-12 02:39:34 +06:00

148 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from "react";
const PolygonWidget: React.FC = () => {
const [n, setN] = useState(5);
// Math
const interiorSum = (n - 2) * 180;
const eachInterior = Math.round((interiorSum / n) * 100) / 100;
const eachExterior = Math.round((360 / n) * 100) / 100;
// SVG Config
const width = 300;
const height = 300;
const cx = width / 2;
const cy = height / 2;
const r = 80;
// @ts-ignore
const points = [];
for (let i = 0; i < n; i++) {
const angle = (i * 2 * Math.PI) / n - Math.PI / 2; // Start at top
points.push({
x: cx + r * Math.cos(angle),
y: cy + r * Math.sin(angle),
});
}
// Generate path string
const pathD =
points
.map((p, i) => (i === 0 ? `M ${p.x} ${p.y}` : `L ${p.x} ${p.y}`))
.join(" ") + " Z";
// Generate exterior lines (extensions)
const exteriorLines = points.map((p, i) => {
// @ts-ignore
const nextP = points[(i + 1) % n];
// Vector from p to nextP
const dx = nextP.x - p.x;
const dy = nextP.y - p.y;
// Normalize and extend
const len = Math.sqrt(dx * dx + dy * dy);
const exLen = 40;
const exX = nextP.x + (dx / len) * exLen;
const exY = nextP.y + (dy / len) * exLen;
return { x1: nextP.x, y1: nextP.y, x2: exX, y2: exY };
});
return (
<div className="bg-white p-6 rounded-xl shadow-sm border border-slate-200 flex flex-col md:flex-row gap-8 items-center">
<div className="flex-1 w-full max-w-xs">
<label className="block text-sm font-bold text-slate-500 uppercase mb-2">
Number of Sides (n):{" "}
<span className="text-slate-900 text-lg">{n}</span>
</label>
<input
type="range"
min="3"
max="10"
step="1"
value={n}
onChange={(e) => setN(parseInt(e.target.value))}
className="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-emerald-600 mb-6"
/>
<div className="space-y-3 font-mono text-sm">
<div className="p-3 bg-slate-50 rounded border border-slate-200">
<div className="text-xs text-slate-500 font-bold uppercase">
Interior Sum
</div>
<div className="text-slate-800">
(n - 2) × 180° ={" "}
<strong className="text-emerald-600">{interiorSum}°</strong>
</div>
</div>
<div className="p-3 bg-slate-50 rounded border border-slate-200">
<div className="text-xs text-slate-500 font-bold uppercase">
Each Interior Angle
</div>
<div className="text-slate-800">
{interiorSum} / {n} ={" "}
<strong className="text-emerald-600">{eachInterior}°</strong>
</div>
</div>
<div className="p-3 bg-slate-50 rounded border border-slate-200">
<div className="text-xs text-slate-500 font-bold uppercase">
Each Exterior Angle
</div>
<div className="text-slate-800">
360 / {n} ={" "}
<strong className="text-rose-600">{eachExterior}°</strong>
</div>
</div>
</div>
</div>
<div className="flex-shrink-0 relative">
<svg width={width} height={height}>
{/* Extensions for exterior angles */}
{exteriorLines.map((line, i) => (
<line
key={i}
x1={line.x1}
y1={line.y1}
x2={line.x2}
y2={line.y2}
stroke="#cbd5e1"
strokeWidth="2"
strokeDasharray="4,4"
/>
))}
{/* Polygon */}
<path
d={pathD}
fill="rgba(16, 185, 129, 0.1)"
stroke="#059669"
strokeWidth="3"
/>
{/* Vertices */}
{points.map((p, i) => (
<circle key={i} cx={p.x} cy={p.y} r="4" fill="#059669" />
))}
{/* Center text */}
<text
x={cx}
y={cy}
textAnchor="middle"
dominantBaseline="middle"
fill="#059669"
fontSize="24"
fontWeight="bold"
opacity="0.2"
>
{n}-gon
</text>
</svg>
</div>
</div>
);
};
export default PolygonWidget;