diff --git a/frontend/app/game/layout.tsx b/frontend/app/game/layout.tsx new file mode 100644 index 0000000..0e11c3d --- /dev/null +++ b/frontend/app/game/layout.tsx @@ -0,0 +1,7 @@ +export default function GameLayout({ + children, +}: { + children: React.ReactNode; +}) { + return
{children}
; +} diff --git a/frontend/app/game/page.tsx b/frontend/app/game/page.tsx new file mode 100644 index 0000000..6a0a4ee --- /dev/null +++ b/frontend/app/game/page.tsx @@ -0,0 +1,16 @@ +import { GameMap } from "@/components/map"; +import { Background } from "@/components/background"; + +export default function GamePage() { + return ( + <> + +
+ +
+ +
+
+ + ); +} diff --git a/frontend/app/globals.css b/frontend/app/globals.css index ba38cd0..3e9e9a3 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -13,19 +13,25 @@ --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; - --primary: 222.2 47.4% 11.2%; + --primary: 129 17% 51%; --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; + --secondary: 163 66% 14%; --secondary-foreground: 222.2 47.4% 11.2%; + --tertiary: 32 44% 53%; + + --yellow: 44 100% 50%; + --green: 155 51% 34%; + --brown: 33 48% 32%; + --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 84.2% 60.2%; + --destructive: 3 90% 54%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; @@ -73,38 +79,16 @@ body { @apply bg-background text-foreground; } - html, - body { - height: 100%; - } } -@layer utilities { - .text-outline-2 { - text-shadow: -2px -2px 0 #000, -2px -1px 0 #000, -2px 0px 0 #000, -2px 1px 0 #000, - -2px 2px 0 #000, -1px -2px 0 #000, -1px -1px 0 #000, -1px 0px 0 #000, -1px 1px 0 #000, - -1px 2px 0 #000, 0px -2px 0 #000, 0px -1px 0 #000, 0px 0px 0 #000, 0px 1px 0 #000, - 0px 2px 0 #000, 1px -2px 0 #000, 1px -1px 0 #000, 1px 0px 0 #000, 1px 1px 0 #000, - 1px 2px 0 #000, 2px -2px 0 #000, 2px -1px 0 #000, 2px 0px 0 #000, 2px 1px 0 #000, - 2px 2px 0 #000; - } - .text-outline-4 { - text-shadow: -4px -4px 0 #000, -4px -3px 0 #000, -4px -2px 0 #000, -4px -1px 0 #000, - -4px 0px 0 #000, -4px 1px 0 #000, -4px 2px 0 #000, -4px 3px 0 #000, -4px 4px 0 #000, - -3px -4px 0 #000, -3px -3px 0 #000, -3px -2px 0 #000, -3px -1px 0 #000, -3px 0px 0 #000, - -3px 1px 0 #000, -3px 2px 0 #000, -3px 3px 0 #000, -3px 4px 0 #000, -2px -4px 0 #000, - -2px -3px 0 #000, -2px -2px 0 #000, -2px -1px 0 #000, -2px 0px 0 #000, -2px 1px 0 #000, - -2px 2px 0 #000, -2px 3px 0 #000, -2px 4px 0 #000, -1px -4px 0 #000, -1px -3px 0 #000, - -1px -2px 0 #000, -1px -1px 0 #000, -1px 0px 0 #000, -1px 1px 0 #000, -1px 2px 0 #000, - -1px 3px 0 #000, -1px 4px 0 #000, 0px -4px 0 #000, 0px -3px 0 #000, 0px -2px 0 #000, - 0px -1px 0 #000, 0px 0px 0 #000, 0px 1px 0 #000, 0px 2px 0 #000, 0px 3px 0 #000, - 0px 4px 0 #000, 1px -4px 0 #000, 1px -3px 0 #000, 1px -2px 0 #000, 1px -1px 0 #000, - 1px 0px 0 #000, 1px 1px 0 #000, 1px 2px 0 #000, 1px 3px 0 #000, 1px 4px 0 #000, - 2px -4px 0 #000, 2px -3px 0 #000, 2px -2px 0 #000, 2px -1px 0 #000, 2px 0px 0 #000, - 2px 1px 0 #000, 2px 2px 0 #000, 2px 3px 0 #000, 2px 4px 0 #000, 3px -4px 0 #000, - 3px -3px 0 #000, 3px -2px 0 #000, 3px -1px 0 #000, 3px 0px 0 #000, 3px 1px 0 #000, - 3px 2px 0 #000, 3px 3px 0 #000, 3px 4px 0 #000, 4px -4px 0 #000, 4px -3px 0 #000, - 4px -2px 0 #000, 4px -1px 0 #000, 4px 0px 0 #000, 4px 1px 0 #000, 4px 2px 0 #000, - 4px 3px 0 #000, 4px 4px 0 #000; - } +.title-stroke { + text-shadow: 3px 3px 3px var(--secondary), -3px -3px 3px var(--secondary), + 3px -3px 3px var(--secondary), -3px 3px 3px var(--secondary), + 3px 3px 3px var(--secondary); +} + +.stroke { + text-shadow: 2px 2px 2px var(--secondary), -2px -2px 2px var(--secondary), + 2px -2px 2px var(--secondary), -2px 2px 2px var(--secondary), + 2px 2px 2px var(--secondary); } diff --git a/frontend/assets/background.png b/frontend/assets/background.png new file mode 100644 index 0000000..8d29dce Binary files /dev/null and b/frontend/assets/background.png differ diff --git a/frontend/components/background.tsx b/frontend/components/background.tsx new file mode 100644 index 0000000..4a10f39 --- /dev/null +++ b/frontend/components/background.tsx @@ -0,0 +1,20 @@ +import bgImage from "@/assets/background.png"; +import { cn } from "@/lib/utils"; +import Image from "next/image"; + +interface Props { + isFullScreen?: boolean + cls?: string +} +export const Background = ({ isFullScreen = false, cls }: Props) => { + return ( +
+ map-background +
+ ); +}; diff --git a/frontend/components/map/constants/index.ts b/frontend/components/map/constants/index.ts new file mode 100644 index 0000000..3185289 --- /dev/null +++ b/frontend/components/map/constants/index.ts @@ -0,0 +1,3 @@ +export * from './map-data' +export * from './nodes' +export * from './styling' \ No newline at end of file diff --git a/frontend/components/map/constants/map-data.ts b/frontend/components/map/constants/map-data.ts new file mode 100644 index 0000000..d330928 --- /dev/null +++ b/frontend/components/map/constants/map-data.ts @@ -0,0 +1,98 @@ +import { MapData } from "../map.interface"; +import { + BoardwalkNode, + CafeNode, + ChurchNode, + DinerNode, + DocksNode, + FactoryNode, + FarmsteadNode, + GraveyardNode, + GreatHallNode, + HistoricInnNode, + HospitalNode, + JunkyardNode, + MarketNode, + OldMillNode, + ParkNode, + PawnShopNode, + PoliceStationNode, + SecretLodgeNode, + SwampNode, + TheaterNode, + TrainStationNode, + UniversityNode, + WharfNode, + WoodsNode, +} from "./nodes"; + +export const mapData: MapData = { + nodes: [ + // Arkham Nodes + TrainStationNode, + UniversityNode, + PoliceStationNode, + DinerNode, + SecretLodgeNode, + ParkNode, + + // Dunwich Nodes + CafeNode, + OldMillNode, + FarmsteadNode, + ChurchNode, + SwampNode, + HistoricInnNode, + + // Kingsport Nodes + GreatHallNode, + TheaterNode, + GraveyardNode, + WharfNode, + MarketNode, + WoodsNode, + + // Innsmouth Nodes + JunkyardNode, + PawnShopNode, + DocksNode, + HospitalNode, + FactoryNode, + BoardwalkNode, + ], + links: [ + { source: TrainStationNode, target: UniversityNode }, + { source: UniversityNode, target: PoliceStationNode }, + { source: PoliceStationNode, target: ParkNode }, + { source: ParkNode, target: UniversityNode }, + + { source: PoliceStationNode, target: SecretLodgeNode }, + { source: SecretLodgeNode, target: DinerNode }, + { source: ParkNode, target: SecretLodgeNode }, + { source: TrainStationNode, target: CafeNode }, + { source: DinerNode, target: JunkyardNode }, + + { source: CafeNode, target: ChurchNode }, + { source: ChurchNode, target: OldMillNode }, + { source: ChurchNode, target: HistoricInnNode }, + { source: ChurchNode, target: FarmsteadNode }, + { source: HistoricInnNode, target: FarmsteadNode }, + { source: FarmsteadNode, target: SwampNode }, + { source: SwampNode, target: GreatHallNode }, + + { source: JunkyardNode, target: PawnShopNode }, + { source: PawnShopNode, target: HospitalNode }, + { source: HospitalNode, target: FactoryNode }, + { source: FactoryNode, target: PawnShopNode }, + { source: FactoryNode, target: BoardwalkNode }, + { source: BoardwalkNode, target: DocksNode }, + { source: DocksNode, target: WoodsNode }, + + { source: GreatHallNode, target: WoodsNode }, + { source: WoodsNode, target: MarketNode }, + { source: MarketNode, target: GreatHallNode }, + { source: MarketNode, target: TheaterNode }, + { source: MarketNode, target: WharfNode }, + { source: WharfNode, target: GraveyardNode }, + ], +}; diff --git a/frontend/components/map/constants/nodes.ts b/frontend/components/map/constants/nodes.ts new file mode 100644 index 0000000..0886f29 --- /dev/null +++ b/frontend/components/map/constants/nodes.ts @@ -0,0 +1,228 @@ +// Arkham Nodes +export const TrainStationNode = { + id: "Train Station", + city: "Arkham" as City, + fx: 42.1875, + fy: 154.6875, + labelx: 0, + labely: 95, + isBusStation: true +}; + +export const UniversityNode = { + id: "University", + city: "Arkham" as City, + fx: 168.75, + fy: 75.3125, + labelx: 133.75, + labely: 45.3125, +}; + +export const PoliceStationNode = { + id: "Police Station", + city: "Arkham" as City, + fx: 379.6875, + fy: 68.28125, + labelx: 335, + labely: 38.28125, +}; + +export const SecretLodgeNode = { + id: "Secret Lodge", + city: "Arkham" as City, + fx: 337.5, + fy: 203.90625, + labelx: 300, + labely: 250.90625, +}; + +export const DinerNode = { + id: "Diner", + city: "Arkham" as City, + fx: 506.25, + fy: 168.75, + labelx: 485.25, + labely: 105, + isBusStation: true +}; + +export const ParkNode = { + id: "Park", + city: "Arkham" as City, + fx: 196.875, + fy: 281.25, + labelx: 230, + labely: 290.25, + isGate: true +}; + +// Dunwich Nodes +export const CafeNode = { + id: "Cafe", + city: "Dunwich" as City, + fx: 126.5625, + fy: 386.71875, + labelx: 60, + labely: 370, +}; + +export const OldMillNode = { + id: "Old Mill", + city: "Dunwich" as City, + fx: 70.3125, + fy: 506.25, + labelx: 42.3125, + labely: 475.25, + isGate: true +}; + +export const ChurchNode = { + id: "Church", + city: "Dunwich" as City, + fx: 253.125, + fy: 471.09375, + labelx: 227.125, + labely: 437.59375, +}; + +export const FarmsteadNode = { + id: "Farmstead", + city: "Dunwich" as City, + fx: 196.875, + fy: 675, + labelx: 110, + labely: 640, +}; + +export const SwampNode = { + id: "Swamp", + city: "Dunwich" as City, + fx: 386.71875, + fy: 618.75, + labelx: 360, + labely: 585, +}; + +export const HistoricInnNode = { + id: "Historic Inn", + city: "Dunwich" as City, + fx: 450, + fy: 421.875, + labelx: 410.5, + labely: 360, + isBusStation: true +}; + +// Kingsport Nodes +export const GreatHallNode = { + id: "Great Hall", + city: "Kingsport" as City, + fx: 590.625, + fy: 562.5, + labelx: 492.5, + labely: 530, +}; + +export const TheaterNode = { + id: "Theater", + city: "Kingsport" as City, + fx: 787.5, + fy: 618.75, + labelx: 690, + labely: 620, +}; + +export const GraveyardNode = { + id: "Graveyard", + city: "Kingsport" as City, + fx: 914.0625, + fy: 689.0625, + labelx: 950, + labely: 695, + isGate: true +}; + +export const WharfNode = { + id: "Wharf", + city: "Kingsport" as City, + fx: 956.25, + fy: 506.25, + labelx: 932.5, + labely: 472.5, +}; + +export const MarketNode = { + id: "Market", + city: "Kingsport" as City, + fx: 773.4375, + fy: 464.0625, + labelx: 805, + labely: 430, + isBusStation: true +}; + +export const WoodsNode = { + id: "Woods", + city: "Kingsport" as City, + fx: 604.6875, + fy: 393.75, + labelx: 578, + labely: 360, +}; + +// Innsmouth Nodes +export const JunkyardNode = { + id: "Junkyard", + city: "Innsmouth" as City, + fx: 618.75, + fy: 168.75, + labelx: 583.75, + labely: 135.75, +}; + +export const PawnShopNode = { + id: "Pawn Shop", + city: "Innsmouth" as City, + fx: 731.25, + fy: 253.125, + labelx: 610, + labely: 260, +}; + +export const DocksNode = { + id: "Docks", + city: "Innsmouth" as City, + fx: 900, + fy: 323.4375, + labelx: 938, + labely: 330, +}; + +export const HospitalNode = { + id: "Hospital", + city: "Innsmouth" as City, + fx: 787.5, + fy: 70.3125, + labelx: 752.5, + labely: 35, + isGate: true +}; + +export const FactoryNode = { + id: "Factory", + city: "Innsmouth" as City, + fx: 900, + fy: 154.6875, + labelx: 870, + labely: 95, + isBusStation: true +}; + +export const BoardwalkNode = { + id: "Boardwalk", + city: "Innsmouth" as City, + fx: 1047.65625, + fy: 196.875, + labelx: 985, + labely: 160, +}; diff --git a/frontend/components/map/constants/styling.ts b/frontend/components/map/constants/styling.ts new file mode 100644 index 0000000..d0671e3 --- /dev/null +++ b/frontend/components/map/constants/styling.ts @@ -0,0 +1,9 @@ +export const CityColorMapping: Record< + City, + { startColor: string; endColor: string } +> = { + Arkham: { startColor: "#055459", endColor: "#077353" }, + Dunwich: { startColor: "#D55424", endColor: "#EB9E18" }, + Kingsport: { startColor: "#EC0E47", endColor: "#E1368E" }, + Innsmouth: { startColor: "#5E49A2", endColor: "#9E54A6" }, +}; diff --git a/frontend/components/map/draw-map.ts b/frontend/components/map/draw-map.ts new file mode 100644 index 0000000..9250c17 --- /dev/null +++ b/frontend/components/map/draw-map.ts @@ -0,0 +1,69 @@ +import { CityColorMapping } from "./constants"; +import { MapLink, MapNode } from "./map.interface"; + +export const RADIUS = 22.5; + +export const drawMap = ( + context: CanvasRenderingContext2D, + width: number, + height: number, + nodes: MapNode[], + links: MapLink[] +) => { + context.clearRect(0, 0, width, height); + + // Draw the links first + links.forEach((link) => { + context.beginPath(); + context.moveTo(link.source.fx, link.source.fy); + context.lineTo(link.target.fx, link.target.fy); + context.strokeStyle = '#fff' + context.stroke(); + }); + + // Draw the nodes + nodes.forEach((node) => { + if (!node.fx || !node.fy) return; + if(node.isGate) { + const gate = new Image() + gate.src = './props/gate.png' + gate.onload = () => { context.drawImage(gate, node.fx - RADIUS, node.fy - RADIUS, RADIUS * 2, RADIUS * 2)} + } + + if(node.isBusStation) { + const labelWidth = context.measureText(node.id).width + const bus = new Image() + bus.src = './props/bus.png' + bus.onload = () => { context.drawImage(bus, node.labelx + labelWidth / 2 - 12.5, node.labely, 25, 25)} + } + + /* draw node */ + context.beginPath(); + context.moveTo(node.fx + RADIUS, node.fy); + context.arc(node.fx, node.fy, RADIUS, 0, 2 * Math.PI); + context.closePath() + + /* node styling */ + const gradient = context.createLinearGradient(node.fx - RADIUS, node.fy - RADIUS, node.fx + RADIUS, node.fy + RADIUS) + const cityColor = CityColorMapping[node.city] + gradient.addColorStop(0, cityColor.startColor) + gradient.addColorStop(1, cityColor.endColor) + context.fillStyle = gradient + + context.shadowColor = "#333"; + context.shadowOffsetX = 4; + context.shadowOffsetY = 4 + context.shadowBlur = 16; + + context.strokeStyle = '#fff' + context.lineWidth = 2 + context.fill(); + context.stroke(); + + /* node label */ + context.beginPath(); + context.font = 'bold 16px Noto Sans' + context.fillStyle = '#fff' + context.fillText(node.id, node.labelx, node.isBusStation ? node.labely - 7.5 : node.labely) + }); +}; diff --git a/frontend/components/map/index.tsx b/frontend/components/map/index.tsx new file mode 100644 index 0000000..c1fa8a0 --- /dev/null +++ b/frontend/components/map/index.tsx @@ -0,0 +1,57 @@ +"use client"; +import * as d3 from "d3"; +import { useEffect, useRef } from "react"; +import { RADIUS, drawMap } from "./draw-map"; +import { MapLink, MapNode } from "./map.interface"; +import { mapData } from "./constants"; + +type NetworkDiagramProps = { + width: number; + height: number; +}; + +export const GameMap = ({ + width, + height, +}: NetworkDiagramProps) => { + // The force simulation mutates links and nodes, so create a copy first + // Node positions are initialized by d3 + const links: MapLink[] = mapData.links.map((d) => ({ ...d })); + const nodes: MapNode[] = mapData.nodes.map((d) => ({ ...d })); + + const canvasRef = useRef(null); + + useEffect(() => { + // set dimension of the canvas element + const canvas = canvasRef.current; + const context = canvas?.getContext("2d"); + + if (!context) { + return; + } + + // run d3-force to find the position of nodes on the canvas + d3.forceSimulation(nodes) + .force( + "link", + d3.forceLink(links).id((d) => d.id) + ) + .force("collide", d3.forceCollide().radius(RADIUS)) + .force("charge", d3.forceManyBody().strength(-30)) + .force("center", d3.forceCenter(width / 2, height / 2)) + + // at each iteration of the simulation, draw the network diagram with the new node positions + .on("tick", () => { + drawMap(context, width, height, nodes, links); + }); + }, [width, height, nodes, links]); + + return ( + + ); +}; diff --git a/frontend/components/map/map.interface.ts b/frontend/components/map/map.interface.ts new file mode 100644 index 0000000..436c851 --- /dev/null +++ b/frontend/components/map/map.interface.ts @@ -0,0 +1,22 @@ +import { SimulationLinkDatum, SimulationNodeDatum } from "d3"; + +export interface MapNode extends SimulationNodeDatum { + id: string + city: City + fx: number + fy: number + labelx: number + labely: number + isGate?: boolean + isBusStation?: boolean +} + +export interface MapLink extends SimulationLinkDatum { + source: MapNode + target: MapNode +} + +export type MapData = { + nodes: MapNode[] + links: MapLink[] +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8108a4d..b1d5eae 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,6 +13,7 @@ "@radix-ui/react-slot": "^1.0.2", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", + "d3": "^7.8.5", "lucide-react": "^0.288.0", "next": "13.5.5", "react": "^18", @@ -22,6 +23,7 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { + "@types/d3": "^7.4.3", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -740,6 +742,265 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dev": true, + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "dev": true + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "dev": true + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "dev": true + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dev": true, + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "dev": true + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", + "dev": true + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "dev": true + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "dev": true + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dev": true, + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.9.tgz", + "integrity": "sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==", + "dev": true + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "dev": true + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dev": true, + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.6.tgz", + "integrity": "sha512-qlmD/8aMk5xGorUvTUWHCiumvgaUXYldYjNVOWtYoTYY/L+WwIEAmJxUmTgr9LoGNG0PPAOmqMDJVDPc7DOpPw==", + "dev": true + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dev": true, + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz", + "integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==", + "dev": true + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "dev": true + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "dev": true + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "dev": true + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dev": true, + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==", + "dev": true + }, + "node_modules/@types/d3-selection": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==", + "dev": true + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dev": true, + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", + "dev": true + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "dev": true + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "dev": true + }, + "node_modules/@types/d3-transition": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", + "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dev": true, + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.13", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz", + "integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1832,6 +2093,384 @@ "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", "devOptional": true }, + "node_modules/d3": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", + "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -1914,6 +2553,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "dependencies": { + "robust-predicates": "^3.0.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3016,6 +3663,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -3102,6 +3760,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -4566,6 +5232,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4588,6 +5259,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "node_modules/safe-array-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", @@ -4639,6 +5315,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 132652b..46eded8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,7 @@ "@radix-ui/react-slot": "^1.0.2", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", + "d3": "^7.8.5", "lucide-react": "^0.288.0", "next": "13.5.5", "react": "^18", @@ -24,6 +25,7 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { + "@types/d3": "^7.4.3", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/frontend/public/props/bus.png b/frontend/public/props/bus.png new file mode 100644 index 0000000..8d4f1f7 Binary files /dev/null and b/frontend/public/props/bus.png differ diff --git a/frontend/public/props/gate.png b/frontend/public/props/gate.png new file mode 100644 index 0000000..61420a3 Binary files /dev/null and b/frontend/public/props/gate.png differ diff --git a/frontend/shared/type/type.d.ts b/frontend/shared/type/type.d.ts index c090641..3f11364 100644 --- a/frontend/shared/type/type.d.ts +++ b/frontend/shared/type/type.d.ts @@ -7,6 +7,7 @@ type Investigator = | "occultist" | "reporter"; type Sanity = "sane" | "insane"; +type City = 'Arkham' | 'Dunwich' | 'Kingsport' | 'Innsmouth'; interface InvestigatorData { name: Investigator; @@ -14,3 +15,4 @@ interface InvestigatorData { insaneImage: string; sanity: Sanity; } + diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index a2ab943..6475f1a 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -29,27 +29,30 @@ module.exports = { background: "hsl(var(--background))", foreground: "hsl(var(--foreground))", primary: { - DEFAULT: "#6D9773", - foreground: "#6D9773", + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', }, secondary: { - DEFAULT: "#0C3B2E", + DEFAULT: 'hsl(var(--secondary))', foreground: "#0C3B2E", }, tertiary: { - DEFAULT: "#BB8A52", + DEFAULT: "hsl(var(--tertiary))", foreground: "#BB8A52", }, yellow: { - DEFAULT: "#FFBA00", + DEFAULT: 'hsl(var(--yellow))', foreground: "#FFBA00", }, + green: { + DEFAULT: 'hsl(var(--green))', + }, brown: { - DEFAULT: "#77542A", + DEFAULT: 'hsl(var(--brown))', foreground: "#77542A", }, destructive: { - DEFAULT: "#F32C22", + DEFAULT: 'hsl(var(--destructive))', foreground: "#F32C22", }, muted: {