diff --git a/README.md b/README.md index 42efac9..eee480b 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ - [Circle Loop](#circle-loop) - [Domain Warp](#domain-warp) - [Circle Noise](#circle-noise) + - [Perlin Perls](#perlin-perls) - [Color Canva](#color-canva) - [Julia Set](#julia-set) - [Black Hole](#black-hole) @@ -83,6 +84,7 @@ This package is still working in progress. More types would be added. Welcome an - Black Hole - Color Canva - Domain Warp +- Perlin Perls For these kinds of art, the package provides as many parameters to control the appearance. @@ -123,6 +125,7 @@ NewYarn(n int) NewBlackHole(circleN int, density, circleGap float64) NewColorCanve(seg float64) NewDomainWrap(scale, scale2, xOffset, yOffset float64, cmap ColorMapping) +NewPerlinPerls(circleN, dotsN, colorMin, colorMax int) ``` ## Docs @@ -460,6 +463,24 @@ func main() { ![](images/circlenoise.png) +### Perlin Perls + +```go +func main() { + rand.Seed(time.Now().Unix()) + c := generativeart.NewCanva(500, 500) + c.SetBackground(common.White) + c.SetAlpha(120) + c.SetLineWidth(0.3) + c.FillBackground() + c.SetIterations(200) + c.Draw(arts.NewPerlinPerls(10, 200, 40, 80)) + c.ToPNG("perlinperls.png") +} +``` + +![](../images/perlinperls.png) + ### Color Canva ```go diff --git a/arts/circlenoise.go b/arts/circlenoise.go index d883a4b..480c19c 100644 --- a/arts/circlenoise.go +++ b/arts/circlenoise.go @@ -18,6 +18,7 @@ type dot struct { prevx, prevy float64 theta float64 count int + cx, cy float64 } func NewCircleNoise(dotsN, colorMin, colorMax int) *circleNoise { diff --git a/arts/domainwrap.go b/arts/domainwrap.go index ae01bcb..a1196cd 100644 --- a/arts/domainwrap.go +++ b/arts/domainwrap.go @@ -12,7 +12,7 @@ type ColorMapping func(float64, float64, float64) color.RGBA type domainWrap struct { noise *common.PerlinNoise scale float64 - scale2 float64 + scale2 float64 xOffset, yOffset float64 fn ColorMapping } @@ -21,7 +21,7 @@ type domainWrap struct { func NewDomainWrap(scale, scale2, xOffset, yOffset float64, cmap ColorMapping) *domainWrap { return &domainWrap{ scale: scale, - scale2: scale2, + scale2: scale2, xOffset: xOffset, yOffset: yOffset, noise: common.NewPerlinNoise(), diff --git a/arts/perlinpearls.go b/arts/perlinpearls.go new file mode 100644 index 0000000..0af4612 --- /dev/null +++ b/arts/perlinpearls.go @@ -0,0 +1,123 @@ +package arts + +import ( + "github.com/fogleman/gg" + "github.com/jdxyw/generativeart" + "github.com/jdxyw/generativeart/common" + "math" + "math/rand" +) + +type perlinPearls struct { + circleN int + dotsN int + colorMin, colorMax int +} + +type circles struct { + x, y float64 + radius float64 + colorMin, colorMax int +} + +func NewPerlinPerls(circleN, dotsN, colorMin, colorMax int) *perlinPearls { + return &perlinPearls{ + circleN: circleN, + dotsN: dotsN, + colorMin: colorMin, + colorMax: colorMax, + } +} + +// Generative draws a circle with perlin noise. +func (pp *perlinPearls) Generative(c *generativeart.Canva) { + ctex := gg.NewContextForRGBA(c.Img()) + ctex.SetLineWidth(0.5) + ctex.SetColor(common.Black) + + cs := make([]circles, 0) + + for len(cs) < pp.circleN { + c := circles{ + x: common.RandomRangeFloat64(100, float64(c.Width())-50), + y: common.RandomRangeFloat64(100, float64(c.Height())-50), + radius: common.RandomRangeFloat64(20, 100), + colorMin: pp.colorMin, + colorMax: pp.colorMax, + } + var overlapping bool + for _, cl := range cs { + d := common.Distance(c.x, c.y, cl.x, cl.y) + if d < c.radius+cl.radius { + overlapping = true + break + } + } + + if overlapping == false { + cs = append(cs, c) + } + + } + + ds := make([][]dot, 0) + + for i := 0; i < pp.circleN; i++ { + dots := make([]dot, 0) + for j := 0; j < pp.dotsN; j++ { + theta := rand.Float64() * math.Pi * 2 + //x, y := float64(c.Width())/2+math.Sin(theta)*radius, float64(c.Height())/2+math.Cos(theta)*radius + dots = append(dots, dot{ + theta: theta, + cx: cs[i].x, + cy: cs[i].y, + x: cs[i].x + math.Sin(theta)*cs[i].radius, + y: cs[i].y + math.Cos(theta)*cs[i].radius, + prevx: cs[i].x + math.Sin(theta)*cs[i].radius, + prevy: cs[i].y + math.Cos(theta)*cs[i].radius, + count: 0, + }) + } + + ds = append(ds, dots) + } + + noise := common.NewPerlinNoise() + + for i := 0; i < pp.circleN; i++ { + ctex.SetLineWidth(0.5) + ctex.SetColor(common.Black) + ctex.DrawCircle(cs[i].x, cs[i].y, cs[i].radius) + ctex.Stroke() + + var factor = 0.008 + for j := 0; j < c.Opts().NIters(); j++ { + for k := range ds[i] { + n := noise.Noise2D(ds[i][k].x*factor, ds[i][k].y*factor) + nx, ny := math.Cos(n*math.Pi*2+float64(ds[i][k].count)*math.Pi)*2, math.Sin(n*math.Pi*2+float64(ds[i][k].count)*math.Pi)*2 + ds[i][k].prevx, ds[i][k].prevy = ds[i][k].x, ds[i][k].y + ds[i][k].x, ds[i][k].y = ds[i][k].x+nx, ds[i][k].y+ny + + hsv := common.HSV{ + H: int(common.Remap(n, 0, 1, float64(cs[i].colorMin), float64(cs[i].colorMax))), + S: 100, + V: 20, + } + + if common.Distance(cs[i].x, cs[i].y, ds[i][k].x, ds[i][k].y) > cs[i].radius+1 { + ds[i][k].count += 1 + } + + if common.Distance(cs[i].x, cs[i].y, ds[i][k].x, ds[i][k].y) < cs[i].radius && + common.Distance(cs[i].x, cs[i].y, ds[i][k].prevx, ds[i][k].prevy) < cs[i].radius { + ctex.SetLineWidth(c.Opts().LineWidth()) + rgb := hsv.ToRGB(100, 100, 100) + rgb.A = uint8(c.Opts().Alpha()) + ctex.SetColor(rgb) + ctex.DrawLine(ds[i][k].prevx, ds[i][k].prevy, ds[i][k].x, ds[i][k].y) + ctex.Stroke() + } + } + } + } +} diff --git a/common/vector.go b/common/vector.go index d814468..7b6d66a 100644 --- a/common/vector.go +++ b/common/vector.go @@ -15,3 +15,13 @@ func (v *Vector) Multiple(z float64) { v.X = v.X * z v.Y = v.Y * z } + +func (v *Vector) Normalize() { + l := Magnitude(v.X, v.Y) + v.Multiple(1 / l) +} + +func (v *Vector) SetMag(m float64) { + v.Normalize() + v.Multiple(m) +} diff --git a/docs/doc.md b/docs/doc.md index de860c8..68795fd 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -38,6 +38,8 @@ - [parameters](#parameters-16) - [Domain Warping](#domain-warping) - [parameters](#parameters-17) + - [Perlin Perls](#perlin-perls) + - [parameters](#parameters-18) ## Color Circle 2 @@ -281,4 +283,19 @@ Warping, or domain distortion is a very common technique in computer graphics fo d := arts.NewDomainWrap(0.01, 4, 8, cmap) ``` -![](../images/domainwarp.png) \ No newline at end of file +![](../images/domainwarp.png) + +## Perlin Perls + +### parameters + +- circleN: The number of circle on this image. +- dotsN: The number of dots in each circle. +- colorMin: The minimum color. +- colorMax: The maximum color. + +```go +pp := arts.NewPerlinPerls(10, 200, 40, 80 +``` + +![](../images/perlinperls.png) \ No newline at end of file diff --git a/example/example_circlenoise.go b/example/example_circlenoise.go index fb036ce..51f3964 100644 --- a/example/example_circlenoise.go +++ b/example/example_circlenoise.go @@ -12,7 +12,7 @@ func main() { rand.Seed(time.Now().Unix()) c := generativeart.NewCanva(500, 500) c.SetBackground(common.White) - c.SetAlpha(80) + c.SetAlpha(5) c.SetLineWidth(0.3) c.FillBackground() c.SetIterations(400) diff --git a/example/example_domainwrap.go b/example/example_domainwrap.go index 6b5dfda..1cbeb59 100644 --- a/example/example_domainwrap.go +++ b/example/example_domainwrap.go @@ -24,6 +24,6 @@ func main() { c := generativeart.NewCanva(500, 500) c.SetBackground(common.Black) c.FillBackground() - c.Draw(arts.NewDomainWrap(0.01, 4,4, 20, cmap)) + c.Draw(arts.NewDomainWrap(0.01, 4, 4, 20, cmap)) c.ToPNG("domainwarp.png") } diff --git a/example/example_perlinpearls.go b/example/example_perlinpearls.go new file mode 100644 index 0000000..ac3ebef --- /dev/null +++ b/example/example_perlinpearls.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/jdxyw/generativeart" + "github.com/jdxyw/generativeart/arts" + "github.com/jdxyw/generativeart/common" + "math/rand" + "time" +) + +func main() { + rand.Seed(time.Now().Unix()) + c := generativeart.NewCanva(500, 500) + c.SetBackground(common.White) + c.SetAlpha(120) + c.SetLineWidth(0.3) + c.FillBackground() + c.SetIterations(200) + c.Draw(arts.NewPerlinPerls(10, 200, 40, 80)) + c.ToPNG("perlinperls.png") +} diff --git a/images/perlinperls.png b/images/perlinperls.png new file mode 100644 index 0000000..d021ffa Binary files /dev/null and b/images/perlinperls.png differ