108 lines
4.8 KiB
TypeScript
108 lines
4.8 KiB
TypeScript
import React, { useState } from 'react';
|
|
|
|
const SystemVisualizerWidget: React.FC = () => {
|
|
// Line 1: y = m1x + b1
|
|
const [m1, setM1] = useState(1);
|
|
const [b1, setB1] = useState(2);
|
|
|
|
// Line 2: y = m2x + b2
|
|
const [m2, setM2] = useState(-1);
|
|
const [b2, setB2] = useState(6);
|
|
|
|
// Visualization params
|
|
const range = 10;
|
|
const scale = 20;
|
|
const size = 300;
|
|
const center = size / 2;
|
|
|
|
const toPx = (v: number, isY = false) => {
|
|
return isY ? center - v * scale : center + v * scale;
|
|
};
|
|
|
|
// Logic
|
|
let intersectX = 0;
|
|
let intersectY = 0;
|
|
let solutionType = 'one'; // 'one', 'none', 'inf'
|
|
|
|
if (m1 === m2) {
|
|
if (b1 === b2) solutionType = 'inf';
|
|
else solutionType = 'none';
|
|
} else {
|
|
intersectX = (b2 - b1) / (m1 - m2);
|
|
intersectY = m1 * intersectX + b1;
|
|
}
|
|
|
|
const getLinePath = (m: number, b: number) => {
|
|
const x1 = -range;
|
|
const y1 = m * x1 + b;
|
|
const x2 = range;
|
|
const y2 = m * x2 + b;
|
|
return `M ${toPx(x1)} ${toPx(y1, true)} L ${toPx(x2)} ${toPx(y2, true)}`;
|
|
};
|
|
|
|
return (
|
|
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200">
|
|
<div className="flex flex-col md:flex-row gap-8">
|
|
<div className="w-full md:w-1/3 space-y-6">
|
|
{/* Line 1 */}
|
|
<div className="p-4 bg-indigo-50 border border-indigo-100 rounded-lg">
|
|
<div className="flex justify-between items-center mb-2">
|
|
<span className="font-bold text-indigo-800 text-sm">Line 1: y = {m1}x + {b1}</span>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<input type="range" min="-4" max="4" step="0.5" value={m1} onChange={e => setM1(parseFloat(e.target.value))} className="w-full h-1 bg-indigo-200 rounded accent-indigo-600"/>
|
|
<input type="range" min="-8" max="8" step="1" value={b1} onChange={e => setB1(parseFloat(e.target.value))} className="w-full h-1 bg-indigo-200 rounded accent-indigo-600"/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Line 2 */}
|
|
<div className="p-4 bg-rose-50 border border-rose-100 rounded-lg">
|
|
<div className="flex justify-between items-center mb-2">
|
|
<span className="font-bold text-rose-800 text-sm">Line 2: y = {m2}x + {b2}</span>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<input type="range" min="-4" max="4" step="0.5" value={m2} onChange={e => setM2(parseFloat(e.target.value))} className="w-full h-1 bg-rose-200 rounded accent-rose-600"/>
|
|
<input type="range" min="-8" max="8" step="1" value={b2} onChange={e => setB2(parseFloat(e.target.value))} className="w-full h-1 bg-rose-200 rounded accent-rose-600"/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Result */}
|
|
<div className={`p-4 rounded-lg text-center font-bold border-2 ${
|
|
solutionType === 'one' ? 'bg-emerald-50 border-emerald-200 text-emerald-800' :
|
|
solutionType === 'none' ? 'bg-slate-50 border-slate-200 text-slate-500' :
|
|
'bg-amber-50 border-amber-200 text-amber-800'
|
|
}`}>
|
|
{solutionType === 'one' && `Intersection: (${intersectX.toFixed(1)}, ${intersectY.toFixed(1)})`}
|
|
{solutionType === 'none' && "No Solution (Parallel Lines)"}
|
|
{solutionType === 'inf' && "Infinite Solutions (Same Line)"}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex-1 flex justify-center">
|
|
<div className="relative w-[300px] h-[300px] bg-white border border-slate-200 rounded-xl overflow-hidden">
|
|
<svg width="300" height="300" viewBox="0 0 300 300">
|
|
<defs>
|
|
<pattern id="grid-sys" width="20" height="20" patternUnits="userSpaceOnUse">
|
|
<path d="M 20 0 L 0 0 0 20" fill="none" stroke="#f1f5f9" strokeWidth="1"/>
|
|
</pattern>
|
|
</defs>
|
|
<rect width="100%" height="100%" fill="url(#grid-sys)" />
|
|
|
|
<line x1="0" y1={center} x2={size} y2={center} stroke="#cbd5e1" strokeWidth="2" />
|
|
<line x1={center} y1="0" x2={center} y2={size} stroke="#cbd5e1" strokeWidth="2" />
|
|
|
|
<path d={getLinePath(m1, b1)} stroke="#4f46e5" strokeWidth="3" />
|
|
<path d={getLinePath(m2, b2)} stroke="#e11d48" strokeWidth="3" strokeDasharray={solutionType === 'inf' ? "5,5" : ""} />
|
|
|
|
{solutionType === 'one' && (
|
|
<circle cx={toPx(intersectX)} cy={toPx(intersectY, true)} r="5" fill="#10b981" stroke="white" strokeWidth="2" />
|
|
)}
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SystemVisualizerWidget; |