no-wrap option; pivoted exponential zoom
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package world
|
||||
|
||||
// ClampCameraNoWrapViewport clamps camera center so that the VIEWPORT world-rect
|
||||
// stays within the bounded world [0..worldW) x [0..worldH), when possible.
|
||||
//
|
||||
// This is the correct clamp for user panning when wrap is disabled.
|
||||
// Margins (expanded canvas) are intentionally ignored here; they may extend outside the world.
|
||||
func ClampCameraNoWrapViewport(
|
||||
cameraXWorldFp, cameraYWorldFp int,
|
||||
viewportW, viewportH int,
|
||||
zoomFp int,
|
||||
worldW, worldH int,
|
||||
) (int, int) {
|
||||
if zoomFp <= 0 {
|
||||
panic("ClampCameraNoWrapViewport: invalid zoom")
|
||||
}
|
||||
if viewportW < 0 || viewportH < 0 {
|
||||
panic("ClampCameraNoWrapViewport: negative viewport")
|
||||
}
|
||||
if worldW <= 0 || worldH <= 0 {
|
||||
panic("ClampCameraNoWrapViewport: invalid world size")
|
||||
}
|
||||
|
||||
spanW := PixelSpanToWorldFixed(viewportW, zoomFp)
|
||||
spanH := PixelSpanToWorldFixed(viewportH, zoomFp)
|
||||
|
||||
halfW := spanW / 2
|
||||
halfH := spanH / 2
|
||||
|
||||
cameraXWorldFp = clampCameraAxis(cameraXWorldFp, worldW, halfW)
|
||||
cameraYWorldFp = clampCameraAxis(cameraYWorldFp, worldH, halfH)
|
||||
|
||||
return cameraXWorldFp, cameraYWorldFp
|
||||
}
|
||||
|
||||
// ClampCameraNoWrapExpanded clamps camera center so that the EXPANDED CANVAS world-rect
|
||||
// (viewport + margins) stays within the bounded world, when possible.
|
||||
//
|
||||
// This is stricter than viewport-based clamp and can prevent panning when margins are large.
|
||||
func ClampCameraNoWrapExpanded(
|
||||
cameraXWorldFp, cameraYWorldFp int,
|
||||
viewportW, viewportH int,
|
||||
marginX, marginY int,
|
||||
zoomFp int,
|
||||
worldW, worldH int,
|
||||
) (int, int) {
|
||||
if zoomFp <= 0 {
|
||||
panic("ClampCameraNoWrapExpanded: invalid zoom")
|
||||
}
|
||||
if viewportW < 0 || viewportH < 0 || marginX < 0 || marginY < 0 {
|
||||
panic("ClampCameraNoWrapExpanded: negative sizes")
|
||||
}
|
||||
if worldW <= 0 || worldH <= 0 {
|
||||
panic("ClampCameraNoWrapExpanded: invalid world size")
|
||||
}
|
||||
|
||||
canvasW := viewportW + 2*marginX
|
||||
canvasH := viewportH + 2*marginY
|
||||
|
||||
spanW := PixelSpanToWorldFixed(canvasW, zoomFp)
|
||||
spanH := PixelSpanToWorldFixed(canvasH, zoomFp)
|
||||
|
||||
halfW := spanW / 2
|
||||
halfH := spanH / 2
|
||||
|
||||
cameraXWorldFp = clampCameraAxis(cameraXWorldFp, worldW, halfW)
|
||||
cameraYWorldFp = clampCameraAxis(cameraYWorldFp, worldH, halfH)
|
||||
|
||||
return cameraXWorldFp, cameraYWorldFp
|
||||
}
|
||||
|
||||
func clampCameraAxis(cam, worldSize, halfSpan int) int {
|
||||
// If viewport/span does not fit: force center.
|
||||
if 2*halfSpan > worldSize {
|
||||
return worldSize / 2
|
||||
}
|
||||
|
||||
minCam := halfSpan
|
||||
maxCam := worldSize - halfSpan
|
||||
|
||||
if cam < minCam {
|
||||
return minCam
|
||||
}
|
||||
if cam > maxCam {
|
||||
return maxCam
|
||||
}
|
||||
return cam
|
||||
}
|
||||
|
||||
// ClampRenderParamsNoWrap clamps camera center in-place when wrap is disabled.
|
||||
// It uses viewport-based clamp (NOT expanded) so panning remains possible even with margins.
|
||||
func (w *World) ClampRenderParamsNoWrap(p *RenderParams) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
allowWrap := true
|
||||
if p.Options != nil && p.Options.DisableWrapScroll {
|
||||
allowWrap = false
|
||||
}
|
||||
if allowWrap {
|
||||
return
|
||||
}
|
||||
|
||||
zoomFp, err := p.CameraZoomFp()
|
||||
if err != nil || zoomFp <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
cx, cy := ClampCameraNoWrapViewport(
|
||||
p.CameraXWorldFp, p.CameraYWorldFp,
|
||||
p.ViewportWidthPx, p.ViewportHeightPx,
|
||||
zoomFp,
|
||||
w.W, w.H,
|
||||
)
|
||||
p.CameraXWorldFp = cx
|
||||
p.CameraYWorldFp = cy
|
||||
}
|
||||
Reference in New Issue
Block a user