feat: add working paths

This commit is contained in:
doprz
2024-10-23 12:38:23 -05:00
parent 910c75a730
commit 8bf8a7fff0
4 changed files with 1911 additions and 1 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,94 @@
import React, { useState } from 'react';
import { generateAllBuildingPaths, graphNodes, PathSegment } from './mapUtils';
const UTMapURL = new URL('/src/assets/UT-Map.png', import.meta.url).href;
/**
* CampusMap component renders an interactive map of the UT campus.
*
* The map includes:
* - An image of the campus map.
* - An SVG overlay to draw paths and nodes.
* - Building selection controls to highlight specific paths.
*/
export default function CampusMap() {
const [highlightedPath, setHighlightedPath] = useState<string[]>([]);
return (
<div className='relative h-full w-full'>
{/* Map Image: 784x754 */}
<img src={UTMapURL} alt='UT Campus Map' className='h-full w-full object-contain' />
{/* SVG Overlay */}
<svg className='absolute left-0 top-0 h-full w-full' viewBox='0 0 784 754' preserveAspectRatio='none'>
{/* Draw all building-to-building paths */}
{generateAllBuildingPaths().map(path => (
<g key={path.id}>
{path.points.slice(0, -1).map((startNode, index) => (
<PathSegment
key={`${startNode}-${path.points[index + 1]}`}
start={startNode}
end={path.points[index + 1] || ''}
isHighlighted={highlightedPath.includes(path.id)}
/>
))}
</g>
))}
{/* Draw nodes */}
{Object.entries(graphNodes).map(([id, node]) => (
<g key={id}>
<circle
cx={node.x}
cy={node.y}
r={node.type === 'building' ? 6 : 4}
fill={node.type === 'building' ? '#BF5700' : '#666666'}
stroke='white'
strokeWidth='2'
className='opacity-90'
/>
{/* Only label buildings */}
{node.type === 'building' && (
<text x={node.x + 12} y={node.y + 4} fill='#000000' fontSize='14' className='font-bold'>
{id}
</text>
)}
</g>
))}
</svg>
{/* Building Selection Controls */}
<div className='absolute right-8 top-8 rounded-md bg-white/90 p-3 shadow-sm space-y-4'>
<div className='text-sm space-y-2'>
<div className='flex items-center gap-2'>
<div className='h-3 w-3 rounded-full bg-[#BF5700]' />
<span>Buildings</span>
</div>
<div className='flex items-center gap-2'>
<div className='h-3 w-3 rounded-full bg-[#666666]' />
<span>Path Intersections</span>
</div>
<div className='flex items-center gap-2'>
<div className='h-1 w-6 bg-[#BF5700]' />
<span>Walking Paths</span>
</div>
</div>
<div className='space-y-2'>
<p className='text-sm font-medium'>Building Paths:</p>
{generateAllBuildingPaths().map(path => (
<button
key={path.id}
onClick={() => setHighlightedPath([path.id])}
className='block text-sm text-gray-600 hover:text-gray-900'
>
{path.points[0]} {path.points[path.points.length - 1]}
</button>
))}
</div>
</div>
</div>
);
}

View File

