package engine // Grid represents a 4x4 layout grid for positioning shapes in a jdenticon type Grid struct { Size float64 Cell int X int Y int Padding int } // Position represents an x, y coordinate pair type Position struct { X, Y int } // NewGrid creates a new Grid with the specified icon size and padding ratio func NewGrid(iconSize float64, paddingRatio float64) *Grid { // Calculate padding and round to nearest integer (matches JS: (0.5 + size * parsedConfig.iconPadding) | 0) padding := int(0.5 + iconSize*paddingRatio) size := iconSize - float64(padding*2) // Calculate cell size and ensure it is an integer (matches JS: 0 | (size / 4)) cell := int(size / 4) // Center the icon since cell size is integer-based (matches JS implementation) // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon x := padding + int((size - float64(cell*4))/2) y := padding + int((size - float64(cell*4))/2) return &Grid{ Size: size, Cell: cell, X: x, Y: y, Padding: padding, } } // CellToCoordinate converts a grid cell position to actual coordinates func (g *Grid) CellToCoordinate(cellX, cellY int) (x, y float64) { return float64(g.X + cellX*g.Cell), float64(g.Y + cellY*g.Cell) } // GetCellSize returns the size of each cell in the grid func (g *Grid) GetCellSize() float64 { return float64(g.Cell) } // LayoutEngine manages the overall layout and positioning of icon elements type LayoutEngine struct { grid *Grid } // NewLayoutEngine creates a new LayoutEngine with the specified parameters func NewLayoutEngine(iconSize float64, paddingRatio float64) *LayoutEngine { return &LayoutEngine{ grid: NewGrid(iconSize, paddingRatio), } } // Grid returns the underlying grid func (le *LayoutEngine) Grid() *Grid { return le.grid } // GetShapePositions returns the positions for different shape types based on the jdenticon pattern func (le *LayoutEngine) GetShapePositions(shapeType string) []Position { switch shapeType { case "sides": // Sides: positions around the perimeter (8 positions) return []Position{ {1, 0}, {2, 0}, {2, 3}, {1, 3}, // top and bottom {0, 1}, {3, 1}, {3, 2}, {0, 2}, // left and right } case "corners": // Corners: four corner positions return []Position{ {0, 0}, {3, 0}, {3, 3}, {0, 3}, } case "center": // Center: four center positions return []Position{ {1, 1}, {2, 1}, {2, 2}, {1, 2}, } default: return []Position{} } } // ApplySymmetry applies symmetrical transformations to position indices // This ensures the icon has the characteristic jdenticon symmetry func ApplySymmetry(positions []Position, index int) []Position { if index >= len(positions) { return positions } // For jdenticon, we apply rotational symmetry // The pattern is designed to be symmetrical, so we don't need to modify positions // The symmetry is achieved through the predefined position arrays return positions } // GetTransformedPosition applies rotation and returns the final position func (le *LayoutEngine) GetTransformedPosition(cellX, cellY int, rotation int) (x, y float64, cellSize float64) { // Apply rotation if needed (rotation is 0-3 for 0°, 90°, 180°, 270°) switch rotation % 4 { case 0: // 0° // No rotation case 1: // 90° clockwise cellX, cellY = cellY, 3-cellX case 2: // 180° cellX, cellY = 3-cellX, 3-cellY case 3: // 270° clockwise (90° counter-clockwise) cellX, cellY = 3-cellY, cellX } x, y = le.grid.CellToCoordinate(cellX, cellY) cellSize = le.grid.GetCellSize() return } // ValidateGrid checks if the grid configuration is valid func (g *Grid) ValidateGrid() bool { return g.Cell > 0 && g.Size > 0 && g.Padding >= 0 } // GetIconBounds returns the bounds of the icon within the grid func (g *Grid) GetIconBounds() (x, y, width, height float64) { return float64(g.X), float64(g.Y), float64(g.Cell * 4), float64(g.Cell * 4) } // GetCenterOffset returns the offset needed to center content within a cell func (g *Grid) GetCenterOffset() (dx, dy float64) { return float64(g.Cell) / 2, float64(g.Cell) / 2 }