-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfractal.go
140 lines (124 loc) · 3.5 KB
/
fractal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package texture
import (
g2d "github.com/jphsd/graphics2d"
"math"
)
// Fractal holds the pieces necessary for fractal generation. Xfm defines the affine transformation
// applied successively to the coordinate space and Comb, how the multiple resultant values should be combined.
// Bands can also be manipulated through the weight values (typically [0, 1]) which allows certain frequencies
// to be attenuated.
type Fractal struct {
Name string
Src Field
Xfm *g2d.Aff3
Comb OctaveCombiner
Octaves float64
Weights []float64
}
// NewFractal returns a new Fractal instance.
func NewFractal(src Field, xfm *g2d.Aff3, comb OctaveCombiner, octaves float64) *Fractal {
oct := int(octaves)
oct++
w := make([]float64, oct)
for i := 0; i < oct; i++ {
w[i] = 1
}
return &Fractal{"Fractal", src, xfm, comb, octaves, w}
}
// Eval2 implements the Field interface.
func (f *Fractal) Eval2(x, y float64) float64 {
n := int(f.Octaves)
r := f.Octaves - float64(n)
n++
nv := make([]float64, n)
for i := 0; i < n; i++ {
nv[i] = f.Src.Eval2(x, y) * f.Weights[i]
pt := f.Xfm.Apply([]float64{x, y})
x, y = pt[0][0], pt[0][1]
}
nv[n-1] *= r
return clamp(f.Comb.Combine(nv...))
}
type VariableFractal struct {
Name string
Src Field
Xfm *g2d.Aff3
Comb OctaveCombiner
OctSrc Field
Scale float64
Weights []float64
}
// NewFractal returns a new Fractal instance.
func NewVariableFractal(src Field, xfm *g2d.Aff3, comb OctaveCombiner, octsrc Field, scale float64) *VariableFractal {
n := int(scale)
w := make([]float64, n)
for i := 0; i < n; i++ {
w[i] = 1
}
return &VariableFractal{"VariableFractal", src, xfm, comb, octsrc, scale / 2, w}
}
// Eval2 implements the Field interface.
func (f *VariableFractal) Eval2(x, y float64) float64 {
oct := (f.OctSrc.Eval2(x, y) + 1) * f.Scale
n := int(oct)
r := oct - float64(n)
n++
nv := make([]float64, n)
for i := 0; i < n; i++ {
nv[i] = f.Src.Eval2(x, y)
pt := f.Xfm.Apply([]float64{x, y})
x, y = pt[0][0], pt[0][1]
}
nv[n-1] *= r
return clamp(f.Comb.Combine(nv...))
}
type OctaveCombiner interface {
Combine(...float64) float64
}
// FBM holds the precomputed weights for an fBM.
type FBM struct {
Name string
Weights []float64
}
// NewFBM returns a new FBM instance based on the Hurst and Lacunarity parameters.
func NewFBM(hurst, lacunarity float64, maxoct int) *FBM {
maxoct++
w := make([]float64, maxoct)
for i := 0; i < maxoct; i++ {
w[i] = math.Pow(lacunarity, -hurst*float64(i+1))
}
return &FBM{"FBM", w}
}
// Combine takes the values from the successive applications of the affine transform and
// combines them using the precomputed weights.
func (f *FBM) Combine(values ...float64) float64 {
res := 0.0
for i := 0; i < len(values); i++ {
res += values[i] * f.Weights[i]
}
return res
}
// MF holds the precomputed weights and offset for an multifractal.
type MF struct {
Name string
Weights []float64
Offset float64
}
// NewMF returns a new MF instance based on the Hurst and Lacunarity parameters.
func NewMF(hurst, lacunarity, offset float64, maxoct int) *MF {
maxoct++
w := make([]float64, maxoct)
for i := 0; i < maxoct; i++ {
w[i] = math.Pow(lacunarity, -hurst*float64(i+1))
}
return &MF{"MF", w, offset}
}
// Combine takes the values from the successive applications of the affine transform and
// combines them using the precomputed weights and offset.
func (f *MF) Combine(values ...float64) float64 {
res := 0.0
for i := 0; i < len(values); i++ {
res += (values[i] + f.Offset) * f.Weights[i]
}
return res
}