package modes import ( "image/color" "math" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/inpututil" "kidskeyboard/internal/ui" ) type shapeKind int const ( shapeCircle shapeKind = iota shapeRect shapeTriangle shapeLine shapeStar ) type shape struct { kind shapeKind x float32 y float32 w float32 h float32 size float32 color color.NRGBA } type GeometryMode struct { ctx Context shapes map[ebiten.Key]*shape order []ebiten.Key } func NewGeometryMode(ctx Context) *GeometryMode { return &GeometryMode{ctx: ctx, shapes: map[ebiten.Key]*shape{}} } func (m *GeometryMode) Name() string { return "CTRL+F2 Geometry" } func (m *GeometryMode) OnEnter() {} func (m *GeometryMode) OnLeave() {} func (m *GeometryMode) HandleInput() { if inpututil.IsKeyJustPressed(ebiten.KeyDelete) { m.shapes = map[ebiten.Key]*shape{} m.order = nil return } if inpututil.IsKeyJustPressed(ebiten.KeyArrowLeft) { m.moveLast(-20, 0) } if inpututil.IsKeyJustPressed(ebiten.KeyArrowRight) { m.moveLast(20, 0) } if inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) { m.moveLast(0, -20) } if inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) { m.moveLast(0, 20) } if inpututil.IsKeyJustPressed(ebiten.KeyEqual) || inpututil.IsKeyJustPressed(ebiten.KeyNumpadAdd) { m.scaleLast(1.12) } if inpututil.IsKeyJustPressed(ebiten.KeyMinus) || inpututil.IsKeyJustPressed(ebiten.KeyNumpadSubtract) { m.scaleLast(0.88) } for _, key := range justPressedKeys() { if !childKey(key) || geometryControlKey(key) { continue } if _, ok := m.shapes[key]; ok { delete(m.shapes, key) m.removeOrder(key) continue } m.shapes[key] = m.randomShape() m.order = append(m.order, key) } } func (m *GeometryMode) Update() {} func (m *GeometryMode) Draw(screen *ebiten.Image) { screen.Fill(color.Black) for _, key := range m.order { if s := m.shapes[key]; s != nil { drawShape(screen, s) } } ui.Text(screen, "CTRL+F2", 20, 20, color.White, 2) } func (m *GeometryMode) randomShape() *shape { w, h := 1280, 720 x := float32(80 + m.ctx.RNG.Intn(max(1, w-160))) y := float32(80 + m.ctx.RNG.Intn(max(1, h-160))) size := float32(35 + m.ctx.RNG.Intn(100)) return &shape{ kind: shapeKind(m.ctx.RNG.Intn(5)), x: x, y: y, w: size + float32(m.ctx.RNG.Intn(90)), h: size + float32(m.ctx.RNG.Intn(90)), size: size, color: color.NRGBA{ R: uint8(80 + m.ctx.RNG.Intn(176)), G: uint8(80 + m.ctx.RNG.Intn(176)), B: uint8(80 + m.ctx.RNG.Intn(176)), A: 255, }, } } func drawShape(screen *ebiten.Image, s *shape) { switch s.kind { case shapeCircle: ui.Circle(screen, s.x, s.y, s.size, s.color) case shapeRect: ui.Rect(screen, s.x-s.w/2, s.y-s.h/2, s.w, s.h, s.color) case shapeTriangle: ui.Triangle(screen, s.x, s.y-s.h/2, s.x-s.w/2, s.y+s.h/2, s.x+s.w/2, s.y+s.h/2, s.color) case shapeLine: ui.Line(screen, s.x-s.w/2, s.y-s.h/2, s.x+s.w/2, s.y+s.h/2, float32(math.Max(4, float64(s.size/8))), s.color) case shapeStar: ui.Star(screen, s.x, s.y, s.size, s.size*0.45, 5, s.color) } } func (m *GeometryMode) moveLast(dx, dy float32) { last := m.last() if last == nil { return } last.x += dx last.y += dy } func (m *GeometryMode) scaleLast(factor float32) { last := m.last() if last == nil { return } last.w *= factor last.h *= factor last.size *= factor } func (m *GeometryMode) last() *shape { for i := len(m.order) - 1; i >= 0; i-- { if s := m.shapes[m.order[i]]; s != nil { return s } } return nil } func (m *GeometryMode) removeOrder(key ebiten.Key) { next := m.order[:0] for _, k := range m.order { if k != key { next = append(next, k) } } m.order = next } func geometryControlKey(key ebiten.Key) bool { switch key { case ebiten.KeyDelete, ebiten.KeyArrowLeft, ebiten.KeyArrowRight, ebiten.KeyArrowUp, ebiten.KeyArrowDown, ebiten.KeyEqual, ebiten.KeyMinus, ebiten.KeyNumpadAdd, ebiten.KeyNumpadSubtract: return true default: return false } }