From 8ac5283df1de4c96f9a62a3772f9303f37b619b4 Mon Sep 17 00:00:00 2001 From: lublak <44057030+lublak@users.noreply.github.com> Date: Thu, 24 Oct 2024 20:57:21 +0200 Subject: [PATCH] implement functions with custom rand handler --- colorgens.go | 36 ++++++++++++++++++++++++------------ colorgens_test.go | 30 +++++++++++++++++++++++++----- doc/colorgens/colorgens.go | 31 ++++++++++++++++++------------- doc/colorsort/colorsort.go | 9 +++++---- happy_palettegen.go | 18 +++++++++++------- rand.go | 22 ++++++++++++++++++++++ soft_palettegen.go | 13 ++++++++++--- warm_palettegen.go | 18 +++++++++++------- 8 files changed, 126 insertions(+), 51 deletions(-) create mode 100644 rand.go diff --git a/colorgens.go b/colorgens.go index 2e2e49e..ac697d6 100644 --- a/colorgens.go +++ b/colorgens.go @@ -2,28 +2,32 @@ package colorful -import ( - "math/rand" -) - // Creates a random dark, "warm" color through a restricted HSV space. -func FastWarmColor() Color { +func FastWarmColorWithRand(rand RandInterface) Color { return Hsv( rand.Float64()*360.0, 0.5+rand.Float64()*0.3, 0.3+rand.Float64()*0.3) } +func FastWarmColor() Color { + return FastWarmColorWithRand(getDefaultGlobalRand()) +} + // Creates a random dark, "warm" color through restricted HCL space. // This is slower than FastWarmColor but will likely give you colors which have // the same "warmness" if you run it many times. -func WarmColor() (c Color) { - for c = randomWarm(); !c.IsValid(); c = randomWarm() { +func WarmColorWithRand(rand RandInterface) (c Color) { + for c = randomWarmWithRand(rand); !c.IsValid(); c = randomWarmWithRand(rand) { } return } -func randomWarm() Color { +func WarmColor() (c Color) { + return WarmColorWithRand(getDefaultGlobalRand()) +} + +func randomWarmWithRand(rand RandInterface) Color { return Hcl( rand.Float64()*360.0, 0.1+rand.Float64()*0.3, @@ -31,23 +35,31 @@ func randomWarm() Color { } // Creates a random bright, "pimpy" color through a restricted HSV space. -func FastHappyColor() Color { +func FastHappyColorWithRand(rand RandInterface) Color { return Hsv( rand.Float64()*360.0, 0.7+rand.Float64()*0.3, 0.6+rand.Float64()*0.3) } +func FastHappyColor() Color { + return FastHappyColorWithRand(getDefaultGlobalRand()) +} + // Creates a random bright, "pimpy" color through restricted HCL space. // This is slower than FastHappyColor but will likely give you colors which // have the same "brightness" if you run it many times. -func HappyColor() (c Color) { - for c = randomPimp(); !c.IsValid(); c = randomPimp() { +func HappyColorWithRand(rand RandInterface) (c Color) { + for c = randomPimpWithRand(rand); !c.IsValid(); c = randomPimpWithRand(rand) { } return } -func randomPimp() Color { +func HappyColor() (c Color) { + return HappyColorWithRand(getDefaultGlobalRand()) +} + +func randomPimpWithRand(rand RandInterface) Color { return Hcl( rand.Float64()*360.0, 0.5+rand.Float64()*0.3, diff --git a/colorgens_test.go b/colorgens_test.go index 564e7a8..5ffdea5 100644 --- a/colorgens_test.go +++ b/colorgens_test.go @@ -10,23 +10,43 @@ import ( // Check if it returns all valid colors. func TestColorValidity(t *testing.T) { + // with default seed + for i := 0; i < 100; i++ { + if col := WarmColor(); !col.IsValid() { + t.Errorf("Warm color %v is not valid! Seed was: default", col) + } + + if col := FastWarmColor(); !col.IsValid() { + t.Errorf("Fast warm color %v is not valid! Seed was: default", col) + } + + if col := HappyColor(); !col.IsValid() { + t.Errorf("Happy color %v is not valid! Seed was: default", col) + } + + if col := FastHappyColor(); !col.IsValid() { + t.Errorf("Fast happy color %v is not valid! Seed was: default", col) + } + } + + // with custom seed seed := time.Now().UTC().UnixNano() - rand.Seed(seed) + rand := rand.New(rand.NewSource(seed)) for i := 0; i < 100; i++ { - if col := WarmColor(); !col.IsValid() { + if col := WarmColorWithRand(rand); !col.IsValid() { t.Errorf("Warm color %v is not valid! Seed was: %v", col, seed) } - if col := FastWarmColor(); !col.IsValid() { + if col := FastWarmColorWithRand(rand); !col.IsValid() { t.Errorf("Fast warm color %v is not valid! Seed was: %v", col, seed) } - if col := HappyColor(); !col.IsValid() { + if col := HappyColorWithRand(rand); !col.IsValid() { t.Errorf("Happy color %v is not valid! Seed was: %v", col, seed) } - if col := FastHappyColor(); !col.IsValid() { + if col := FastHappyColorWithRand(rand); !col.IsValid() { t.Errorf("Fast happy color %v is not valid! Seed was: %v", col, seed) } } diff --git a/doc/colorgens/colorgens.go b/doc/colorgens/colorgens.go index 6171ee9..32f9443 100644 --- a/doc/colorgens/colorgens.go +++ b/doc/colorgens/colorgens.go @@ -1,27 +1,32 @@ package main -import "fmt" -import "github.com/lucasb-eyer/go-colorful" -import "image" -import "image/draw" -import "image/png" -import "math/rand" -import "os" -import "time" +import ( + "fmt" + "image" + "image/draw" + "image/png" + "math/rand" + "os" + "time" + + "github.com/lucasb-eyer/go-colorful" +) func main() { blocks := 10 blockw := 40 space := 5 - rand.Seed(time.Now().UTC().UnixNano()) + seed := time.Now().UTC().UnixNano() + + rand := rand.New(rand.NewSource(seed)) img := image.NewRGBA(image.Rect(0, 0, blocks*blockw+space*(blocks-1), 4*(blockw+space))) for i := 0; i < blocks; i++ { - warm := colorful.WarmColor() - fwarm := colorful.FastWarmColor() - happy := colorful.HappyColor() - fhappy := colorful.FastHappyColor() + warm := colorful.WarmColorWithRand(rand) + fwarm := colorful.FastWarmColorWithRand(rand) + happy := colorful.HappyColorWithRand(rand) + fhappy := colorful.FastHappyColorWithRand(rand) draw.Draw(img, image.Rect(i*(blockw+space), 0, i*(blockw+space)+blockw, blockw), &image.Uniform{warm}, image.Point{}, draw.Src) draw.Draw(img, image.Rect(i*(blockw+space), blockw+space, i*(blockw+space)+blockw, 2*blockw+space), &image.Uniform{fwarm}, image.Point{}, draw.Src) draw.Draw(img, image.Rect(i*(blockw+space), 2*blockw+3*space, i*(blockw+space)+blockw, 3*blockw+3*space), &image.Uniform{happy}, image.Point{}, draw.Src) diff --git a/doc/colorsort/colorsort.go b/doc/colorsort/colorsort.go index 664dcb8..bbb414d 100644 --- a/doc/colorsort/colorsort.go +++ b/doc/colorsort/colorsort.go @@ -17,7 +17,7 @@ import ( ) // randomColors produces a slice of random colors. -func randomColors(n int) []colorful.Color { +func randomColors(n int, rand colorful.RandInterface) []colorful.Color { cs := make([]colorful.Color, n) for i := range cs { cs[i] = colorful.Color{ @@ -49,7 +49,7 @@ func writeImage(fn string, img image.Image) { panic(err) } defer w.Close() - png.Encode(w, img) + err = png.Encode(w, img) if err != nil { panic(err) } @@ -57,8 +57,9 @@ func writeImage(fn string, img image.Image) { func main() { n := 512 - rand.Seed(8675309) - cs1 := randomColors(n) + const SEED = 8675309 + rand := rand.New(rand.NewSource(SEED)) + cs1 := randomColors(n, rand) cs2 := make([]colorful.Color, n) copy(cs2, cs1) sort.Slice(cs2, func(i, j int) bool { diff --git a/happy_palettegen.go b/happy_palettegen.go index bb66dfa..0cb9286 100644 --- a/happy_palettegen.go +++ b/happy_palettegen.go @@ -1,13 +1,9 @@ package colorful -import ( - "math/rand" -) - // Uses the HSV color space to generate colors with similar S,V but distributed // evenly along their Hue. This is fast but not always pretty. // If you've got time to spare, use Lab (the non-fast below). -func FastHappyPalette(colorsCount int) (colors []Color) { +func FastHappyPaletteWithRand(colorsCount int, rand RandInterface) (colors []Color) { colors = make([]Color, colorsCount) for i := 0; i < colorsCount; i++ { @@ -16,10 +12,18 @@ func FastHappyPalette(colorsCount int) (colors []Color) { return } -func HappyPalette(colorsCount int) ([]Color, error) { +func FastHappyPalette(colorsCount int) (colors []Color) { + return FastHappyPaletteWithRand(colorsCount, getDefaultGlobalRand()) +} + +func HappyPaletteWithRand(colorsCount int, rand RandInterface) ([]Color, error) { pimpy := func(l, a, b float64) bool { _, c, _ := LabToHcl(l, a, b) return 0.3 <= c && 0.4 <= l && l <= 0.8 } - return SoftPaletteEx(colorsCount, SoftPaletteSettings{pimpy, 50, true}) + return SoftPaletteExWithRand(colorsCount, SoftPaletteSettings{pimpy, 50, true}, rand) +} + +func HappyPalette(colorsCount int) ([]Color, error) { + return HappyPaletteWithRand(colorsCount, getDefaultGlobalRand()) } diff --git a/rand.go b/rand.go new file mode 100644 index 0000000..d3a2d5b --- /dev/null +++ b/rand.go @@ -0,0 +1,22 @@ +package colorful + +import "math/rand" + +type RandInterface interface { + Float64() float64 + Intn(n int) int +} + +type defaultGlobalRand struct{} + +func (df defaultGlobalRand) Float64() float64 { + return rand.Float64() +} + +func (df defaultGlobalRand) Intn(n int) int { + return rand.Intn(n) +} + +func getDefaultGlobalRand() RandInterface { + return defaultGlobalRand{} +} diff --git a/soft_palettegen.go b/soft_palettegen.go index 9f7bf6f..95e18b1 100644 --- a/soft_palettegen.go +++ b/soft_palettegen.go @@ -6,7 +6,6 @@ package colorful import ( "fmt" "math" - "math/rand" ) // The algorithm works in L*a*b* color space and converts to RGB in the end. @@ -32,7 +31,7 @@ type SoftPaletteSettings struct { // as a new palette of distinctive colors. Falls back to K-medoid if the mean // happens to fall outside of the color-space, which can only happen if you // specify a CheckColor function. -func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, error) { +func SoftPaletteExWithRand(colorsCount int, settings SoftPaletteSettings, rand RandInterface) ([]Color, error) { // Checks whether it's a valid RGB and also fulfills the potentially provided constraint. check := func(col lab_t) bool { @@ -148,9 +147,17 @@ func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, erro return labs2cols(means), nil } +func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, error) { + return SoftPaletteExWithRand(colorsCount, settings, getDefaultGlobalRand()) +} + // A wrapper which uses common parameters. +func SoftPaletteWithRand(colorsCount int, rand RandInterface) ([]Color, error) { + return SoftPaletteExWithRand(colorsCount, SoftPaletteSettings{nil, 50, false}, rand) +} + func SoftPalette(colorsCount int) ([]Color, error) { - return SoftPaletteEx(colorsCount, SoftPaletteSettings{nil, 50, false}) + return SoftPaletteWithRand(colorsCount, getDefaultGlobalRand()) } func in(haystack []lab_t, upto int, needle lab_t) bool { diff --git a/warm_palettegen.go b/warm_palettegen.go index 00f42a5..d294fb4 100644 --- a/warm_palettegen.go +++ b/warm_palettegen.go @@ -1,13 +1,9 @@ package colorful -import ( - "math/rand" -) - // Uses the HSV color space to generate colors with similar S,V but distributed // evenly along their Hue. This is fast but not always pretty. // If you've got time to spare, use Lab (the non-fast below). -func FastWarmPalette(colorsCount int) (colors []Color) { +func FastWarmPaletteWithRand(colorsCount int, rand RandInterface) (colors []Color) { colors = make([]Color, colorsCount) for i := 0; i < colorsCount; i++ { @@ -16,10 +12,18 @@ func FastWarmPalette(colorsCount int) (colors []Color) { return } -func WarmPalette(colorsCount int) ([]Color, error) { +func FastWarmPalette(colorsCount int) (colors []Color) { + return FastWarmPaletteWithRand(colorsCount, getDefaultGlobalRand()) +} + +func WarmPaletteWithRand(colorsCount int, rand RandInterface) ([]Color, error) { warmy := func(l, a, b float64) bool { _, c, _ := LabToHcl(l, a, b) return 0.1 <= c && c <= 0.4 && 0.2 <= l && l <= 0.5 } - return SoftPaletteEx(colorsCount, SoftPaletteSettings{warmy, 50, true}) + return SoftPaletteExWithRand(colorsCount, SoftPaletteSettings{warmy, 50, true}, rand) +} + +func WarmPalette(colorsCount int) ([]Color, error) { + return WarmPaletteWithRand(colorsCount, getDefaultGlobalRand()) }