├── .gitignore ├── .travis.yml ├── go.mod ├── test_files └── samples.json.gz ├── doc_test.go ├── opensimplex_float32.go ├── LICENSE ├── opensimplex_test.go ├── Readme.md ├── opensimplex.go ├── opensimplex_normalized.go ├── opensimplex_internal.go └── opensimplex_base.go /.gitignore: -------------------------------------------------------------------------------- 1 | opensimplex 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 1.15.x 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ojrac/opensimplex-go 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /test_files/samples.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojrac/opensimplex-go/HEAD/test_files/samples.json.gz -------------------------------------------------------------------------------- /doc_test.go: -------------------------------------------------------------------------------- 1 | package opensimplex 2 | 3 | import "math/rand" 4 | 5 | func Example() { 6 | noise := New(rand.Int63()) 7 | 8 | w, h := 100, 100 9 | heightmap := make([]float64, w*h) 10 | for y := 0; y < h; y++ { 11 | for x := 0; x < w; x++ { 12 | xFloat := float64(x) / float64(w) 13 | yFloat := float64(y) / float64(h) 14 | heightmap[(y*w)+x] = noise.Eval2(xFloat, yFloat) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /opensimplex_float32.go: -------------------------------------------------------------------------------- 1 | package opensimplex 2 | 3 | // Wraps a noise instance to work with float32 values 4 | 5 | type cast32Noise struct { 6 | base Noise 7 | } 8 | 9 | func (n *cast32Noise) Eval2(x, y float32) float32 { 10 | return float32(n.base.Eval2(float64(x), float64(y))) 11 | } 12 | func (n *cast32Noise) Eval3(x, y, z float32) float32 { 13 | return float32(n.base.Eval3(float64(x), float64(y), float64(z))) 14 | } 15 | func (n *cast32Noise) Eval4(x, y, z, w float32) float32 { 16 | return float32(n.base.Eval4(float64(x), float64(y), float64(z), float64(w))) 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /opensimplex_test.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Tests for OpenSimplex noise, based on the output of 3 | * the Java implementation. 4 | * 5 | * All reference samples were rendered with the default seed (0). Each version 6 | * of the noise function (2D, 3D and 4D) was run to output 2D samples slicing 7 | * across two of the function's axes. There is one 2D slice, three 3D slices 8 | * and 6 4D slices; the 3D slices each pin one axis to the value 3.8; 4D slices 9 | * pin one axis (the first in the filename) to 3.8 and the second to 2.7. These 10 | * values were chosen arbitrarily. 11 | * 12 | * Each sample is a 512x512 greyscale PNG; each pixel is 1/24 wide in the 13 | * OpenSimplex's space -- i.e. pixel (24, 24) in the 2D noise sample was 14 | * computed by evaluating the 2D noise at (1.0, 1.0) and converting from a [-1, 15 | * +1] scale to [0, +1]. 16 | */ 17 | package opensimplex 18 | 19 | import ( 20 | "compress/gzip" 21 | "encoding/json" 22 | "io" 23 | "os" 24 | "path" 25 | "testing" 26 | ) 27 | 28 | func loadSamples() <-chan []float64 { 29 | c := make(chan []float64) 30 | go func() { 31 | f, err := os.Open(path.Join("test_files", "samples.json.gz")) 32 | if err != nil { 33 | panic(err.Error()) 34 | } 35 | defer f.Close() 36 | 37 | gz, err := gzip.NewReader(f) 38 | if err != nil { 39 | panic(err.Error()) 40 | } 41 | 42 | dec := json.NewDecoder(gz) 43 | for { 44 | var sample []float64 45 | if err := dec.Decode(&sample); err == io.EOF { 46 | break 47 | } else if err != nil { 48 | panic(err.Error()) 49 | } else { 50 | c <- sample 51 | } 52 | } 53 | close(c) 54 | }() 55 | 56 | return c 57 | } 58 | 59 | func TestSamplesMatch(t *testing.T) { 60 | samples := loadSamples() 61 | n := New(0) 62 | 63 | for s := range samples { 64 | var expected, actual float64 65 | switch len(s) { 66 | case 3: 67 | expected = s[2] 68 | actual = n.Eval2(s[0], s[1]) 69 | case 4: 70 | expected = s[3] 71 | actual = n.Eval3(s[0], s[1], s[2]) 72 | case 5: 73 | expected = s[4] 74 | actual = n.Eval4(s[0], s[1], s[2], s[3]) 75 | default: 76 | t.Fatalf("Unexpected size sample: %d", len(s)) 77 | } 78 | 79 | if expected != actual { 80 | t.Fatalf("Expected %v, got %v for %dD sample at %v", 81 | expected, actual, len(s)-1, s[:len(s)-1]) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ARCHIVED 2 | ======== 3 | 4 | Two things have happened since this repository was built, that you might care about: 5 | 6 | - The Simplex noise patent has expired 7 | - There's a new revision of this algorithm, [OpenSimplex2](https://github.com/KdotJPG/OpenSimplex2) 8 | 9 | Feel free to use this library, but I don't intend to make any future updates to it. You may need to create a fork if you'd like it to work differently. 10 | 11 | OpenSimplex in Go 12 | ================= 13 | 14 | 15 | [![GoDoc](https://godoc.org/github.com/ojrac/opensimplex-go?status.svg)](http://godoc.org/github.com/ojrac/opensimplex-go) 16 | [![Build Status](https://travis-ci.org/ojrac/opensimplex-go.svg?branch=master)](https://travis-ci.org/ojrac/opensimplex-go) 17 | 18 | OpenSimplex noise is a random noise algorithm by Kurt Spencer, made as a 19 | patent-free alternative to Perlin and Simplex noise. This Go port is based on 20 | Kurt's [Java implementation](https://gist.github.com/KdotJPG/b1270127455a94ac5d19). 21 | 22 | For an introduction to OpenSimplex noise, see [Kurt Spencer's 23 | post](http://uniblock.tumblr.com/post/97868843242/noise) announcing it. If 24 | you're not familiar with random noise, the Wikipedia post on [Perlin 25 | noise](https://en.wikipedia.org/wiki/Perlin_noise) is a good introduction. 26 | 27 | 28 | Why not Perlin noise? 29 | --------------------- 30 | 31 | As Kurt explains [in his 32 | post](http://uniblock.tumblr.com/post/97868843242/noise), Perlin noise tends to 33 | generate noise with noticeable axis-aligned artifacts. Simplex noise fixes 34 | these artifacts, but it's patented. OpenSimplex noise is for people who don't 35 | want to deal with Simplex's patent. 36 | 37 | The difference between Perlin and OpenSimplex noise is easiest to see in 38 | pictures. This is Perlin noise, with a noticeable bias towards vertical and 39 | horizontal artifacts: 40 | 41 | ![Perlin Noise sample](http://67.media.tumblr.com/153d6efb739746f114c52f86391c3932/tumblr_inline_nc4swj5tX51seaucq.png) 42 | 43 | Here's what OpenSimplex noise looks like: 44 | 45 | ![OpenSimplex Noise sample](http://67.media.tumblr.com/6186a25f7bafb258c30101ee3c0c87b4/tumblr_inline_ngubweRMTr1seaucq.png) 46 | 47 | 48 | Tests 49 | ----------- 50 | This implementation of OpenSimplex's tests verify its output against the output 51 | of the reference Java implementation. I haven't run these tests on different 52 | architectures, so results may vary. 53 | 54 | License 55 | ------- 56 | This code is under the same "license" as Kurt's OpenSimplex - the public domain 57 | "unlicense." 58 | 59 | Next Steps 60 | ---------- 61 | * More documentation 62 | * Benchmarks 63 | -------------------------------------------------------------------------------- /opensimplex.go: -------------------------------------------------------------------------------- 1 | // opensimplex is a Go implementation of Kurt Spencer's patent-free alternative 2 | // to Perlin and Simplex noise. 3 | // 4 | // Given a seed, it generates smoothly-changing deterministic random values in 5 | // 2, 3 or 4 dimensions. It's commonly used for procedurally generated images, 6 | // geometry, or other randomly-influenced applications that require a random 7 | // gradient. 8 | // 9 | // For more information on OpenSimplex noise, read more from the creator of the 10 | // algorithm: http://uniblock.tumblr.com/post/97868843242/noise 11 | package opensimplex 12 | 13 | /** 14 | * OpenSimplex Noise in Go. 15 | * algorithm by Kurt Spencer 16 | * ported by Owen Raccuglia 17 | * 18 | * Based on Java v1.1 (October 5, 2014) 19 | */ 20 | 21 | // A seeded 64-bit noise instance 22 | type Noise interface { 23 | Eval2(x, y float64) float64 24 | Eval3(x, y, z float64) float64 25 | Eval4(x, y, z, w float64) float64 26 | } 27 | 28 | // A seeded 32-bit noise instance 29 | type Noise32 interface { 30 | Eval2(x, y float32) float32 31 | Eval3(x, y, z float32) float32 32 | Eval4(x, y, z, w float32) float32 33 | } 34 | 35 | // Construct a Noise instance with a 64-bit seed. Two Noise instances with the 36 | // same seed will have the same output. 37 | func New(seed int64) Noise { 38 | s := &noise{ 39 | perm: make([]int16, 256), 40 | permGradIndex3D: make([]int16, 256), 41 | } 42 | 43 | source := make([]int16, 256) 44 | for i := range source { 45 | source[i] = int16(i) 46 | } 47 | 48 | seed = seed*6364136223846793005 + 1442695040888963407 49 | seed = seed*6364136223846793005 + 1442695040888963407 50 | seed = seed*6364136223846793005 + 1442695040888963407 51 | for i := int32(255); i >= 0; i-- { 52 | seed = seed*6364136223846793005 + 1442695040888963407 53 | r := int32((seed + 31) % int64(i+1)) 54 | if r < 0 { 55 | r += i + 1 56 | } 57 | 58 | s.perm[i] = source[r] 59 | s.permGradIndex3D[i] = (s.perm[i] % (int16(len(gradients3D)) / 3)) * 3 60 | source[r] = source[i] 61 | } 62 | 63 | return s 64 | } 65 | 66 | // Construct a Noise32 instance with a 64-bit seed. Two Noise32 instances with the 67 | // same seed will have the same output. 68 | func New32(seed int64) Noise32 { 69 | return &cast32Noise{base: New(seed)} 70 | } 71 | 72 | // Construct a normalized Noise instance with a 64-bit seed. Eval methods will 73 | // return values in [0, 1). Two Noise instances with the same seed will have 74 | // the same output. 75 | func NewNormalized(seed int64) Noise { 76 | return &normNoise{base: New(seed)} 77 | } 78 | 79 | // Construct a normalized Noise32 instance with a 64-bit seed. Eval methods will 80 | // return values in [0, 1). Two Noise32 instances with the same seed will have 81 | // the same output. 82 | func NewNormalized32(seed int64) Noise32 { 83 | return &normNoise32{base: New(seed)} 84 | } 85 | -------------------------------------------------------------------------------- /opensimplex_normalized.go: -------------------------------------------------------------------------------- 1 | package opensimplex 2 | 3 | const ( 4 | // The normMin and normScale constants are used 5 | // in the formula for normalizing the raw output 6 | // of the OpenSimplex algorithm. They were 7 | // derived from empirical observations of the 8 | // range of raw values. Different constants are 9 | // required for each of Eval2, Eval3, and Eval4. 10 | normMin2 = 0.8659203878240322 11 | normScale2 = 0.577420288914181 12 | 13 | normMin3 = 0.9871048542519545 14 | normScale3 = 0.506595297177236 15 | 16 | normMin4 = 1.0040848236330158 17 | normScale4 = 0.5007450643319374 18 | ) 19 | 20 | type normNoise struct { 21 | base Noise 22 | } 23 | 24 | // Eval2 returns a random noise value in two dimensions 25 | // in the range [0, 1). 26 | func (s *normNoise) Eval2(x, y float64) float64 { 27 | //return norm2_64(s.base.Eval2(x, y)) 28 | r := s.base.Eval2(x, y) 29 | return (r + normMin2) * normScale2 30 | } 31 | 32 | // Eval3 returns a random noise value in three dimensions 33 | // in the range [0, 1). 34 | func (s *normNoise) Eval3(x, y, z float64) float64 { 35 | r := s.base.Eval3(x, y, z) 36 | return (r + normMin3) * normScale3 37 | } 38 | 39 | // Eval4 returns a random noise value in four dimensions 40 | // in the range [0, 1). 41 | func (s *normNoise) Eval4(x, y, z, t float64) float64 { 42 | r := s.base.Eval4(x, y, z, t) 43 | return (r + normMin4) * normScale4 44 | } 45 | 46 | type normNoise32 struct { 47 | base Noise 48 | } 49 | 50 | // Eval2 returns a random noise value in two dimensions 51 | // in the range [0, 1). 52 | func (s *normNoise32) Eval2(x, y float32) float32 { 53 | r := s.base.Eval2(float64(x), float64(y)) 54 | norm64 := (r + normMin2) * normScale2 55 | norm32 := float32(norm64) 56 | 57 | // Empirical testing shows that a simple float32 cast 58 | // from the normalized float64, as above, will sometimes 59 | // produce a value of 1.0. 60 | if norm32 >= 1.0 { 61 | return float32(0.999999) 62 | } else { 63 | return norm32 64 | } 65 | } 66 | 67 | // Eval3 returns a random noise value in three dimensions 68 | // in the range [0, 1). 69 | func (s *normNoise32) Eval3(x, y, z float32) float32 { 70 | r := s.base.Eval3(float64(x), float64(y), float64(z)) 71 | norm64 := (r + normMin3) * normScale3 72 | norm32 := float32(norm64) 73 | 74 | // Unlike Eval2, have not actually tested whether a 75 | // simple float32 cast will produce 1.0, but it seems likely. 76 | if norm32 >= 1.0 { 77 | return float32(0.999999) 78 | } else { 79 | return norm32 80 | } 81 | } 82 | 83 | // Eval4 returns a random noise value in four dimensions 84 | // in the range [0, 1). 85 | func (s *normNoise32) Eval4(x, y, z, t float32) float32 { 86 | r := s.base.Eval4(float64(x), float64(y), float64(z), float64(t)) 87 | norm64 := (r + normMin4) * normScale4 88 | norm32 := float32(norm64) 89 | 90 | // Unlike Eval2, have not actually tested whether a 91 | // simple float32 cast will produce 1.0, but it seems likely. 92 | if norm32 >= 1.0 { 93 | return float32(0.999999) 94 | } else { 95 | return norm32 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /opensimplex_internal.go: -------------------------------------------------------------------------------- 1 | package opensimplex 2 | 3 | const ( 4 | stretchConstant2D = -0.211324865405187 // (1/Math.sqrt(2+1)-1)/2 5 | squishConstant2D = 0.366025403784439 // (Math.sqrt(2+1)-1)/2 6 | stretchConstant3D = -1.0 / 6 // (1/Math.sqrt(3+1)-1)/3 7 | squishConstant3D = 1.0 / 3 // (Math.sqrt(3+1)-1)/3 8 | stretchConstant4D = -0.138196601125011 // (1/Math.sqrt(4+1)-1)/4 9 | squishConstant4D = 0.309016994374947 // (Math.sqrt(4+1)-1)/4 10 | 11 | normConstant2D = 47 12 | normConstant3D = 103 13 | normConstant4D = 30 14 | 15 | defaultSeed = 0 16 | ) 17 | 18 | func (s *noise) extrapolate2(xsb, ysb int32, dx, dy float64) float64 { 19 | index := s.perm[(int32(s.perm[xsb&0xFF])+ysb)&0xFF] & 0x0E 20 | return float64(gradients2D[index])*dx + float64(gradients2D[index+1])*dy 21 | } 22 | 23 | func (s *noise) extrapolate3(xsb, ysb, zsb int32, dx, dy, dz float64) float64 { 24 | index := s.permGradIndex3D[(int32(s.perm[(int32(s.perm[xsb&0xFF])+ysb)&0xFF])+zsb)&0xFF] 25 | return float64(gradients3D[index])*dx + float64(gradients3D[index+1])*dy + float64(gradients3D[index+2])*dz 26 | } 27 | 28 | func (s *noise) extrapolate4(xsb, ysb, zsb, wsb int32, dx, dy, dz, dw float64) float64 { 29 | index := s.perm[(int32(s.perm[(int32(s.perm[(int32(s.perm[xsb&0xFF])+ysb)&0xFF])+zsb)&0xFF])+wsb)&0xFF] & 0xFC 30 | return float64(gradients4D[index])*dx + float64(gradients4D[index+1])*dy + float64(gradients4D[index+2])*dz + float64(gradients4D[index+3])*dw 31 | } 32 | 33 | // Gradients for 2D. They approximate the directions to the 34 | // vertices of an octagon from the center. 35 | var gradients2D = []int8{ 36 | 5, 2, 2, 5, 37 | -5, 2, -2, 5, 38 | 5, -2, 2, -5, 39 | -5, -2, -2, -5, 40 | } 41 | 42 | // Gradients for 3D. They approximate the directions to the 43 | // vertices of a rhombicuboctahedron from the center, skewed so 44 | // that the triangular and square facets can be inscribed inside 45 | // circles of the same radius. 46 | var gradients3D = []int8{ 47 | -11, 4, 4, -4, 11, 4, -4, 4, 11, 48 | 11, 4, 4, 4, 11, 4, 4, 4, 11, 49 | -11, -4, 4, -4, -11, 4, -4, -4, 11, 50 | 11, -4, 4, 4, -11, 4, 4, -4, 11, 51 | -11, 4, -4, -4, 11, -4, -4, 4, -11, 52 | 11, 4, -4, 4, 11, -4, 4, 4, -11, 53 | -11, -4, -4, -4, -11, -4, -4, -4, -11, 54 | 11, -4, -4, 4, -11, -4, 4, -4, -11, 55 | } 56 | 57 | // Gradients for 4D. They approximate the directions to the 58 | // vertices of a disprismatotesseractihexadecachoron from the center, 59 | // skewed so that the tetrahedral and cubic facets can be inscribed inside 60 | // spheres of the same radius. 61 | var gradients4D = []int8{ 62 | 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 63 | -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, 64 | 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, 65 | -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, 66 | 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, 67 | -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, 68 | 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, 69 | -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, 70 | 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, 71 | -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, 72 | 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, 73 | -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, 74 | 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, 75 | -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, 76 | 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, 77 | -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, 78 | } 79 | -------------------------------------------------------------------------------- /opensimplex_base.go: -------------------------------------------------------------------------------- 1 | package opensimplex 2 | 3 | import "math" 4 | 5 | // Vanilla opensimplex implementation, matching Kurt Spencer's Java 6 | // reference implementation as exactly as possible. 7 | 8 | // A seeded Noise instance. Reusing a Noise instance (rather than recreating it 9 | // from a known seed) will save some calculation time. 10 | type noise struct { 11 | perm []int16 12 | permGradIndex3D []int16 13 | } 14 | 15 | // Returns a random noise value in two dimensions. Repeated calls with the same 16 | // x/y inputs will have the same output. 17 | func (s *noise) Eval2(x, y float64) float64 { 18 | // Place input coordinates onto grid. 19 | stretchOffset := (x + y) * stretchConstant2D 20 | xs := float64(x + stretchOffset) 21 | ys := float64(y + stretchOffset) 22 | 23 | // Floor to get grid coordinates of rhombus (stretched square) super-cell origin. 24 | xsb := int32(math.Floor(xs)) 25 | ysb := int32(math.Floor(ys)) 26 | 27 | // Skew out to get actual coordinates of rhombus origin. We'll need these later. 28 | squishOffset := float64(xsb+ysb) * squishConstant2D 29 | xb := float64(xsb) + squishOffset 30 | yb := float64(ysb) + squishOffset 31 | 32 | // Compute grid coordinates relative to rhombus origin. 33 | xins := xs - float64(xsb) 34 | yins := ys - float64(ysb) 35 | 36 | // Sum those together to get a value that determines which region we're in. 37 | inSum := xins + yins 38 | 39 | // Positions relative to origin point. 40 | dx0 := x - xb 41 | dy0 := y - yb 42 | 43 | // We'll be defining these inside the next block and using them afterwards. 44 | var dx_ext, dy_ext float64 45 | var xsv_ext, ysv_ext int32 46 | 47 | value := float64(0) 48 | 49 | // Contribution (1,0) 50 | dx1 := dx0 - 1 - squishConstant2D 51 | dy1 := dy0 - 0 - squishConstant2D 52 | attn1 := 2 - dx1*dx1 - dy1*dy1 53 | if attn1 > 0 { 54 | attn1 *= attn1 55 | value += attn1 * attn1 * s.extrapolate2(xsb+1, ysb+0, dx1, dy1) 56 | } 57 | 58 | // Contribution (0,1) 59 | dx2 := dx0 - 0 - squishConstant2D 60 | dy2 := dy0 - 1 - squishConstant2D 61 | attn2 := 2 - dx2*dx2 - dy2*dy2 62 | if attn2 > 0 { 63 | attn2 *= attn2 64 | value += attn2 * attn2 * s.extrapolate2(xsb+0, ysb+1, dx2, dy2) 65 | } 66 | 67 | if inSum <= 1 { // We're inside the triangle (2-Simplex) at (0,0) 68 | zins := 1 - inSum 69 | if zins > xins || zins > yins { // (0,0) is one of the closest two triangular vertices 70 | if xins > yins { 71 | xsv_ext = xsb + 1 72 | ysv_ext = ysb - 1 73 | dx_ext = dx0 - 1 74 | dy_ext = dy0 + 1 75 | } else { 76 | xsv_ext = xsb - 1 77 | ysv_ext = ysb + 1 78 | dx_ext = dx0 + 1 79 | dy_ext = dy0 - 1 80 | } 81 | } else { // (1,0) and (0,1) are the closest two vertices. 82 | xsv_ext = xsb + 1 83 | ysv_ext = ysb + 1 84 | dx_ext = dx0 - 1 - 2*squishConstant2D 85 | dy_ext = dy0 - 1 - 2*squishConstant2D 86 | } 87 | } else { // We're inside the triangle (2-Simplex) at (1,1) 88 | zins := 2 - inSum 89 | if zins < xins || zins < yins { // (0,0) is one of the closest two triangular vertices 90 | if xins > yins { 91 | xsv_ext = xsb + 2 92 | ysv_ext = ysb + 0 93 | dx_ext = dx0 - 2 - 2*squishConstant2D 94 | dy_ext = dy0 + 0 - 2*squishConstant2D 95 | } else { 96 | xsv_ext = xsb + 0 97 | ysv_ext = ysb + 2 98 | dx_ext = dx0 + 0 - 2*squishConstant2D 99 | dy_ext = dy0 - 2 - 2*squishConstant2D 100 | } 101 | } else { // (1,0) and (0,1) are the closest two vertices. 102 | dx_ext = dx0 103 | dy_ext = dy0 104 | xsv_ext = xsb 105 | ysv_ext = ysb 106 | } 107 | xsb += 1 108 | ysb += 1 109 | dx0 = dx0 - 1 - 2*squishConstant2D 110 | dy0 = dy0 - 1 - 2*squishConstant2D 111 | } 112 | 113 | // Contribution (0,0) or (1,1) 114 | attn0 := 2 - dx0*dx0 - dy0*dy0 115 | if attn0 > 0 { 116 | attn0 *= attn0 117 | value += attn0 * attn0 * s.extrapolate2(xsb, ysb, dx0, dy0) 118 | } 119 | 120 | // Extra Vertex 121 | attn_ext := 2 - dx_ext*dx_ext - dy_ext*dy_ext 122 | if attn_ext > 0 { 123 | attn_ext *= attn_ext 124 | value += attn_ext * attn_ext * s.extrapolate2(xsv_ext, ysv_ext, dx_ext, dy_ext) 125 | } 126 | 127 | return value / normConstant2D 128 | } 129 | 130 | // Returns a random noise value in three dimensions. 131 | func (s *noise) Eval3(x, y, z float64) float64 { 132 | // Place input coordinates on simplectic honeycomb. 133 | stretchOffset := (x + y + z) * stretchConstant3D 134 | xs := float64(x + stretchOffset) 135 | ys := float64(y + stretchOffset) 136 | zs := float64(z + stretchOffset) 137 | 138 | // Floor to get simplectic honeycomb coordinates of rhombohedron (stretched cube) super-cell origin. 139 | xsb := int32(math.Floor(xs)) 140 | ysb := int32(math.Floor(ys)) 141 | zsb := int32(math.Floor(zs)) 142 | 143 | // Skew out to get actual coordinates of rhombohedron origin. We'll need these later. 144 | squishOffset := float64(xsb+ysb+zsb) * squishConstant3D 145 | xb := float64(xsb) + squishOffset 146 | yb := float64(ysb) + squishOffset 147 | zb := float64(zsb) + squishOffset 148 | 149 | // Compute simplectic honeycomb coordinates relative to rhombohedral origin. 150 | xins := xs - float64(xsb) 151 | yins := ys - float64(ysb) 152 | zins := zs - float64(zsb) 153 | 154 | // Sum those together to get a value that determines which region we're in. 155 | inSum := xins + yins + zins 156 | 157 | // Positions relative to origin point. 158 | dx0 := x - xb 159 | dy0 := y - yb 160 | dz0 := z - zb 161 | 162 | // We'll be defining these inside the next block and using them afterwards. 163 | var dx_ext0, dy_ext0, dz_ext0 float64 164 | var dx_ext1, dy_ext1, dz_ext1 float64 165 | var xsv_ext0, ysv_ext0, zsv_ext0 int32 166 | var xsv_ext1, ysv_ext1, zsv_ext1 int32 167 | 168 | value := float64(0) 169 | if inSum <= 1 { // We're inside the tetrahedron (3-Simplex) at (0,0,0) 170 | 171 | // Determine which two of (0,0,1), (0,1,0), (1,0,0) are closest. 172 | aPoint := byte(0x01) 173 | bPoint := byte(0x02) 174 | aScore := xins 175 | bScore := yins 176 | if aScore >= bScore && zins > bScore { 177 | bScore = zins 178 | bPoint = 0x04 179 | } else if aScore < bScore && zins > aScore { 180 | aScore = zins 181 | aPoint = 0x04 182 | } 183 | 184 | // Now we determine the two lattice points not part of the tetrahedron that may contribute. 185 | // This depends on the closest two tetrahedral vertices, including (0,0,0) 186 | wins := 1 - inSum 187 | if wins > aScore || wins > bScore { // (0,0,0) is one of the closest two tetrahedral vertices. 188 | var c byte // Our other closest vertex is the closest out of a and b. 189 | if bScore > aScore { 190 | c = bPoint 191 | } else { 192 | c = aPoint 193 | } 194 | 195 | if (c & 0x01) == 0 { 196 | xsv_ext0 = xsb - 1 197 | xsv_ext1 = xsb 198 | dx_ext0 = dx0 + 1 199 | dx_ext1 = dx0 200 | } else { 201 | xsv_ext1 = xsb + 1 202 | xsv_ext0 = xsv_ext1 203 | dx_ext1 = dx0 - 1 204 | dx_ext0 = dx_ext1 205 | } 206 | 207 | if (c & 0x02) == 0 { 208 | ysv_ext1 = ysb 209 | ysv_ext0 = ysv_ext1 210 | dy_ext1 = dy0 211 | dy_ext0 = dy_ext1 212 | if (c & 0x01) == 0 { 213 | ysv_ext1 -= 1 214 | dy_ext1 += 1 215 | } else { 216 | ysv_ext0 -= 1 217 | dy_ext0 += 1 218 | } 219 | } else { 220 | ysv_ext1 = ysb + 1 221 | ysv_ext0 = ysv_ext1 222 | dy_ext1 = dy0 - 1 223 | dy_ext0 = dy_ext1 224 | } 225 | 226 | if (c & 0x04) == 0 { 227 | zsv_ext0 = zsb 228 | zsv_ext1 = zsb - 1 229 | dz_ext0 = dz0 230 | dz_ext1 = dz0 + 1 231 | } else { 232 | zsv_ext1 = zsb + 1 233 | zsv_ext0 = zsv_ext1 234 | dz_ext1 = dz0 - 1 235 | dz_ext0 = dz_ext1 236 | } 237 | } else { // (0,0,0) is not one of the closest two tetrahedral vertices. 238 | c := aPoint | bPoint // Our two extra vertices are determined by the closest two. 239 | 240 | if (c & 0x01) == 0 { 241 | xsv_ext0 = xsb 242 | xsv_ext1 = xsb - 1 243 | dx_ext0 = dx0 - 2*squishConstant3D 244 | dx_ext1 = dx0 + 1 - squishConstant3D 245 | } else { 246 | xsv_ext1 = xsb + 1 247 | xsv_ext0 = xsv_ext1 248 | dx_ext0 = dx0 - 1 - 2*squishConstant3D 249 | dx_ext1 = dx0 - 1 - squishConstant3D 250 | } 251 | 252 | if (c & 0x02) == 0 { 253 | ysv_ext0 = ysb 254 | ysv_ext1 = ysb - 1 255 | dy_ext0 = dy0 - 2*squishConstant3D 256 | dy_ext1 = dy0 + 1 - squishConstant3D 257 | } else { 258 | ysv_ext1 = ysb + 1 259 | ysv_ext0 = ysv_ext1 260 | dy_ext0 = dy0 - 1 - 2*squishConstant3D 261 | dy_ext1 = dy0 - 1 - squishConstant3D 262 | } 263 | 264 | if (c & 0x04) == 0 { 265 | zsv_ext0 = zsb 266 | zsv_ext1 = zsb - 1 267 | dz_ext0 = dz0 - 2*squishConstant3D 268 | dz_ext1 = dz0 + 1 - squishConstant3D 269 | } else { 270 | zsv_ext1 = zsb + 1 271 | zsv_ext0 = zsv_ext1 272 | dz_ext0 = dz0 - 1 - 2*squishConstant3D 273 | dz_ext1 = dz0 - 1 - squishConstant3D 274 | } 275 | } 276 | 277 | // Contribution (0,0,0) 278 | attn0 := 2 - dx0*dx0 - dy0*dy0 - dz0*dz0 279 | if attn0 > 0 { 280 | attn0 *= attn0 281 | value += attn0 * attn0 * s.extrapolate3(xsb+0, ysb+0, zsb+0, dx0, dy0, dz0) 282 | } 283 | 284 | // Contribution (1,0,0) 285 | dx1 := dx0 - 1 - squishConstant3D 286 | dy1 := dy0 - 0 - squishConstant3D 287 | dz1 := dz0 - 0 - squishConstant3D 288 | attn1 := 2 - dx1*dx1 - dy1*dy1 - dz1*dz1 289 | if attn1 > 0 { 290 | attn1 *= attn1 291 | value += attn1 * attn1 * s.extrapolate3(xsb+1, ysb+0, zsb+0, dx1, dy1, dz1) 292 | } 293 | 294 | // Contribution (0,1,0) 295 | dx2 := dx0 - 0 - squishConstant3D 296 | dy2 := dy0 - 1 - squishConstant3D 297 | dz2 := dz1 298 | attn2 := 2 - dx2*dx2 - dy2*dy2 - dz2*dz2 299 | if attn2 > 0 { 300 | attn2 *= attn2 301 | value += attn2 * attn2 * s.extrapolate3(xsb+0, ysb+1, zsb+0, dx2, dy2, dz2) 302 | } 303 | 304 | // Contribution (0,0,1) 305 | dx3 := dx2 306 | dy3 := dy1 307 | dz3 := dz0 - 1 - squishConstant3D 308 | attn3 := 2 - dx3*dx3 - dy3*dy3 - dz3*dz3 309 | if attn3 > 0 { 310 | attn3 *= attn3 311 | value += attn3 * attn3 * s.extrapolate3(xsb+0, ysb+0, zsb+1, dx3, dy3, dz3) 312 | } 313 | } else if inSum >= 2 { // We're inside the tetrahedron (3-Simplex) at (1,1,1) 314 | 315 | // Determine which two tetrahedral vertices are the closest, out of (1,1,0), (1,0,1), (0,1,1) but not (1,1,1). 316 | aPoint := byte(0x06) 317 | aScore := xins 318 | bPoint := byte(0x05) 319 | bScore := yins 320 | if aScore <= bScore && zins < bScore { 321 | bScore = zins 322 | bPoint = 0x03 323 | } else if aScore > bScore && zins < aScore { 324 | aScore = zins 325 | aPoint = 0x03 326 | } 327 | 328 | // Now we determine the two lattice points not part of the tetrahedron that may contribute. 329 | // This depends on the closest two tetrahedral vertices, including (1,1,1) 330 | wins := 3 - inSum 331 | if wins < aScore || wins < bScore { // (1,1,1) is one of the closest two tetrahedral vertices. 332 | var c byte // Our other closest vertex is the closest out of a and b. 333 | if bScore < aScore { 334 | c = bPoint 335 | } else { 336 | c = aPoint 337 | } 338 | 339 | if (c & 0x01) != 0 { 340 | xsv_ext0 = xsb + 2 341 | xsv_ext1 = xsb + 1 342 | dx_ext0 = dx0 - 2 - 3*squishConstant3D 343 | dx_ext1 = dx0 - 1 - 3*squishConstant3D 344 | } else { 345 | xsv_ext1 = xsb 346 | xsv_ext0 = xsv_ext1 347 | dx_ext1 = dx0 - 3*squishConstant3D 348 | dx_ext0 = dx_ext1 349 | } 350 | 351 | if (c & 0x02) != 0 { 352 | ysv_ext1 = ysb + 1 353 | ysv_ext0 = ysv_ext1 354 | dy_ext1 = dy0 - 1 - 3*squishConstant3D 355 | dy_ext0 = dy_ext1 356 | if (c & 0x01) != 0 { 357 | ysv_ext1 += 1 358 | dy_ext1 -= 1 359 | } else { 360 | ysv_ext0 += 1 361 | dy_ext0 -= 1 362 | } 363 | } else { 364 | ysv_ext1 = ysb 365 | ysv_ext0 = ysv_ext1 366 | dy_ext1 = dy0 - 3*squishConstant3D 367 | dy_ext0 = dy_ext1 368 | } 369 | 370 | if (c & 0x04) != 0 { 371 | zsv_ext0 = zsb + 1 372 | zsv_ext1 = zsb + 2 373 | dz_ext0 = dz0 - 1 - 3*squishConstant3D 374 | dz_ext1 = dz0 - 2 - 3*squishConstant3D 375 | } else { 376 | zsv_ext1 = zsb 377 | zsv_ext0 = zsv_ext1 378 | dz_ext1 = dz0 - 3*squishConstant3D 379 | dz_ext0 = dz_ext1 380 | } 381 | } else { // (1,1,1) is not one of the closest two tetrahedral vertices. 382 | c := aPoint & bPoint // Our two extra vertices are determined by the closest two. 383 | 384 | if (c & 0x01) != 0 { 385 | xsv_ext0 = xsb + 1 386 | xsv_ext1 = xsb + 2 387 | dx_ext0 = dx0 - 1 - squishConstant3D 388 | dx_ext1 = dx0 - 2 - 2*squishConstant3D 389 | } else { 390 | xsv_ext1 = xsb 391 | xsv_ext0 = xsv_ext1 392 | dx_ext0 = dx0 - squishConstant3D 393 | dx_ext1 = dx0 - 2*squishConstant3D 394 | } 395 | 396 | if (c & 0x02) != 0 { 397 | ysv_ext0 = ysb + 1 398 | ysv_ext1 = ysb + 2 399 | dy_ext0 = dy0 - 1 - squishConstant3D 400 | dy_ext1 = dy0 - 2 - 2*squishConstant3D 401 | } else { 402 | ysv_ext1 = ysb 403 | ysv_ext0 = ysv_ext1 404 | dy_ext0 = dy0 - squishConstant3D 405 | dy_ext1 = dy0 - 2*squishConstant3D 406 | } 407 | 408 | if (c & 0x04) != 0 { 409 | zsv_ext0 = zsb + 1 410 | zsv_ext1 = zsb + 2 411 | dz_ext0 = dz0 - 1 - squishConstant3D 412 | dz_ext1 = dz0 - 2 - 2*squishConstant3D 413 | } else { 414 | zsv_ext1 = zsb 415 | zsv_ext0 = zsv_ext1 416 | dz_ext0 = dz0 - squishConstant3D 417 | dz_ext1 = dz0 - 2*squishConstant3D 418 | } 419 | } 420 | 421 | // Contribution (1,1,0) 422 | dx3 := dx0 - 1 - 2*squishConstant3D 423 | dy3 := dy0 - 1 - 2*squishConstant3D 424 | dz3 := dz0 - 0 - 2*squishConstant3D 425 | attn3 := 2 - dx3*dx3 - dy3*dy3 - dz3*dz3 426 | if attn3 > 0 { 427 | attn3 *= attn3 428 | value += attn3 * attn3 * s.extrapolate3(xsb+1, ysb+1, zsb+0, dx3, dy3, dz3) 429 | } 430 | 431 | // Contribution (1,0,1) 432 | dx2 := dx3 433 | dy2 := dy0 - 0 - 2*squishConstant3D 434 | dz2 := dz0 - 1 - 2*squishConstant3D 435 | attn2 := 2 - dx2*dx2 - dy2*dy2 - dz2*dz2 436 | if attn2 > 0 { 437 | attn2 *= attn2 438 | value += attn2 * attn2 * s.extrapolate3(xsb+1, ysb+0, zsb+1, dx2, dy2, dz2) 439 | } 440 | 441 | // Contribution (0,1,1) 442 | dx1 := dx0 - 0 - 2*squishConstant3D 443 | dy1 := dy3 444 | dz1 := dz2 445 | attn1 := 2 - dx1*dx1 - dy1*dy1 - dz1*dz1 446 | if attn1 > 0 { 447 | attn1 *= attn1 448 | value += attn1 * attn1 * s.extrapolate3(xsb+0, ysb+1, zsb+1, dx1, dy1, dz1) 449 | } 450 | 451 | // Contribution (1,1,1) 452 | dx0 = dx0 - 1 - 3*squishConstant3D 453 | dy0 = dy0 - 1 - 3*squishConstant3D 454 | dz0 = dz0 - 1 - 3*squishConstant3D 455 | attn0 := 2 - dx0*dx0 - dy0*dy0 - dz0*dz0 456 | if attn0 > 0 { 457 | attn0 *= attn0 458 | value += attn0 * attn0 * s.extrapolate3(xsb+1, ysb+1, zsb+1, dx0, dy0, dz0) 459 | } 460 | } else { // We're inside the octahedron (Rectified 3-Simplex) in between. 461 | var aScore, bScore float64 462 | var aPoint, bPoint byte 463 | var aIsFurtherSide, bIsFurtherSide bool 464 | 465 | // Decide between point (0,0,1) and (1,1,0) as closest 466 | p1 := xins + yins 467 | if p1 > 1 { 468 | aScore = p1 - 1 469 | aPoint = 0x03 470 | aIsFurtherSide = true 471 | } else { 472 | aScore = 1 - p1 473 | aPoint = 0x04 474 | aIsFurtherSide = false 475 | } 476 | 477 | // Decide between point (0,1,0) and (1,0,1) as closest 478 | p2 := xins + zins 479 | if p2 > 1 { 480 | bScore = p2 - 1 481 | bPoint = 0x05 482 | bIsFurtherSide = true 483 | } else { 484 | bScore = 1 - p2 485 | bPoint = 0x02 486 | bIsFurtherSide = false 487 | } 488 | 489 | // The closest out of the two (1,0,0) and (0,1,1) will replace the furthest out of the two decided above, if closer. 490 | p3 := yins + zins 491 | if p3 > 1 { 492 | score := p3 - 1 493 | if aScore <= bScore && aScore < score { 494 | aScore = score 495 | aPoint = 0x06 496 | aIsFurtherSide = true 497 | } else if aScore > bScore && bScore < score { 498 | bScore = score 499 | bPoint = 0x06 500 | bIsFurtherSide = true 501 | } 502 | } else { 503 | score := 1 - p3 504 | if aScore <= bScore && aScore < score { 505 | aScore = score 506 | aPoint = 0x01 507 | aIsFurtherSide = false 508 | } else if aScore > bScore && bScore < score { 509 | bScore = score 510 | bPoint = 0x01 511 | bIsFurtherSide = false 512 | } 513 | } 514 | 515 | // Where each of the two closest points are determines how the extra two vertices are calculated. 516 | if aIsFurtherSide == bIsFurtherSide { 517 | if aIsFurtherSide { // Both closest points on (1,1,1) side 518 | 519 | // One of the two extra points is (1,1,1) 520 | dx_ext0 = dx0 - 1 - 3*squishConstant3D 521 | dy_ext0 = dy0 - 1 - 3*squishConstant3D 522 | dz_ext0 = dz0 - 1 - 3*squishConstant3D 523 | xsv_ext0 = xsb + 1 524 | ysv_ext0 = ysb + 1 525 | zsv_ext0 = zsb + 1 526 | 527 | // Other extra point is based on the shared axis. 528 | c := aPoint & bPoint 529 | if (c & 0x01) != 0 { 530 | dx_ext1 = dx0 - 2 - 2*squishConstant3D 531 | dy_ext1 = dy0 - 2*squishConstant3D 532 | dz_ext1 = dz0 - 2*squishConstant3D 533 | xsv_ext1 = xsb + 2 534 | ysv_ext1 = ysb 535 | zsv_ext1 = zsb 536 | } else if (c & 0x02) != 0 { 537 | dx_ext1 = dx0 - 2*squishConstant3D 538 | dy_ext1 = dy0 - 2 - 2*squishConstant3D 539 | dz_ext1 = dz0 - 2*squishConstant3D 540 | xsv_ext1 = xsb 541 | ysv_ext1 = ysb + 2 542 | zsv_ext1 = zsb 543 | } else { 544 | dx_ext1 = dx0 - 2*squishConstant3D 545 | dy_ext1 = dy0 - 2*squishConstant3D 546 | dz_ext1 = dz0 - 2 - 2*squishConstant3D 547 | xsv_ext1 = xsb 548 | ysv_ext1 = ysb 549 | zsv_ext1 = zsb + 2 550 | } 551 | } else { // Both closest points on (0,0,0) side 552 | 553 | // One of the two extra points is (0,0,0) 554 | dx_ext0 = dx0 555 | dy_ext0 = dy0 556 | dz_ext0 = dz0 557 | xsv_ext0 = xsb 558 | ysv_ext0 = ysb 559 | zsv_ext0 = zsb 560 | 561 | // Other extra point is based on the omitted axis. 562 | c := aPoint | bPoint 563 | if (c & 0x01) == 0 { 564 | dx_ext1 = dx0 + 1 - squishConstant3D 565 | dy_ext1 = dy0 - 1 - squishConstant3D 566 | dz_ext1 = dz0 - 1 - squishConstant3D 567 | xsv_ext1 = xsb - 1 568 | ysv_ext1 = ysb + 1 569 | zsv_ext1 = zsb + 1 570 | } else if (c & 0x02) == 0 { 571 | dx_ext1 = dx0 - 1 - squishConstant3D 572 | dy_ext1 = dy0 + 1 - squishConstant3D 573 | dz_ext1 = dz0 - 1 - squishConstant3D 574 | xsv_ext1 = xsb + 1 575 | ysv_ext1 = ysb - 1 576 | zsv_ext1 = zsb + 1 577 | } else { 578 | dx_ext1 = dx0 - 1 - squishConstant3D 579 | dy_ext1 = dy0 - 1 - squishConstant3D 580 | dz_ext1 = dz0 + 1 - squishConstant3D 581 | xsv_ext1 = xsb + 1 582 | ysv_ext1 = ysb + 1 583 | zsv_ext1 = zsb - 1 584 | } 585 | } 586 | } else { // One point on (0,0,0) side, one point on (1,1,1) side 587 | var c1, c2 byte 588 | if aIsFurtherSide { 589 | c1 = aPoint 590 | c2 = bPoint 591 | } else { 592 | c1 = bPoint 593 | c2 = aPoint 594 | } 595 | 596 | // One contribution is a permutation of (1,1,-1) 597 | if (c1 & 0x01) == 0 { 598 | dx_ext0 = dx0 + 1 - squishConstant3D 599 | dy_ext0 = dy0 - 1 - squishConstant3D 600 | dz_ext0 = dz0 - 1 - squishConstant3D 601 | xsv_ext0 = xsb - 1 602 | ysv_ext0 = ysb + 1 603 | zsv_ext0 = zsb + 1 604 | } else if (c1 & 0x02) == 0 { 605 | dx_ext0 = dx0 - 1 - squishConstant3D 606 | dy_ext0 = dy0 + 1 - squishConstant3D 607 | dz_ext0 = dz0 - 1 - squishConstant3D 608 | xsv_ext0 = xsb + 1 609 | ysv_ext0 = ysb - 1 610 | zsv_ext0 = zsb + 1 611 | } else { 612 | dx_ext0 = dx0 - 1 - squishConstant3D 613 | dy_ext0 = dy0 - 1 - squishConstant3D 614 | dz_ext0 = dz0 + 1 - squishConstant3D 615 | xsv_ext0 = xsb + 1 616 | ysv_ext0 = ysb + 1 617 | zsv_ext0 = zsb - 1 618 | } 619 | 620 | // One contribution is a permutation of (0,0,2) 621 | dx_ext1 = dx0 - 2*squishConstant3D 622 | dy_ext1 = dy0 - 2*squishConstant3D 623 | dz_ext1 = dz0 - 2*squishConstant3D 624 | xsv_ext1 = xsb 625 | ysv_ext1 = ysb 626 | zsv_ext1 = zsb 627 | if (c2 & 0x01) != 0 { 628 | dx_ext1 -= 2 629 | xsv_ext1 += 2 630 | } else if (c2 & 0x02) != 0 { 631 | dy_ext1 -= 2 632 | ysv_ext1 += 2 633 | } else { 634 | dz_ext1 -= 2 635 | zsv_ext1 += 2 636 | } 637 | } 638 | 639 | // Contribution (1,0,0) 640 | dx1 := dx0 - 1 - squishConstant3D 641 | dy1 := dy0 - 0 - squishConstant3D 642 | dz1 := dz0 - 0 - squishConstant3D 643 | attn1 := 2 - dx1*dx1 - dy1*dy1 - dz1*dz1 644 | if attn1 > 0 { 645 | attn1 *= attn1 646 | value += attn1 * attn1 * s.extrapolate3(xsb+1, ysb+0, zsb+0, dx1, dy1, dz1) 647 | } 648 | 649 | // Contribution (0,1,0) 650 | dx2 := dx0 - 0 - squishConstant3D 651 | dy2 := dy0 - 1 - squishConstant3D 652 | dz2 := dz1 653 | attn2 := 2 - dx2*dx2 - dy2*dy2 - dz2*dz2 654 | if attn2 > 0 { 655 | attn2 *= attn2 656 | value += attn2 * attn2 * s.extrapolate3(xsb+0, ysb+1, zsb+0, dx2, dy2, dz2) 657 | } 658 | 659 | // Contribution (0,0,1) 660 | dx3 := dx2 661 | dy3 := dy1 662 | dz3 := dz0 - 1 - squishConstant3D 663 | attn3 := 2 - dx3*dx3 - dy3*dy3 - dz3*dz3 664 | if attn3 > 0 { 665 | attn3 *= attn3 666 | value += attn3 * attn3 * s.extrapolate3(xsb+0, ysb+0, zsb+1, dx3, dy3, dz3) 667 | } 668 | 669 | // Contribution (1,1,0) 670 | dx4 := dx0 - 1 - 2*squishConstant3D 671 | dy4 := dy0 - 1 - 2*squishConstant3D 672 | dz4 := dz0 - 0 - 2*squishConstant3D 673 | attn4 := 2 - dx4*dx4 - dy4*dy4 - dz4*dz4 674 | if attn4 > 0 { 675 | attn4 *= attn4 676 | value += attn4 * attn4 * s.extrapolate3(xsb+1, ysb+1, zsb+0, dx4, dy4, dz4) 677 | } 678 | 679 | // Contribution (1,0,1) 680 | dx5 := dx4 681 | dy5 := dy0 - 0 - 2*squishConstant3D 682 | dz5 := dz0 - 1 - 2*squishConstant3D 683 | attn5 := 2 - dx5*dx5 - dy5*dy5 - dz5*dz5 684 | if attn5 > 0 { 685 | attn5 *= attn5 686 | value += attn5 * attn5 * s.extrapolate3(xsb+1, ysb+0, zsb+1, dx5, dy5, dz5) 687 | } 688 | 689 | // Contribution (0,1,1) 690 | dx6 := dx0 - 0 - 2*squishConstant3D 691 | dy6 := dy4 692 | dz6 := dz5 693 | attn6 := 2 - dx6*dx6 - dy6*dy6 - dz6*dz6 694 | if attn6 > 0 { 695 | attn6 *= attn6 696 | value += attn6 * attn6 * s.extrapolate3(xsb+0, ysb+1, zsb+1, dx6, dy6, dz6) 697 | } 698 | } 699 | 700 | // First extra vertex 701 | attn_ext0 := 2 - dx_ext0*dx_ext0 - dy_ext0*dy_ext0 - dz_ext0*dz_ext0 702 | if attn_ext0 > 0 { 703 | attn_ext0 *= attn_ext0 704 | value += attn_ext0 * attn_ext0 * s.extrapolate3(xsv_ext0, ysv_ext0, zsv_ext0, dx_ext0, dy_ext0, dz_ext0) 705 | } 706 | 707 | // Second extra vertex 708 | attn_ext1 := 2 - dx_ext1*dx_ext1 - dy_ext1*dy_ext1 - dz_ext1*dz_ext1 709 | if attn_ext1 > 0 { 710 | attn_ext1 *= attn_ext1 711 | value += attn_ext1 * attn_ext1 * s.extrapolate3(xsv_ext1, ysv_ext1, zsv_ext1, dx_ext1, dy_ext1, dz_ext1) 712 | } 713 | 714 | return value / normConstant3D 715 | } 716 | 717 | // Returns a random noise value in four dimensions. 718 | func (s *noise) Eval4(x, y, z, w float64) float64 { 719 | // Place input coordinates on simplectic honeycomb. 720 | stretchOffset := (x + y + z + w) * stretchConstant4D 721 | xs := x + stretchOffset 722 | ys := y + stretchOffset 723 | zs := z + stretchOffset 724 | ws := w + stretchOffset 725 | 726 | // Floor to get simplectic honeycomb coordinates of rhombo-hypercube super-cell origin. 727 | xsb := int32(math.Floor(xs)) 728 | ysb := int32(math.Floor(ys)) 729 | zsb := int32(math.Floor(zs)) 730 | wsb := int32(math.Floor(ws)) 731 | 732 | // Skew out to get actual coordinates of stretched rhombo-hypercube origin. We'll need these later. 733 | squishOffset := float64(xsb+ysb+zsb+wsb) * squishConstant4D 734 | xb := float64(xsb) + squishOffset 735 | yb := float64(ysb) + squishOffset 736 | zb := float64(zsb) + squishOffset 737 | wb := float64(wsb) + squishOffset 738 | 739 | // Compute simplectic honeycomb coordinates relative to rhombo-hypercube origin. 740 | xins := xs - float64(xsb) 741 | yins := ys - float64(ysb) 742 | zins := zs - float64(zsb) 743 | wins := ws - float64(wsb) 744 | 745 | // Sum those together to get a value that determines which region we're in. 746 | inSum := xins + yins + zins + wins 747 | 748 | // Positions relative to origin point. 749 | dx0 := x - xb 750 | dy0 := y - yb 751 | dz0 := z - zb 752 | dw0 := w - wb 753 | 754 | // We'll be defining these inside the next block and using them afterwards. 755 | var dx_ext0, dy_ext0, dz_ext0, dw_ext0 float64 756 | var dx_ext1, dy_ext1, dz_ext1, dw_ext1 float64 757 | var dx_ext2, dy_ext2, dz_ext2, dw_ext2 float64 758 | var xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0 int32 759 | var xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1 int32 760 | var xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2 int32 761 | 762 | var value float64 = 0 763 | if inSum <= 1 { // We're inside the pentachoron (4-Simplex) at (0,0,0,0) 764 | // Determine which two of (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0) are closest. 765 | var aPoint byte = 0x01 766 | aScore := xins 767 | var bPoint byte = 0x02 768 | bScore := yins 769 | if aScore >= bScore && zins > bScore { 770 | bScore = zins 771 | bPoint = 0x04 772 | } else if aScore < bScore && zins > aScore { 773 | aScore = zins 774 | aPoint = 0x04 775 | } 776 | if aScore >= bScore && wins > bScore { 777 | bScore = wins 778 | bPoint = 0x08 779 | } else if aScore < bScore && wins > aScore { 780 | aScore = wins 781 | aPoint = 0x08 782 | } 783 | 784 | // Now we determine the three lattice points not part of the pentachoron that may contribute. 785 | // This depends on the closest two pentachoron vertices, including (0,0,0,0) 786 | uins := 1 - inSum 787 | if uins > aScore || uins > bScore { // (0,0,0,0) is one of the closest two pentachoron vertices. 788 | var c byte 789 | // Our other closest vertex is the closest out of a and b. 790 | if bScore > aScore { 791 | c = bPoint 792 | } else { 793 | c = aPoint 794 | } 795 | if (c & 0x01) == 0 { 796 | xsv_ext0 = xsb - 1 797 | xsv_ext2 = xsb 798 | xsv_ext1 = xsv_ext2 799 | dx_ext0 = dx0 + 1 800 | dx_ext2 = dx0 801 | dx_ext1 = dx_ext2 802 | } else { 803 | xsv_ext2 = xsb + 1 804 | xsv_ext1 = xsv_ext2 805 | xsv_ext0 = xsv_ext1 806 | dx_ext2 = dx0 - 1 807 | dx_ext1 = dx_ext2 808 | dx_ext0 = dx_ext1 809 | } 810 | 811 | if (c & 0x02) == 0 { 812 | ysv_ext2 = ysb 813 | ysv_ext1 = ysv_ext2 814 | ysv_ext0 = ysv_ext1 815 | dy_ext2 = dy0 816 | dy_ext1 = dy_ext2 817 | dy_ext0 = dy_ext1 818 | if (c & 0x01) == 0x01 { 819 | ysv_ext0 -= 1 820 | dy_ext0 += 1 821 | } else { 822 | ysv_ext1 -= 1 823 | dy_ext1 += 1 824 | } 825 | } else { 826 | ysv_ext2 = ysb + 1 827 | ysv_ext1 = ysv_ext2 828 | ysv_ext0 = ysv_ext1 829 | dy_ext2 = dy0 - 1 830 | dy_ext1 = dy_ext2 831 | dy_ext0 = dy_ext1 832 | } 833 | 834 | if (c & 0x04) == 0 { 835 | zsv_ext2 = zsb 836 | zsv_ext1 = zsv_ext2 837 | zsv_ext0 = zsv_ext1 838 | dz_ext2 = dz0 839 | dz_ext1 = dz_ext2 840 | dz_ext0 = dz_ext1 841 | if (c & 0x03) != 0 { 842 | if (c & 0x03) == 0x03 { 843 | zsv_ext0 -= 1 844 | dz_ext0 += 1 845 | } else { 846 | zsv_ext1 -= 1 847 | dz_ext1 += 1 848 | } 849 | } else { 850 | zsv_ext2 -= 1 851 | dz_ext2 += 1 852 | } 853 | } else { 854 | zsv_ext2 = zsb + 1 855 | zsv_ext1 = zsv_ext2 856 | zsv_ext0 = zsv_ext1 857 | dz_ext2 = dz0 - 1 858 | dz_ext1 = dz_ext2 859 | dz_ext0 = dz_ext1 860 | } 861 | 862 | if (c & 0x08) == 0 { 863 | wsv_ext1 = wsb 864 | wsv_ext0 = wsv_ext1 865 | wsv_ext2 = wsb - 1 866 | dw_ext1 = dw0 867 | dw_ext0 = dw_ext1 868 | dw_ext2 = dw0 + 1 869 | } else { 870 | wsv_ext2 = wsb + 1 871 | wsv_ext1 = wsv_ext2 872 | wsv_ext0 = wsv_ext1 873 | dw_ext2 = dw0 - 1 874 | dw_ext1 = dw_ext2 875 | dw_ext0 = dw_ext1 876 | } 877 | } else { // (0,0,0,0) is not one of the closest two pentachoron vertices. 878 | c := aPoint | bPoint // Our three extra vertices are determined by the closest two. 879 | 880 | if (c & 0x01) == 0 { 881 | xsv_ext2 = xsb 882 | xsv_ext0 = xsv_ext2 883 | xsv_ext1 = xsb - 1 884 | dx_ext0 = dx0 - 2*squishConstant4D 885 | dx_ext1 = dx0 + 1 - squishConstant4D 886 | dx_ext2 = dx0 - squishConstant4D 887 | } else { 888 | xsv_ext2 = xsb + 1 889 | xsv_ext1 = xsv_ext2 890 | xsv_ext0 = xsv_ext1 891 | dx_ext0 = dx0 - 1 - 2*squishConstant4D 892 | dx_ext2 = dx0 - 1 - squishConstant4D 893 | dx_ext1 = dx_ext2 894 | } 895 | 896 | if (c & 0x02) == 0 { 897 | ysv_ext2 = ysb 898 | ysv_ext1 = ysv_ext2 899 | ysv_ext0 = ysv_ext1 900 | dy_ext0 = dy0 - 2*squishConstant4D 901 | dy_ext2 = dy0 - squishConstant4D 902 | dy_ext1 = dy_ext2 903 | if (c & 0x01) == 0x01 { 904 | ysv_ext1 -= 1 905 | dy_ext1 += 1 906 | } else { 907 | ysv_ext2 -= 1 908 | dy_ext2 += 1 909 | } 910 | } else { 911 | ysv_ext2 = ysb + 1 912 | ysv_ext1 = ysv_ext2 913 | ysv_ext0 = ysv_ext1 914 | dy_ext0 = dy0 - 1 - 2*squishConstant4D 915 | dy_ext2 = dy0 - 1 - squishConstant4D 916 | dy_ext1 = dy_ext2 917 | } 918 | 919 | if (c & 0x04) == 0 { 920 | zsv_ext2 = zsb 921 | zsv_ext1 = zsv_ext2 922 | zsv_ext0 = zsv_ext1 923 | dz_ext0 = dz0 - 2*squishConstant4D 924 | dz_ext2 = dz0 - squishConstant4D 925 | dz_ext1 = dz_ext2 926 | if (c & 0x03) == 0x03 { 927 | zsv_ext1 -= 1 928 | dz_ext1 += 1 929 | } else { 930 | zsv_ext2 -= 1 931 | dz_ext2 += 1 932 | } 933 | } else { 934 | zsv_ext2 = zsb + 1 935 | zsv_ext1 = zsv_ext2 936 | zsv_ext0 = zsv_ext1 937 | dz_ext0 = dz0 - 1 - 2*squishConstant4D 938 | dz_ext2 = dz0 - 1 - squishConstant4D 939 | dz_ext1 = dz_ext2 940 | } 941 | 942 | if (c & 0x08) == 0 { 943 | wsv_ext1 = wsb 944 | wsv_ext0 = wsv_ext1 945 | wsv_ext2 = wsb - 1 946 | dw_ext0 = dw0 - 2*squishConstant4D 947 | dw_ext1 = dw0 - squishConstant4D 948 | dw_ext2 = dw0 + 1 - squishConstant4D 949 | } else { 950 | wsv_ext2 = wsb + 1 951 | wsv_ext1 = wsv_ext2 952 | wsv_ext0 = wsv_ext1 953 | dw_ext0 = dw0 - 1 - 2*squishConstant4D 954 | dw_ext2 = dw0 - 1 - squishConstant4D 955 | dw_ext1 = dw_ext2 956 | } 957 | } 958 | 959 | // Contribution (0,0,0,0) 960 | attn0 := 2 - dx0*dx0 - dy0*dy0 - dz0*dz0 - dw0*dw0 961 | if attn0 > 0 { 962 | attn0 *= attn0 963 | value += attn0 * attn0 * s.extrapolate4(xsb+0, ysb+0, zsb+0, wsb+0, dx0, dy0, dz0, dw0) 964 | } 965 | 966 | // Contribution (1,0,0,0) 967 | dx1 := dx0 - 1 - squishConstant4D 968 | dy1 := dy0 - 0 - squishConstant4D 969 | dz1 := dz0 - 0 - squishConstant4D 970 | dw1 := dw0 - 0 - squishConstant4D 971 | attn1 := 2 - dx1*dx1 - dy1*dy1 - dz1*dz1 - dw1*dw1 972 | if attn1 > 0 { 973 | attn1 *= attn1 974 | value += attn1 * attn1 * s.extrapolate4(xsb+1, ysb+0, zsb+0, wsb+0, dx1, dy1, dz1, dw1) 975 | } 976 | 977 | // Contribution (0,1,0,0) 978 | dx2 := dx0 - 0 - squishConstant4D 979 | dy2 := dy0 - 1 - squishConstant4D 980 | dz2 := dz1 981 | dw2 := dw1 982 | attn2 := 2 - dx2*dx2 - dy2*dy2 - dz2*dz2 - dw2*dw2 983 | if attn2 > 0 { 984 | attn2 *= attn2 985 | value += attn2 * attn2 * s.extrapolate4(xsb+0, ysb+1, zsb+0, wsb+0, dx2, dy2, dz2, dw2) 986 | } 987 | 988 | // Contribution (0,0,1,0) 989 | dx3 := dx2 990 | dy3 := dy1 991 | dz3 := dz0 - 1 - squishConstant4D 992 | dw3 := dw1 993 | attn3 := 2 - dx3*dx3 - dy3*dy3 - dz3*dz3 - dw3*dw3 994 | if attn3 > 0 { 995 | attn3 *= attn3 996 | value += attn3 * attn3 * s.extrapolate4(xsb+0, ysb+0, zsb+1, wsb+0, dx3, dy3, dz3, dw3) 997 | } 998 | 999 | // Contribution (0,0,0,1) 1000 | dx4 := dx2 1001 | dy4 := dy1 1002 | dz4 := dz1 1003 | dw4 := dw0 - 1 - squishConstant4D 1004 | attn4 := 2 - dx4*dx4 - dy4*dy4 - dz4*dz4 - dw4*dw4 1005 | if attn4 > 0 { 1006 | attn4 *= attn4 1007 | value += attn4 * attn4 * s.extrapolate4(xsb+0, ysb+0, zsb+0, wsb+1, dx4, dy4, dz4, dw4) 1008 | } 1009 | } else if inSum >= 3 { // We're inside the pentachoron (4-Simplex) at (1,1,1,1) 1010 | // Determine which two of (1,1,1,0), (1,1,0,1), (1,0,1,1), (0,1,1,1) are closest. 1011 | var aPoint byte = 0x0E 1012 | aScore := xins 1013 | var bPoint byte = 0x0D 1014 | bScore := yins 1015 | if aScore <= bScore && zins < bScore { 1016 | bScore = zins 1017 | bPoint = 0x0B 1018 | } else if aScore > bScore && zins < aScore { 1019 | aScore = zins 1020 | aPoint = 0x0B 1021 | } 1022 | if aScore <= bScore && wins < bScore { 1023 | bScore = wins 1024 | bPoint = 0x07 1025 | } else if aScore > bScore && wins < aScore { 1026 | aScore = wins 1027 | aPoint = 0x07 1028 | } 1029 | 1030 | // Now we determine the three lattice points not part of the pentachoron that may contribute. 1031 | // This depends on the closest two pentachoron vertices, including (0,0,0,0) 1032 | uins := 4 - inSum 1033 | if uins < aScore || uins < bScore { // (1,1,1,1) is one of the closest two pentachoron vertices. 1034 | var c byte 1035 | // Our other closest vertex is the closest out of a and b. 1036 | if bScore < aScore { 1037 | c = bPoint 1038 | } else { 1039 | c = aPoint 1040 | } 1041 | 1042 | if (c & 0x01) != 0 { 1043 | xsv_ext0 = xsb + 2 1044 | xsv_ext2 = xsb + 1 1045 | xsv_ext1 = xsv_ext2 1046 | dx_ext0 = dx0 - 2 - 4*squishConstant4D 1047 | dx_ext2 = dx0 - 1 - 4*squishConstant4D 1048 | dx_ext1 = dx_ext2 1049 | } else { 1050 | xsv_ext2 = xsb 1051 | xsv_ext1 = xsv_ext2 1052 | xsv_ext0 = xsv_ext1 1053 | dx_ext2 = dx0 - 4*squishConstant4D 1054 | dx_ext1 = dx_ext2 1055 | dx_ext0 = dx_ext1 1056 | } 1057 | 1058 | if (c & 0x02) != 0 { 1059 | ysv_ext2 = ysb + 1 1060 | ysv_ext1 = ysv_ext2 1061 | ysv_ext0 = ysv_ext1 1062 | dy_ext2 = dy0 - 1 - 4*squishConstant4D 1063 | dy_ext1 = dy_ext2 1064 | dy_ext0 = dy_ext1 1065 | if (c & 0x01) != 0 { 1066 | ysv_ext1 += 1 1067 | dy_ext1 -= 1 1068 | } else { 1069 | ysv_ext0 += 1 1070 | dy_ext0 -= 1 1071 | } 1072 | } else { 1073 | ysv_ext2 = ysb 1074 | ysv_ext1 = ysv_ext2 1075 | ysv_ext0 = ysv_ext1 1076 | dy_ext2 = dy0 - 4*squishConstant4D 1077 | dy_ext1 = dy_ext2 1078 | dy_ext0 = dy_ext1 1079 | } 1080 | 1081 | if (c & 0x04) != 0 { 1082 | zsv_ext2 = zsb + 1 1083 | zsv_ext1 = zsv_ext2 1084 | zsv_ext0 = zsv_ext1 1085 | dz_ext2 = dz0 - 1 - 4*squishConstant4D 1086 | dz_ext1 = dz_ext2 1087 | dz_ext0 = dz_ext1 1088 | if (c & 0x03) != 0x03 { 1089 | if (c & 0x03) == 0 { 1090 | zsv_ext0 += 1 1091 | dz_ext0 -= 1 1092 | } else { 1093 | zsv_ext1 += 1 1094 | dz_ext1 -= 1 1095 | } 1096 | } else { 1097 | zsv_ext2 += 1 1098 | dz_ext2 -= 1 1099 | } 1100 | } else { 1101 | zsv_ext2 = zsb 1102 | zsv_ext1 = zsv_ext2 1103 | zsv_ext0 = zsv_ext1 1104 | dz_ext2 = dz0 - 4*squishConstant4D 1105 | dz_ext1 = dz_ext2 1106 | dz_ext0 = dz_ext1 1107 | } 1108 | 1109 | if (c & 0x08) != 0 { 1110 | wsv_ext1 = wsb + 1 1111 | wsv_ext0 = wsv_ext1 1112 | wsv_ext2 = wsb + 2 1113 | dw_ext1 = dw0 - 1 - 4*squishConstant4D 1114 | dw_ext0 = dw_ext1 1115 | dw_ext2 = dw0 - 2 - 4*squishConstant4D 1116 | } else { 1117 | wsv_ext2 = wsb 1118 | wsv_ext1 = wsv_ext2 1119 | wsv_ext0 = wsv_ext1 1120 | dw_ext2 = dw0 - 4*squishConstant4D 1121 | dw_ext1 = dw_ext2 1122 | dw_ext0 = dw_ext1 1123 | } 1124 | } else { // (1,1,1,1) is not one of the closest two pentachoron vertices. 1125 | c := aPoint & bPoint // Our three extra vertices are determined by the closest two. 1126 | 1127 | if (c & 0x01) != 0 { 1128 | xsv_ext2 = xsb + 1 1129 | xsv_ext0 = xsv_ext2 1130 | xsv_ext1 = xsb + 2 1131 | dx_ext0 = dx0 - 1 - 2*squishConstant4D 1132 | dx_ext1 = dx0 - 2 - 3*squishConstant4D 1133 | dx_ext2 = dx0 - 1 - 3*squishConstant4D 1134 | } else { 1135 | xsv_ext2 = xsb 1136 | xsv_ext1 = xsv_ext2 1137 | xsv_ext0 = xsv_ext1 1138 | dx_ext0 = dx0 - 2*squishConstant4D 1139 | dx_ext2 = dx0 - 3*squishConstant4D 1140 | dx_ext1 = dx_ext2 1141 | } 1142 | 1143 | if (c & 0x02) != 0 { 1144 | ysv_ext2 = ysb + 1 1145 | ysv_ext1 = ysv_ext2 1146 | ysv_ext0 = ysv_ext1 1147 | dy_ext0 = dy0 - 1 - 2*squishConstant4D 1148 | dy_ext2 = dy0 - 1 - 3*squishConstant4D 1149 | dy_ext1 = dy_ext2 1150 | if (c & 0x01) != 0 { 1151 | ysv_ext2 += 1 1152 | dy_ext2 -= 1 1153 | } else { 1154 | ysv_ext1 += 1 1155 | dy_ext1 -= 1 1156 | } 1157 | } else { 1158 | ysv_ext2 = ysb 1159 | ysv_ext1 = ysv_ext2 1160 | ysv_ext0 = ysv_ext1 1161 | dy_ext0 = dy0 - 2*squishConstant4D 1162 | dy_ext2 = dy0 - 3*squishConstant4D 1163 | dy_ext1 = dy_ext2 1164 | } 1165 | 1166 | if (c & 0x04) != 0 { 1167 | zsv_ext2 = zsb + 1 1168 | zsv_ext1 = zsv_ext2 1169 | zsv_ext0 = zsv_ext1 1170 | dz_ext0 = dz0 - 1 - 2*squishConstant4D 1171 | dz_ext2 = dz0 - 1 - 3*squishConstant4D 1172 | dz_ext1 = dz_ext2 1173 | if (c & 0x03) != 0 { 1174 | zsv_ext2 += 1 1175 | dz_ext2 -= 1 1176 | } else { 1177 | zsv_ext1 += 1 1178 | dz_ext1 -= 1 1179 | } 1180 | } else { 1181 | zsv_ext2 = zsb 1182 | zsv_ext1 = zsv_ext2 1183 | zsv_ext0 = zsv_ext1 1184 | dz_ext0 = dz0 - 2*squishConstant4D 1185 | dz_ext2 = dz0 - 3*squishConstant4D 1186 | dz_ext1 = dz_ext2 1187 | } 1188 | 1189 | if (c & 0x08) != 0 { 1190 | wsv_ext1 = wsb + 1 1191 | wsv_ext0 = wsv_ext1 1192 | wsv_ext2 = wsb + 2 1193 | dw_ext0 = dw0 - 1 - 2*squishConstant4D 1194 | dw_ext1 = dw0 - 1 - 3*squishConstant4D 1195 | dw_ext2 = dw0 - 2 - 3*squishConstant4D 1196 | } else { 1197 | wsv_ext2 = wsb 1198 | wsv_ext1 = wsv_ext2 1199 | wsv_ext0 = wsv_ext1 1200 | dw_ext0 = dw0 - 2*squishConstant4D 1201 | dw_ext2 = dw0 - 3*squishConstant4D 1202 | dw_ext1 = dw_ext2 1203 | } 1204 | } 1205 | 1206 | // Contribution (1,1,1,0) 1207 | dx4 := dx0 - 1 - 3*squishConstant4D 1208 | dy4 := dy0 - 1 - 3*squishConstant4D 1209 | dz4 := dz0 - 1 - 3*squishConstant4D 1210 | dw4 := dw0 - 3*squishConstant4D 1211 | attn4 := 2 - dx4*dx4 - dy4*dy4 - dz4*dz4 - dw4*dw4 1212 | if attn4 > 0 { 1213 | attn4 *= attn4 1214 | value += attn4 * attn4 * s.extrapolate4(xsb+1, ysb+1, zsb+1, wsb+0, dx4, dy4, dz4, dw4) 1215 | } 1216 | 1217 | // Contribution (1,1,0,1) 1218 | dx3 := dx4 1219 | dy3 := dy4 1220 | dz3 := dz0 - 3*squishConstant4D 1221 | dw3 := dw0 - 1 - 3*squishConstant4D 1222 | attn3 := 2 - dx3*dx3 - dy3*dy3 - dz3*dz3 - dw3*dw3 1223 | if attn3 > 0 { 1224 | attn3 *= attn3 1225 | value += attn3 * attn3 * s.extrapolate4(xsb+1, ysb+1, zsb+0, wsb+1, dx3, dy3, dz3, dw3) 1226 | } 1227 | 1228 | // Contribution (1,0,1,1) 1229 | dx2 := dx4 1230 | dy2 := dy0 - 3*squishConstant4D 1231 | dz2 := dz4 1232 | dw2 := dw3 1233 | attn2 := 2 - dx2*dx2 - dy2*dy2 - dz2*dz2 - dw2*dw2 1234 | if attn2 > 0 { 1235 | attn2 *= attn2 1236 | value += attn2 * attn2 * s.extrapolate4(xsb+1, ysb+0, zsb+1, wsb+1, dx2, dy2, dz2, dw2) 1237 | } 1238 | 1239 | // Contribution (0,1,1,1) 1240 | dx1 := dx0 - 3*squishConstant4D 1241 | dz1 := dz4 1242 | dy1 := dy4 1243 | dw1 := dw3 1244 | attn1 := 2 - dx1*dx1 - dy1*dy1 - dz1*dz1 - dw1*dw1 1245 | if attn1 > 0 { 1246 | attn1 *= attn1 1247 | value += attn1 * attn1 * s.extrapolate4(xsb+0, ysb+1, zsb+1, wsb+1, dx1, dy1, dz1, dw1) 1248 | } 1249 | 1250 | // Contribution (1,1,1,1) 1251 | dx0 = dx0 - 1 - 4*squishConstant4D 1252 | dy0 = dy0 - 1 - 4*squishConstant4D 1253 | dz0 = dz0 - 1 - 4*squishConstant4D 1254 | dw0 = dw0 - 1 - 4*squishConstant4D 1255 | attn0 := 2 - dx0*dx0 - dy0*dy0 - dz0*dz0 - dw0*dw0 1256 | if attn0 > 0 { 1257 | attn0 *= attn0 1258 | value += attn0 * attn0 * s.extrapolate4(xsb+1, ysb+1, zsb+1, wsb+1, dx0, dy0, dz0, dw0) 1259 | } 1260 | } else if inSum <= 2 { // We're inside the first dispentachoron (Rectified 4-Simplex) 1261 | var aScore, bScore float64 1262 | var aPoint, bPoint byte 1263 | var aIsBiggerSide bool = true 1264 | var bIsBiggerSide bool = true 1265 | 1266 | // Decide between (1,1,0,0) and (0,0,1,1) 1267 | if xins+yins > zins+wins { 1268 | aScore = xins + yins 1269 | aPoint = 0x03 1270 | } else { 1271 | aScore = zins + wins 1272 | aPoint = 0x0C 1273 | } 1274 | 1275 | // Decide between (1,0,1,0) and (0,1,0,1) 1276 | if xins+zins > yins+wins { 1277 | bScore = xins + zins 1278 | bPoint = 0x05 1279 | } else { 1280 | bScore = yins + wins 1281 | bPoint = 0x0A 1282 | } 1283 | 1284 | // Closer between (1,0,0,1) and (0,1,1,0) will replace the further of a and b, if closer. 1285 | if xins+wins > yins+zins { 1286 | score := xins + wins 1287 | if aScore >= bScore && score > bScore { 1288 | bScore = score 1289 | bPoint = 0x09 1290 | } else if aScore < bScore && score > aScore { 1291 | aScore = score 1292 | aPoint = 0x09 1293 | } 1294 | } else { 1295 | score := yins + zins 1296 | if aScore >= bScore && score > bScore { 1297 | bScore = score 1298 | bPoint = 0x06 1299 | } else if aScore < bScore && score > aScore { 1300 | aScore = score 1301 | aPoint = 0x06 1302 | } 1303 | } 1304 | 1305 | // Decide if (1,0,0,0) is closer. 1306 | p1 := 2 - inSum + xins 1307 | if aScore >= bScore && p1 > bScore { 1308 | bScore = p1 1309 | bPoint = 0x01 1310 | bIsBiggerSide = false 1311 | } else if aScore < bScore && p1 > aScore { 1312 | aScore = p1 1313 | aPoint = 0x01 1314 | aIsBiggerSide = false 1315 | } 1316 | 1317 | // Decide if (0,1,0,0) is closer. 1318 | p2 := 2 - inSum + yins 1319 | if aScore >= bScore && p2 > bScore { 1320 | bScore = p2 1321 | bPoint = 0x02 1322 | bIsBiggerSide = false 1323 | } else if aScore < bScore && p2 > aScore { 1324 | aScore = p2 1325 | aPoint = 0x02 1326 | aIsBiggerSide = false 1327 | } 1328 | 1329 | // Decide if (0,0,1,0) is closer. 1330 | p3 := 2 - inSum + zins 1331 | if aScore >= bScore && p3 > bScore { 1332 | bScore = p3 1333 | bPoint = 0x04 1334 | bIsBiggerSide = false 1335 | } else if aScore < bScore && p3 > aScore { 1336 | aScore = p3 1337 | aPoint = 0x04 1338 | aIsBiggerSide = false 1339 | } 1340 | 1341 | // Decide if (0,0,0,1) is closer. 1342 | p4 := 2 - inSum + wins 1343 | if aScore >= bScore && p4 > bScore { 1344 | bScore = p4 1345 | bPoint = 0x08 1346 | bIsBiggerSide = false 1347 | } else if aScore < bScore && p4 > aScore { 1348 | aScore = p4 1349 | aPoint = 0x08 1350 | aIsBiggerSide = false 1351 | } 1352 | 1353 | // Where each of the two closest points are determines how the extra three vertices are calculated. 1354 | if aIsBiggerSide == bIsBiggerSide { 1355 | if aIsBiggerSide { // Both closest points on the bigger side 1356 | c1 := aPoint | bPoint 1357 | c2 := aPoint & bPoint 1358 | if (c1 & 0x01) == 0 { 1359 | xsv_ext0 = xsb 1360 | xsv_ext1 = xsb - 1 1361 | dx_ext0 = dx0 - 3*squishConstant4D 1362 | dx_ext1 = dx0 + 1 - 2*squishConstant4D 1363 | } else { 1364 | xsv_ext1 = xsb + 1 1365 | xsv_ext0 = xsv_ext1 1366 | dx_ext0 = dx0 - 1 - 3*squishConstant4D 1367 | dx_ext1 = dx0 - 1 - 2*squishConstant4D 1368 | } 1369 | 1370 | if (c1 & 0x02) == 0 { 1371 | ysv_ext0 = ysb 1372 | ysv_ext1 = ysb - 1 1373 | dy_ext0 = dy0 - 3*squishConstant4D 1374 | dy_ext1 = dy0 + 1 - 2*squishConstant4D 1375 | } else { 1376 | ysv_ext1 = ysb + 1 1377 | ysv_ext0 = ysv_ext1 1378 | dy_ext0 = dy0 - 1 - 3*squishConstant4D 1379 | dy_ext1 = dy0 - 1 - 2*squishConstant4D 1380 | } 1381 | 1382 | if (c1 & 0x04) == 0 { 1383 | zsv_ext0 = zsb 1384 | zsv_ext1 = zsb - 1 1385 | dz_ext0 = dz0 - 3*squishConstant4D 1386 | dz_ext1 = dz0 + 1 - 2*squishConstant4D 1387 | } else { 1388 | zsv_ext1 = zsb + 1 1389 | zsv_ext0 = zsv_ext1 1390 | dz_ext0 = dz0 - 1 - 3*squishConstant4D 1391 | dz_ext1 = dz0 - 1 - 2*squishConstant4D 1392 | } 1393 | 1394 | if (c1 & 0x08) == 0 { 1395 | wsv_ext0 = wsb 1396 | wsv_ext1 = wsb - 1 1397 | dw_ext0 = dw0 - 3*squishConstant4D 1398 | dw_ext1 = dw0 + 1 - 2*squishConstant4D 1399 | } else { 1400 | wsv_ext1 = wsb + 1 1401 | wsv_ext0 = wsv_ext1 1402 | dw_ext0 = dw0 - 1 - 3*squishConstant4D 1403 | dw_ext1 = dw0 - 1 - 2*squishConstant4D 1404 | } 1405 | 1406 | // One combination is a permutation of (0,0,0,2) based on c2 1407 | xsv_ext2 = xsb 1408 | ysv_ext2 = ysb 1409 | zsv_ext2 = zsb 1410 | wsv_ext2 = wsb 1411 | dx_ext2 = dx0 - 2*squishConstant4D 1412 | dy_ext2 = dy0 - 2*squishConstant4D 1413 | dz_ext2 = dz0 - 2*squishConstant4D 1414 | dw_ext2 = dw0 - 2*squishConstant4D 1415 | if (c2 & 0x01) != 0 { 1416 | xsv_ext2 += 2 1417 | dx_ext2 -= 2 1418 | } else if (c2 & 0x02) != 0 { 1419 | ysv_ext2 += 2 1420 | dy_ext2 -= 2 1421 | } else if (c2 & 0x04) != 0 { 1422 | zsv_ext2 += 2 1423 | dz_ext2 -= 2 1424 | } else { 1425 | wsv_ext2 += 2 1426 | dw_ext2 -= 2 1427 | } 1428 | 1429 | } else { // Both closest points on the smaller side 1430 | // One of the two extra points is (0,0,0,0) 1431 | xsv_ext2 = xsb 1432 | ysv_ext2 = ysb 1433 | zsv_ext2 = zsb 1434 | wsv_ext2 = wsb 1435 | dx_ext2 = dx0 1436 | dy_ext2 = dy0 1437 | dz_ext2 = dz0 1438 | dw_ext2 = dw0 1439 | 1440 | // Other two points are based on the omitted axes. 1441 | c := aPoint | bPoint 1442 | 1443 | if (c & 0x01) == 0 { 1444 | xsv_ext0 = xsb - 1 1445 | xsv_ext1 = xsb 1446 | dx_ext0 = dx0 + 1 - squishConstant4D 1447 | dx_ext1 = dx0 - squishConstant4D 1448 | } else { 1449 | xsv_ext1 = xsb + 1 1450 | xsv_ext0 = xsv_ext1 1451 | dx_ext1 = dx0 - 1 - squishConstant4D 1452 | dx_ext0 = dx_ext1 1453 | } 1454 | 1455 | if (c & 0x02) == 0 { 1456 | ysv_ext1 = ysb 1457 | ysv_ext0 = ysv_ext1 1458 | dy_ext1 = dy0 - squishConstant4D 1459 | dy_ext0 = dy_ext1 1460 | if (c & 0x01) == 0x01 { 1461 | ysv_ext0 -= 1 1462 | dy_ext0 += 1 1463 | } else { 1464 | ysv_ext1 -= 1 1465 | dy_ext1 += 1 1466 | } 1467 | } else { 1468 | ysv_ext1 = ysb + 1 1469 | ysv_ext0 = ysv_ext1 1470 | dy_ext1 = dy0 - 1 - squishConstant4D 1471 | dy_ext0 = dy_ext1 1472 | } 1473 | 1474 | if (c & 0x04) == 0 { 1475 | zsv_ext1 = zsb 1476 | zsv_ext0 = zsv_ext1 1477 | dz_ext1 = dz0 - squishConstant4D 1478 | dz_ext0 = dz_ext1 1479 | if (c & 0x03) == 0x03 { 1480 | zsv_ext0 -= 1 1481 | dz_ext0 += 1 1482 | } else { 1483 | zsv_ext1 -= 1 1484 | dz_ext1 += 1 1485 | } 1486 | } else { 1487 | zsv_ext1 = zsb + 1 1488 | zsv_ext0 = zsv_ext1 1489 | dz_ext1 = dz0 - 1 - squishConstant4D 1490 | dz_ext0 = dz_ext1 1491 | } 1492 | 1493 | if (c & 0x08) == 0 { 1494 | wsv_ext0 = wsb 1495 | wsv_ext1 = wsb - 1 1496 | dw_ext0 = dw0 - squishConstant4D 1497 | dw_ext1 = dw0 + 1 - squishConstant4D 1498 | } else { 1499 | wsv_ext1 = wsb + 1 1500 | wsv_ext0 = wsv_ext1 1501 | dw_ext1 = dw0 - 1 - squishConstant4D 1502 | dw_ext0 = dw_ext1 1503 | } 1504 | 1505 | } 1506 | } else { // One point on each "side" 1507 | var c1, c2 byte 1508 | if aIsBiggerSide { 1509 | c1 = aPoint 1510 | c2 = bPoint 1511 | } else { 1512 | c1 = bPoint 1513 | c2 = aPoint 1514 | } 1515 | 1516 | // Two contributions are the bigger-sided point with each 0 replaced with -1. 1517 | if (c1 & 0x01) == 0 { 1518 | xsv_ext0 = xsb - 1 1519 | xsv_ext1 = xsb 1520 | dx_ext0 = dx0 + 1 - squishConstant4D 1521 | dx_ext1 = dx0 - squishConstant4D 1522 | } else { 1523 | xsv_ext1 = xsb + 1 1524 | xsv_ext0 = xsv_ext1 1525 | dx_ext1 = dx0 - 1 - squishConstant4D 1526 | dx_ext0 = dx_ext1 1527 | } 1528 | 1529 | if (c1 & 0x02) == 0 { 1530 | ysv_ext1 = ysb 1531 | ysv_ext0 = ysv_ext1 1532 | dy_ext1 = dy0 - squishConstant4D 1533 | dy_ext0 = dy_ext1 1534 | if (c1 & 0x01) == 0x01 { 1535 | ysv_ext0 -= 1 1536 | dy_ext0 += 1 1537 | } else { 1538 | ysv_ext1 -= 1 1539 | dy_ext1 += 1 1540 | } 1541 | } else { 1542 | ysv_ext1 = ysb + 1 1543 | ysv_ext0 = ysv_ext1 1544 | dy_ext1 = dy0 - 1 - squishConstant4D 1545 | dy_ext0 = dy_ext1 1546 | } 1547 | 1548 | if (c1 & 0x04) == 0 { 1549 | zsv_ext1 = zsb 1550 | zsv_ext0 = zsv_ext1 1551 | dz_ext1 = dz0 - squishConstant4D 1552 | dz_ext0 = dz_ext1 1553 | if (c1 & 0x03) == 0x03 { 1554 | zsv_ext0 -= 1 1555 | dz_ext0 += 1 1556 | } else { 1557 | zsv_ext1 -= 1 1558 | dz_ext1 += 1 1559 | } 1560 | } else { 1561 | zsv_ext1 = zsb + 1 1562 | zsv_ext0 = zsv_ext1 1563 | dz_ext1 = dz0 - 1 - squishConstant4D 1564 | dz_ext0 = dz_ext1 1565 | } 1566 | 1567 | if (c1 & 0x08) == 0 { 1568 | wsv_ext0 = wsb 1569 | wsv_ext1 = wsb - 1 1570 | dw_ext0 = dw0 - squishConstant4D 1571 | dw_ext1 = dw0 + 1 - squishConstant4D 1572 | } else { 1573 | wsv_ext1 = wsb + 1 1574 | wsv_ext0 = wsv_ext1 1575 | dw_ext1 = dw0 - 1 - squishConstant4D 1576 | dw_ext0 = dw_ext1 1577 | } 1578 | 1579 | // One contribution is a permutation of (0,0,0,2) based on the smaller-sided point 1580 | xsv_ext2 = xsb 1581 | ysv_ext2 = ysb 1582 | zsv_ext2 = zsb 1583 | wsv_ext2 = wsb 1584 | dx_ext2 = dx0 - 2*squishConstant4D 1585 | dy_ext2 = dy0 - 2*squishConstant4D 1586 | dz_ext2 = dz0 - 2*squishConstant4D 1587 | dw_ext2 = dw0 - 2*squishConstant4D 1588 | if (c2 & 0x01) != 0 { 1589 | xsv_ext2 += 2 1590 | dx_ext2 -= 2 1591 | } else if (c2 & 0x02) != 0 { 1592 | ysv_ext2 += 2 1593 | dy_ext2 -= 2 1594 | } else if (c2 & 0x04) != 0 { 1595 | zsv_ext2 += 2 1596 | dz_ext2 -= 2 1597 | } else { 1598 | wsv_ext2 += 2 1599 | dw_ext2 -= 2 1600 | } 1601 | } 1602 | 1603 | // Contribution (1,0,0,0) 1604 | dx1 := dx0 - 1 - squishConstant4D 1605 | dy1 := dy0 - 0 - squishConstant4D 1606 | dz1 := dz0 - 0 - squishConstant4D 1607 | dw1 := dw0 - 0 - squishConstant4D 1608 | attn1 := 2 - dx1*dx1 - dy1*dy1 - dz1*dz1 - dw1*dw1 1609 | if attn1 > 0 { 1610 | attn1 *= attn1 1611 | value += attn1 * attn1 * s.extrapolate4(xsb+1, ysb+0, zsb+0, wsb+0, dx1, dy1, dz1, dw1) 1612 | } 1613 | 1614 | // Contribution (0,1,0,0) 1615 | dx2 := dx0 - 0 - squishConstant4D 1616 | dy2 := dy0 - 1 - squishConstant4D 1617 | dz2 := dz1 1618 | dw2 := dw1 1619 | attn2 := 2 - dx2*dx2 - dy2*dy2 - dz2*dz2 - dw2*dw2 1620 | if attn2 > 0 { 1621 | attn2 *= attn2 1622 | value += attn2 * attn2 * s.extrapolate4(xsb+0, ysb+1, zsb+0, wsb+0, dx2, dy2, dz2, dw2) 1623 | } 1624 | 1625 | // Contribution (0,0,1,0) 1626 | dx3 := dx2 1627 | dy3 := dy1 1628 | dz3 := dz0 - 1 - squishConstant4D 1629 | dw3 := dw1 1630 | attn3 := 2 - dx3*dx3 - dy3*dy3 - dz3*dz3 - dw3*dw3 1631 | if attn3 > 0 { 1632 | attn3 *= attn3 1633 | value += attn3 * attn3 * s.extrapolate4(xsb+0, ysb+0, zsb+1, wsb+0, dx3, dy3, dz3, dw3) 1634 | } 1635 | 1636 | // Contribution (0,0,0,1) 1637 | dx4 := dx2 1638 | dy4 := dy1 1639 | dz4 := dz1 1640 | dw4 := dw0 - 1 - squishConstant4D 1641 | attn4 := 2 - dx4*dx4 - dy4*dy4 - dz4*dz4 - dw4*dw4 1642 | if attn4 > 0 { 1643 | attn4 *= attn4 1644 | value += attn4 * attn4 * s.extrapolate4(xsb+0, ysb+0, zsb+0, wsb+1, dx4, dy4, dz4, dw4) 1645 | } 1646 | 1647 | // Contribution (1,1,0,0) 1648 | dx5 := dx0 - 1 - 2*squishConstant4D 1649 | dy5 := dy0 - 1 - 2*squishConstant4D 1650 | dz5 := dz0 - 0 - 2*squishConstant4D 1651 | dw5 := dw0 - 0 - 2*squishConstant4D 1652 | attn5 := 2 - dx5*dx5 - dy5*dy5 - dz5*dz5 - dw5*dw5 1653 | if attn5 > 0 { 1654 | attn5 *= attn5 1655 | value += attn5 * attn5 * s.extrapolate4(xsb+1, ysb+1, zsb+0, wsb+0, dx5, dy5, dz5, dw5) 1656 | } 1657 | 1658 | // Contribution (1,0,1,0) 1659 | dx6 := dx0 - 1 - 2*squishConstant4D 1660 | dy6 := dy0 - 0 - 2*squishConstant4D 1661 | dz6 := dz0 - 1 - 2*squishConstant4D 1662 | dw6 := dw0 - 0 - 2*squishConstant4D 1663 | attn6 := 2 - dx6*dx6 - dy6*dy6 - dz6*dz6 - dw6*dw6 1664 | if attn6 > 0 { 1665 | attn6 *= attn6 1666 | value += attn6 * attn6 * s.extrapolate4(xsb+1, ysb+0, zsb+1, wsb+0, dx6, dy6, dz6, dw6) 1667 | } 1668 | 1669 | // Contribution (1,0,0,1) 1670 | dx7 := dx0 - 1 - 2*squishConstant4D 1671 | dy7 := dy0 - 0 - 2*squishConstant4D 1672 | dz7 := dz0 - 0 - 2*squishConstant4D 1673 | dw7 := dw0 - 1 - 2*squishConstant4D 1674 | attn7 := 2 - dx7*dx7 - dy7*dy7 - dz7*dz7 - dw7*dw7 1675 | if attn7 > 0 { 1676 | attn7 *= attn7 1677 | value += attn7 * attn7 * s.extrapolate4(xsb+1, ysb+0, zsb+0, wsb+1, dx7, dy7, dz7, dw7) 1678 | } 1679 | 1680 | // Contribution (0,1,1,0) 1681 | dx8 := dx0 - 0 - 2*squishConstant4D 1682 | dy8 := dy0 - 1 - 2*squishConstant4D 1683 | dz8 := dz0 - 1 - 2*squishConstant4D 1684 | dw8 := dw0 - 0 - 2*squishConstant4D 1685 | attn8 := 2 - dx8*dx8 - dy8*dy8 - dz8*dz8 - dw8*dw8 1686 | if attn8 > 0 { 1687 | attn8 *= attn8 1688 | value += attn8 * attn8 * s.extrapolate4(xsb+0, ysb+1, zsb+1, wsb+0, dx8, dy8, dz8, dw8) 1689 | } 1690 | 1691 | // Contribution (0,1,0,1) 1692 | dx9 := dx0 - 0 - 2*squishConstant4D 1693 | dy9 := dy0 - 1 - 2*squishConstant4D 1694 | dz9 := dz0 - 0 - 2*squishConstant4D 1695 | dw9 := dw0 - 1 - 2*squishConstant4D 1696 | attn9 := 2 - dx9*dx9 - dy9*dy9 - dz9*dz9 - dw9*dw9 1697 | if attn9 > 0 { 1698 | attn9 *= attn9 1699 | value += attn9 * attn9 * s.extrapolate4(xsb+0, ysb+1, zsb+0, wsb+1, dx9, dy9, dz9, dw9) 1700 | } 1701 | 1702 | // Contribution (0,0,1,1) 1703 | dx10 := dx0 - 0 - 2*squishConstant4D 1704 | dy10 := dy0 - 0 - 2*squishConstant4D 1705 | dz10 := dz0 - 1 - 2*squishConstant4D 1706 | dw10 := dw0 - 1 - 2*squishConstant4D 1707 | attn10 := 2 - dx10*dx10 - dy10*dy10 - dz10*dz10 - dw10*dw10 1708 | if attn10 > 0 { 1709 | attn10 *= attn10 1710 | value += attn10 * attn10 * s.extrapolate4(xsb+0, ysb+0, zsb+1, wsb+1, dx10, dy10, dz10, dw10) 1711 | } 1712 | } else { // We're inside the second dispentachoron (Rectified 4-Simplex) 1713 | var aScore, bScore float64 1714 | var aPoint, bPoint byte 1715 | var aIsBiggerSide bool = true 1716 | var bIsBiggerSide bool = true 1717 | 1718 | // Decide between (0,0,1,1) and (1,1,0,0) 1719 | if xins+yins < zins+wins { 1720 | aScore = xins + yins 1721 | aPoint = 0x0C 1722 | } else { 1723 | aScore = zins + wins 1724 | aPoint = 0x03 1725 | } 1726 | 1727 | // Decide between (0,1,0,1) and (1,0,1,0) 1728 | if xins+zins < yins+wins { 1729 | bScore = xins + zins 1730 | bPoint = 0x0A 1731 | } else { 1732 | bScore = yins + wins 1733 | bPoint = 0x05 1734 | } 1735 | 1736 | // Closer between (0,1,1,0) and (1,0,0,1) will replace the further of a and b, if closer. 1737 | if xins+wins < yins+zins { 1738 | score := xins + wins 1739 | if aScore <= bScore && score < bScore { 1740 | bScore = score 1741 | bPoint = 0x06 1742 | } else if aScore > bScore && score < aScore { 1743 | aScore = score 1744 | aPoint = 0x06 1745 | } 1746 | } else { 1747 | score := yins + zins 1748 | if aScore <= bScore && score < bScore { 1749 | bScore = score 1750 | bPoint = 0x09 1751 | } else if aScore > bScore && score < aScore { 1752 | aScore = score 1753 | aPoint = 0x09 1754 | } 1755 | } 1756 | 1757 | // Decide if (0,1,1,1) is closer. 1758 | p1 := 3 - inSum + xins 1759 | if aScore <= bScore && p1 < bScore { 1760 | bScore = p1 1761 | bPoint = 0x0E 1762 | bIsBiggerSide = false 1763 | } else if aScore > bScore && p1 < aScore { 1764 | aScore = p1 1765 | aPoint = 0x0E 1766 | aIsBiggerSide = false 1767 | } 1768 | 1769 | // Decide if (1,0,1,1) is closer. 1770 | p2 := 3 - inSum + yins 1771 | if aScore <= bScore && p2 < bScore { 1772 | bScore = p2 1773 | bPoint = 0x0D 1774 | bIsBiggerSide = false 1775 | } else if aScore > bScore && p2 < aScore { 1776 | aScore = p2 1777 | aPoint = 0x0D 1778 | aIsBiggerSide = false 1779 | } 1780 | 1781 | // Decide if (1,1,0,1) is closer. 1782 | p3 := 3 - inSum + zins 1783 | if aScore <= bScore && p3 < bScore { 1784 | bScore = p3 1785 | bPoint = 0x0B 1786 | bIsBiggerSide = false 1787 | } else if aScore > bScore && p3 < aScore { 1788 | aScore = p3 1789 | aPoint = 0x0B 1790 | aIsBiggerSide = false 1791 | } 1792 | 1793 | // Decide if (1,1,1,0) is closer. 1794 | p4 := 3 - inSum + wins 1795 | if aScore <= bScore && p4 < bScore { 1796 | bScore = p4 1797 | bPoint = 0x07 1798 | bIsBiggerSide = false 1799 | } else if aScore > bScore && p4 < aScore { 1800 | aScore = p4 1801 | aPoint = 0x07 1802 | aIsBiggerSide = false 1803 | } 1804 | 1805 | // Where each of the two closest points are determines how the extra three vertices are calculated. 1806 | if aIsBiggerSide == bIsBiggerSide { 1807 | if aIsBiggerSide { // Both closest points on the bigger side 1808 | c1 := aPoint & bPoint 1809 | c2 := aPoint | bPoint 1810 | 1811 | // Two contributions are permutations of (0,0,0,1) and (0,0,0,2) based on c1 1812 | xsv_ext1 = xsb 1813 | xsv_ext0 = xsv_ext1 1814 | ysv_ext1 = ysb 1815 | ysv_ext0 = ysv_ext1 1816 | zsv_ext1 = zsb 1817 | zsv_ext0 = zsv_ext1 1818 | wsv_ext1 = wsb 1819 | wsv_ext0 = wsv_ext1 1820 | dx_ext0 = dx0 - squishConstant4D 1821 | dy_ext0 = dy0 - squishConstant4D 1822 | dz_ext0 = dz0 - squishConstant4D 1823 | dw_ext0 = dw0 - squishConstant4D 1824 | dx_ext1 = dx0 - 2*squishConstant4D 1825 | dy_ext1 = dy0 - 2*squishConstant4D 1826 | dz_ext1 = dz0 - 2*squishConstant4D 1827 | dw_ext1 = dw0 - 2*squishConstant4D 1828 | if (c1 & 0x01) != 0 { 1829 | xsv_ext0 += 1 1830 | dx_ext0 -= 1 1831 | xsv_ext1 += 2 1832 | dx_ext1 -= 2 1833 | } else if (c1 & 0x02) != 0 { 1834 | ysv_ext0 += 1 1835 | dy_ext0 -= 1 1836 | ysv_ext1 += 2 1837 | dy_ext1 -= 2 1838 | } else if (c1 & 0x04) != 0 { 1839 | zsv_ext0 += 1 1840 | dz_ext0 -= 1 1841 | zsv_ext1 += 2 1842 | dz_ext1 -= 2 1843 | } else { 1844 | wsv_ext0 += 1 1845 | dw_ext0 -= 1 1846 | wsv_ext1 += 2 1847 | dw_ext1 -= 2 1848 | } 1849 | 1850 | // One contribution is a permutation of (1,1,1,-1) based on c2 1851 | xsv_ext2 = xsb + 1 1852 | ysv_ext2 = ysb + 1 1853 | zsv_ext2 = zsb + 1 1854 | wsv_ext2 = wsb + 1 1855 | dx_ext2 = dx0 - 1 - 2*squishConstant4D 1856 | dy_ext2 = dy0 - 1 - 2*squishConstant4D 1857 | dz_ext2 = dz0 - 1 - 2*squishConstant4D 1858 | dw_ext2 = dw0 - 1 - 2*squishConstant4D 1859 | if (c2 & 0x01) == 0 { 1860 | xsv_ext2 -= 2 1861 | dx_ext2 += 2 1862 | } else if (c2 & 0x02) == 0 { 1863 | ysv_ext2 -= 2 1864 | dy_ext2 += 2 1865 | } else if (c2 & 0x04) == 0 { 1866 | zsv_ext2 -= 2 1867 | dz_ext2 += 2 1868 | } else { 1869 | wsv_ext2 -= 2 1870 | dw_ext2 += 2 1871 | } 1872 | } else { // Both closest points on the smaller side 1873 | // One of the two extra points is (1,1,1,1) 1874 | xsv_ext2 = xsb + 1 1875 | ysv_ext2 = ysb + 1 1876 | zsv_ext2 = zsb + 1 1877 | wsv_ext2 = wsb + 1 1878 | dx_ext2 = dx0 - 1 - 4*squishConstant4D 1879 | dy_ext2 = dy0 - 1 - 4*squishConstant4D 1880 | dz_ext2 = dz0 - 1 - 4*squishConstant4D 1881 | dw_ext2 = dw0 - 1 - 4*squishConstant4D 1882 | 1883 | // Other two points are based on the shared axes. 1884 | c := aPoint & bPoint 1885 | 1886 | if (c & 0x01) != 0 { 1887 | xsv_ext0 = xsb + 2 1888 | xsv_ext1 = xsb + 1 1889 | dx_ext0 = dx0 - 2 - 3*squishConstant4D 1890 | dx_ext1 = dx0 - 1 - 3*squishConstant4D 1891 | } else { 1892 | xsv_ext1 = xsb 1893 | xsv_ext0 = xsv_ext1 1894 | dx_ext1 = dx0 - 3*squishConstant4D 1895 | dx_ext0 = dx_ext1 1896 | } 1897 | 1898 | if (c & 0x02) != 0 { 1899 | ysv_ext1 = ysb + 1 1900 | ysv_ext0 = ysv_ext1 1901 | dy_ext1 = dy0 - 1 - 3*squishConstant4D 1902 | dy_ext0 = dy_ext1 1903 | if (c & 0x01) == 0 { 1904 | ysv_ext0 += 1 1905 | dy_ext0 -= 1 1906 | } else { 1907 | ysv_ext1 += 1 1908 | dy_ext1 -= 1 1909 | } 1910 | } else { 1911 | ysv_ext1 = ysb 1912 | ysv_ext0 = ysv_ext1 1913 | dy_ext1 = dy0 - 3*squishConstant4D 1914 | dy_ext0 = dy_ext1 1915 | } 1916 | 1917 | if (c & 0x04) != 0 { 1918 | zsv_ext1 = zsb + 1 1919 | zsv_ext0 = zsv_ext1 1920 | dz_ext1 = dz0 - 1 - 3*squishConstant4D 1921 | dz_ext0 = dz_ext1 1922 | if (c & 0x03) == 0 { 1923 | zsv_ext0 += 1 1924 | dz_ext0 -= 1 1925 | } else { 1926 | zsv_ext1 += 1 1927 | dz_ext1 -= 1 1928 | } 1929 | } else { 1930 | zsv_ext1 = zsb 1931 | zsv_ext0 = zsv_ext1 1932 | dz_ext1 = dz0 - 3*squishConstant4D 1933 | dz_ext0 = dz_ext1 1934 | } 1935 | 1936 | if (c & 0x08) != 0 { 1937 | wsv_ext0 = wsb + 1 1938 | wsv_ext1 = wsb + 2 1939 | dw_ext0 = dw0 - 1 - 3*squishConstant4D 1940 | dw_ext1 = dw0 - 2 - 3*squishConstant4D 1941 | } else { 1942 | wsv_ext1 = wsb 1943 | wsv_ext0 = wsv_ext1 1944 | dw_ext1 = dw0 - 3*squishConstant4D 1945 | dw_ext0 = dw_ext1 1946 | } 1947 | } 1948 | } else { // One point on each "side" 1949 | var c1, c2 byte 1950 | if aIsBiggerSide { 1951 | c1 = aPoint 1952 | c2 = bPoint 1953 | } else { 1954 | c1 = bPoint 1955 | c2 = aPoint 1956 | } 1957 | 1958 | // Two contributions are the bigger-sided point with each 1 replaced with 2. 1959 | if (c1 & 0x01) != 0 { 1960 | xsv_ext0 = xsb + 2 1961 | xsv_ext1 = xsb + 1 1962 | dx_ext0 = dx0 - 2 - 3*squishConstant4D 1963 | dx_ext1 = dx0 - 1 - 3*squishConstant4D 1964 | } else { 1965 | xsv_ext1 = xsb 1966 | xsv_ext0 = xsv_ext1 1967 | dx_ext1 = dx0 - 3*squishConstant4D 1968 | dx_ext0 = dx_ext1 1969 | } 1970 | 1971 | if (c1 & 0x02) != 0 { 1972 | ysv_ext1 = ysb + 1 1973 | ysv_ext0 = ysv_ext1 1974 | dy_ext1 = dy0 - 1 - 3*squishConstant4D 1975 | dy_ext0 = dy_ext1 1976 | if (c1 & 0x01) == 0 { 1977 | ysv_ext0 += 1 1978 | dy_ext0 -= 1 1979 | } else { 1980 | ysv_ext1 += 1 1981 | dy_ext1 -= 1 1982 | } 1983 | } else { 1984 | ysv_ext1 = ysb 1985 | ysv_ext0 = ysv_ext1 1986 | dy_ext1 = dy0 - 3*squishConstant4D 1987 | dy_ext0 = dy_ext1 1988 | } 1989 | 1990 | if (c1 & 0x04) != 0 { 1991 | zsv_ext1 = zsb + 1 1992 | zsv_ext0 = zsv_ext1 1993 | dz_ext1 = dz0 - 1 - 3*squishConstant4D 1994 | dz_ext0 = dz_ext1 1995 | if (c1 & 0x03) == 0 { 1996 | zsv_ext0 += 1 1997 | dz_ext0 -= 1 1998 | } else { 1999 | zsv_ext1 += 1 2000 | dz_ext1 -= 1 2001 | } 2002 | } else { 2003 | zsv_ext1 = zsb 2004 | zsv_ext0 = zsv_ext1 2005 | dz_ext1 = dz0 - 3*squishConstant4D 2006 | dz_ext0 = dz_ext1 2007 | } 2008 | 2009 | if (c1 & 0x08) != 0 { 2010 | wsv_ext0 = wsb + 1 2011 | wsv_ext1 = wsb + 2 2012 | dw_ext0 = dw0 - 1 - 3*squishConstant4D 2013 | dw_ext1 = dw0 - 2 - 3*squishConstant4D 2014 | } else { 2015 | wsv_ext1 = wsb 2016 | wsv_ext0 = wsv_ext1 2017 | dw_ext1 = dw0 - 3*squishConstant4D 2018 | dw_ext0 = dw_ext1 2019 | } 2020 | 2021 | // One contribution is a permutation of (1,1,1,-1) based on the smaller-sided point 2022 | xsv_ext2 = xsb + 1 2023 | ysv_ext2 = ysb + 1 2024 | zsv_ext2 = zsb + 1 2025 | wsv_ext2 = wsb + 1 2026 | dx_ext2 = dx0 - 1 - 2*squishConstant4D 2027 | dy_ext2 = dy0 - 1 - 2*squishConstant4D 2028 | dz_ext2 = dz0 - 1 - 2*squishConstant4D 2029 | dw_ext2 = dw0 - 1 - 2*squishConstant4D 2030 | if (c2 & 0x01) == 0 { 2031 | xsv_ext2 -= 2 2032 | dx_ext2 += 2 2033 | } else if (c2 & 0x02) == 0 { 2034 | ysv_ext2 -= 2 2035 | dy_ext2 += 2 2036 | } else if (c2 & 0x04) == 0 { 2037 | zsv_ext2 -= 2 2038 | dz_ext2 += 2 2039 | } else { 2040 | wsv_ext2 -= 2 2041 | dw_ext2 += 2 2042 | } 2043 | } 2044 | 2045 | // Contribution (1,1,1,0) 2046 | dx4 := dx0 - 1 - 3*squishConstant4D 2047 | dy4 := dy0 - 1 - 3*squishConstant4D 2048 | dz4 := dz0 - 1 - 3*squishConstant4D 2049 | dw4 := dw0 - 3*squishConstant4D 2050 | attn4 := 2 - dx4*dx4 - dy4*dy4 - dz4*dz4 - dw4*dw4 2051 | if attn4 > 0 { 2052 | attn4 *= attn4 2053 | value += attn4 * attn4 * s.extrapolate4(xsb+1, ysb+1, zsb+1, wsb+0, dx4, dy4, dz4, dw4) 2054 | } 2055 | 2056 | // Contribution (1,1,0,1) 2057 | dx3 := dx4 2058 | dy3 := dy4 2059 | dz3 := dz0 - 3*squishConstant4D 2060 | dw3 := dw0 - 1 - 3*squishConstant4D 2061 | attn3 := 2 - dx3*dx3 - dy3*dy3 - dz3*dz3 - dw3*dw3 2062 | if attn3 > 0 { 2063 | attn3 *= attn3 2064 | value += attn3 * attn3 * s.extrapolate4(xsb+1, ysb+1, zsb+0, wsb+1, dx3, dy3, dz3, dw3) 2065 | } 2066 | 2067 | // Contribution (1,0,1,1) 2068 | dx2 := dx4 2069 | dy2 := dy0 - 3*squishConstant4D 2070 | dz2 := dz4 2071 | dw2 := dw3 2072 | attn2 := 2 - dx2*dx2 - dy2*dy2 - dz2*dz2 - dw2*dw2 2073 | if attn2 > 0 { 2074 | attn2 *= attn2 2075 | value += attn2 * attn2 * s.extrapolate4(xsb+1, ysb+0, zsb+1, wsb+1, dx2, dy2, dz2, dw2) 2076 | } 2077 | 2078 | // Contribution (0,1,1,1) 2079 | dx1 := dx0 - 3*squishConstant4D 2080 | dz1 := dz4 2081 | dy1 := dy4 2082 | dw1 := dw3 2083 | attn1 := 2 - dx1*dx1 - dy1*dy1 - dz1*dz1 - dw1*dw1 2084 | if attn1 > 0 { 2085 | attn1 *= attn1 2086 | value += attn1 * attn1 * s.extrapolate4(xsb+0, ysb+1, zsb+1, wsb+1, dx1, dy1, dz1, dw1) 2087 | } 2088 | 2089 | // Contribution (1,1,0,0) 2090 | dx5 := dx0 - 1 - 2*squishConstant4D 2091 | dy5 := dy0 - 1 - 2*squishConstant4D 2092 | dz5 := dz0 - 0 - 2*squishConstant4D 2093 | dw5 := dw0 - 0 - 2*squishConstant4D 2094 | attn5 := 2 - dx5*dx5 - dy5*dy5 - dz5*dz5 - dw5*dw5 2095 | if attn5 > 0 { 2096 | attn5 *= attn5 2097 | value += attn5 * attn5 * s.extrapolate4(xsb+1, ysb+1, zsb+0, wsb+0, dx5, dy5, dz5, dw5) 2098 | } 2099 | 2100 | // Contribution (1,0,1,0) 2101 | dx6 := dx0 - 1 - 2*squishConstant4D 2102 | dy6 := dy0 - 0 - 2*squishConstant4D 2103 | dz6 := dz0 - 1 - 2*squishConstant4D 2104 | dw6 := dw0 - 0 - 2*squishConstant4D 2105 | attn6 := 2 - dx6*dx6 - dy6*dy6 - dz6*dz6 - dw6*dw6 2106 | if attn6 > 0 { 2107 | attn6 *= attn6 2108 | value += attn6 * attn6 * s.extrapolate4(xsb+1, ysb+0, zsb+1, wsb+0, dx6, dy6, dz6, dw6) 2109 | } 2110 | 2111 | // Contribution (1,0,0,1) 2112 | dx7 := dx0 - 1 - 2*squishConstant4D 2113 | dy7 := dy0 - 0 - 2*squishConstant4D 2114 | dz7 := dz0 - 0 - 2*squishConstant4D 2115 | dw7 := dw0 - 1 - 2*squishConstant4D 2116 | attn7 := 2 - dx7*dx7 - dy7*dy7 - dz7*dz7 - dw7*dw7 2117 | if attn7 > 0 { 2118 | attn7 *= attn7 2119 | value += attn7 * attn7 * s.extrapolate4(xsb+1, ysb+0, zsb+0, wsb+1, dx7, dy7, dz7, dw7) 2120 | } 2121 | 2122 | // Contribution (0,1,1,0) 2123 | dx8 := dx0 - 0 - 2*squishConstant4D 2124 | dy8 := dy0 - 1 - 2*squishConstant4D 2125 | dz8 := dz0 - 1 - 2*squishConstant4D 2126 | dw8 := dw0 - 0 - 2*squishConstant4D 2127 | attn8 := 2 - dx8*dx8 - dy8*dy8 - dz8*dz8 - dw8*dw8 2128 | if attn8 > 0 { 2129 | attn8 *= attn8 2130 | value += attn8 * attn8 * s.extrapolate4(xsb+0, ysb+1, zsb+1, wsb+0, dx8, dy8, dz8, dw8) 2131 | } 2132 | 2133 | // Contribution (0,1,0,1) 2134 | dx9 := dx0 - 0 - 2*squishConstant4D 2135 | dy9 := dy0 - 1 - 2*squishConstant4D 2136 | dz9 := dz0 - 0 - 2*squishConstant4D 2137 | dw9 := dw0 - 1 - 2*squishConstant4D 2138 | attn9 := 2 - dx9*dx9 - dy9*dy9 - dz9*dz9 - dw9*dw9 2139 | if attn9 > 0 { 2140 | attn9 *= attn9 2141 | value += attn9 * attn9 * s.extrapolate4(xsb+0, ysb+1, zsb+0, wsb+1, dx9, dy9, dz9, dw9) 2142 | } 2143 | 2144 | // Contribution (0,0,1,1) 2145 | dx10 := dx0 - 0 - 2*squishConstant4D 2146 | dy10 := dy0 - 0 - 2*squishConstant4D 2147 | dz10 := dz0 - 1 - 2*squishConstant4D 2148 | dw10 := dw0 - 1 - 2*squishConstant4D 2149 | attn10 := 2 - dx10*dx10 - dy10*dy10 - dz10*dz10 - dw10*dw10 2150 | if attn10 > 0 { 2151 | attn10 *= attn10 2152 | value += attn10 * attn10 * s.extrapolate4(xsb+0, ysb+0, zsb+1, wsb+1, dx10, dy10, dz10, dw10) 2153 | } 2154 | } 2155 | 2156 | // First extra vertex 2157 | attn_ext0 := 2 - dx_ext0*dx_ext0 - dy_ext0*dy_ext0 - dz_ext0*dz_ext0 - dw_ext0*dw_ext0 2158 | if attn_ext0 > 0 { 2159 | attn_ext0 *= attn_ext0 2160 | value += attn_ext0 * attn_ext0 * s.extrapolate4(xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0, dx_ext0, dy_ext0, dz_ext0, dw_ext0) 2161 | } 2162 | 2163 | // Second extra vertex 2164 | attn_ext1 := 2 - dx_ext1*dx_ext1 - dy_ext1*dy_ext1 - dz_ext1*dz_ext1 - dw_ext1*dw_ext1 2165 | if attn_ext1 > 0 { 2166 | attn_ext1 *= attn_ext1 2167 | value += attn_ext1 * attn_ext1 * s.extrapolate4(xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1, dx_ext1, dy_ext1, dz_ext1, dw_ext1) 2168 | } 2169 | 2170 | // Third extra vertex 2171 | attn_ext2 := 2 - dx_ext2*dx_ext2 - dy_ext2*dy_ext2 - dz_ext2*dz_ext2 - dw_ext2*dw_ext2 2172 | if attn_ext2 > 0 { 2173 | attn_ext2 *= attn_ext2 2174 | value += attn_ext2 * attn_ext2 * s.extrapolate4(xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2, dx_ext2, dy_ext2, dz_ext2, dw_ext2) 2175 | } 2176 | 2177 | return value / normConstant4D 2178 | } 2179 | --------------------------------------------------------------------------------