diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6c3c727 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +* text=auto eol=lf + +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.webp binary +*.ico binary +*.pdf binary +*.ttf binary +*.woff binary +*.woff2 binary diff --git a/.gitignore b/.gitignore index 35de4ee..97be300 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,20 @@ node_modules/ .tmp-ts/ +miniprogram_npm/ +dist/ +build/ +coverage/ project.private.config.json +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +.idea/ +.vscode/ +*.suo +*.user +*.tmp +*.swp .DS_Store Thumbs.db diff --git a/miniprogram/app.ts b/miniprogram/app.ts index 1af73a8..a070974 100644 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -9,8 +9,7 @@ App({ // 登录 wx.login({ - success: res => { - console.log(res.code) + success: () => { // 发送 res.code 到后台换取 openId, sessionKey, unionId }, }) diff --git a/miniprogram/engine/map/mapEngine.ts b/miniprogram/engine/map/mapEngine.ts index 11cf138..5109ba5 100644 --- a/miniprogram/engine/map/mapEngine.ts +++ b/miniprogram/engine/map/mapEngine.ts @@ -7,6 +7,8 @@ import { worldTileToLonLat, type LonLatPoint } from '../../utils/projection' const RENDER_MODE = 'Single WebGL Pipeline' const PROJECTION_MODE = 'WGS84 -> WorldTile -> Camera -> Screen' const MAP_NORTH_OFFSET_DEG = 0 +const MAGNETIC_DECLINATION_DEG = -6.91 +const MAGNETIC_DECLINATION_TEXT = '6.91° W' const MIN_ZOOM = 15 const MAX_ZOOM = 20 const DEFAULT_ZOOM = 17 @@ -25,12 +27,13 @@ const INERTIA_MIN_SPEED = 0.02 const PREVIEW_RESET_DURATION_MS = 140 const UI_SYNC_INTERVAL_MS = 80 const ROTATE_STEP_DEG = 15 -const AUTO_ROTATE_FRAME_MS = 12 -const AUTO_ROTATE_EASE = 0.2 +const AUTO_ROTATE_FRAME_MS = 8 +const AUTO_ROTATE_EASE = 0.34 const AUTO_ROTATE_SNAP_DEG = 0.1 -const AUTO_ROTATE_DEADZONE_DEG = 0.35 -const AUTO_ROTATE_MAX_STEP_DEG = 1.35 +const AUTO_ROTATE_DEADZONE_DEG = 4 +const AUTO_ROTATE_MAX_STEP_DEG = 0.75 const AUTO_ROTATE_HEADING_SMOOTHING = 0.32 +const COMPASS_NEEDLE_SMOOTHING = 0.12 const SAMPLE_TRACK_WGS84: LonLatPoint[] = [ worldTileToLonLat({ x: DEFAULT_CENTER_TILE_X - 0.72, y: DEFAULT_CENTER_TILE_Y + 0.44 }, DEFAULT_ZOOM), @@ -49,6 +52,9 @@ type GestureMode = 'idle' | 'pan' | 'pinch' type RotationMode = 'manual' | 'auto' type OrientationMode = 'manual' | 'north-up' | 'heading-up' type AutoRotateSourceMode = 'sensor' | 'course' | 'fusion' +type NorthReferenceMode = 'magnetic' | 'true' + +const DEFAULT_NORTH_REFERENCE_MODE: NorthReferenceMode = 'magnetic' export interface MapEngineStageRect { width: number @@ -73,6 +79,8 @@ export interface MapEngineViewState { orientationMode: OrientationMode orientationModeText: string sensorHeadingText: string + compassDeclinationText: string + northReferenceButtonText: string autoRotateSourceText: string autoRotateCalibrationText: string northReferenceText: string @@ -120,6 +128,8 @@ const VIEW_SYNC_KEYS: Array = [ 'orientationMode', 'orientationModeText', 'sensorHeadingText', + 'compassDeclinationText', + 'northReferenceButtonText', 'autoRotateSourceText', 'autoRotateCalibrationText', 'northReferenceText', @@ -247,16 +257,84 @@ function formatAutoRotateCalibrationText(pending: boolean, offsetDeg: number | n return `Offset ${Math.round(normalizeRotationDeg(offsetDeg))}deg` } -function formatNorthReferenceText(): string { - return 'Map North = 0deg (TFW aligned)' +function getTrueHeadingDeg(magneticHeadingDeg: number): number { + return normalizeRotationDeg(magneticHeadingDeg + MAGNETIC_DECLINATION_DEG) } -function formatCompassNeedleDeg(headingDeg: number | null): number { - if (headingDeg === null) { +function getMagneticHeadingDeg(trueHeadingDeg: number): number { + return normalizeRotationDeg(trueHeadingDeg - MAGNETIC_DECLINATION_DEG) +} + +function getMapNorthOffsetDeg(_mode: NorthReferenceMode): number { + return MAP_NORTH_OFFSET_DEG +} + +function getCompassReferenceHeadingDeg(mode: NorthReferenceMode, magneticHeadingDeg: number): number { + if (mode === 'true') { + return getTrueHeadingDeg(magneticHeadingDeg) + } + + return normalizeRotationDeg(magneticHeadingDeg) +} + +function getMapReferenceHeadingDegFromSensor(mode: NorthReferenceMode, magneticHeadingDeg: number): number { + if (mode === 'magnetic') { + return normalizeRotationDeg(magneticHeadingDeg) + } + + return getTrueHeadingDeg(magneticHeadingDeg) +} + +function getMapReferenceHeadingDegFromCourse(mode: NorthReferenceMode, trueHeadingDeg: number): number { + if (mode === 'magnetic') { + return getMagneticHeadingDeg(trueHeadingDeg) + } + + return normalizeRotationDeg(trueHeadingDeg) +} + +function formatNorthReferenceText(mode: NorthReferenceMode): string { + if (mode === 'magnetic') { + return `Compass Magnetic / Heading-Up Magnetic (${MAGNETIC_DECLINATION_TEXT})` + } + + return `Compass True / Heading-Up True (${MAGNETIC_DECLINATION_TEXT})` +} + +function formatCompassDeclinationText(mode: NorthReferenceMode): string { + if (mode === 'true') { + return MAGNETIC_DECLINATION_TEXT + } + + return '' +} + +function formatNorthReferenceButtonText(mode: NorthReferenceMode): string { + return mode === 'magnetic' ? '北参考:磁北' : '北参考:真北' +} + +function formatNorthReferenceStatusText(mode: NorthReferenceMode): string { + if (mode === 'magnetic') { + return '已切到磁北模式' + } + + return '已切到真北模式' +} + +function getNextNorthReferenceMode(mode: NorthReferenceMode): NorthReferenceMode { + return mode === 'magnetic' ? 'true' : 'magnetic' +} + +function formatCompassNeedleDegForMode(mode: NorthReferenceMode, magneticHeadingDeg: number | null): number { + if (magneticHeadingDeg === null) { return 0 } - return normalizeRotationDeg(360 - headingDeg) + const referenceHeadingDeg = mode === 'true' + ? getTrueHeadingDeg(magneticHeadingDeg) + : normalizeRotationDeg(magneticHeadingDeg) + + return normalizeRotationDeg(360 - referenceHeadingDeg) } function formatCacheHitRate(memoryHitCount: number, diskHitCount: number, networkFetchCount: number): string { @@ -295,8 +373,10 @@ export class MapEngine { autoRotateTimer: number pendingViewPatch: Partial mounted: boolean + northReferenceMode: NorthReferenceMode sensorHeadingDeg: number | null smoothedSensorHeadingDeg: number | null + compassDisplayHeadingDeg: number | null autoRotateHeadingDeg: number | null courseHeadingDeg: number | null targetAutoRotationDeg: number | null @@ -341,9 +421,11 @@ export class MapEngine { orientationMode: 'manual', orientationModeText: formatOrientationModeText('manual'), sensorHeadingText: '--', + compassDeclinationText: formatCompassDeclinationText(DEFAULT_NORTH_REFERENCE_MODE), + northReferenceButtonText: formatNorthReferenceButtonText(DEFAULT_NORTH_REFERENCE_MODE), autoRotateSourceText: formatAutoRotateSourceText('fusion', false), - autoRotateCalibrationText: formatAutoRotateCalibrationText(false, MAP_NORTH_OFFSET_DEG), - northReferenceText: formatNorthReferenceText(), + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, getMapNorthOffsetDeg(DEFAULT_NORTH_REFERENCE_MODE)), + northReferenceText: formatNorthReferenceText(DEFAULT_NORTH_REFERENCE_MODE), compassNeedleDeg: 0, centerTileX: DEFAULT_CENTER_TILE_X, centerTileY: DEFAULT_CENTER_TILE_Y, @@ -388,13 +470,15 @@ export class MapEngine { this.autoRotateTimer = 0 this.pendingViewPatch = {} this.mounted = false + this.northReferenceMode = DEFAULT_NORTH_REFERENCE_MODE this.sensorHeadingDeg = null this.smoothedSensorHeadingDeg = null + this.compassDisplayHeadingDeg = null this.autoRotateHeadingDeg = null this.courseHeadingDeg = null this.targetAutoRotationDeg = null this.autoRotateSourceMode = 'fusion' - this.autoRotateCalibrationOffsetDeg = MAP_NORTH_OFFSET_DEG + this.autoRotateCalibrationOffsetDeg = getMapNorthOffsetDeg(DEFAULT_NORTH_REFERENCE_MODE) this.autoRotateCalibrationPending = false } @@ -670,12 +754,13 @@ export class MapEngine { return } - if (!this.state.rotationDeg) { + const targetRotationDeg = MAP_NORTH_OFFSET_DEG + if (Math.abs(normalizeAngleDeltaDeg(this.state.rotationDeg - targetRotationDeg)) <= 0.01) { return } const exactCenter = this.getExactCenterFromTranslate(this.state.tileTranslateX, this.state.tileTranslateY) - const resolvedViewport = this.resolveViewportForExactCenter(exactCenter.x, exactCenter.y, 0) + const resolvedViewport = this.resolveViewportForExactCenter(exactCenter.x, exactCenter.y, targetRotationDeg) this.clearInertiaTimer() this.clearPreviewResetTimer() @@ -685,10 +770,10 @@ export class MapEngine { this.commitViewport( { ...resolvedViewport, - rotationDeg: 0, - rotationText: formatRotationText(0), + rotationDeg: targetRotationDeg, + rotationText: formatRotationText(targetRotationDeg), }, - `旋转角度已归零 (${this.buildVersion})`, + `旋转角度已回到真北参考 (${this.buildVersion})`, true, () => { this.resetPreviewState() @@ -724,6 +809,10 @@ export class MapEngine { this.setHeadingUpMode() } + handleCycleNorthReferenceMode(): void { + this.cycleNorthReferenceMode() + } + handleAutoRotateCalibrate(): void { if (this.state.orientationMode !== 'heading-up') { this.setState({ @@ -765,22 +854,25 @@ export class MapEngine { this.targetAutoRotationDeg = null this.autoRotateCalibrationPending = false + const mapNorthOffsetDeg = MAP_NORTH_OFFSET_DEG + this.autoRotateCalibrationOffsetDeg = mapNorthOffsetDeg const exactCenter = this.getExactCenterFromTranslate(this.state.tileTranslateX, this.state.tileTranslateY) - const resolvedViewport = this.resolveViewportForExactCenter(exactCenter.x, exactCenter.y, MAP_NORTH_OFFSET_DEG) + const resolvedViewport = this.resolveViewportForExactCenter(exactCenter.x, exactCenter.y, mapNorthOffsetDeg) this.commitViewport( { ...resolvedViewport, - rotationDeg: MAP_NORTH_OFFSET_DEG, - rotationText: formatRotationText(MAP_NORTH_OFFSET_DEG), + rotationDeg: mapNorthOffsetDeg, + rotationText: formatRotationText(mapNorthOffsetDeg), rotationMode: 'manual', rotationModeText: formatRotationModeText('north-up'), rotationToggleText: formatRotationToggleText('north-up'), orientationMode: 'north-up', orientationModeText: formatOrientationModeText('north-up'), - autoRotateCalibrationText: formatAutoRotateCalibrationText(false, this.autoRotateCalibrationOffsetDeg), + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, mapNorthOffsetDeg), + northReferenceText: formatNorthReferenceText(this.northReferenceMode), }, - `地图已固定为北朝上 (${this.buildVersion})`, + `地图已固定为真北朝上 (${this.buildVersion})`, true, () => { this.resetPreviewState() @@ -791,7 +883,7 @@ export class MapEngine { setHeadingUpMode(): void { this.autoRotateCalibrationPending = false - this.autoRotateCalibrationOffsetDeg = MAP_NORTH_OFFSET_DEG + this.autoRotateCalibrationOffsetDeg = getMapNorthOffsetDeg(this.northReferenceMode) this.targetAutoRotationDeg = null this.setState({ rotationMode: 'auto', @@ -800,6 +892,7 @@ export class MapEngine { orientationMode: 'heading-up', orientationModeText: formatOrientationModeText('heading-up'), autoRotateCalibrationText: formatAutoRotateCalibrationText(false, this.autoRotateCalibrationOffsetDeg), + northReferenceText: formatNorthReferenceText(this.northReferenceMode), statusText: `正在启用朝向朝上模式 (${this.buildVersion})`, }, true) if (this.refreshAutoRotateTarget()) { @@ -813,12 +906,20 @@ export class MapEngine { ? this.sensorHeadingDeg : interpolateAngleDeg(this.smoothedSensorHeadingDeg, this.sensorHeadingDeg, AUTO_ROTATE_HEADING_SMOOTHING) - this.autoRotateHeadingDeg = this.smoothedSensorHeadingDeg + const compassHeadingDeg = getCompassReferenceHeadingDeg(this.northReferenceMode, this.smoothedSensorHeadingDeg) + this.compassDisplayHeadingDeg = this.compassDisplayHeadingDeg === null + ? compassHeadingDeg + : interpolateAngleDeg(this.compassDisplayHeadingDeg, compassHeadingDeg, COMPASS_NEEDLE_SMOOTHING) + + this.autoRotateHeadingDeg = this.resolveAutoRotateInputHeadingDeg() this.setState({ - sensorHeadingText: formatHeadingText(this.smoothedSensorHeadingDeg), + sensorHeadingText: formatHeadingText(compassHeadingDeg), + compassDeclinationText: formatCompassDeclinationText(this.northReferenceMode), + northReferenceButtonText: formatNorthReferenceButtonText(this.northReferenceMode), autoRotateSourceText: formatAutoRotateSourceText(this.autoRotateSourceMode, this.courseHeadingDeg !== null), - compassNeedleDeg: formatCompassNeedleDeg(this.smoothedSensorHeadingDeg), + compassNeedleDeg: formatCompassNeedleDegForMode(this.northReferenceMode, this.smoothedSensorHeadingDeg), + northReferenceText: formatNorthReferenceText(this.northReferenceMode), }) if (!this.refreshAutoRotateTarget()) { @@ -839,6 +940,58 @@ export class MapEngine { statusText: `${message} (${this.buildVersion})`, }, true) } + + cycleNorthReferenceMode(): void { + const nextMode = getNextNorthReferenceMode(this.northReferenceMode) + const nextMapNorthOffsetDeg = getMapNorthOffsetDeg(nextMode) + const compassHeadingDeg = this.smoothedSensorHeadingDeg === null + ? null + : getCompassReferenceHeadingDeg(nextMode, this.smoothedSensorHeadingDeg) + + this.northReferenceMode = nextMode + this.autoRotateCalibrationOffsetDeg = nextMapNorthOffsetDeg + this.compassDisplayHeadingDeg = compassHeadingDeg + + if (this.state.orientationMode === 'north-up') { + const exactCenter = this.getExactCenterFromTranslate(this.state.tileTranslateX, this.state.tileTranslateY) + const resolvedViewport = this.resolveViewportForExactCenter(exactCenter.x, exactCenter.y, MAP_NORTH_OFFSET_DEG) + this.commitViewport( + { + ...resolvedViewport, + rotationDeg: MAP_NORTH_OFFSET_DEG, + rotationText: formatRotationText(MAP_NORTH_OFFSET_DEG), + northReferenceText: formatNorthReferenceText(nextMode), + sensorHeadingText: formatHeadingText(compassHeadingDeg), + compassDeclinationText: formatCompassDeclinationText(nextMode), + northReferenceButtonText: formatNorthReferenceButtonText(nextMode), + compassNeedleDeg: formatCompassNeedleDegForMode(nextMode, this.smoothedSensorHeadingDeg), + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, nextMapNorthOffsetDeg), + }, + `${formatNorthReferenceStatusText(nextMode)} (${this.buildVersion})`, + true, + () => { + this.resetPreviewState() + this.syncRenderer() + }, + ) + return + } + + this.setState({ + northReferenceText: formatNorthReferenceText(nextMode), + sensorHeadingText: formatHeadingText(compassHeadingDeg), + compassDeclinationText: formatCompassDeclinationText(nextMode), + northReferenceButtonText: formatNorthReferenceButtonText(nextMode), + compassNeedleDeg: formatCompassNeedleDegForMode(nextMode, this.smoothedSensorHeadingDeg), + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, nextMapNorthOffsetDeg), + statusText: `${formatNorthReferenceStatusText(nextMode)} (${this.buildVersion})`, + }, true) + + if (this.state.orientationMode === 'heading-up' && this.refreshAutoRotateTarget()) { + this.scheduleAutoRotate() + } + } + setCourseHeading(headingDeg: number | null): void { this.courseHeadingDeg = headingDeg === null ? null : normalizeRotationDeg(headingDeg) this.setState({ @@ -851,8 +1004,12 @@ export class MapEngine { } resolveAutoRotateInputHeadingDeg(): number | null { - const sensorHeadingDeg = this.smoothedSensorHeadingDeg - const courseHeadingDeg = this.courseHeadingDeg + const sensorHeadingDeg = this.smoothedSensorHeadingDeg === null + ? null + : getMapReferenceHeadingDegFromSensor(this.northReferenceMode, this.smoothedSensorHeadingDeg) + const courseHeadingDeg = this.courseHeadingDeg === null + ? null + : getMapReferenceHeadingDegFromCourse(this.northReferenceMode, this.courseHeadingDeg) if (this.autoRotateSourceMode === 'sensor') { return sensorHeadingDeg diff --git a/miniprogram/engine/renderer/canvasMapRenderer.ts b/miniprogram/engine/renderer/canvasMapRenderer.ts deleted file mode 100644 index c7650f4..0000000 --- a/miniprogram/engine/renderer/canvasMapRenderer.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { getTileSizePx, type CameraState } from '../camera/camera' -import { - TileStore, - type TileStoreCallbacks, - type TileStoreStats, -} from '../tile/tileStore' -import { type LonLatPoint } from '../../utils/projection' -import { type MapLayer } from '../layer/mapLayer' -import { TileLayer } from '../layer/tileLayer' -import { TrackLayer } from '../layer/trackLayer' -import { GpsLayer } from '../layer/gpsLayer' - -const RENDER_FRAME_MS = 16 - -export interface CanvasMapScene { - tileSource: string - zoom: number - centerTileX: number - centerTileY: number - viewportWidth: number - viewportHeight: number - visibleColumns: number - overdraw: number - translateX: number - translateY: number - rotationRad: number - previewScale: number - previewOriginX: number - previewOriginY: number - track: LonLatPoint[] - gpsPoint: LonLatPoint -} - -export type CanvasMapRendererStats = TileStoreStats - -function buildCamera(scene: CanvasMapScene): CameraState { - return { - centerWorldX: scene.centerTileX, - centerWorldY: scene.centerTileY, - viewportWidth: scene.viewportWidth, - viewportHeight: scene.viewportHeight, - visibleColumns: scene.visibleColumns, - rotationRad: scene.rotationRad, - } -} - -export class CanvasMapRenderer { - canvas: any - ctx: any - dpr: number - scene: CanvasMapScene | null - tileStore: TileStore - tileLayer: TileLayer - layers: MapLayer[] - renderTimer: number - animationTimer: number - destroyed: boolean - animationPaused: boolean - pulseFrame: number - lastStats: CanvasMapRendererStats - onStats?: (stats: CanvasMapRendererStats) => void - onTileError?: (message: string) => void - - constructor( - onStats?: (stats: CanvasMapRendererStats) => void, - onTileError?: (message: string) => void, - ) { - this.onStats = onStats - this.onTileError = onTileError - this.canvas = null - this.ctx = null - this.dpr = 1 - this.scene = null - this.tileStore = new TileStore({ - onTileReady: () => { - this.scheduleRender() - }, - onTileError: (message) => { - if (this.onTileError) { - this.onTileError(message) - } - this.scheduleRender() - }, - } satisfies TileStoreCallbacks) - this.tileLayer = new TileLayer() - this.layers = [ - this.tileLayer, - new TrackLayer(), - new GpsLayer(), - ] - this.renderTimer = 0 - this.animationTimer = 0 - this.destroyed = false - this.animationPaused = false - this.pulseFrame = 0 - this.lastStats = { - visibleTileCount: 0, - readyTileCount: 0, - memoryTileCount: 0, - diskTileCount: 0, - memoryHitCount: 0, - diskHitCount: 0, - networkFetchCount: 0, - } - } - - attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void { - this.canvas = canvasNode - this.ctx = canvasNode.getContext('2d') - this.dpr = dpr || 1 - - canvasNode.width = Math.max(1, Math.floor(width * this.dpr)) - canvasNode.height = Math.max(1, Math.floor(height * this.dpr)) - - if (typeof this.ctx.setTransform === 'function') { - this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0) - } else { - this.ctx.scale(this.dpr, this.dpr) - } - - this.tileStore.attachCanvas(canvasNode) - this.startAnimation() - this.scheduleRender() - } - - updateScene(scene: CanvasMapScene): void { - this.scene = scene - this.scheduleRender() - } - - setAnimationPaused(paused: boolean): void { - this.animationPaused = paused - if (!paused) { - this.scheduleRender() - } - } - - destroy(): void { - this.destroyed = true - if (this.renderTimer) { - clearTimeout(this.renderTimer) - this.renderTimer = 0 - } - if (this.animationTimer) { - clearTimeout(this.animationTimer) - this.animationTimer = 0 - } - this.tileStore.destroy() - this.canvas = null - this.ctx = null - this.scene = null - } - - startAnimation(): void { - if (this.animationTimer) { - return - } - - const tick = () => { - if (this.destroyed) { - this.animationTimer = 0 - return - } - - if (!this.animationPaused) { - this.pulseFrame = (this.pulseFrame + 1) % 360 - this.scheduleRender() - } - - this.animationTimer = setTimeout(tick, 33) as unknown as number - } - - tick() - } - - scheduleRender(): void { - if (this.renderTimer || !this.ctx || !this.scene || this.destroyed) { - return - } - - this.renderTimer = setTimeout(() => { - this.renderTimer = 0 - this.renderFrame() - }, RENDER_FRAME_MS) as unknown as number - } - - emitStats(stats: CanvasMapRendererStats): void { - if ( - stats.visibleTileCount === this.lastStats.visibleTileCount - && stats.readyTileCount === this.lastStats.readyTileCount - && stats.memoryTileCount === this.lastStats.memoryTileCount - && stats.diskTileCount === this.lastStats.diskTileCount - && stats.memoryHitCount === this.lastStats.memoryHitCount - && stats.diskHitCount === this.lastStats.diskHitCount - && stats.networkFetchCount === this.lastStats.networkFetchCount - ) { - return - } - - this.lastStats = stats - if (this.onStats) { - this.onStats(stats) - } - } - - renderFrame(): void { - if (!this.ctx || !this.scene) { - return - } - - const scene = this.scene - const ctx = this.ctx - const camera = buildCamera(scene) - const tileSize = getTileSizePx(camera) - - ctx.clearRect(0, 0, scene.viewportWidth, scene.viewportHeight) - ctx.fillStyle = '#dbeed4' - ctx.fillRect(0, 0, scene.viewportWidth, scene.viewportHeight) - - if (!tileSize) { - this.emitStats(this.tileStore.getStats(0, 0)) - return - } - - const previewScale = scene.previewScale || 1 - const previewOriginX = scene.previewOriginX || scene.viewportWidth / 2 - const previewOriginY = scene.previewOriginY || scene.viewportHeight / 2 - - ctx.save() - ctx.translate(previewOriginX, previewOriginY) - ctx.scale(previewScale, previewScale) - ctx.translate(-previewOriginX, -previewOriginY) - - for (const layer of this.layers) { - layer.draw({ - ctx, - camera, - scene, - pulseFrame: this.pulseFrame, - tileStore: this.tileStore, - }) - } - - ctx.restore() - this.emitStats(this.tileStore.getStats(this.tileLayer.lastVisibleTileCount, this.tileLayer.lastReadyTileCount)) - } -} diff --git a/miniprogram/engine/renderer/canvasOverlayRenderer.ts b/miniprogram/engine/renderer/canvasOverlayRenderer.ts deleted file mode 100644 index 51f66c7..0000000 --- a/miniprogram/engine/renderer/canvasOverlayRenderer.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { type MapLayer } from '../layer/mapLayer' -import { buildCamera, type MapScene } from './mapRenderer' -import { type TileStore } from '../tile/tileStore' - -export class CanvasOverlayRenderer { - canvas: any - ctx: any - dpr: number - layers: MapLayer[] - - constructor(layers: MapLayer[]) { - this.canvas = null - this.ctx = null - this.dpr = 1 - this.layers = layers - } - - attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void { - this.canvas = canvasNode - this.ctx = canvasNode.getContext('2d') - this.dpr = dpr || 1 - - canvasNode.width = Math.max(1, Math.floor(width * this.dpr)) - canvasNode.height = Math.max(1, Math.floor(height * this.dpr)) - - if (typeof this.ctx.setTransform === 'function') { - this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0) - } else { - this.ctx.scale(this.dpr, this.dpr) - } - } - - clear(): void { - this.canvas = null - this.ctx = null - } - - render(scene: MapScene, tileStore: TileStore, pulseFrame: number): void { - if (!this.ctx) { - return - } - - const camera = buildCamera(scene) - const ctx = this.ctx - const previewScale = scene.previewScale || 1 - const previewOriginX = scene.previewOriginX || scene.viewportWidth / 2 - const previewOriginY = scene.previewOriginY || scene.viewportHeight / 2 - - ctx.clearRect(0, 0, scene.viewportWidth, scene.viewportHeight) - ctx.save() - ctx.translate(previewOriginX, previewOriginY) - ctx.scale(previewScale, previewScale) - ctx.translate(-previewOriginX, -previewOriginY) - - for (const layer of this.layers) { - layer.draw({ - ctx, - camera, - scene, - pulseFrame, - tileStore, - }) - } - - ctx.restore() - } -} diff --git a/miniprogram/pages/index/index.ts b/miniprogram/pages/index/index.ts index c7aaf97..332828a 100644 --- a/miniprogram/pages/index/index.ts +++ b/miniprogram/pages/index/index.ts @@ -1,6 +1,5 @@ // index.ts // 获取应用实例 -const app = getApp() const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0' Component({ @@ -42,7 +41,6 @@ Component({ wx.getUserProfile({ desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 success: (res) => { - console.log(res) this.setData({ userInfo: res.userInfo, hasUserInfo: true diff --git a/miniprogram/pages/map/map.ts b/miniprogram/pages/map/map.ts index 049b18c..a238cb2 100644 --- a/miniprogram/pages/map/map.ts +++ b/miniprogram/pages/map/map.ts @@ -1,6 +1,10 @@ import { MapEngine, type MapEngineStageRect, type MapEngineViewState } from '../../engine/map/mapEngine' -const INTERNAL_BUILD_VERSION = 'map-build-58' +type MapPageData = MapEngineViewState & { + showDebugPanel: boolean +} + +const INTERNAL_BUILD_VERSION = 'map-build-75' let mapEngine: MapEngine | null = null @@ -18,7 +22,7 @@ function getFallbackStageRect(): MapEngineStageRect { } Page({ - data: {} as MapEngineViewState, + data: { showDebugPanel: false } as MapPageData, onLoad() { mapEngine = new MapEngine(INTERNAL_BUILD_VERSION, { @@ -27,7 +31,7 @@ Page({ }, }) - this.setData(mapEngine.getInitialData()) + this.setData({ ...mapEngine.getInitialData(), showDebugPanel: false }) }, onReady() { @@ -149,11 +153,23 @@ Page({ } }, + handleCycleNorthReferenceMode() { + if (mapEngine) { + mapEngine.handleCycleNorthReferenceMode() + } + }, + handleAutoRotateCalibrate() { if (mapEngine) { mapEngine.handleAutoRotateCalibrate() } }, + + handleToggleDebugPanel() { + this.setData({ + showDebugPanel: !this.data.showDebugPanel, + }) + }, }) diff --git a/miniprogram/pages/map/map.wxml b/miniprogram/pages/map/map.wxml index 87b8121..448190f 100644 --- a/miniprogram/pages/map/map.wxml +++ b/miniprogram/pages/map/map.wxml @@ -42,24 +42,13 @@ {{sensorHeadingText}} + {{compassDeclinationText}} - - Build - {{buildVersion}} - - - Renderer - {{renderMode}} - - - Projection - {{projectionMode}} - Heading Mode {{orientationModeText}} @@ -68,22 +57,10 @@ Sensor Heading {{sensorHeadingText}} - + North Ref {{northReferenceText}} - - Auto Source - {{autoRotateSourceText}} - - - Calibration - {{autoRotateCalibrationText}} - - - Tile URL - {{tileSource}} - Zoom {{zoom}} @@ -92,47 +69,78 @@ Rotation {{rotationText}} - - Center Tile - {{centerText}} - - - Tile Size - {{tileSizePx}}px - - - Visible Tiles - {{visibleTileCount}} - - - Ready Tiles - {{readyTileCount}} - - - Memory Tiles - {{memoryTileCount}} - - - Disk Tiles - {{diskTileCount}} - - - Cache Hit - {{cacheHitRateText}} - - - Disk Hits - {{diskHitCount}} - - - Net Fetches - {{networkFetchCount}} - Status {{statusText}} + + {{showDebugPanel ? '隐藏调试' : '查看调试'}} + + + + + Build + {{buildVersion}} + + + Renderer + {{renderMode}} + + + Projection + {{projectionMode}} + + + Auto Source + {{autoRotateSourceText}} + + + Calibration + {{autoRotateCalibrationText}} + + + Tile URL + {{tileSource}} + + + Center Tile + {{centerText}} + + + Tile Size + {{tileSizePx}}px + + + Visible Tiles + {{visibleTileCount}} + + + Ready Tiles + {{readyTileCount}} + + + Memory Tiles + {{memoryTileCount}} + + + Disk Tiles + {{diskTileCount}} + + + Cache Hit + {{cacheHitRateText}} + + + Disk Hits + {{diskHitCount}} + + + Net Fetches + {{networkFetchCount}} + + + 回到首屏 旋转归零 @@ -142,6 +150,9 @@ 北朝上 朝向朝上 + + {{northReferenceButtonText}} + 按当前方向校准 diff --git a/miniprogram/pages/map/map.wxss b/miniprogram/pages/map/map.wxss index 1fa5164..23ee6e5 100644 --- a/miniprogram/pages/map/map.wxss +++ b/miniprogram/pages/map/map.wxss @@ -213,6 +213,15 @@ box-shadow: 0 8rpx 18rpx rgba(22, 48, 32, 0.08); } +.compass-widget__hint { + margin-top: 8rpx; + font-size: 18rpx; + line-height: 1.4; + color: #d62828; + text-align: center; + font-weight: 700; +} + .info-panel { flex: 1; min-height: 0;