init
This commit is contained in:
237
internal/renderer/renderer.go
Normal file
237
internal/renderer/renderer.go
Normal file
@@ -0,0 +1,237 @@
|
||||
package renderer
|
||||
|
||||
import (
|
||||
"github.com/kevin/go-jdenticon/internal/engine"
|
||||
)
|
||||
|
||||
// Renderer defines the interface for rendering identicons to various output formats.
|
||||
// It provides a set of drawing primitives that can be implemented by concrete renderers
|
||||
// such as SVG, PNG, or other custom formats.
|
||||
type Renderer interface {
|
||||
// Drawing primitives
|
||||
MoveTo(x, y float64)
|
||||
LineTo(x, y float64)
|
||||
CurveTo(x1, y1, x2, y2, x, y float64)
|
||||
ClosePath()
|
||||
|
||||
// Fill and stroke operations
|
||||
Fill(color string)
|
||||
Stroke(color string, width float64)
|
||||
|
||||
// Shape management
|
||||
BeginShape(color string)
|
||||
EndShape()
|
||||
|
||||
// Background and configuration
|
||||
SetBackground(fillColor string, opacity float64)
|
||||
|
||||
// High-level shape methods
|
||||
AddPolygon(points []engine.Point)
|
||||
AddCircle(topLeft engine.Point, size float64, invert bool)
|
||||
AddRectangle(x, y, width, height float64)
|
||||
AddTriangle(p1, p2, p3 engine.Point)
|
||||
|
||||
// Utility methods
|
||||
GetSize() int
|
||||
Clear()
|
||||
}
|
||||
|
||||
// BaseRenderer provides default implementations for common renderer functionality.
|
||||
// Concrete renderers can embed this struct and override specific methods as needed.
|
||||
type BaseRenderer struct {
|
||||
iconSize int
|
||||
currentColor string
|
||||
background string
|
||||
backgroundOp float64
|
||||
|
||||
// Current path state for primitive operations
|
||||
currentPath []PathCommand
|
||||
pathStart engine.Point
|
||||
currentPos engine.Point
|
||||
}
|
||||
|
||||
// PathCommandType represents the type of path command
|
||||
type PathCommandType int
|
||||
|
||||
const (
|
||||
MoveToCommand PathCommandType = iota
|
||||
LineToCommand
|
||||
CurveToCommand
|
||||
ClosePathCommand
|
||||
)
|
||||
|
||||
// PathCommand represents a single drawing command in a path
|
||||
type PathCommand struct {
|
||||
Type PathCommandType
|
||||
Points []engine.Point
|
||||
}
|
||||
|
||||
// NewBaseRenderer creates a new base renderer with the specified icon size
|
||||
func NewBaseRenderer(iconSize int) *BaseRenderer {
|
||||
return &BaseRenderer{
|
||||
iconSize: iconSize,
|
||||
currentPath: make([]PathCommand, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// MoveTo moves the current drawing position to the specified coordinates
|
||||
func (r *BaseRenderer) MoveTo(x, y float64) {
|
||||
pos := engine.Point{X: x, Y: y}
|
||||
r.currentPos = pos
|
||||
r.pathStart = pos
|
||||
r.currentPath = append(r.currentPath, PathCommand{
|
||||
Type: MoveToCommand,
|
||||
Points: []engine.Point{pos},
|
||||
})
|
||||
}
|
||||
|
||||
// LineTo draws a line from the current position to the specified coordinates
|
||||
func (r *BaseRenderer) LineTo(x, y float64) {
|
||||
pos := engine.Point{X: x, Y: y}
|
||||
r.currentPos = pos
|
||||
r.currentPath = append(r.currentPath, PathCommand{
|
||||
Type: LineToCommand,
|
||||
Points: []engine.Point{pos},
|
||||
})
|
||||
}
|
||||
|
||||
// CurveTo draws a cubic Bézier curve from the current position to (x, y) using (x1, y1) and (x2, y2) as control points
|
||||
func (r *BaseRenderer) CurveTo(x1, y1, x2, y2, x, y float64) {
|
||||
endPos := engine.Point{X: x, Y: y}
|
||||
r.currentPos = endPos
|
||||
r.currentPath = append(r.currentPath, PathCommand{
|
||||
Type: CurveToCommand,
|
||||
Points: []engine.Point{
|
||||
{X: x1, Y: y1},
|
||||
{X: x2, Y: y2},
|
||||
endPos,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ClosePath closes the current path by drawing a line back to the path start
|
||||
func (r *BaseRenderer) ClosePath() {
|
||||
r.currentPos = r.pathStart
|
||||
r.currentPath = append(r.currentPath, PathCommand{
|
||||
Type: ClosePathCommand,
|
||||
Points: []engine.Point{},
|
||||
})
|
||||
}
|
||||
|
||||
// Fill fills the current path with the specified color
|
||||
func (r *BaseRenderer) Fill(color string) {
|
||||
// Default implementation - concrete renderers should override
|
||||
}
|
||||
|
||||
// Stroke strokes the current path with the specified color and width
|
||||
func (r *BaseRenderer) Stroke(color string, width float64) {
|
||||
// Default implementation - concrete renderers should override
|
||||
}
|
||||
|
||||
// BeginShape starts a new shape with the specified color
|
||||
func (r *BaseRenderer) BeginShape(color string) {
|
||||
r.currentColor = color
|
||||
r.currentPath = make([]PathCommand, 0)
|
||||
}
|
||||
|
||||
// EndShape ends the current shape
|
||||
func (r *BaseRenderer) EndShape() {
|
||||
// Default implementation - concrete renderers should override
|
||||
}
|
||||
|
||||
// SetBackground sets the background color and opacity
|
||||
func (r *BaseRenderer) SetBackground(fillColor string, opacity float64) {
|
||||
r.background = fillColor
|
||||
r.backgroundOp = opacity
|
||||
}
|
||||
|
||||
// AddPolygon adds a polygon to the renderer using the current fill color
|
||||
func (r *BaseRenderer) AddPolygon(points []engine.Point) {
|
||||
if len(points) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Move to first point
|
||||
r.MoveTo(points[0].X, points[0].Y)
|
||||
|
||||
// Line to subsequent points
|
||||
for i := 1; i < len(points); i++ {
|
||||
r.LineTo(points[i].X, points[i].Y)
|
||||
}
|
||||
|
||||
// Close the path
|
||||
r.ClosePath()
|
||||
|
||||
// Fill with current color
|
||||
r.Fill(r.currentColor)
|
||||
}
|
||||
|
||||
// AddCircle adds a circle to the renderer using the current fill color
|
||||
func (r *BaseRenderer) AddCircle(topLeft engine.Point, size float64, invert bool) {
|
||||
// Approximate circle using cubic Bézier curves
|
||||
// Magic number for circle approximation with Bézier curves
|
||||
const kappa = 0.5522847498307936 // 4/3 * (sqrt(2) - 1)
|
||||
|
||||
radius := size / 2
|
||||
centerX := topLeft.X + radius
|
||||
centerY := topLeft.Y + radius
|
||||
|
||||
cp := kappa * radius // Control point distance
|
||||
|
||||
// Start at rightmost point
|
||||
r.MoveTo(centerX+radius, centerY)
|
||||
|
||||
// Four cubic curves to approximate circle
|
||||
r.CurveTo(centerX+radius, centerY+cp, centerX+cp, centerY+radius, centerX, centerY+radius)
|
||||
r.CurveTo(centerX-cp, centerY+radius, centerX-radius, centerY+cp, centerX-radius, centerY)
|
||||
r.CurveTo(centerX-radius, centerY-cp, centerX-cp, centerY-radius, centerX, centerY-radius)
|
||||
r.CurveTo(centerX+cp, centerY-radius, centerX+radius, centerY-cp, centerX+radius, centerY)
|
||||
|
||||
r.ClosePath()
|
||||
r.Fill(r.currentColor)
|
||||
}
|
||||
|
||||
// AddRectangle adds a rectangle to the renderer
|
||||
func (r *BaseRenderer) AddRectangle(x, y, width, height float64) {
|
||||
points := []engine.Point{
|
||||
{X: x, Y: y},
|
||||
{X: x + width, Y: y},
|
||||
{X: x + width, Y: y + height},
|
||||
{X: x, Y: y + height},
|
||||
}
|
||||
r.AddPolygon(points)
|
||||
}
|
||||
|
||||
// AddTriangle adds a triangle to the renderer
|
||||
func (r *BaseRenderer) AddTriangle(p1, p2, p3 engine.Point) {
|
||||
points := []engine.Point{p1, p2, p3}
|
||||
r.AddPolygon(points)
|
||||
}
|
||||
|
||||
// GetSize returns the icon size
|
||||
func (r *BaseRenderer) GetSize() int {
|
||||
return r.iconSize
|
||||
}
|
||||
|
||||
// Clear clears the renderer state
|
||||
func (r *BaseRenderer) Clear() {
|
||||
r.currentPath = make([]PathCommand, 0)
|
||||
r.currentColor = ""
|
||||
r.background = ""
|
||||
r.backgroundOp = 0
|
||||
}
|
||||
|
||||
// GetCurrentPath returns the current path commands
|
||||
func (r *BaseRenderer) GetCurrentPath() []PathCommand {
|
||||
return r.currentPath
|
||||
}
|
||||
|
||||
// GetCurrentColor returns the current drawing color
|
||||
func (r *BaseRenderer) GetCurrentColor() string {
|
||||
return r.currentColor
|
||||
}
|
||||
|
||||
// GetBackground returns the background color and opacity
|
||||
func (r *BaseRenderer) GetBackground() (string, float64) {
|
||||
return r.background, r.backgroundOp
|
||||
}
|
||||
Reference in New Issue
Block a user