@@ -11,6 +11,7 @@ import CalendarFooter from '../calendar/CalendarFooter';
import { CalendarSchedules } from '../calendar/CalendarSchedules'; import { CalendarSchedules } from '../calendar/CalendarSchedules';
import ImportantLinks from '../calendar/ImportantLinks'; import ImportantLinks from '../calendar/ImportantLinks';
import TeamLinks from '../calendar/TeamLinks'; import TeamLinks from '../calendar/TeamLinks';
import CampusMap from './CampusMap';
const manifest = chrome.runtime.getManifest(); const manifest = chrome.runtime.getManifest();
const LDIconURL = new URL('/src/assets/LD-icon.png', import.meta.url).href; const LDIconURL = new URL('/src/assets/LD-icon.png', import.meta.url).href;
@@ -52,7 +53,7 @@ export default function Map(): JSX.Element {
<CalendarFooter /> <CalendarFooter />
</div> </div>
<div className='flex p-12'> <div className='flex p-12'>
<img src={UTMapURL} alt='LD Icon' /> <CampusMap />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,329 @@
import type { MainCampusBuildingsCode } from '@shared/types/MainCampusBuildings';
import React from 'react';
// Type definitions
type NodeType = 'building' | 'intersection';
interface Node {
x: number;
y: number;
// connections: string[];
type: NodeType;
}
interface GraphNodes {
[key: string]: Node;
}
interface Path {
id: string;
points: string[]; // Array of node IDs representing the path
}
// Helper functions for path finding
export const findNearestIntersection = (buildingId: string, graph: GraphNodes): string => {
const building = graph[buildingId];
let nearestIntersection = '';
let shortestDistance = Infinity;
Object.entries(graph).forEach(([nodeId, node]) => {
if (node.type === 'intersection' && building) {
const distance = Math.sqrt((node.x - building.x) ** 2 + (node.y - building.y) ** 2);
if (distance < shortestDistance) {
shortestDistance = distance;
nearestIntersection = nodeId;
}
}
});
return nearestIntersection;
};
// Get all buildings from the graph
export const getAllBuildings = (graph: GraphNodes): string[] =>
Object.entries(graph)
.filter(([_key, node]) => node.type === 'building')
.map(([id]) => id);
// Generate all possible building-to-building paths
export const generateAllBuildingPaths = (): Path[] => {
const buildings = getAllBuildings(graphNodes);
const paths: Path[] = [];
for (let i = 0; i < buildings.length; i++) {
for (let j = i + 1; j < buildings.length; j++) {
const building1 = buildings[i];
const building2 = buildings[j];
if (building1 && building2) {
// Get nearest intersections for both buildings
const int1 = findNearestIntersection(building1, graphNodes);
const int2 = findNearestIntersection(building2, graphNodes);
paths.push({
id: `${building1}-${building2}`,
points: [building1, int1, int2, building2],
});
}
}
}
return paths;
};
// Draw a single path segment
export const PathSegment = ({
start,
end,
isHighlighted,
}: {
start: string;
end: string;
isHighlighted?: boolean;
}): JSX.Element | null => {
const startNode = graphNodes[start];
const endNode = graphNodes[end];
if (!startNode || !endNode) return null;
return (
<line
x1={startNode.x}
y1={startNode.y}
x2={endNode.x}
y2={endNode.y}
// stroke={isHighlighted ? '#FF8C00' : '#BF5700'}
stroke={isHighlighted ? '#000' : '#BF5700'} // TODO
// strokeWidth={isHighlighted ? '4' : '2'}
strokeWidth={isHighlighted ? '10' : '2'}
strokeLinecap='round'
className={`opacity-60 ${isHighlighted ? 'z-1000' : ''}`}
/>
);
};
export const graphNodes: GraphNodes = {
// Building nodes
GDC: {
x: 257,
y: 283,
type: 'building',
},
PCL: {
x: 222,
y: 430,
type: 'building',
},
JES: {
x: 260,
y: 420,
type: 'building',
},
GRE: {
x: 260,
y: 375,
type: 'building',
},
MAI: {
x: 167,
y: 310,
type: 'building',
},
WEL: {
x: 216,
y: 268,
type: 'building',
},
BEL: {
x: 365,
y: 377,
type: 'building',
},
WCP: {
x: 260,
y: 343,
type: 'building',
},
RLP: {
x: 300,
y: 335,
type: 'building',
},
UTC: {
x: 197,
y: 410,
type: 'building',
},
CBA: {
x: 232,
y: 363,
type: 'building',
},
GSB: {
x: 208,
y: 382,
type: 'building',
},
PMA: {
x: 255,
y: 185,
type: 'building',
},
PAT: {
x: 258,
y: 222,
type: 'building',
},
EER: {
x: 289,
y: 208,
type: 'building',
},
ECJ: {
x: 289,
y: 280,
type: 'building',
},
UNB: {
x: 105,
y: 288,
type: 'building',
},
FAC: {
x: 133,
y: 298,
type: 'building',
},
HRC: {
x: 112,
y: 380,
type: 'building',
},
COM: {
x: 195,
y: 318,
type: 'building',
},
// Intersection nodes
'speedway-24th': {
x: 241,
y: 250,
type: 'intersection',
},
'speedway-21st': {
x: 241,
y: 400,
type: 'intersection',
},
'speedway-e-mai-stairs': {
x: 241,
y: 315,
type: 'intersection',
},
'speedway-w-eer': {
x: 241,
y: 208,
type: 'intersection',
},
'guad-24th': {
x: 89,
y: 250,
type: 'intersection',
},
'guad-21st': {
x: 89,
y: 400,
type: 'intersection',
},
'guad-icd': {
x: 89,
y: 353,
type: 'intersection',
},
'uni-ave-21st': {
x: 166,
y: 400,
type: 'intersection',
},
'wichita-21st': {
x: 187,
y: 400,
type: 'intersection',
},
'n-mai-24th': {
x: 167,
y: 250,
type: 'intersection',
},
's-mai-stairs': {
x: 167,
y: 347,
type: 'intersection',
},
'e-mai-stairs': {
x: 215,
y: 315,
type: 'intersection',
},
'guad-w-mai': {
x: 89,
y: 317,
type: 'intersection',
},
'n-mai-turtle-pond': {
x: 167,
y: 282,
type: 'intersection',
},
'icd-ne': {
x: 207,
y: 289,
type: 'intersection',
},
'icd-nne': {
x: 190,
y: 282,
type: 'intersection',
},
'icd-se': {
x: 212,
y: 338,
type: 'intersection',
},
'icd-sse': {
x: 190,
y: 347,
type: 'intersection',
},
'san-jac-21th': {
x: 354,
y: 400,
type: 'intersection',
},
'san-jac-24th': {
x: 357,
y: 250,
type: 'intersection',
},
'san-jac-23rd': {
x: 358,
y: 318,
type: 'intersection',
},
'mlk-jr-statue': {
x: 280,
y: 318,
type: 'intersection',
},
'pcl-nw-21st-walkway': {
x: 208,
y: 400,
type: 'intersection',
},
'pcl-w-speedway': {
x: 241,
y: 425,
type: 'intersection',
},
};