import { FederatedMouseEvent, FederatedWheelEvent } from "pixi.js"; import { Component, ReactNode, useEffect, useState } from "react"; import { useTransform } from "./TransformContext"; import { useMapData } from "./MapDataContext"; import { SCALE_FACTOR } from "./Constants"; import { useApplication } from "@pixi/react"; class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean }> { state = { hasError: false }; static getDerivedStateFromError() { return { hasError: true }; } componentDidCatch(error: Error, info: React.ErrorInfo) { console.error("Error caught:", error, info); } render() { return this.state.hasError ?
Whoopsie Daisy!
: this.props.children; } } export function InfiniteCanvas({children} : Readonly<{children?: ReactNode}>) { const { position, setPosition, scale, setScale, rotation, setRotation, setScreenCenter, screenCenter } = useTransform(); const { routeData, originalRouteData } = useMapData(); const applicationRef = useApplication(); const [isDragging, setIsDragging] = useState(false); const [startMousePosition, setStartMousePosition] = useState({ x: 0, y: 0 }); const [startRotation, setStartRotation] = useState(0); const [startPosition, setStartPosition] = useState({ x: 0, y: 0 }); useEffect(() => { const canvas = applicationRef?.app.canvas; if (!canvas) return; const canvasRect = canvas.getBoundingClientRect(); const canvasLeft = canvasRect?.left ?? 0; const canvasTop = canvasRect?.top ?? 0; const centerX = window.innerWidth / 2 - canvasLeft; const centerY = window.innerHeight / 2 - canvasTop; setScreenCenter({x: centerX, y: centerY}); }, [applicationRef?.app.canvas, window.innerWidth, window.innerHeight]); const handlePointerDown = (e: FederatedMouseEvent) => { setIsDragging(true); setStartPosition({ x: position.x, y: position.y }); setStartMousePosition({ x: e.globalX, y: e.globalY }); setStartRotation(rotation); e.stopPropagation(); }; useEffect(() => { setRotation((originalRouteData?.rotate ?? 0) * Math.PI / 180); }, [originalRouteData?.rotate]); // Get canvas element and its dimensions/position const handlePointerMove = (e: FederatedMouseEvent) => { if (!isDragging) return; if (e.shiftKey) { const center = screenCenter ?? {x: 0, y: 0}; const startAngle = Math.atan2(startMousePosition.y - center.y, startMousePosition.x - center.x); const currentAngle = Math.atan2(e.globalY - center.y, e.globalX - center.x); // Calculate rotation difference in radians const rotationDiff = currentAngle - startAngle; // Update rotation setRotation(startRotation + rotationDiff); const cosDelta = Math.cos(rotationDiff); const sinDelta = Math.sin(rotationDiff); setPosition({ x: center.x * (1 - cosDelta) + startPosition.x * cosDelta + (center.y - startPosition.y) * sinDelta, y: center.y * (1 - cosDelta) + startPosition.y * cosDelta + (startPosition.x - center.x) * sinDelta }); } else { setRotation(startRotation); setPosition({ x: startPosition.x - startMousePosition.x + e.globalX, y: startPosition.y - startMousePosition.y + e.globalY }); } e.stopPropagation(); }; // Handle mouse up const handlePointerUp = (e: FederatedMouseEvent) => { setIsDragging(false); e.stopPropagation(); }; // Handle mouse wheel for zooming const handleWheel = (e: FederatedWheelEvent) => { e.stopPropagation(); // Get mouse position relative to canvas const mouseX = e.globalX - position.x; const mouseY = e.globalY - position.y; // Calculate new scale const scaleMin = (routeData?.scale_min ?? 10)/SCALE_FACTOR; const scaleMax = (routeData?.scale_max ?? 20)/SCALE_FACTOR; let zoomFactor = e.deltaY > 0 ? 0.9 : 1.1; // Zoom out/in //const newScale = scale * zoomFactor; const newScale = Math.max(scaleMin, Math.min(scaleMax, scale * zoomFactor)); zoomFactor = newScale / scale; if (scale === newScale) { return; } // Update position to zoom towards mouse cursor setPosition({ x: position.x + mouseX * (1 - zoomFactor), y: position.y + mouseY * (1 - zoomFactor) }); setScale(newScale); }; useEffect(() => { applicationRef?.app.render(); console.log(position, scale, rotation); }, [position, scale, rotation]); return (