Initial release: Go Jdenticon library v0.1.0

- Core library with SVG and PNG generation
- CLI tool with generate and batch commands
- Cross-platform path handling for Windows compatibility
- Comprehensive test suite with integration tests
This commit is contained in:
Kevin McIntyre
2026-01-02 23:56:48 -05:00
parent f84b511895
commit d9e84812ff
292 changed files with 19725 additions and 38884 deletions

View File

@@ -2,6 +2,50 @@ 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
@@ -80,13 +124,13 @@ func (g *Graphics) AddTriangle(x, y, w, h float64, r int, invert bool) {
{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)
}
@@ -104,11 +148,11 @@ func (g *Graphics) AddRhombus(x, y, w, h float64, invert bool) {
// 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
k := cell * centerShapeAsymmetricCornerRatio
points := []Point{
{X: 0, Y: 0},
{X: cell, Y: 0},
@@ -117,53 +161,53 @@ func RenderCenterShape(g *Graphics, shapeIndex int, cell, positionIndex float64)
{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)
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 * 0.1
inner := cell * centerShapeInnerMarginRatio
var outer float64
if cell < 6 {
if cell < smallCellThreshold6 {
outer = 1
} else if cell < 8 {
} else if cell < smallCellThreshold8 {
outer = 2
} else {
outer = math.Floor(cell * 0.25)
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 * 0.15)
w := math.Floor(cell * 0.5)
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 * 0.1
outer := inner * 4
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},
@@ -171,71 +215,71 @@ func RenderCenterShape(g *Graphics, shapeIndex int, cell, positionIndex float64)
{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: 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 * 0.14
inner := cell * centerShapeRect9InnerRatio
var outer float64
if cell < 4 {
if cell < smallCellThreshold4 {
outer = 1
} else if cell < 6 {
} else if cell < smallCellThreshold6 {
outer = 2
} else {
outer = math.Floor(cell * 0.35)
outer = math.Floor(cell * centerShapeOuterMarginRatio9)
}
if cell >= 8 {
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 * 0.12
outer := inner * 3
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 * 0.25
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 * 0.4
w := cell * 1.2
m := cell * centerShapeLargeCircleMarginRatio
w := cell * centerShapeLargeCircleWidthRatio
g.AddCircle(m, m, w, false)
}
}
@@ -244,23 +288,23 @@ func RenderCenterShape(g *Graphics, shapeIndex int, cell, positionIndex float64)
// 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
m := cell * outerShapeCircleMarginRatio
g.AddCircle(m, m, cell-2*m, false)
}
}
}