bedrocktool/ui/gui/pages/worlds/map.go

135 lines
3.3 KiB
Go
Raw Normal View History

2023-03-14 01:07:39 +00:00
package worlds
import (
"image"
"image/draw"
2023-03-21 10:21:31 +00:00
"math"
2023-03-14 01:07:39 +00:00
2023-03-14 15:32:18 +00:00
"gioui.org/f32"
2023-03-14 01:07:39 +00:00
"gioui.org/io/pointer"
"gioui.org/layout"
2023-03-14 15:32:18 +00:00
"gioui.org/op"
2023-03-21 10:21:31 +00:00
"gioui.org/op/clip"
2023-03-14 01:07:39 +00:00
"gioui.org/op/paint"
2023-03-18 11:12:54 +00:00
"github.com/bedrock-tool/bedrocktool/ui/messages"
2023-03-14 01:07:39 +00:00
"github.com/sandertv/gophertunnel/minecraft/protocol"
)
type Map struct {
2023-03-14 15:32:18 +00:00
click f32.Point
imageOp paint.ImageOp
2023-03-21 10:21:31 +00:00
scaleFactor float32
center f32.Point
transform f32.Affine2D
grabbed bool
2023-03-14 01:07:39 +00:00
MapImage *image.RGBA
BoundsMin protocol.ChunkPos
BoundsMax protocol.ChunkPos
}
2023-03-21 10:21:31 +00:00
func (m *Map) HandlePointerEvent(e pointer.Event) {
switch e.Type {
case pointer.Press:
m.click = e.Position
m.grabbed = true
case pointer.Drag:
m.transform = m.transform.Offset(e.Position.Sub(m.click))
m.click = e.Position
case pointer.Release:
m.grabbed = false
case pointer.Scroll:
m.HandleScrollEvent(e)
2023-03-14 01:07:39 +00:00
}
2023-03-21 10:21:31 +00:00
}
2023-03-14 01:07:39 +00:00
2023-03-21 10:21:31 +00:00
func (m *Map) HandleScrollEvent(e pointer.Event) {
scaleFactor := float32(math.Pow(1.01, float64(e.Scroll.Y)))
m.transform = m.transform.Scale(e.Position.Sub(m.center), f32.Pt(scaleFactor, scaleFactor))
m.scaleFactor *= scaleFactor
}
2023-03-14 15:32:18 +00:00
2023-03-21 10:21:31 +00:00
func (m *Map) Layout(gtx layout.Context) layout.Dimensions {
// here we loop through all the events associated with this button.
for _, e := range gtx.Events(m) {
if e, ok := e.(pointer.Event); ok {
m.HandlePointerEvent(e)
}
2023-03-14 15:32:18 +00:00
}
if m.MapImage != nil {
2023-03-21 10:21:31 +00:00
// Calculate the size of the widget based on the size of the image and the current scale factor.
dx := float32(m.MapImage.Bounds().Dx())
dy := float32(m.MapImage.Bounds().Dy())
size := f32.Pt(dx*m.scaleFactor, dy*m.scaleFactor)
m.center = f32.Pt(
float32(gtx.Constraints.Max.X),
float32(gtx.Constraints.Max.Y),
).Div(2)
// Calculate the offset required to center the image within the widget.
offset := m.center.Sub(size.Div(2))
// Draw the image at the correct position and scale.
defer clip.Rect{Max: gtx.Constraints.Max}.Push(gtx.Ops).Pop()
op.Affine(m.transform.Offset(offset)).Add(gtx.Ops)
2023-03-14 15:32:18 +00:00
m.imageOp.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops)
}
2023-03-21 10:21:31 +00:00
size := gtx.Constraints.Max
pointer.InputOp{
Tag: m,
Grab: m.grabbed,
Types: pointer.Scroll | pointer.Drag | pointer.Press | pointer.Release,
ScrollBounds: image.Rect(-size.X, -size.Y, size.X, size.Y),
}.Add(gtx.Ops)
return layout.Dimensions{Size: size}
2023-03-14 01:07:39 +00:00
}
func drawTile(img *image.RGBA, min, pos protocol.ChunkPos, tile *image.RGBA) {
px := image.Pt(
int((pos.X()-min[0])*16),
2023-03-21 10:21:31 +00:00
int((pos.Z()-min[1])*16),
2023-03-14 01:07:39 +00:00
)
draw.Draw(img, image.Rect(
px.X, px.Y,
px.X+16, px.Y+16,
), tile, image.Point{}, draw.Src)
}
2023-03-18 11:12:54 +00:00
func (m *Map) Update(u *messages.UpdateMapPayload) {
2023-03-14 15:32:18 +00:00
if m.MapImage == nil {
2023-03-21 10:21:31 +00:00
m.scaleFactor = 1
2023-03-14 15:32:18 +00:00
}
2023-03-14 01:07:39 +00:00
needNewImage := false
if m.BoundsMin != u.BoundsMin {
needNewImage = true
m.BoundsMin = u.BoundsMin
}
if m.BoundsMax != u.BoundsMax {
needNewImage = true
m.BoundsMax = u.BoundsMax
}
if needNewImage {
chunksX := int(m.BoundsMax[0] - m.BoundsMin[0] + 1) // how many chunk lengths is x coordinate
chunksY := int(m.BoundsMax[1] - m.BoundsMin[1] + 1)
m.MapImage = image.NewRGBA(image.Rect(0, 0, chunksX*16, chunksY*16))
for pos, tile := range u.Tiles {
drawTile(m.MapImage, m.BoundsMin, pos, tile)
}
} else {
for _, pos := range u.UpdatedTiles {
tile := u.Tiles[pos]
drawTile(m.MapImage, m.BoundsMin, pos, tile)
}
}
2023-03-14 15:32:18 +00:00
2023-03-21 11:03:11 +00:00
m.imageOp = paint.NewImageOpFilter(m.MapImage, paint.FilterNearest)
2023-03-14 01:07:39 +00:00
}