shithub: wildlife

ref: eca59fe0c9f2e78eca5246e626950ee5b408ca12
dir: /main.go/

View raw version
package main

import (
	"9fans.net/go/draw"
	"bytes"
	"fmt"
	"image"
	imagedraw "image/draw"
	"flag"
	"github.com/mjl-/duit"
	"github.com/psilva261/szdd"
	xdraw "golang.org/x/image/draw"
	"log"
	"math"
	"os"

	_ "golang.org/x/image/bmp"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
)

var (
	dui *duit.DUI
	scale int
)

type Folder struct {
	Pre string
	Suf string
	Name string
	Items map[string]*Item
}

func getFolder(root, pre string) (ff *Folder) {
	for _, f := range folders {
		if f.Pre == pre {
			ff = f
		}
	}
	return
}

func (f Folder) Main() *Item {
	fnSuf := f.Suf
	if _, ok := f.Items[f.Suf]; !ok {
		fnSuf = "0001"
	}
	return f.getItem(fnSuf)
}

func (f *Folder) Decode(k string) (fName, itName string) {
	if _, ok := f.Items[k]; ok {
		return f.Pre, k
	}
	var ff *Folder
	fName = k
	for _, fff := range folders {
		if fff.Pre == k {
			ff = fff
		}
	}
	if ff == nil {
		return f.Pre, k
	}
	itName = ff.Suf
	if _, ok := ff.Items[itName]; !ok {
		itName = "0001"
	}
	return
}

func (f Folder) getItem(k string) *Item {
	for key, it := range f.Items {
		if key == k {
			if it.Image == nil {
				fn := fmt.Sprintf("./data/about/%v%v/%v%v.dib", f.Pre, f.Suf, f.Pre, k)
				it.Image = &duit.Image{
					Image: readImagePath(fn),
				}
			}
			return it
		}
	}
	return nil
}

/*func (f Folder) getItem(k string) string {
	fnSuf := k
	return fmt.Sprintf("./data/about/%v%v/%v%v.dib", f.Pre, f.Suf, f.Pre, fnSuf)
}*/

type Item struct {
	*duit.Image

	f *Folder
	name string
	Targets map[string]Target
}

func (it *Item) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {
	if m.Buttons&1 == 1 {
		//log.Printf("m=%+v, origM=%+v", m, origM)
		for k, t := range it.Targets {
			if t.has(m.Point) {
				fName, itName := it.f.Decode(k)
				_=fName
				tf := getFolder(it.f.Pre, k)
				if tf == nil {
					tf = it.f
				}
				it := tf.getItem(itName)
				dui.Top.UI = it
				dui.MarkLayout(dui.Top.UI)
				dui.MarkDraw(dui.Top.UI)
				dui.Render()
			}
		}
		return duit.Result{
			Consumed: true,
		}
	} else if m.Buttons&4 == 4 {
		tf := getFolder("", "cont")
		it := tf.Main()
		dui.Top.UI = it
		dui.MarkLayout(dui.Top.UI)
		dui.MarkDraw(dui.Top.UI)
		dui.Render()
	}
	return it.Image.Mouse(dui, self, m, origM, orig)
}

type Target interface {
	has(draw.Point) bool
}

type Circle struct {
	x int
	y int
	r int
}

func C(x, y, r int) Circle {
	return Circle{x: x, y: y, r: r}
}

func (c Circle) has(p draw.Point) bool {
	x := p.X * 2 / scale
	y := p.Y * 2 / scale
	return math.Abs(float64(x)-float64(c.x)) < float64(c.r) && math.Abs(float64(y)-float64(c.y)) < float64(c.r)
}

type Rectangle struct {
	x int
	y int
	x2 int
	y2 int
}

func R(x, y, x2, y2 int) Rectangle {
	return Rectangle{x: x, y: y, x2: x2, y2: y2}
}

