add circle move

This commit is contained in:
Yongwei Xing 2021-03-16 11:33:09 +08:00
parent 4a07d63005
commit 27fac585da
11 changed files with 151 additions and 55 deletions

View file

@ -37,6 +37,7 @@ This package is still working in progress. More types would be added. Welcome an
- Circle Loop2 - Circle Loop2
- Pixel Hole - Pixel Hole
- Dots Wave - Dots Wave
- Circle Move
For these kinds of art, the package provides as many parameters to control the appearance. For these kinds of art, the package provides as many parameters to control the appearance.
@ -71,6 +72,7 @@ NewNoiseLine(n int)
NewCircleLoop2(depth int) NewCircleLoop2(depth int)
NewPixelHole(dotN int) NewPixelHole(dotN int)
NewDotsWave(dotsN int) NewDotsWave(dotsN int)
NewCircleMove(circleNum int)
``` ```
## Docs ## Docs
@ -419,22 +421,20 @@ func main() {
![](images/silksky.png) ![](images/silksky.png)
### Maze ### Circle Move
```go ```go
func main() { func main() {
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
c := generativeart.NewCanva(600, 600) c := generativeart.NewCanva(1200, 500)
c.SetBackground(generativeart.Azure) c.SetBackground(common.White)
c.SetLineWidth(3)
c.SetLineColor(generativeart.Orange)
c.FillBackground() c.FillBackground()
c.Draw(generativeart.NewMaze(20)) c.Draw(generativeart.NewCircleMove(1000))
c.ToPNG("maze.png") c.ToPNG("circlemove.png")
} }
``` ```
![](images/maze.png) ![](images/circlemove.png)
### Random Circle ### Random Circle

60
circlemove.go Normal file
View file

@ -0,0 +1,60 @@
package generativeart
import (
"github.com/fogleman/gg"
"github.com/jdxyw/generativeart/common"
"math"
"math/rand"
)
type circleMove struct {
circleNum int
}
func NewCircleMove(circleNum int) *circleMove {
return &circleMove{
circleNum: circleNum,
}
}
// Generative draws a sircle moving images.
func (cm *circleMove) Generative(c *canva) {
ctex := gg.NewContextForRGBA(c.img)
ctex.SetLineWidth(0.3)
noise := common.NewPerlinNoise()
cl := rand.Intn(255)
for i := 0; i < cm.circleNum; i++ {
//var sx, sy float64
var cxx float64
var np = 300.0
for j := 0.0; j < np; j += 1.0 {
theta := common.Remap(j, 0, np, 0, math.Pi*2)
cx := float64(i)*3 - 200.0
cy := float64(c.height)/2 + math.Sin(float64(i)/50)*float64(c.height)/12.0
xx := math.Cos(theta+cx/10)*float64(c.height)/6.0
yy := math.Sin(theta+cx/10)*float64(c.height)/6.0
p := common.NewVector(xx, yy)
xx = (xx + cx) / 150
yy = (yy + cy) / 150
p.Multiple(1 + 1.5*noise.Noise2D(xx, yy))
ctex.LineTo(cx+p.X, cy+p.Y)
cxx = cx
}
hue := int(cxx/4) - cl
if hue < 0 {
hue += 255
}
h := common.HSV{
H: hue,
S: 180,
V: 120,
}
rgba := h.ToRGB(255, 255, 255)
rgba.A = 255
ctex.SetColor(rgba)
ctex.Stroke()
ctex.ClosePath()
}
}

View file

@ -14,14 +14,14 @@ const (
func Blend(src, dest *image.RGBA, mode BlendMode) *image.RGBA { func Blend(src, dest *image.RGBA, mode BlendMode) *image.RGBA {
img := image.NewRGBA(src.Bounds()) img := image.NewRGBA(src.Bounds())
for i := 0; i <src.Bounds().Max.X; i++ { for i := 0; i < src.Bounds().Max.X; i++ {
for j :=0; j<src.Bounds().Max.Y; j++ { for j := 0; j < src.Bounds().Max.Y; j++ {
switch mode { switch mode {
case Add: case Add:
if compareColor(src.At(i, j), Black) { if compareColor(src.RGBAAt(i, j), Black) {
img.Set(i, j, dest.At(i, j)) img.Set(i, j, dest.RGBAAt(i, j))
} else { } else {
img.SetRGBA(i, j, add(src.At(i, j), dest.At(i, j))) img.SetRGBA(i, j, add(src.RGBAAt(i, j), dest.RGBAAt(i, j)))
} }
} }
} }
@ -29,9 +29,9 @@ func Blend(src, dest *image.RGBA, mode BlendMode) *image.RGBA {
return img return img
} }
func compareColor(src, dst color.Color) bool { func compareColor(src, dst color.RGBA) bool {
sr, sg, sb ,sa := src.RGBA() sr, sg, sb, sa := src.R, src.G, src.B, src.A
dr, dg, db, da := dst.RGBA() dr, dg, db, da := dst.R, dst.G, dst.B, dst.A
if sr == dr && sg == dg && sb == db && sa == da { if sr == dr && sg == dg && sb == db && sa == da {
return true return true
@ -39,24 +39,18 @@ func compareColor(src, dst color.Color) bool {
return false return false
} }
func add(srcC, dstC color.Color) color.RGBA { func add(srcC, dstC color.RGBA) color.RGBA {
c := color.RGBA{} c := color.RGBA{}
sr, sg, sb ,sa := srcC.RGBA() sr, sg, sb, sa := srcC.R, srcC.G, srcC.B, srcC.A
dr, dg, db, da := dstC.RGBA() dr, dg, db, da := dstC.R, dstC.G, dstC.B, dstC.A
//aSrc := float64(sa)/255.0 aSrc := float64(sa) / 255.0
//aDst := 1.0 - aSrc aDst := 1.0 - aSrc
//c.R = uint8(float64(sr) * aSrc + float64(dr)*aDst) c.R = uint8(ConstrainInt(int(float64(sr)*aSrc+float64(dr)*aDst), 0, 255))
//c.G = uint8(float64(sg) * aSrc + float64(dg)*aDst) c.G = uint8(ConstrainInt(int(float64(sg)*aSrc+float64(dg)*aDst), 0, 255))
//c.B = uint8(float64(sb) * aSrc + float64(db)*aDst) c.B = uint8(ConstrainInt(int(float64(sb)*aSrc+float64(db)*aDst), 0, 255))
//c.A = uint8(float64(sa) * aSrc + float64(da)*aDst) c.A = uint8(ConstrainInt(int(float64(sa)*aSrc+float64(da)*aDst), 0, 255))
c.R = uint8(ConstrainInt(int(uint32(float64(sr)*0.05)+dr), 0, 255))
c.G = uint8(ConstrainInt(int(uint32(float64(sg)*0.05)+dg), 0, 255))
c.B = uint8(ConstrainInt(int(uint32(float64(sb)*0.05)+db), 0, 255))
c.A = uint8(ConstrainInt(int(uint32(float64(sa)*0.9)+da), 0, 255))
//c.A = uint8(float64(sa) * aSrc + float64(da)*aDst)
return c return c
} }

View file

@ -15,15 +15,15 @@ func ConstrainInt(val, low, high int) int {
return MaxInt(MinInt(val, high), low) return MaxInt(MinInt(val, high), low)
} }
func MinInt(a, b int)int { func MinInt(a, b int) int {
if a < b { if a < b {
return a return a
} }
return b return b
} }
func MaxInt(a, b int)int { func MaxInt(a, b int) int {
if a > b{ if a > b {
return a return a
} }
return b return b

View file

@ -13,9 +13,9 @@ func TestConstrain(t *testing.T) {
args args args args
want float64 want float64
}{ }{
{name: "testcase1", args: args{val: 1.0, low:0.5, high: 1.5}, want: 1.0}, {name: "testcase1", args: args{val: 1.0, low: 0.5, high: 1.5}, want: 1.0},
{name: "testcase2", args: args{val: 0.4, low:0.5, high: 1.5}, want: 0.5}, {name: "testcase2", args: args{val: 0.4, low: 0.5, high: 1.5}, want: 0.5},
{name: "testcase3", args: args{val: -1.0, low:-3.5, high: 1.5}, want: -1.0}, {name: "testcase3", args: args{val: -1.0, low: -3.5, high: 1.5}, want: -1.0},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -48,4 +48,4 @@ func TestConstrainInt(t *testing.T) {
} }
}) })
} }
} }

17
common/vector.go Normal file
View file

@ -0,0 +1,17 @@
package common
type Vector struct {
X, Y float64
}
func NewVector(x, y float64) *Vector {
return &Vector{
X: x,
Y: y,
}
}
func (v *Vector) Multiple(z float64) {
v.X = v.X * z
v.Y = v.Y * z
}

View file

@ -162,4 +162,17 @@ ph := generativeart.NewPixelHole(60)
d := generativeart.NewDotsWave(300) d := generativeart.NewDotsWave(300)
``` ```
![](../images/dotswave.png) ![](../images/dotswave.png)
## Circle Move
### parameters
- circleNum: The number of the circle in the image.
```go
cm := generativeart.NewCircleMove(1000)
```
![](../images/circlemove.png)

View file

@ -0,0 +1,17 @@
package main
import (
"github.com/jdxyw/generativeart"
"github.com/jdxyw/generativeart/common"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
c := generativeart.NewCanva(1200, 500)
c.SetBackground(common.White)
c.FillBackground()
c.Draw(generativeart.NewCircleMove(1000))
c.ToPNG("circlemove.png")
}

View file

@ -31,11 +31,11 @@ type Options struct {
} }
// NewCanva returns a canva. // NewCanva returns a canva.
func NewCanva(h, w int) *canva { func NewCanva(w, h int) *canva {
return &canva{ return &canva{
height: h, height: h,
width: w, width: w,
img: image.NewRGBA(image.Rect(0, 0, h, w)), img: image.NewRGBA(image.Rect(0, 0, w, h)),
// Set some defaults value // Set some defaults value
opts: Options{ opts: Options{
background: common.Azure, background: common.Azure,

BIN
images/circlemove.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

View file

@ -16,7 +16,6 @@ func NewSolarFlare() *solarFlare {
return &solarFlare{} return &solarFlare{}
} }
// Generative draws a solar flare images. // Generative draws a solar flare images.
func (o *solarFlare) Generative(c *canva) { func (o *solarFlare) Generative(c *canva) {
var xOffset, yOffset float64 var xOffset, yOffset float64
@ -26,14 +25,13 @@ func (o *solarFlare) Generative(c *canva) {
var m = 1.005 var m = 1.005
noise := common.NewPerlinNoise() noise := common.NewPerlinNoise()
for r < 200 { for r < 200 {
for i :=0; i <10; i++ { for i := 0; i < 10; i++ {
nPoints := int(2*math.Pi*r) nPoints := int(2 * math.Pi * r)
nPoints = common.MinInt(nPoints, 500) nPoints = common.MinInt(nPoints, 500)
img := image.NewRGBA(image.Rect(0, 0, c.width, c.height)) img := image.NewRGBA(image.Rect(0, 0, c.width, c.height))
draw.Draw(img, img.Bounds(), &image.Uniform{color.RGBA{0,0,0,255}}, image.ZP, draw.Src) draw.Draw(img, img.Bounds(), &image.Uniform{color.Black}, image.ZP, draw.Src)
ctex := gg.NewContextForRGBA(img) ctex := gg.NewContextForRGBA(img)
ctex.Push() ctex.Push()
@ -41,11 +39,11 @@ func (o *solarFlare) Generative(c *canva) {
ctex.SetLineWidth(1.0) ctex.SetLineWidth(1.0)
ctex.SetColor(c.opts.lineColor) ctex.SetColor(c.opts.lineColor)
for j :=0.0; j<float64(nPoints+1); j+=1.0 { for j := 0.0; j < float64(nPoints+1); j += 1.0 {
a := j/float64(nPoints) * math.Pi * 2 a := j / float64(nPoints) * math.Pi * 2
px := math.Cos(a) px := math.Cos(a)
py := math.Sin(a) py := math.Sin(a)
n := noise.Noise2D(xOffset + px * inc, yOffset + py * inc)*r n := noise.Noise2D(xOffset+px*inc, yOffset+py*inc) * r
px *= n px *= n
py *= n py *= n
ctex.LineTo(px, py) ctex.LineTo(px, py)
@ -54,12 +52,9 @@ func (o *solarFlare) Generative(c *canva) {
ctex.Pop() ctex.Pop()
c.img = common.Blend(img, c.img, common.Add) c.img = common.Blend(img, c.img, common.Add)
//cc := NewCanva(0,0)
//cc.img = c.img
//cc.ToPNG(fmt.Sprintf("xxxx%v.png", r))
xOffset += offsetInc xOffset += offsetInc
yOffset += offsetInc yOffset += offsetInc
r*=m r *= m
} }
} }
} }