Files
go-jdenticon/internal/engine/layout.go
Kevin McIntyre f84b511895 init
2025-06-18 01:00:00 -04:00

136 lines
4.1 KiB
Go

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
}