ludic/packages/ludic.minimap/view.ludic

83 lines
4.2 KiB
Text

# view.ludic - the window onto the map: a square of `size` pixels at (x0, y0) showing `span` metres
# across at zoom 1, centred on a world point. World to window and back, and a scale bar's length.
# metres across the window at zoom 1, the zoom's range, and how close a focus goes at least
export function minimap_config_view(minimap_st: mut MinimapState, span: float, zoom_min: float, zoom_max: float, focus: float) -> void {
minimap_st.mm_span = Math.max(span, 1.0)
minimap_st.mm_zoom_min = zoom_min
minimap_st.mm_zoom_max = Math.max(zoom_max, zoom_min)
minimap_st.mm_zoom_focus = focus
minimap_st.mm_zoom = Math.clamp(1.0, minimap_st.mm_zoom_min, minimap_st.mm_zoom_max)
minimap_st.mm_have_view = false
}
# where the window is on the screen this frame
export function minimap_window(minimap_st: mut MinimapState, x0: float, y0: float, size: float) -> void {
minimap_st.mm_x0 = x0
minimap_st.mm_y0 = y0
minimap_st.mm_size = Math.max(size, 1.0)
}
export function minimap_size(minimap_st: MinimapState) -> float { return minimap_st.mm_size }
export function minimap_zoom(minimap_st: MinimapState) -> float { return minimap_st.mm_zoom }
export function minimap_view_x(minimap_st: MinimapState) -> float { return minimap_st.mm_vx }
export function minimap_view_z(minimap_st: MinimapState) -> float { return minimap_st.mm_vz }
export function minimap_have_view(minimap_st: MinimapState) -> bool { return minimap_st.mm_have_view }
# the metres the window shows across at the zoom
export function minimap_span(minimap_st: MinimapState) -> float { return minimap_st.mm_span / minimap_st.mm_zoom }
# pixels per metre
export function minimap_scale(minimap_st: MinimapState) -> float { return minimap_st.mm_size / minimap_span(minimap_st) }
# a world point on the screen
export function minimap_px(minimap_st: MinimapState, x: float) -> float { return minimap_st.mm_x0 + minimap_st.mm_size * 0.5 + (x - minimap_st.mm_vx) * minimap_scale(minimap_st) }
export function minimap_py(minimap_st: MinimapState, z: float) -> float { return minimap_st.mm_y0 + minimap_st.mm_size * 0.5 + (z - minimap_st.mm_vz) * minimap_scale(minimap_st) }
# the world under a point of the window (pixels from its corner)
export function minimap_wx(minimap_st: MinimapState, rx: float) -> float { return minimap_st.mm_vx + (rx - minimap_st.mm_size * 0.5) / minimap_scale(minimap_st) }
export function minimap_wz(minimap_st: MinimapState, ry: float) -> float { return minimap_st.mm_vz + (ry - minimap_st.mm_size * 0.5) / minimap_scale(minimap_st) }
# a round distance whose bar is no wider than a quarter of the window
export function minimap_scale_metres(minimap_st: MinimapState) -> int {
for k in 0 .. 7 {
let m = mm_scale_step(k)
if float(m) * minimap_scale(minimap_st) <= minimap_st.mm_size / 4.0 { return m }
}
return 25
}
# the view's centre is kept where MinimapWorld allows, so the land can reach the edge but not leave
export function minimap_clamp(minimap_st: mut MinimapState) -> void {
minimap_st.mm_vx = Math.clamp(minimap_st.mm_vx, MinimapWorld.lo_x(), MinimapWorld.hi_x())
minimap_st.mm_vz = Math.clamp(minimap_st.mm_vz, MinimapWorld.lo_z(), MinimapWorld.hi_z())
}
export function minimap_centre(minimap_st: mut MinimapState, x: float, z: float) -> void {
minimap_st.mm_vx = x
minimap_st.mm_vz = z
minimap_st.mm_have_view = true
minimap_clamp(minimap_st)
}
# the view on the camp, or on you
export function minimap_recentre(minimap_st: mut MinimapState, on_you: bool) -> void {
if on_you { minimap_centre(minimap_st, MinimapWorld.you_x(), MinimapWorld.you_z()) } else { minimap_centre(minimap_st, MinimapWorld.camp_x(), MinimapWorld.camp_z()) }
}
# go there AND close in; never out, since a player already closer asked for that
export function minimap_focus(minimap_st: mut MinimapState, on_you: bool) -> void {
minimap_recentre(minimap_st, on_you)
if minimap_st.mm_zoom < minimap_st.mm_zoom_focus { minimap_st.mm_zoom = Math.clamp(minimap_st.mm_zoom_focus, minimap_st.mm_zoom_min, minimap_st.mm_zoom_max) }
}
# the round distances a scale bar can show, largest first
function mm_scale_step(k: int) -> int {
if k == 0 { return 2000 }
if k == 1 { return 1000 }
if k == 2 { return 500 }
if k == 3 { return 250 }
if k == 4 { return 100 }
if k == 5 { return 50 }
return 25
}