Skip to content

Instantly share code, notes, and snippets.

@phrozen
Created March 28, 2012 23:51
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save phrozen/2231611 to your computer and use it in GitHub Desktop.
Save phrozen/2231611 to your computer and use it in GitHub Desktop.
Peter De Jong Attractors in Go
/*
Processing version
http://www.openprocessing.org/sketch/2097
by Thor Frølich
Go version
by Guillermo Estrada
*/
package main
import (
"fmt"
"image"
"image/color"
"image/jpeg"
"math"
"math/rand"
"os"
)
const (
half = SIZE / 2
scale = (SIZE / 4) - 1
)
func interpolate(value, low1, high1, low2, high2 float64) float64 {
return low2 + (high2-low2)*((value-low1)/(high1-low1))
}
func conv(x float64) uint8 {
if x < 0 {
return 0
}
if x > 1 {
return 255
}
return uint8(int(x*255 + 0.5))
}
// HSVToRGB converts an HSV triple to a RGB triple.
//
// Ported from http://goo.gl/Vg1h9
// Obtained from Gorilla Toolkit
// http://code.google.com/p/gorilla/source/browse/color/hsv.go
func HSVToRGB(h, s, v float64) color.NRGBA {
var fR, fG, fB float64
i := math.Floor(h * 6)
f := h*6 - i
p := v * (1.0 - s)
q := v * (1.0 - f*s)
t := v * (1.0 - (1.0-f)*s)
switch int(i) % 6 {
case 0:
fR, fG, fB = v, t, p
case 1:
fR, fG, fB = q, v, p
case 2:
fR, fG, fB = p, v, t
case 3:
fR, fG, fB = p, q, v
case 4:
fR, fG, fB = t, p, v
case 5:
fR, fG, fB = v, p, q
}
r, g, b := conv(fR), conv(fG), conv(fB)
return color.NRGBA{r, g, b, 255}
}
func SoftLight(c1, c2 uint8) uint8 {
v1, v2 := float64(c1), float64(c2)
if v1 > 127.5 {
return uint8(v2 + (255-v2)*((v1-127.5)/127.5)*(0.5-math.Abs(v2-127.5)/255))
}
return uint8(v2 - v2*((127.5-v1)/127.5)*(0.5-math.Abs(v2-127.5)/255))
}
func Blend(low, high color.NRGBA) color.NRGBA {
r := SoftLight(high.R, low.R)
g := SoftLight(high.G, low.G)
b := SoftLight(high.B, low.B)
return color.NRGBA{r, g, b, 255}
}
type DeJong struct {
img *image.NRGBA
a, b, c, d float64
x0, y0, x, y float64
maxdense int
logmaxd float64
density [SIZE][SIZE]int
previousx [SIZE][SIZE]float64
}
func NewDeJong(a, b, c, d float64) *DeJong {
dj := new(DeJong)
dj.img = image.NewNRGBA(image.Rect(0, 0, SIZE, SIZE))
dj.maxdense = 0
dj.a, dj.b, dj.c, dj.d = a, b, c, d
return dj
}
func (dj *DeJong) clear() {
for i := 0; i < SIZE; i++ {
for j := 0; j < SIZE; j++ {
dj.density[i][j] = 0
dj.previousx[i][j] = 0
}
}
}
func (dj *DeJong) populate(samples int, clear bool) {
// Clear attractor
if clear {
dj.clear()
}
for j := 0; j < samples; j++ {
for i := 0; i < 10000; i++ {
dj.x = math.Sin(dj.a*dj.y0) - math.Cos(dj.b*dj.x0)
dj.y = math.Sin(dj.c*dj.x0) - math.Cos(dj.d*dj.y0)
x := half + (dj.x * scale)
y := half + (dj.y * scale)
// Smoothie
if BLUR > 0.0 {
dj.x += (rand.Float64()*2 - 1) * BLUR
dj.y += (rand.Float64()*2 - 1) * BLUR
}
dj.previousx[int(x)][int(y)] = dj.x0
dj.density[int(x)][int(y)] += 1
dj.x0, dj.y0 = dj.x, dj.y
}
}
// Put maximum density and its log()-value into variables
for i := 0; i < SIZE; i++ {
for j := 0; j < SIZE; j++ {
if dj.density[i][j] > dj.maxdense {
dj.maxdense = dj.density[i][j]
dj.logmaxd = math.Log(float64(dj.maxdense))
}
}
}
}
// Sets the actual colors to the image using Soft Light Blend Mode witth the previous call to plot.
func (dj *DeJong) plot(factor float64, clear bool) *image.NRGBA {
if clear {
for i := 0; i < SIZE; i++ {
for j := 0; j < SIZE; j++ {
dj.img.SetNRGBA(i, j, color.NRGBA{0, 0, 0, 255})
}
}
}
for i := 0; i < SIZE; i++ {
for j := 0; j < SIZE; j++ {
if dj.density[i][j] > 0 {
// Use the previous x to generate the current X for the Hue
hue := interpolate(dj.previousx[i][j], -2.0, 2.0, HUE_LOW, HUE_HIGH)
// Use the Logarithmic density of the coordinate for saturation and brightness
sat := interpolate(math.Log(float64(dj.density[i][j])), 0, dj.logmaxd, SAT_HIGH, 0)
bright := interpolate(math.Log(float64(dj.density[i][j])), 0, dj.logmaxd, 0, 1.0) + factor
newc := HSVToRGB(hue, sat, bright)
r, g, b, _ := dj.img.At(i, j).RGBA()
oldc := color.NRGBA{uint8((r >> 8)), uint8((g >> 8)), uint8((b >> 8)), 255}
newc = Blend(newc, oldc)
dj.img.SetNRGBA(i, j, newc)
}
}
}
return dj.img
}
func (dj *DeJong) incrementalupdate() {
dj.populate(20, false)
dj.plot(0, false)
}
const (
SIZE = 4096 // Size of the image in pixels
ITER = 200 // Number of iterations * 200000
BLUR = 0.001 // Amount of BLUR random noise
QUALITY = 85 // JPEG encoding quality [0-100]
HUE_LOW = 0.0 // Start value of the hue [0.0-1.0]
HUE_HIGH = 1.0 // End value of the hue [0.0-1.0]
SAT_HIGH = 0.5 // Top value of the saturation [0.0-1.0]
)
func main() {
a, b, c, d := 1.35, 2.0, 2.0, 1.35
//a, b, c, d := 1.4, -2.2, 2.4, -2.1
fmt.Println("Calculating deJong Attractors...")
attr := NewDeJong(a, b, c, d)
attr.populate(1, true)
attr.plot(100, true)
fmt.Printf("Iteration (%v): ", ITER)
for i := 0; i < ITER; i++ {
fmt.Printf("%v ", i)
attr.incrementalupdate()
}
fmt.Println(" DONE!")
fmt.Printf("Max density: %v\n", attr.maxdense)
filename := fmt.Sprintf("dejong_[%v,%v,%v,%v]_i%v_b(%v)_q%v_.jpg", a, b, c, d, ITER, BLUR, QUALITY)
output, err := os.Create("./img/" + filename)
if err != nil {
panic(err)
}
if err = jpeg.Encode(output, attr.img, &jpeg.Options{QUALITY}); err != nil {
panic(err)
}
err = output.Close()
if err != nil {
panic(err)
}
fmt.Printf("Saved: %v", filename)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment