WIP: add solar flare

This commit is contained in:
Yongwei Xing 2021-03-15 18:53:45 +08:00
parent dfd32aa0a6
commit 4a07d63005
5 changed files with 216 additions and 0 deletions

62
common/blend.go Normal file
View file

@ -0,0 +1,62 @@
package common
import (
"image"
"image/color"
)
type BlendMode int
const (
Add BlendMode = iota
)
func Blend(src, dest *image.RGBA, mode BlendMode) *image.RGBA {
img := image.NewRGBA(src.Bounds())
for i := 0; i <src.Bounds().Max.X; i++ {
for j :=0; j<src.Bounds().Max.Y; j++ {
switch mode {
case Add:
if compareColor(src.At(i, j), Black) {
img.Set(i, j, dest.At(i, j))
} else {
img.SetRGBA(i, j, add(src.At(i, j), dest.At(i, j)))
}
}
}
}
return img
}
func compareColor(src, dst color.Color) bool {
sr, sg, sb ,sa := src.RGBA()
dr, dg, db, da := dst.RGBA()
if sr == dr && sg == dg && sb == db && sa == da {
return true
}
return false
}
func add(srcC, dstC color.Color) color.RGBA {
c := color.RGBA{}
sr, sg, sb ,sa := srcC.RGBA()
dr, dg, db, da := dstC.RGBA()
//aSrc := float64(sa)/255.0
//aDst := 1.0 - aSrc
//c.R = uint8(float64(sr) * aSrc + float64(dr)*aDst)
//c.G = uint8(float64(sg) * aSrc + float64(dg)*aDst)
//c.B = uint8(float64(sb) * aSrc + float64(db)*aDst)
//c.A = uint8(float64(sa) * aSrc + float64(da)*aDst)
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
}

View file

@ -10,6 +10,25 @@ func Constrain(val, low, high float64) float64 {
return math.Max(math.Min(val, high), low)
}
// ConstrainInt returns a int value between a minimum and maximum value.
func ConstrainInt(val, low, high int) int {
return MaxInt(MinInt(val, high), low)
}
func MinInt(a, b int)int {
if a < b {
return a
}
return b
}
func MaxInt(a, b int)int {
if a > b{
return a
}
return b
}
// 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

View file

@ -0,0 +1,51 @@
package common
import "testing"
func TestConstrain(t *testing.T) {
type args struct {
val float64
low float64
high float64
}
tests := []struct {
name string
args args
want float64
}{
{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: "testcase3", args: args{val: -1.0, low:-3.5, high: 1.5}, want: -1.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Constrain(tt.args.val, tt.args.low, tt.args.high); got != tt.want {
t.Errorf("Constrain() = %v, want %v", got, tt.want)
}
})
}
}
func TestConstrainInt(t *testing.T) {
type args struct {
val int
low int
high int
}
tests := []struct {
name string
args args
want int
}{
{name: "testcase1", args: args{val: 256, low: 0, high: 255}, want: 255},
{name: "testcase2", args: args{val: -1, low: 0, high: 255}, want: 0},
{name: "testcase3", args: args{val: 100, low: 0, high: 255}, want: 100},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ConstrainInt(tt.args.val, tt.args.low, tt.args.high); got != tt.want {
t.Errorf("ConstrainInt() = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -0,0 +1,19 @@
package main
import (
"github.com/jdxyw/generativeart"
"github.com/jdxyw/generativeart/common"
"image/color"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
c := generativeart.NewCanva(500, 500)
c.SetBackground(common.Black)
c.FillBackground()
c.SetLineColor(color.RGBA{255, 64, 8, 128})
c.Draw(generativeart.NewSolarFlare())
c.ToPNG("solarflare.png")
}

65
solarflare.go Normal file
View file

@ -0,0 +1,65 @@
package generativeart
import (
"github.com/fogleman/gg"
"github.com/jdxyw/generativeart/common"
"image"
"image/color"
"image/draw"
"math"
)
type solarFlare struct {
}
func NewSolarFlare() *solarFlare {
return &solarFlare{}
}
// Generative draws a solar flare images.
func (o *solarFlare) Generative(c *canva) {
var xOffset, yOffset float64
var offsetInc = 0.006
var inc = 1.0
var r = 1.0
var m = 1.005
noise := common.NewPerlinNoise()
for r < 200 {
for i :=0; i <10; i++ {
nPoints := int(2*math.Pi*r)
nPoints = common.MinInt(nPoints, 500)
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)
ctex := gg.NewContextForRGBA(img)
ctex.Push()
ctex.Translate(float64(c.width/2), float64(c.height/2))
ctex.SetLineWidth(1.0)
ctex.SetColor(c.opts.lineColor)
for j :=0.0; j<float64(nPoints+1); j+=1.0 {
a := j/float64(nPoints) * math.Pi * 2
px := math.Cos(a)
py := math.Sin(a)
n := noise.Noise2D(xOffset + px * inc, yOffset + py * inc)*r
px *= n
py *= n
ctex.LineTo(px, py)
}
ctex.Stroke()
ctex.Pop()
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
yOffset += offsetInc
r*=m
}
}
}