diff --git a/README.md b/README.md index 616639e..9e44ee2 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ This package is still working in progress. More types would be added. Welcome an - Contour Line - Noise Line - Ocean Fish +- Circle Loop2 For these kinds of art, the package provides as many parameters to control the appearance. @@ -66,6 +67,7 @@ NewColorCircle2(circleNum int) NewCircleGrid(circleNumMin, circleNumMax int) NewContourLine(lineNum int) NewNoiseLine(n int) +NewCircleLoop2(depth int) ``` ## Docs @@ -181,6 +183,29 @@ func main() { ![](images/circlegrid.png) +## Circle Composes Circle + +```go +func main() { + rand.Seed(time.Now().Unix()) + colors := []color.RGBA{ + {0xF9, 0xC8, 0x0E, 0xFF}, + {0xF8, 0x66, 0x24, 0xFF}, + {0xEA, 0x35, 0x46, 0xFF}, + {0x66, 0x2E, 0x9B, 0xFF}, + {0x43, 0xBC, 0xCD, 0xFF}, + } + c := generativeart.NewCanva(500, 500) + c.SetBackground(color.RGBA{8, 10, 20, 255}) + c.FillBackground() + c.SetColorSchema(colors) + c.Draw(generativeart.NewCircleLoop2(7)) + c.ToPNG("colorloop2.png") +} +``` + +![](images/colorloop2.png) + ### Silk Smoke ```go @@ -390,7 +415,7 @@ func main() { ## Contribution -Thanks for the following sites and repos, I got lots of ideas, inspiration, code and tricks from them. The list would be very long, sorry for forgetting some of them. +Thanks for the following sites and repos, I got lots of ideas, inspiration, code, and tricks from them. The list would be very long; sorry for forgetting some of them. - https://inconvergent.net/ - https://fronkonstin.com/ @@ -406,4 +431,5 @@ Thanks for the following sites and repos, I got lots of ideas, inspiration, code - https://openprocessing.org/sketch/1071233 - https://twitter.com/okazz_ - https://openprocessing.org/sketch/738638 -- https://openprocessing.org/sketch/1102157 \ No newline at end of file +- https://openprocessing.org/sketch/1102157 +- https://openprocessing.org/sketch/1071233 \ No newline at end of file diff --git a/circleloop2.go b/circleloop2.go new file mode 100644 index 0000000..87bd1fb --- /dev/null +++ b/circleloop2.go @@ -0,0 +1,80 @@ +package generativeart + +import ( + "github.com/fogleman/gg" + "github.com/jdxyw/generativeart/common" + "math" + "math/rand" +) + +type circleLoop2 struct { + depth int + noise *common.PerlinNoise +} + +func NewCircleLoop2(depth int) *circleLoop2 { + return &circleLoop2{ + depth: depth, + noise: common.NewPerlinNoise(), + } +} + +// Generative draws a circle composed by many colored circles. +func (cl *circleLoop2) Generative(c *canva) { + ctex := gg.NewContextForRGBA(c.img) + ctex.Translate(float64(c.width)/2, float64(c.height)/2) + cl.recursionDraw(ctex, c, float64(c.width), cl.depth) +} + +func (cl *circleLoop2) recursionDraw(ctex *gg.Context, c *canva, x float64, depth int) { + if depth <= 0 { + return + } + + cl.draw(ctex, c, x) + cl.recursionDraw(ctex, c, 1*x/4.0, depth-1) + cl.recursionDraw(ctex, c, 2*x/4.0, depth-1) + cl.recursionDraw(ctex, c, 3*x/4.0, depth-1) +} + +func (cl *circleLoop2) draw(ctex *gg.Context, c *canva, x float64) { + var lw float64 + if rand.Float64() < 0.8 { + lw = 1 + } else { + lw = common.RandomRangeFloat64(1.0, common.RandomRangeFloat64(1, 3)) + } + ctex.SetLineWidth(lw) + + noise := cl.noise.Noise3D(x*0.02+123.234, (1-x)*0.02, 345.4123) + noise = math.Pow(noise, 0.5) + a2 := common.Remap(noise, 0.15, 0.85, 0.1, 0.6) + + px := math.Pow(x/float64(c.height), a2) * float64(c.height) + py := math.Pow(1-x/float64(c.height), a2)*float64(c.height) - + common.RandomRangeFloat64(0, common.RandomRangeFloat64(float64(c.height)*0.18, common.RandomRangeFloat64(float64(c.height)*0.18, float64(c.height)*0.7))) + + cls := c.opts.colorSchema[rand.Intn(len(c.opts.colorSchema))] + ctex.SetColor(cls) + nCircles := common.RandomRangeInt(1, 6) + if rand.Float64() < 0.03 { + nCircles = common.RandomRangeInt(8, 10) + } + + r := math.Pow(rand.Float64(), 2) * 50 + + var flag bool + if rand.Float64() < 0.7 { + flag = true + } + + for i := 0; i < nCircles; i++ { + if flag { + ctex.DrawCircle(px*0.39, py*0.39, rand.Float64()*float64(i)*r/float64(nCircles)) + } else { + ctex.DrawCircle(px*0.39, py*0.39, float64(i)*r/float64(nCircles)) + } + ctex.Stroke() + } + ctex.Rotate(x / float64(c.height) * 0.2) +} diff --git a/common/calculation.go b/common/calculation.go new file mode 100644 index 0000000..3153364 --- /dev/null +++ b/common/calculation.go @@ -0,0 +1,19 @@ +package common + +import "math" + +// Constrain returns a value between a minimum and maximum value. +func Constrain(val, low, high float64) float64 { + return math.Max(math.Min(val, high), low) +} + +// Remap re-maps a number from one range to another. +func Remap(n, start1, stop1, start2, stop2 float64) float64 { + newval := (n-start1)/(stop1-start1)*(stop2-start2) + start2 + + if start2 < stop2 { + return Constrain(newval, start2, stop2) + } else { + return Constrain(newval, stop2, start2) + } +} diff --git a/common/perlinnoise.go b/common/perlinnoise.go index 093244a..f177f13 100644 --- a/common/perlinnoise.go +++ b/common/perlinnoise.go @@ -15,12 +15,12 @@ const ( perlinAmpFalloff = 0.5 ) -type perlinNoise struct { +type PerlinNoise struct { perlin []float64 } -func NewPerlinNoise() *perlinNoise { - perlin := &perlinNoise{perlin: nil} +func NewPerlinNoise() *PerlinNoise { + perlin := &PerlinNoise{perlin: nil} perlin.perlin = make([]float64, perlinSize+1) for i, _ := range perlin.perlin { @@ -29,7 +29,19 @@ func NewPerlinNoise() *perlinNoise { return perlin } -func (p *perlinNoise) Noise(x, y, z float64) float64 { +func (p *PerlinNoise) Noise1D(x float64) float64 { + return p.noise(x, 0, 0) +} + +func (p *PerlinNoise) Noise2D(x, y float64) float64 { + return p.noise(x, y, 0) +} + +func (p *PerlinNoise) Noise3D(x, y, z float64) float64 { + return p.noise(x, y, z) +} + +func (p *PerlinNoise) noise(x, y, z float64) float64 { if x < 0 { x = -x } diff --git a/contourline.go b/contourline.go index 7b5359c..42e115b 100644 --- a/contourline.go +++ b/contourline.go @@ -29,7 +29,7 @@ func (cl *contourLine) Generative(c *canva) { for j := 0; j < 1500; j++ { - theta := noise.Noise(x/800.0, y/800.0, 0) * math.Pi * 2 * 800 + theta := noise.Noise2D(x/800.0, y/800.0) * math.Pi * 2 * 800 x += math.Cos(theta) * 0.4 y += math.Sin(theta) * 0.4 diff --git a/docs/doc.md b/docs/doc.md index 2ed2d83..1371d18 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -111,7 +111,7 @@ nl := generativeart.NewNoiseLine(1000) ## Ocean Fish -`Ocean Fish` draws a ocean and some fishes in the center. +`Ocean Fish` draws an ocean and some fishes in the center. ### parameters @@ -122,4 +122,18 @@ nl := generativeart.NewNoiseLine(1000) o := generativeart.NewOceanFish(100, 8) ``` -![](../images/oceanfish.png) \ No newline at end of file +![](../images/oceanfish.png) + +## Circle Loop2 + +`Circle loop2` draws a circle composed by colored circles. + +### parameters + +- depth: Control the number of circles. + +```go +cl := generativeart.NewCircleLoop2(7) +``` + +![](../images/colorloop2.png) \ No newline at end of file diff --git a/example/example_circleloop2.go b/example/example_circleloop2.go new file mode 100644 index 0000000..e0dfedf --- /dev/null +++ b/example/example_circleloop2.go @@ -0,0 +1,25 @@ +package main + +import ( + "github.com/jdxyw/generativeart" + "image/color" + "math/rand" + "time" +) + +func main() { + rand.Seed(time.Now().Unix()) + colors := []color.RGBA{ + {0xF9, 0xC8, 0x0E, 0xFF}, + {0xF8, 0x66, 0x24, 0xFF}, + {0xEA, 0x35, 0x46, 0xFF}, + {0x66, 0x2E, 0x9B, 0xFF}, + {0x43, 0xBC, 0xCD, 0xFF}, + } + c := generativeart.NewCanva(500, 500) + c.SetBackground(color.RGBA{8, 10, 20, 255}) + c.FillBackground() + c.SetColorSchema(colors) + c.Draw(generativeart.NewCircleLoop2(7)) + c.ToPNG("colorloop2.png") +} diff --git a/images/colorloop2.png b/images/colorloop2.png new file mode 100644 index 0000000..ed1583f Binary files /dev/null and b/images/colorloop2.png differ diff --git a/noiseline.go b/noiseline.go index bdb9e04..ac1b1e8 100644 --- a/noiseline.go +++ b/noiseline.go @@ -45,7 +45,7 @@ func (nl *noiseLine) Generative(c *canva) { for j := 0; j < l; j++ { var ns = 0.0005 w := math.Sin(math.Pi*float64(j)/float64(l-1)) * 5 - theta := noise.Noise(x*ns, y*ns, t) * 100 + theta := noise.Noise3D(x*ns, y*ns, t) * 100 ctex.SetColor(cl) ctex.DrawCircle(x, y, w) ctex.Fill()