From 86b19c4afbdb1a0ce39fa4bef97a398872e2ac6a Mon Sep 17 00:00:00 2001 From: Kevin Chabowski Date: Fri, 16 Aug 2013 20:05:44 +0200 Subject: Tools can now be used. * Mouse button events are now handled. * Panning works a bit different now. * Added cache to make the tools faster (Fill is still very slow). --- mapwidget.go | 100 ++++++++++++++++++++++++++++++++++++++++++++----------- region_cached.go | 78 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 20 deletions(-) create mode 100644 region_cached.go diff --git a/mapwidget.go b/mapwidget.go index a493c9a..4b033cf 100644 --- a/mapwidget.go +++ b/mapwidget.go @@ -14,6 +14,7 @@ const ( zoom = 2 tileSize = zoom * mcmap.ChunkSizeXZ halfChunkSize = mcmap.ChunkSizeXZ / 2 + cacheSize = 4 ) type tileCmd int @@ -73,8 +74,9 @@ type MapWidget struct { showBiomes bool - offX, offZ int - mx1, mx2, my1, my2 int + offX, offZ int + mx1, mx2, my1, my2 int + primMButton, middleMButton bool pixmap *gdk.Pixmap pixmapGC *gdk.GC @@ -82,7 +84,7 @@ type MapWidget struct { bg *gdk.Pixmap - region *mcmap.Region + region *CachedRegion maptiles map[XZPos]*gdk.Pixmap biotiles map[XZPos]*gdk.Pixmap @@ -92,6 +94,7 @@ type MapWidget struct { tileCmds chan tileCmd tool Tool + bio mcmap.Biome } var ( @@ -122,8 +125,20 @@ func (mw *MapWidget) doTileCmds() { for cmd := range mw.tileCmds { switch cmd { case cmdSave: - mw.region.Save() + if err := mw.region.Flush(); err != nil { + mw.reportFail(fmt.Sprintf("Error while flushing cache: %s", err)) + return + } + if err := mw.region.Region.Save(); err != nil { + mw.reportFail(fmt.Sprintf("Error while saving: %s", err)) + return + } case cmdFlushTiles: + if err := mw.region.Flush(); err != nil { + mw.reportFail(fmt.Sprintf("Error while flushing cache: %s", err)) + return + } + gdk.ThreadsEnter() for _, mt := range mw.maptiles { mt.Unref() @@ -293,9 +308,7 @@ func (mw *MapWidget) movement(ctx *glib.CallbackContext) { } mw.updateInfo(x, z, bio) - switch { - case mt&gdk.BUTTON1_MASK != 0: - case mt&gdk.BUTTON2_MASK != 0: + if mw.middleMButton { if (mw.mx1 != -1) && (mw.my1 != -1) { mw.offX += mw.mx1 - mw.mx2 mw.offZ += mw.my1 - mw.my2 @@ -306,9 +319,47 @@ func (mw *MapWidget) movement(ctx *glib.CallbackContext) { } } + if mw.primMButton { + mw.tool.Do(mw.bio, mw, x, z) + + gdk.ThreadsLeave() + mw.redraw <- true + gdk.ThreadsEnter() + } + mw.mx1, mw.my1 = mw.mx2, mw.my2 } +func (mw *MapWidget) buttonChanged(ctx *glib.CallbackContext) { + arg := ctx.Args(0) + bev := *(**gdk.EventButton)(unsafe.Pointer(&arg)) + + switch gdk.EventType(bev.Type) { + case gdk.BUTTON_RELEASE: + mw.primMButton, mw.middleMButton = false, false + case gdk.BUTTON_PRESS: + switch bev.Button { + case 1: + if mw.region == nil { + return + } + x := (mw.offX + int(bev.X)) / zoom + z := (mw.offZ + int(bev.Y)) / zoom + mw.tool.Do(mw.bio, mw, x, z) + + gdk.ThreadsLeave() + mw.redraw <- true + gdk.ThreadsEnter() + + if !mw.tool.SingleClick() { + mw.primMButton = true + } + case 2: + mw.middleMButton = true + } + } +} + func (mw *MapWidget) expose() { mw.dArea.GetWindow().GetDrawable().DrawDrawable(mw.pixmapGC, mw.pixmap.GetDrawable(), 0, 0, 0, 0, -1, -1) } @@ -342,17 +393,19 @@ func (mw *MapWidget) init() { mw.dArea.Connect("configure-event", mw.configure) mw.dArea.Connect("expose-event", mw.expose) mw.dArea.Connect("motion-notify-event", mw.movement) + mw.dArea.Connect("button-press-event", mw.buttonChanged) + mw.dArea.Connect("button-release-event", mw.buttonChanged) - mw.dArea.SetEvents(int(gdk.POINTER_MOTION_MASK | gdk.POINTER_MOTION_HINT_MASK | gdk.BUTTON_PRESS_MASK)) + mw.dArea.SetEvents(int(gdk.POINTER_MOTION_MASK | gdk.POINTER_MOTION_HINT_MASK | gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK)) } func (mw *MapWidget) setRegion(region *mcmap.Region) { mw.tileCmds <- cmdFlushTiles - mw.region = region + mw.region = NewCachedRegion(region, cacheSize) mw.tileCmds <- cmdUpdateTiles } -func (mw *MapWidget) GetBiome(x, z int) (mcmap.Biome, bool) { +func (mw *MapWidget) GetBiomeAt(x, z int) (mcmap.Biome, bool) { cx, cz, bx, bz := mcmap.BlockToChunk(x, z) pos := XZPos{cx, cz} @@ -360,7 +413,7 @@ func (mw *MapWidget) GetBiome(x, z int) (mcmap.Biome, bool) { return bc[bz*mcmap.ChunkSizeXZ+bx], true } - chunk, err := mw.region.Chunk(x, z) + chunk, err := mw.region.Chunk(cx, cz) switch err { case nil: case mcmap.NotAvailable: @@ -369,7 +422,6 @@ func (mw *MapWidget) GetBiome(x, z int) (mcmap.Biome, bool) { mw.reportFail(fmt.Sprintf("Error while getting chunk %d, %d: %s", cx, cz, err)) return mcmap.BioUncalculated, false } - defer chunk.MarkUnused() bc := make([]mcmap.Biome, mcmap.ChunkRectXZ) i := 0 @@ -384,16 +436,11 @@ func (mw *MapWidget) GetBiome(x, z int) (mcmap.Biome, bool) { return chunk.Biome(bx, bz), true } -func (mw *MapWidget) SetBiome(x, z int, bio mcmap.Biome) { +func (mw *MapWidget) SetBiomeAt(x, z int, bio mcmap.Biome) { cx, cz, bx, bz := mcmap.BlockToChunk(x, z) pos := XZPos{cx, cz} - // Update cache - if bc, ok := mw.bioCache[pos]; ok { - bc[bz*mcmap.ChunkSizeXZ+bx] = bio - } - - chunk, err := mw.region.Chunk(x, z) + chunk, err := mw.region.Chunk(cx, cz) switch err { case nil: case mcmap.NotAvailable: @@ -402,9 +449,22 @@ func (mw *MapWidget) SetBiome(x, z int, bio mcmap.Biome) { mw.reportFail(fmt.Sprintf("Error while getting chunk %d, %d: %s", cx, cz, err)) return } - defer chunk.MarkUnused() chunk.SetBiome(bx, bz, bio) + chunk.MarkModified() + + // Update cache + if bc, ok := mw.bioCache[pos]; ok { + bc[bz*mcmap.ChunkSizeXZ+bx] = bio + } + + // Update tile + if tile, ok := mw.biotiles[pos]; ok { + drawable := tile.GetDrawable() + gc := gdk.NewGC(drawable) + gc.SetRgbFgColor(bioColors[bio]) + drawable.DrawRectangle(gc, true, bx*zoom, bz*zoom, zoom, zoom) + } } func NewMapWidget(reportFail func(msg string), updateInfo func(x, z int, bio mcmap.Biome)) *MapWidget { diff --git a/region_cached.go b/region_cached.go new file mode 100644 index 0000000..66cea16 --- /dev/null +++ b/region_cached.go @@ -0,0 +1,78 @@ +package main + +import ( + "errors" + "fmt" + "github.com/kch42/gomcmap/mcmap" +) + +type CachedRegion struct { + Region *mcmap.Region + cacheChunks []*mcmap.Chunk + cachePos []XZPos + cachesize int +} + +func NewCachedRegion(reg *mcmap.Region, cachesize int) *CachedRegion { + if cachesize <= 0 { + panic(errors.New("Cachesize must be >0")) + } + return &CachedRegion{ + Region: reg, + cacheChunks: make([]*mcmap.Chunk, cachesize), + cachePos: make([]XZPos, cachesize), + cachesize: cachesize, + } +} + +func (cr *CachedRegion) Chunk(x, z int) (*mcmap.Chunk, error) { + pos := XZPos{x, z} + + for i, p := range cr.cachePos { + if p == pos { + if cr.cacheChunks[i] != nil { + chunk := cr.cacheChunks[i] + for j := i; j >= 1; j-- { + cr.cacheChunks[j] = cr.cacheChunks[j-1] + cr.cachePos[j] = cr.cachePos[j-1] + } + return chunk, nil + } + } + } + + chunk, err := cr.Region.Chunk(x, z) + if err != nil { + return nil, err + } + + if cr.cacheChunks[cr.cachesize-1] != nil { + if err := cr.cacheChunks[cr.cachesize-1].MarkUnused(); err != nil { + return nil, fmt.Errorf("Could not remove oldest cache element: %s", err) + } + } + + for i := cr.cachesize - 1; i >= 1; i-- { + cr.cacheChunks[i] = cr.cacheChunks[i-1] + cr.cachePos[i] = cr.cachePos[i-1] + } + cr.cacheChunks[0] = chunk + cr.cachePos[0] = pos + + return chunk, nil +} + +func (cr *CachedRegion) Flush() error { + for i, chunk := range cr.cacheChunks { + if chunk == nil { + continue + } + + if err := chunk.MarkUnused(); err != nil { + return err + } + cr.cacheChunks[i] = nil + } + + return nil +} -- cgit v1.2.3-54-g00ecf