package engine import "math" // ColorConfig represents the configuration for color generation type ColorConfig struct { // Saturation settings ColorSaturation float64 // Saturation for normal colors [0, 1] GrayscaleSaturation float64 // Saturation for grayscale colors [0, 1] // Lightness ranges ColorLightness LightnessRange // Lightness range for normal colors GrayscaleLightness LightnessRange // Lightness range for grayscale colors // Hue restrictions Hues []float64 // Allowed hues in degrees [0, 360] or range [0, 1]. Empty means no restriction // Background color BackColor *Color // Background color (nil for transparent) // Icon padding IconPadding float64 // Padding as percentage of icon size [0, 1] } // LightnessRange represents a range of lightness values type LightnessRange struct { Min float64 // Minimum lightness [0, 1] Max float64 // Maximum lightness [0, 1] } // GetLightness returns a lightness value for the given position in range [0, 1] // where 0 returns Min and 1 returns Max func (lr LightnessRange) GetLightness(value float64) float64 { // Clamp value to [0, 1] range value = clamp(value, 0, 1) // Linear interpolation between min and max result := lr.Min + value*(lr.Max-lr.Min) // Clamp result to valid lightness range return clamp(result, 0, 1) } // DefaultColorConfig returns the default configuration matching the JavaScript implementation func DefaultColorConfig() ColorConfig { return ColorConfig{ ColorSaturation: 0.5, GrayscaleSaturation: 0.0, ColorLightness: LightnessRange{Min: 0.4, Max: 0.8}, GrayscaleLightness: LightnessRange{Min: 0.3, Max: 0.9}, Hues: nil, // No hue restriction BackColor: nil, // Transparent background IconPadding: 0.08, } } // RestrictHue applies hue restrictions to the given hue value // Returns the restricted hue in range [0, 1] func (c ColorConfig) RestrictHue(originalHue float64) float64 { // Normalize hue to [0, 1) range hue := math.Mod(originalHue, 1.0) if hue < 0 { hue += 1.0 } // If no hue restrictions, return original if len(c.Hues) == 0 { return hue } // Find the closest allowed hue // originalHue is in range [0, 1], multiply by 0.999 to get range [0, 1) // then truncate to get index index := int((0.999 * hue * float64(len(c.Hues)))) if index >= len(c.Hues) { index = len(c.Hues) - 1 } restrictedHue := c.Hues[index] // Convert from degrees to turns in range [0, 1) // Handle any turn - e.g. 746° is valid result := math.Mod(restrictedHue/360.0, 1.0) if result < 0 { result += 1.0 } return result } // ValidateConfig validates and corrects a ColorConfig to ensure all values are within valid ranges func (c *ColorConfig) Validate() { // Clamp saturation values c.ColorSaturation = clamp(c.ColorSaturation, 0, 1) c.GrayscaleSaturation = clamp(c.GrayscaleSaturation, 0, 1) // Validate lightness ranges c.ColorLightness.Min = clamp(c.ColorLightness.Min, 0, 1) c.ColorLightness.Max = clamp(c.ColorLightness.Max, 0, 1) if c.ColorLightness.Min > c.ColorLightness.Max { c.ColorLightness.Min, c.ColorLightness.Max = c.ColorLightness.Max, c.ColorLightness.Min } c.GrayscaleLightness.Min = clamp(c.GrayscaleLightness.Min, 0, 1) c.GrayscaleLightness.Max = clamp(c.GrayscaleLightness.Max, 0, 1) if c.GrayscaleLightness.Min > c.GrayscaleLightness.Max { c.GrayscaleLightness.Min, c.GrayscaleLightness.Max = c.GrayscaleLightness.Max, c.GrayscaleLightness.Min } // Clamp icon padding c.IconPadding = clamp(c.IconPadding, 0, 1) // Validate hues (no need to clamp as RestrictHue handles normalization) } // ColorConfigBuilder provides a fluent interface for building ColorConfig type ColorConfigBuilder struct { config ColorConfig } // NewColorConfigBuilder creates a new builder with default values func NewColorConfigBuilder() *ColorConfigBuilder { return &ColorConfigBuilder{ config: DefaultColorConfig(), } } // WithColorSaturation sets the color saturation func (b *ColorConfigBuilder) WithColorSaturation(saturation float64) *ColorConfigBuilder { b.config.ColorSaturation = saturation return b } // WithGrayscaleSaturation sets the grayscale saturation func (b *ColorConfigBuilder) WithGrayscaleSaturation(saturation float64) *ColorConfigBuilder { b.config.GrayscaleSaturation = saturation return b } // WithColorLightness sets the color lightness range func (b *ColorConfigBuilder) WithColorLightness(min, max float64) *ColorConfigBuilder { b.config.ColorLightness = LightnessRange{Min: min, Max: max} return b } // WithGrayscaleLightness sets the grayscale lightness range func (b *ColorConfigBuilder) WithGrayscaleLightness(min, max float64) *ColorConfigBuilder { b.config.GrayscaleLightness = LightnessRange{Min: min, Max: max} return b } // WithHues sets the allowed hues in degrees func (b *ColorConfigBuilder) WithHues(hues ...float64) *ColorConfigBuilder { b.config.Hues = make([]float64, len(hues)) copy(b.config.Hues, hues) return b } // WithBackColor sets the background color func (b *ColorConfigBuilder) WithBackColor(color Color) *ColorConfigBuilder { b.config.BackColor = &color return b } // WithIconPadding sets the icon padding func (b *ColorConfigBuilder) WithIconPadding(padding float64) *ColorConfigBuilder { b.config.IconPadding = padding return b } // Build returns the configured ColorConfig after validation func (b *ColorConfigBuilder) Build() ColorConfig { b.config.Validate() return b.config }