| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- import { type CameraState } from '../camera/camera'
- import { calibratedLonLatToWorldTile } from '../../utils/projection'
- import { worldToScreen } from '../camera/camera'
- import { type MapLayer, type LayerRenderContext } from './mapLayer'
- import { type MapScene } from '../renderer/mapRenderer'
- export interface ScreenPoint {
- x: number
- y: number
- }
- function smoothTrackScreenPoints(points: ScreenPoint[]): ScreenPoint[] {
- if (points.length < 3) {
- return points
- }
- const smoothed: ScreenPoint[] = [points[0]]
- for (let index = 1; index < points.length - 1; index += 1) {
- const prev = points[index - 1]
- const current = points[index]
- const next = points[index + 1]
- smoothed.push({
- x: prev.x * 0.2 + current.x * 0.6 + next.x * 0.2,
- y: prev.y * 0.2 + current.y * 0.6 + next.y * 0.2,
- })
- }
- smoothed.push(points[points.length - 1])
- return smoothed
- }
- function buildVectorCamera(scene: MapScene): CameraState {
- return {
- centerWorldX: scene.exactCenterWorldX,
- centerWorldY: scene.exactCenterWorldY,
- viewportWidth: scene.viewportWidth,
- viewportHeight: scene.viewportHeight,
- visibleColumns: scene.visibleColumns,
- rotationRad: scene.rotationRad,
- }
- }
- export class TrackLayer implements MapLayer {
- projectPoints(scene: MapScene): ScreenPoint[] {
- const camera = buildVectorCamera(scene)
- return scene.track.map((point) => {
- const worldPoint = calibratedLonLatToWorldTile(point, scene.zoom, scene.gpsCalibration, scene.gpsCalibrationOrigin)
- return worldToScreen(camera, worldPoint, false)
- })
- }
- draw(context: LayerRenderContext): void {
- const { ctx, scene } = context
- if (scene.trackMode === 'none') {
- return
- }
- const points = smoothTrackScreenPoints(this.projectPoints(scene))
- if (!points.length) {
- return
- }
- ctx.save()
- ctx.lineCap = 'round'
- ctx.lineJoin = 'round'
- if (scene.trackMode === 'tail') {
- const baseAlpha = 0.12 + scene.trackStyleConfig.glowStrength * 0.08
- points.forEach((screenPoint, index) => {
- if (index === 0) {
- return
- }
- const progress = index / Math.max(1, points.length - 1)
- ctx.strokeStyle = `rgba(84, 243, 216, ${baseAlpha + progress * 0.58})`
- ctx.lineWidth = 1.4 + progress * 4.2
- ctx.beginPath()
- ctx.moveTo(points[index - 1].x, points[index - 1].y)
- ctx.lineTo(screenPoint.x, screenPoint.y)
- ctx.stroke()
- })
- const head = points[points.length - 1]
- ctx.fillStyle = 'rgba(84, 243, 216, 0.24)'
- ctx.beginPath()
- ctx.arc(head.x, head.y, 11, 0, Math.PI * 2)
- ctx.fill()
- ctx.fillStyle = '#54f3d8'
- ctx.beginPath()
- ctx.arc(head.x, head.y, 5.2, 0, Math.PI * 2)
- ctx.fill()
- } else {
- ctx.strokeStyle = 'rgba(23, 109, 93, 0.96)'
- ctx.lineWidth = 4.2
- ctx.beginPath()
- points.forEach((screenPoint, index) => {
- if (index === 0) {
- ctx.moveTo(screenPoint.x, screenPoint.y)
- return
- }
- ctx.lineTo(screenPoint.x, screenPoint.y)
- })
- ctx.stroke()
- }
- ctx.restore()
- }
- }
|