Skip to content

Commit

Permalink
implement functions with custom rand handler
Browse files Browse the repository at this point in the history
  • Loading branch information
lublak committed Oct 24, 2024
1 parent e144b2c commit 8ac5283
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 51 deletions.
36 changes: 24 additions & 12 deletions colorgens.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,64 @@

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,
0.2+rand.Float64()*0.3)
}

// 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,
Expand Down
30 changes: 25 additions & 5 deletions colorgens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
31 changes: 18 additions & 13 deletions doc/colorgens/colorgens.go
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
9 changes: 5 additions & 4 deletions doc/colorsort/colorsort.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -49,16 +49,17 @@ 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)
}
}

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 {
Expand Down
18 changes: 11 additions & 7 deletions happy_palettegen.go
Original file line number Diff line number Diff line change
@@ -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++ {
Expand All @@ -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())
}
22 changes: 22 additions & 0 deletions rand.go
Original file line number Diff line number Diff line change
@@ -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{}
}
13 changes: 10 additions & 3 deletions soft_palettegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
18 changes: 11 additions & 7 deletions warm_palettegen.go
Original file line number Diff line number Diff line change
@@ -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++ {
Expand All @@ -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())
}

0 comments on commit 8ac5283

Please sign in to comment.