import React, { useState, useRef } from 'react'; const TangentPropertiesWidget: React.FC = () => { const [pointP, setPointP] = useState({ x: 350, y: 150 }); const isDragging = useRef(false); const svgRef = useRef(null); const center = { x: 150, y: 150 }; const radius = 60; // Interaction const handleMouseMove = (e: React.MouseEvent) => { if (!isDragging.current || !svgRef.current) return; const rect = svgRef.current.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // Constrain P to be outside the circle (distance > radius) const dx = x - center.x; const dy = y - center.y; const dist = Math.sqrt(dx * dx + dy * dy); // Min distance to keep things looking nice (radius + padding) if (dist < radius + 20) { const angle = Math.atan2(dy, dx); setPointP({ x: center.x + (radius + 20) * Math.cos(angle), y: center.y + (radius + 20) * Math.sin(angle) }); } else { setPointP({ x, y }); } }; // Calculations const dx = pointP.x - center.x; const dy = pointP.y - center.y; const distPO = Math.sqrt(dx * dx + dy * dy); const anglePO = Math.atan2(dy, dx); // Angle offset to tangent points // cos(theta) = Adjacent / Hypotenuse = radius / distPO const theta = Math.acos(radius / distPO); const t1Angle = anglePO - theta; const t2Angle = anglePO + theta; const T1 = { x: center.x + radius * Math.cos(t1Angle), y: center.y + radius * Math.sin(t1Angle) }; const T2 = { x: center.x + radius * Math.cos(t2Angle), y: center.y + radius * Math.sin(t2Angle) }; const tangentLength = Math.sqrt(distPO * distPO - radius * radius); // Right Angle Markers const markerSize = 10; const getRightAnglePath = (p: {x:number, y:number}, angle: number) => { // angle is the angle of the radius. We need to go inwards and perpendicular // Actually simpler: Vector from Center to T, and Vector T to P are perp. // Let's just draw a small square aligned with radius const rAngle = angle; // Point on radius const p1 = { x: p.x - markerSize * Math.cos(rAngle), y: p.y - markerSize * Math.sin(rAngle) }; // Point on tangent (towards P) // Tangent is perpendicular to radius. // We need to know if we go clockwise or counter clockwise. // Vector T->P const tpAngle = Math.atan2(pointP.y - p.y, pointP.x - p.x); const p2 = { x: p.x + markerSize * Math.cos(tpAngle), y: p.y + markerSize * Math.sin(tpAngle) }; // Corner const p3 = { x: p1.x + markerSize * Math.cos(tpAngle), y: p1.y + markerSize * Math.sin(tpAngle) }; return `M ${p1.x} ${p1.y} L ${p3.x} ${p3.y} L ${p2.x} ${p2.y}`; }; return (
isDragging.current = false} onMouseLeave={() => isDragging.current = false} > {/* Circle */} O {/* Radii */} {/* Tangents */} {/* Right Angle Markers */} {/* Points */} A B {/* External Point P */} isDragging.current = true} className="cursor-grab active:cursor-grabbing" > P {/* Length Labels (Midpoints) */} {Math.round(tangentLength)} {Math.round(tangentLength)}

Rule 1 Equal Tangents

Tangents from the same external point are always congruent.

PA = PB = {Math.round(tangentLength)}

Rule 2 Perpendicular Radius

The radius to the point of tangency is always perpendicular to the tangent line.

∠OAP = 90° ∠OBP = 90°

Drag point P to verify!

); }; export default TangentPropertiesWidget;