package engine import "math" // Point represents a 2D point type Point struct { X, Y float64 } // Renderer interface defines the methods that a renderer must implement type Renderer interface { AddPolygon(points []Point) AddCircle(topLeft Point, size float64, invert bool) } // Graphics provides helper functions for rendering common basic shapes type Graphics struct { renderer Renderer currentTransform Transform } // NewGraphics creates a new Graphics instance with the given renderer func NewGraphics(renderer Renderer) *Graphics { return &Graphics{ renderer: renderer, currentTransform: NoTransform, } } // NewGraphicsWithTransform creates a new Graphics instance with the given renderer and transform func NewGraphicsWithTransform(renderer Renderer, transform Transform) *Graphics { return &Graphics{ renderer: renderer, currentTransform: transform, } } // AddPolygon adds a polygon to the underlying renderer func (g *Graphics) AddPolygon(points []Point, invert bool) { // Transform all points transformedPoints := make([]Point, len(points)) if invert { // Reverse the order and transform for i, p := range points { transformedPoints[len(points)-1-i] = g.currentTransform.TransformIconPoint(p.X, p.Y, 0, 0) } } else { // Transform in order for i, p := range points { transformedPoints[i] = g.currentTransform.TransformIconPoint(p.X, p.Y, 0, 0) } } g.renderer.AddPolygon(transformedPoints) } // AddCircle adds a circle to the underlying renderer func (g *Graphics) AddCircle(x, y, size float64, invert bool) { // Transform the circle position transformedPoint := g.currentTransform.TransformIconPoint(x, y, size, size) g.renderer.AddCircle(transformedPoint, size, invert) } // AddRectangle adds a rectangle to the underlying renderer func (g *Graphics) AddRectangle(x, y, w, h float64, invert bool) { points := []Point{ {X: x, Y: y}, {X: x + w, Y: y}, {X: x + w, Y: y + h}, {X: x, Y: y + h}, } g.AddPolygon(points, invert) } // AddTriangle adds a right triangle to the underlying renderer // r is the rotation (0-3), where 0 = right corner in lower left func (g *Graphics) AddTriangle(x, y, w, h float64, r int, invert bool) { points := []Point{ {X: x + w, Y: y}, {X: x + w, Y: y + h}, {X: x, Y: y + h}, {X: x, Y: y}, } // Remove one corner based on rotation removeIndex := (r % 4) * 1 if removeIndex < len(points) { points = append(points[:removeIndex], points[removeIndex+1:]...) } g.AddPolygon(points, invert) } // AddRhombus adds a rhombus (diamond) to the underlying renderer func (g *Graphics) AddRhombus(x, y, w, h float64, invert bool) { points := []Point{ {X: x + w/2, Y: y}, {X: x + w, Y: y + h/2}, {X: x + w/2, Y: y + h}, {X: x, Y: y + h/2}, } g.AddPolygon(points, invert) } // RenderCenterShape renders one of the 14 distinct center shape patterns func RenderCenterShape(g *Graphics, shapeIndex int, cell, positionIndex float64) { index := shapeIndex % 14 switch index { case 0: // Shape 0: Asymmetric polygon k := cell * 0.42 points := []Point{ {X: 0, Y: 0}, {X: cell, Y: 0}, {X: cell, Y: cell - k*2}, {X: cell - k, Y: cell}, {X: 0, Y: cell}, } g.AddPolygon(points, false) case 1: // Shape 1: Triangle w := math.Floor(cell * 0.5) h := math.Floor(cell * 0.8) g.AddTriangle(cell-w, 0, w, h, 2, false) case 2: // Shape 2: Rectangle w := math.Floor(cell / 3) g.AddRectangle(w, w, cell-w, cell-w, false) case 3: // Shape 3: Nested rectangles inner := cell * 0.1 var outer float64 if cell < 6 { outer = 1 } else if cell < 8 { outer = 2 } else { outer = math.Floor(cell * 0.25) } if inner > 1 { inner = math.Floor(inner) } else if inner > 0.5 { inner = 1 } g.AddRectangle(outer, outer, cell-inner-outer, cell-inner-outer, false) case 4: // Shape 4: Circle m := math.Floor(cell * 0.15) w := math.Floor(cell * 0.5) g.AddCircle(cell-w-m, cell-w-m, w, false) case 5: // Shape 5: Rectangle with triangular cutout inner := cell * 0.1 outer := inner * 4 if outer > 3 { outer = math.Floor(outer) } g.AddRectangle(0, 0, cell, cell, false) points := []Point{ {X: outer, Y: outer}, {X: cell - inner, Y: outer}, {X: outer + (cell-outer-inner)/2, Y: cell - inner}, } g.AddPolygon(points, true) case 6: // Shape 6: Complex polygon points := []Point{ {X: 0, Y: 0}, {X: cell, Y: 0}, {X: cell, Y: cell * 0.7}, {X: cell * 0.4, Y: cell * 0.4}, {X: cell * 0.7, Y: cell}, {X: 0, Y: cell}, } g.AddPolygon(points, false) case 7: // Shape 7: Small triangle g.AddTriangle(cell/2, cell/2, cell/2, cell/2, 3, false) case 8: // Shape 8: Composite shape g.AddRectangle(0, 0, cell, cell/2, false) g.AddRectangle(0, cell/2, cell/2, cell/2, false) g.AddTriangle(cell/2, cell/2, cell/2, cell/2, 1, false) case 9: // Shape 9: Rectangle with rectangular cutout inner := cell * 0.14 var outer float64 if cell < 4 { outer = 1 } else if cell < 6 { outer = 2 } else { outer = math.Floor(cell * 0.35) } if cell >= 8 { inner = math.Floor(inner) } g.AddRectangle(0, 0, cell, cell, false) g.AddRectangle(outer, outer, cell-outer-inner, cell-outer-inner, true) case 10: // Shape 10: Rectangle with circular cutout inner := cell * 0.12 outer := inner * 3 g.AddRectangle(0, 0, cell, cell, false) g.AddCircle(outer, outer, cell-inner-outer, true) case 11: // Shape 11: Small triangle (same as 7) g.AddTriangle(cell/2, cell/2, cell/2, cell/2, 3, false) case 12: // Shape 12: Rectangle with rhombus cutout m := cell * 0.25 g.AddRectangle(0, 0, cell, cell, false) g.AddRhombus(m, m, cell-m, cell-m, true) case 13: // Shape 13: Large circle (only for position 0) if positionIndex == 0 { m := cell * 0.4 w := cell * 1.2 g.AddCircle(m, m, w, false) } } } // RenderOuterShape renders one of the 4 distinct outer shape patterns func RenderOuterShape(g *Graphics, shapeIndex int, cell float64) { index := shapeIndex % 4 switch index { case 0: // Shape 0: Triangle g.AddTriangle(0, 0, cell, cell, 0, false) case 1: // Shape 1: Triangle (different orientation) g.AddTriangle(0, cell/2, cell, cell/2, 0, false) case 2: // Shape 2: Rhombus g.AddRhombus(0, 0, cell, cell, false) case 3: // Shape 3: Circle m := cell / 6 g.AddCircle(m, m, cell-2*m, false) } }