package engine import "math" // Shape rendering constants for visual proportions const ( // Center shape proportions - these ratios determine the visual appearance centerShapeAsymmetricCornerRatio = 0.42 // Shape 0: corner cut proportion centerShapeTriangleWidthRatio = 0.5 // Shape 1: triangle width relative to cell centerShapeTriangleHeightRatio = 0.8 // Shape 1: triangle height relative to cell centerShapeInnerMarginRatio = 0.1 // Shape 3,5,9,10: inner margin ratio centerShapeOuterMarginRatio = 0.25 // Shape 3: outer margin ratio for large cells centerShapeOuterMarginRatio9 = 0.35 // Shape 9: outer margin ratio for large cells centerShapeOuterMarginRatio10 = 0.12 // Shape 10: inner ratio for circular cutout centerShapeCircleMarginRatio = 0.15 // Shape 4: circle margin ratio centerShapeCircleWidthRatio = 0.5 // Shape 4: circle width ratio // Shape 6 complex polygon proportions centerShapeComplexHeight1Ratio = 0.7 // First height point centerShapeComplexPoint1XRatio = 0.4 // First point X ratio centerShapeComplexPoint1YRatio = 0.4 // First point Y ratio centerShapeComplexPoint2XRatio = 0.7 // Second point X ratio // Shape 9 rectangular cutout proportions centerShapeRect9InnerRatio = 0.14 // Shape 9: inner rectangle ratio // Shape 12 rhombus cutout proportion centerShapeRhombusCutoutRatio = 0.25 // Shape 12: rhombus cutout margin // Shape 13 large circle proportions (only for center position) centerShapeLargeCircleMarginRatio = 0.4 // Shape 13: circle margin ratio centerShapeLargeCircleWidthRatio = 1.2 // Shape 13: circle width ratio // Outer shape proportions outerShapeCircleMarginRatio = 1.0 / 6.0 // Shape 3: circle margin (1/6 of cell) // Size thresholds for conditional rendering smallCellThreshold4 = 4 // Threshold for shape 3,9 outer margin calculation smallCellThreshold6 = 6 // Threshold for shape 3 outer margin calculation smallCellThreshold8 = 8 // Threshold for shape 3,9 inner margin floor calculation // Multipliers for margin calculations innerOuterMultiplier5 = 4 // Shape 5: inner to outer multiplier innerOuterMultiplier10 = 3 // Shape 10: inner to outer multiplier ) // 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 * centerShapeAsymmetricCornerRatio 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 * centerShapeTriangleWidthRatio) h := math.Floor(cell * centerShapeTriangleHeightRatio) 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 * centerShapeInnerMarginRatio var outer float64 if cell < smallCellThreshold6 { outer = 1 } else if cell < smallCellThreshold8 { outer = 2 } else { outer = math.Floor(cell * centerShapeOuterMarginRatio) } 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 * centerShapeCircleMarginRatio) w := math.Floor(cell * centerShapeCircleWidthRatio) g.AddCircle(cell-w-m, cell-w-m, w, false) case 5: // Shape 5: Rectangle with triangular cutout inner := cell * centerShapeInnerMarginRatio outer := inner * innerOuterMultiplier5 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 * centerShapeComplexHeight1Ratio}, {X: cell * centerShapeComplexPoint1XRatio, Y: cell * centerShapeComplexPoint1YRatio}, {X: cell * centerShapeComplexPoint2XRatio, 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 * centerShapeRect9InnerRatio var outer float64 if cell < smallCellThreshold4 { outer = 1 } else if cell < smallCellThreshold6 { outer = 2 } else { outer = math.Floor(cell * centerShapeOuterMarginRatio9) } if cell >= smallCellThreshold8 { 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 * centerShapeOuterMarginRatio10 outer := inner * innerOuterMultiplier10 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 * centerShapeRhombusCutoutRatio 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 * centerShapeLargeCircleMarginRatio w := cell * centerShapeLargeCircleWidthRatio 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 * outerShapeCircleMarginRatio g.AddCircle(m, m, cell-2*m, false) } }