func (r Rectangle) has(p draw.Point) bool {
	x := p.X * 2 / scale
	y := p.Y * 2 / scale
	return r.x <= x && x <= r.x2 && r.y <= y && y <= r.y2
}

var folders = []*Folder{
	&Folder{
		Pre: "afri",
		Suf: "00aa",
		Name: "Africa",
		Items: map[string]*Item{
			"00aa": &Item{
				name: "Waterhole",
			},
			"00fb": &Item{
				name: "Art",
			},
		},
	},
	&Folder{
		Pre: "atls",
		Suf: "00nv",
		Name: "Atlas",
		Items: map[string]*Item{
			"00nv": &Item{
				name: "Continents",
				Targets: map[string]Target{
					"01pu": C(659, 461, 100),
					"02pu": C(931, 275, 200),
					"03pu": C(1117, 603, 50),
					"04pu": C(600, 262, 50),
					"05pu": C(222, 270, 140),
					"06pu": C(350, 552, 120),
				},
			},
			"01p1": &Item{
				name: "African reptiles",
			},
			"01p2": &Item{
				name: "African herbivores",
			},
			"01pu": &Item{
				name: "Africa",
			},
			"02pu": &Item{
				name: "Asia",
			},
			"03pu": &Item{
				name: "Australia",
			},
			"04pu": &Item{
				name: "Europe",
			},
			"05pu": &Item{
				name: "North America",
			},
			"06pu": &Item{
				name: "South America",
			},
		},
	},
	&Folder{
		Pre: "aust",
		Suf: "00aa",
		Name: "Australia",
		Items: map[string]*Item{
			"00aa": &Item{
				name: "Oddities",
			},
		},
	},
	&Folder{
		Pre: "beet",
		Suf: "00aa",
		Name: "Beetles",
		Items: map[string]*Item{
			"00aa": &Item{
				name: "Beetles",
			},
		},
	},
	&Folder{
		Pre: "frst",
		Suf: "00aa",
		Name: "Forest",
		Items: map[string]*Item{
			"00aa": &Item{
				name: "Forest Environments",
			},
		},
	},
	&Folder{
		Pre: "cont",
		Suf: "00nv",
		Name: "Contents",
		Items: map[string]*Item{
			"00nv": &Item{
				name: "Contents",
				Targets: map[string]Target{
					"atls": C(242, 190, 150),
					"habt": C(256, 588, 200),
					"indx": C(920, 614, 200),
					"weap": C(997, 200, 200),
					"wwfd": C(604, 384, 200),
				},
			},
		},
	},
	&Folder{
		Pre: "guid",
		Suf: "00nv",
		Name: "Guides",
		Items: map[string]*Item{
			"00nv": &Item{
				name: "Guides",
			},
		},
	},
	&Folder{
		Pre: "habt",
		Suf: "00nv",
		Name: "Habitats",
		Items: map[string]*Item{
			"00nv": &Item{
				name: "Habitats",
			},
		},
	},
	&Folder{
		Pre: "help",
		Suf: "00nv",
		Name: "Help",
		Items: map[string]*Item{
			"00nv": &Item{
				name: "Help",
			},
		},
	},
	&Folder{
		Pre: "indx",
		Suf: "00nv",
		Name: "Index",
		Items: map[string]*Item{
			"0001": &Item{
				name: "Index",
				Targets: map[string]Target{
					"0001": R(25,66, 72,114),
					"0002": R(75,70, 119,115),
					"0003": R(122,68, 163,114),
					"0004": R(166,69, 295,115),
					"0005": R(297,66, 389,115),
					"0006": R(394,69, 502,117),
					"0007": R(504,66, 703,115),
					"0008": R(702,66, 791,116),
					"0009": R(794,67, 880,117),
					"0010": R(879,68, 1018,114),
					"0011": R(1013,66, 1210,116),
				},
			},
			"0002": &Item{},
			"0003": &Item{},
			"0004": &Item{},
			"0005": &Item{},
			"0006": &Item{},
			"0007": &Item{},
			"0008": &Item{},
			"0009": &Item{},
			"0010": &Item{},
			"0011": &Item{},
		},
	},
	/*Folder{
		Pre: "titl",
		Suf: "00nv",
		Name: "Title",
		Items: map[string]*Item{
			"00nv": "Title",
		},
	},*/
	&Folder{
		Pre: "weap",
		Suf: "00nv",
		Name: "Index",
		Items: map[string]*Item{
			"00nv": &Item{
				name: "Index",
				Targets: map[string]Target{
					"01pu": C(250, 400, 150),
					"03pu": C(654, 375, 150),
					"02pu": C(1047, 400, 150),
				},
			},
			"01pu": &Item{},
			"03pu": &Item{},
			"02pu": &Item{},
		},
	},
	&Folder{
		Pre: "wwfd",
		Suf: "00aa",
		Name: "WWF",
		Items: map[string]*Item{
			"00aa": &Item{
				name: "WWF",
				Targets: map[string]Target{
					"03pu": C(302, 176, 80),
					"04pu": C(518, 255, 80),
					"02pu": C(685, 415, 80),
					"06pu": C(952, 147, 80),
					"05pu": C(917, 324, 80),
					"01pu": C(1102, 313, 80),
					"07tv": C(202, 465, 80),
					"00fb": C(89, 55, 80),
					"01fb": C(1208, 69, 80),
				},
			},
			"03pu": &Item{},
			"04pu": &Item{},
			"02pu": &Item{},
			"06pu": &Item{},
			"05pu": &Item{},
			"01pu": &Item{},
			"07tv": &Item{},
			"00fb": &Item{},
			"01fb": &Item{},
		},
	},
}

