import { createContext, ReactNode, useContext, useMemo, useState } from "react"; import { SCALE_FACTOR, UP_SCALE } from "./Constants"; const TransformContext = createContext<{ position: { x: number, y: number }, scale: number, rotation: number, screenCenter?: { x: number, y: number }, setPosition: React.Dispatch>, setScale: React.Dispatch>, setRotation: React.Dispatch>, screenToLocal: (x: number, y: number) => { x: number, y: number }, localToScreen: (x: number, y: number) => { x: number, y: number }, rotateToAngle: (to: number, fromPosition?: {x: number, y: number}) => void, setTransform: (latitude: number, longitude: number, rotationDegrees?: number, scale?: number) => void, setScreenCenter: React.Dispatch> }>({ position: { x: 0, y: 0 }, scale: 1, rotation: 0, screenCenter: undefined, setPosition: () => {}, setScale: () => {}, setRotation: () => {}, screenToLocal: () => ({ x: 0, y: 0 }), localToScreen: () => ({ x: 0, y: 0 }), rotateToAngle: () => {}, setTransform: () => {}, setScreenCenter: () => {} }); // Provider component export const TransformProvider = ({ children }: { children: ReactNode }) => { const [position, setPosition] = useState({ x: 0, y: 0 }); const [scale, setScale] = useState(1); const [rotation, setRotation] = useState(0); const [screenCenter, setScreenCenter] = useState<{x: number, y: number}>(); function screenToLocal(screenX: number, screenY: number) { // Translate point relative to current pan position const translatedX = (screenX - position.x) / scale; const translatedY = (screenY - position.y) / scale; // Rotate point around center const cosRotation = Math.cos(-rotation); // Negative rotation to reverse transform const sinRotation = Math.sin(-rotation); const rotatedX = translatedX * cosRotation - translatedY * sinRotation; const rotatedY = translatedX * sinRotation + translatedY * cosRotation; return { x: rotatedX / UP_SCALE, y: rotatedY / UP_SCALE }; } // Inverse of screenToLocal function localToScreen(localX: number, localY: number) { const upscaledX = localX * UP_SCALE; const upscaledY = localY * UP_SCALE; const cosRotation = Math.cos(rotation); const sinRotation = Math.sin(rotation); const rotatedX = upscaledX * cosRotation - upscaledY * sinRotation; const rotatedY = upscaledX * sinRotation + upscaledY * cosRotation; const translatedX = rotatedX*scale + position.x; const translatedY = rotatedY*scale + position.y; return { x: translatedX, y: translatedY }; } function rotateToAngle(to: number, fromPosition?: {x: number, y: number}) { setRotation(to); const rotationDiff = to - rotation; const center = screenCenter ?? {x: 0, y: 0}; const cosDelta = Math.cos(rotationDiff); const sinDelta = Math.sin(rotationDiff); fromPosition ??= position; setPosition({ x: center.x * (1 - cosDelta) + fromPosition.x * cosDelta + (center.y - fromPosition.y) * sinDelta, y: center.y * (1 - cosDelta) + fromPosition.y * cosDelta + (fromPosition.x - center.x) * sinDelta }); } function setTransform(latitude: number, longitude: number, rotationDegrees?: number, useScale ?: number) { const selectedRotation = rotationDegrees ? (rotationDegrees * Math.PI / 180) : rotation; const selectedScale = useScale ? useScale/SCALE_FACTOR : scale; const center = screenCenter ?? {x: 0, y: 0}; console.log("center", center.x, center.y); const newPosition = { x: -latitude * UP_SCALE * selectedScale, y: -longitude * UP_SCALE * selectedScale }; const cos = Math.cos(selectedRotation); const sin = Math.sin(selectedRotation); // Translate point relative to center, rotate, then translate back const dx = newPosition.x; const dy = newPosition.y; newPosition.x = (dx * cos - dy * sin) + center.x; newPosition.y = (dx * sin + dy * cos) + center.y; setPosition(newPosition); setRotation(selectedRotation); setScale(selectedScale); } const value = useMemo(() => ({ position, scale, rotation, screenCenter, setPosition, setScale, setRotation, rotateToAngle, screenToLocal, localToScreen, setTransform, setScreenCenter }), [position, scale, rotation, screenCenter]); return ( {children} ); }; // Custom hook for easy access to transform values export const useTransform = () => { const context = useContext(TransformContext); if (!context) { throw new Error('useTransform must be used within a TransformProvider'); } return context; };