camera.ts 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. export interface CameraState {
  2. centerWorldX: number
  3. centerWorldY: number
  4. viewportWidth: number
  5. viewportHeight: number
  6. visibleColumns: number
  7. translateX?: number
  8. translateY?: number
  9. rotationRad?: number
  10. }
  11. export interface ScreenPoint {
  12. x: number
  13. y: number
  14. }
  15. export interface WorldPoint {
  16. x: number
  17. y: number
  18. }
  19. export function getTileSizePx(camera: CameraState): number {
  20. if (!camera.viewportWidth || !camera.visibleColumns) {
  21. return 0
  22. }
  23. return camera.viewportWidth / camera.visibleColumns
  24. }
  25. export function rotateScreenPoint(point: ScreenPoint, centerX: number, centerY: number, rotationRad: number): ScreenPoint {
  26. if (!rotationRad) {
  27. return point
  28. }
  29. const deltaX = point.x - centerX
  30. const deltaY = point.y - centerY
  31. const cos = Math.cos(rotationRad)
  32. const sin = Math.sin(rotationRad)
  33. return {
  34. x: centerX + deltaX * cos - deltaY * sin,
  35. y: centerY + deltaX * sin + deltaY * cos,
  36. }
  37. }
  38. export function worldToScreen(
  39. camera: CameraState,
  40. world: WorldPoint,
  41. includeTranslate = false,
  42. ): ScreenPoint {
  43. const tileSize = getTileSizePx(camera)
  44. const translateX = includeTranslate ? (camera.translateX || 0) : 0
  45. const translateY = includeTranslate ? (camera.translateY || 0) : 0
  46. const centerX = camera.viewportWidth / 2
  47. const centerY = camera.viewportHeight / 2
  48. const rotated = rotateScreenPoint(
  49. {
  50. x: centerX + (world.x - camera.centerWorldX) * tileSize,
  51. y: centerY + (world.y - camera.centerWorldY) * tileSize,
  52. },
  53. centerX,
  54. centerY,
  55. camera.rotationRad || 0,
  56. )
  57. return {
  58. x: rotated.x + translateX,
  59. y: rotated.y + translateY,
  60. }
  61. }
  62. export function screenToWorld(
  63. camera: CameraState,
  64. screen: ScreenPoint,
  65. includeTranslate = true,
  66. ): WorldPoint {
  67. const tileSize = getTileSizePx(camera)
  68. const translateX = includeTranslate ? (camera.translateX || 0) : 0
  69. const translateY = includeTranslate ? (camera.translateY || 0) : 0
  70. const centerX = camera.viewportWidth / 2
  71. const centerY = camera.viewportHeight / 2
  72. const unrotated = rotateScreenPoint(
  73. {
  74. x: screen.x - translateX,
  75. y: screen.y - translateY,
  76. },
  77. centerX,
  78. centerY,
  79. -(camera.rotationRad || 0),
  80. )
  81. return {
  82. x: camera.centerWorldX + (unrotated.x - centerX) / tileSize,
  83. y: camera.centerWorldY + (unrotated.y - centerY) / tileSize,
  84. }
  85. }