package plotter import ( "errors" "fmt" "math" "math/rand" "github.com/iliadenisov/galaxy/server/internal/bitmap" ) type Plotter struct { factor float64 clearFn func() circleFn func(x, y int, r float64) freeCountFn func() int freeNumberToCoordFn func(int) (int, int, error) } func NewPlotter(width, height uint32, factor float64) (Plotter, error) { return NewBitmapPlotter(NewBitmap(width, height, factor), factor) } func NewBitmap(width, height uint32, factor float64) bitmap.Bitmap { return bitmap.NewBitmap(AsPlotterSize(width, height, factor)) } func NewBitmapPlotter(bm bitmap.Bitmap, factor float64) (Plotter, error) { if factor > 1 || factor <= 0 { return Plotter{}, fmt.Errorf("factor should be: 0 > F <= 1") } return Plotter{ factor: factor, clearFn: bm.Clear, circleFn: func(x, y int, r float64) { bm.Circle(x, y, r, true) }, freeCountFn: bm.FreeCount, freeNumberToCoordFn: bm.GetFreeN, }, nil } func (p Plotter) RandomFreePoint(deadZoneRaduis float64) (float64, float64, error) { fsCount := p.freeCountFn() if fsCount == 0 { return 0, 0, errors.New("RandomFreePoint: no free space left") } next := rand.Intn(fsCount) x, y, err := p.freeNumberToCoordFn(next) if err != nil { return 0, 0, fmt.Errorf("RandomFreePoint: freeNumberToCoordFn: %s", err) } if deadZoneRaduis > 0 { p.plotDeadZone(x, y, deadZoneRaduis) } planetX := float64(x)*p.factor + rand.Float64()*p.factor planetY := float64(y)*p.factor + rand.Float64()*p.factor return planetX, planetY, nil } func (p Plotter) MarkDeadZone(x, y float64, radius float64) { p.plotDeadZone(int(x/p.factor), int(y/p.factor), radius) } func (p Plotter) plotDeadZone(x, y int, radius float64) { // Adding extra 'pixel' to avoid radius became less than deadZoneRaduis // after division by factor due to floating-point operations specifics p.circleFn(x, y, (radius+p.factor)/p.factor) } func (p Plotter) Clear() { p.clearFn() } func AsPlotterSize(width, height uint32, factor float64) (uint32, uint32) { if factor > 1 || factor <= 0 { return width, height } return uint32(math.Ceil(float64(width) / float64(factor))), uint32(math.Ceil(float64(height) / float64(factor))) }