func init() {
	for _, f := range folders {
		for _, it := range f.Items {
			it.f = f
			if f.Pre == "indx" {
				it.Targets = f.Items["0001"].Targets
			}
		}
	}
}

func check(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s\n", msg, err)
	}
}

func resize(img image.Image, scale int) image.Image {
	bounds := img.Bounds()
	newX := bounds.Dx() * scale
	newY:= bounds.Dy() * scale
	dst := image.NewRGBA(image.Rect(0, 0, newX, newY))
	xdraw.NearestNeighbor.Scale(dst, dst.Rect, img, img.Bounds(), xdraw.Over, nil)
	return dst
}

func	readImagePath(path string) *draw.Image {
		bs, err := os.ReadFile(path)
		check(err, "read file")
		data, err := szdd.Expand(bs)
		check(err, "expand")
		img, _, err := image.Decode(bytes.NewReader(data))
		check(err, "decode")
		if scale > 1 {
			img = resize(img, scale)
		}
		bounds := img.Bounds()
		ni, err := dui.Display.AllocImage(bounds, draw.ABGR32, false, draw.White)
		check(err, "allocimage")
		var rgba *image.RGBA
		switch i := img.(type) {
		case *image.RGBA:
			rgba = i
		default:
			b := img.Bounds()
			rgba = image.NewRGBA(image.Rectangle{image.ZP, b.Size()})
			imagedraw.Draw(rgba, rgba.Bounds(), img, b.Min, imagedraw.Src)
		}
		_, err = ni.Load(rgba.Bounds(), rgba.Pix)
		check(err, "load image")
		return ni
	}

func main() {
	sc := flag.Int("x", 0, "scale")
	flag.Parse()
	scale = *sc

	var err error
	dui, err = duit.NewDUI("wildlife", nil)
	check(err, "new dui")

	if scale == 0 {
		if dui.Display.DPI > 100 {
			scale = 2
		} else {
			scale = 1
		}
	}

	f := getFolder("", "cont")

	dui.Top.UI = f.Main()
	dui.Render()

	for {
		select {
		case e := <-dui.Inputs:
			dui.Input(e)

		case err, ok := <-dui.Error:
			if !ok {
				return
			}
			log.Printf("duit: %s\n", err)
		}
	}
}