2021-05-13 23:55:53 +02:00
|
|
|
const MIN_ZOOM = .1
|
|
|
|
|
const MAX_ZOOM = 6
|
|
|
|
|
const ZOOM_STEP = .05
|
2020-12-24 20:17:01 +01:00
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
type ZOOM_DIR = 'in'|'out'
|
|
|
|
|
interface Point {
|
|
|
|
|
x: number
|
|
|
|
|
y: number
|
|
|
|
|
}
|
|
|
|
|
interface Dim {
|
|
|
|
|
w: number
|
|
|
|
|
h: number
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-13 23:55:53 +02:00
|
|
|
export default function Camera () {
|
|
|
|
|
let x = 0
|
|
|
|
|
let y = 0
|
|
|
|
|
let curZoom = 1
|
2021-05-13 23:38:36 +02:00
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
const move = (byX: number, byY: number) => {
|
2021-05-13 23:55:53 +02:00
|
|
|
x += byX / curZoom
|
|
|
|
|
y += byY / curZoom
|
2021-05-13 23:38:36 +02:00
|
|
|
}
|
|
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
const calculateNewZoom = (inout: ZOOM_DIR): number => {
|
2021-05-13 23:38:36 +02:00
|
|
|
const factor = inout === 'in' ? 1 : -1
|
2021-05-13 23:55:53 +02:00
|
|
|
const newzoom = curZoom + ZOOM_STEP * curZoom * factor
|
|
|
|
|
const capped = Math.min(Math.max(newzoom, MIN_ZOOM), MAX_ZOOM)
|
2021-05-13 23:38:36 +02:00
|
|
|
return capped
|
2020-12-24 20:17:01 +01:00
|
|
|
}
|
2020-11-07 17:49:42 +01:00
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
const canZoom = (inout: ZOOM_DIR): boolean => {
|
2021-05-13 23:55:53 +02:00
|
|
|
return curZoom != calculateNewZoom(inout)
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
const setZoom = (newzoom: number, viewportCoordCenter: Point): boolean => {
|
2021-05-13 23:55:53 +02:00
|
|
|
if (curZoom == newzoom) {
|
2020-12-24 20:17:01 +01:00
|
|
|
return false
|
2020-11-07 11:35:29 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-13 23:55:53 +02:00
|
|
|
const zoomFactor = 1 - (curZoom / newzoom)
|
|
|
|
|
move(
|
2021-04-17 19:35:32 +02:00
|
|
|
-viewportCoordCenter.x * zoomFactor,
|
|
|
|
|
-viewportCoordCenter.y * zoomFactor,
|
|
|
|
|
)
|
2021-05-13 23:55:53 +02:00
|
|
|
curZoom = newzoom
|
2020-12-24 20:17:01 +01:00
|
|
|
return true
|
|
|
|
|
}
|
2020-11-12 19:19:02 +01:00
|
|
|
|
2021-05-13 23:38:36 +02:00
|
|
|
/**
|
|
|
|
|
* Zooms towards/away from the provided coordinate, if possible.
|
|
|
|
|
* If at max or min zoom respectively, no zooming is performed.
|
|
|
|
|
*/
|
2021-05-17 00:27:47 +02:00
|
|
|
const zoom = (inout: ZOOM_DIR, viewportCoordCenter: Point): boolean => {
|
2021-05-13 23:55:53 +02:00
|
|
|
return setZoom(calculateNewZoom(inout), viewportCoordCenter)
|
2020-12-24 20:17:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Translate a coordinate in the viewport to a
|
|
|
|
|
* coordinate in the world, rounded
|
2021-04-15 10:36:44 +02:00
|
|
|
* @param {x, y} viewportCoord
|
2020-12-24 20:17:01 +01:00
|
|
|
*/
|
2021-05-17 00:27:47 +02:00
|
|
|
const viewportToWorld = (viewportCoord: Point): Point => {
|
2021-05-13 23:55:53 +02:00
|
|
|
const { x, y } = viewportToWorldRaw(viewportCoord)
|
|
|
|
|
return { x: Math.round(x), y: Math.round(y) }
|
2020-12-24 20:17:01 +01:00
|
|
|
}
|
2020-11-07 11:35:29 +01:00
|
|
|
|
2020-12-24 20:17:01 +01:00
|
|
|
/**
|
|
|
|
|
* Translate a coordinate in the viewport to a
|
|
|
|
|
* coordinate in the world, not rounded
|
2021-04-15 10:36:44 +02:00
|
|
|
* @param {x, y} viewportCoord
|
2020-12-24 20:17:01 +01:00
|
|
|
*/
|
2021-05-17 00:27:47 +02:00
|
|
|
const viewportToWorldRaw = (viewportCoord: Point): Point => {
|
2020-12-24 20:17:01 +01:00
|
|
|
return {
|
2021-05-13 23:55:53 +02:00
|
|
|
x: (viewportCoord.x / curZoom) - x,
|
|
|
|
|
y: (viewportCoord.y / curZoom) - y,
|
2020-11-12 19:19:02 +01:00
|
|
|
}
|
2020-12-24 20:17:01 +01:00
|
|
|
}
|
2020-11-07 11:35:29 +01:00
|
|
|
|
2020-12-24 20:17:01 +01:00
|
|
|
/**
|
|
|
|
|
* Translate a coordinate in the world to a
|
|
|
|
|
* coordinate in the viewport, rounded
|
2021-04-15 10:36:44 +02:00
|
|
|
* @param {x, y} worldCoord
|
2020-12-24 20:17:01 +01:00
|
|
|
*/
|
2021-05-17 00:27:47 +02:00
|
|
|
const worldToViewport = (worldCoord: Point): Point => {
|
2021-05-13 23:55:53 +02:00
|
|
|
const { x, y } = worldToViewportRaw(worldCoord)
|
|
|
|
|
return { x: Math.round(x), y: Math.round(y) }
|
2020-12-24 20:17:01 +01:00
|
|
|
}
|
2020-11-07 11:35:29 +01:00
|
|
|
|
2020-12-24 20:17:01 +01:00
|
|
|
/**
|
|
|
|
|
* Translate a coordinate in the world to a
|
|
|
|
|
* coordinate in the viewport, not rounded
|
2021-04-15 10:36:44 +02:00
|
|
|
* @param {x, y} worldCoord
|
2020-12-24 20:17:01 +01:00
|
|
|
*/
|
2021-05-17 00:27:47 +02:00
|
|
|
const worldToViewportRaw = (worldCoord: Point): Point => {
|
2020-12-24 20:17:01 +01:00
|
|
|
return {
|
2021-05-13 23:55:53 +02:00
|
|
|
x: (worldCoord.x + x) * curZoom,
|
|
|
|
|
y: (worldCoord.y + y) * curZoom,
|
2020-11-07 11:35:29 +01:00
|
|
|
}
|
2020-12-24 20:17:01 +01:00
|
|
|
}
|
2020-11-07 17:49:42 +01:00
|
|
|
|
2020-12-24 20:17:01 +01:00
|
|
|
/**
|
|
|
|
|
* Translate a 2d dimension (width/height) in the world to
|
|
|
|
|
* one in the viewport, rounded
|
2021-04-15 10:36:44 +02:00
|
|
|
* @param {w, h} worldDim
|
2020-12-24 20:17:01 +01:00
|
|
|
*/
|
2021-05-17 00:27:47 +02:00
|
|
|
const worldDimToViewport = (worldDim: Dim): Dim => {
|
2021-05-13 23:55:53 +02:00
|
|
|
const { w, h } = worldDimToViewportRaw(worldDim)
|
|
|
|
|
return { w: Math.round(w), h: Math.round(h) }
|
2020-12-24 20:17:01 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-10 18:48:16 +01:00
|
|
|
|
2020-12-24 20:17:01 +01:00
|
|
|
/**
|
|
|
|
|
* Translate a 2d dimension (width/height) in the world to
|
|
|
|
|
* one in the viewport, not rounded
|
2021-04-15 10:36:44 +02:00
|
|
|
* @param {w, h} worldDim
|
2020-12-24 20:17:01 +01:00
|
|
|
*/
|
2021-05-17 00:27:47 +02:00
|
|
|
const worldDimToViewportRaw = (worldDim: Dim): Dim => {
|
2020-12-24 20:17:01 +01:00
|
|
|
return {
|
2021-05-13 23:55:53 +02:00
|
|
|
w: worldDim.w * curZoom,
|
|
|
|
|
h: worldDim.h * curZoom,
|
2020-11-10 18:48:16 +01:00
|
|
|
}
|
2020-12-24 20:17:01 +01:00
|
|
|
}
|
2021-05-13 23:55:53 +02:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
move,
|
|
|
|
|
canZoom,
|
|
|
|
|
zoom,
|
|
|
|
|
worldToViewport,
|
|
|
|
|
worldToViewportRaw,
|
|
|
|
|
worldDimToViewport, // not used outside
|
|
|
|
|
worldDimToViewportRaw,
|
|
|
|
|
viewportToWorld,
|
|
|
|
|
viewportToWorldRaw, // not used outside
|
|
|
|
|
}
|
2020-11-07 17:49:42 +01:00
|
|
|
}
|