diff --git a/krita/data/aboutdata/developers.txt b/krita/data/aboutdata/developers.txt
index 2712d4f763..69f73890f5 100644
--- a/krita/data/aboutdata/developers.txt
+++ b/krita/data/aboutdata/developers.txt
@@ -1,199 +1,201 @@
Boudewijn Rempt
Aaron J. Seigo
Adam Celarek
Adam Pigg
Adriaan de Groot
Adrian Page
Adrian Schroeter
Albert Astals Cid
Alberto Villa
Alexander Neundorf
Alexander Potashev
Alexis Ménard
Alfredo Beaumont Sainz
Allen Winter
Ana Beatriz Guerrero López
Andras Mantia
Andreas Hartmetz
Andreas Lundin
André Marcelo Alvarenga
Andrew Coles
Andre Woebbeking
Andrius da Costa Ribas
Andy Fawcett
Anne-Marie Mahfouf
Ariya Hidayat
Arjen Hiemstra
Bart Coppens
Ben Cooksley
Benjamin K. Stuhl
Benjamin Meyer
Benjamin Reed
Benoît Jacob
Ben Schleimer
Bernhard Rosenkraenzer
Bo Thorsen
Brad Hards
Bram Schoenmakers
Burkhard Lück
Carlo Segato
C. Boemann
Christer Stenbrenden
Christian Ehrlicher
Christian Mueller
Christoph Feck
Chusslove Illich
Clarence Dang
Cyrille Berger
Daniel M. Duley
Daniel Molkentin
Dan Leinir Turthra Jensen
Dan Meltzer
Danny Allen
David Faure
David Gowers
Dirk Mueller
Dirk Schönberger
Dmitry Kazakov
Edward Apap
Elvis Stansvik
Emanuele Tamponi
+Emmet O'Neill
Enrique Matías Sánchez
+Eoin O'Neill
Fabian Kosmale
Frank Osterfeld
Frederik Schwarzer
Fredrik Edemar
Fredy Yanardi
Friedrich W. H. Kossebau
Gábor Lehel
Gary Cramblitt
Geoffry Song
Gioele Barabucci
Giovanni Venturi
Gopalakrishna Bhat A
Hanna Scott
Harald Sitter
Hasso Tepper
Helge Deller
Helio Castro
Hideki Saito
Hoàng Đức Hiếu
Hugo Pereira Da Costa
Inge Wallin
Ingo Klöcker
İsmail Dönmez
Ivan Yossi
Jaime
Jaime Torres
Jaison Lee
Jakob Petsovits
Jakub Stachowski
Jan Hambrecht
Jarosław Staniek
Jens Herden
Jessica Hall
Johannes Simon
John Layt
Jonathan Riddell
Jonathan Singer
José Luis Vergara
Juan Luis Boya García
Juan Palacios
Jure Repinc
Kai-Uwe Behrmann
Kevin Krammer
Kevin Ottens
Kurt Pfeifle
Laurent Montel
Lauri Watts
Leo Savernik
Lukáš Tinkl
Lukáš Tvrdý
Maciej Mrozowski
Malcolm Hunter
Manuel Riecke
manu tortosa
Marc Pegon
Marijn Kruisselbrink
Martin Ellis
Martin Gräßlin
Matthew Woehlke
Matthias Klumpp
Matthias Kretz
Matus Talcik
Maximiliano Curia
Melchior Franz
Michael David Howell
Michael Drueing
Michael Thaler
Michel Hermier
Mohit Goyal
Mojtaba Shahi Senobari
Montel Laurent
Moritz Molch
Nick Shaforostoff
Nicolas Goutte
Olivier Goffart
Patrick Julien
Patrick Spendrin
Pavel Belskiy
Pavel Heimlich
Peter Simonsson
Pierre Ducroquet
Pierre Stirnweiss
Pino Toscano
Rafael Fernández López
Raphael Langerhorst
Rex Dieter
Rob Buis
Roopesh Chander
Sahil Nagpal
Salil Kapur
Samuel Buttigieg
Sander Koning
Sascha Suelzer
Scott Petrovic
Scott Wheeler
Sebastian Sauer
Shivaraman Aiyer
Siddharth Sharma
Silvio Heinrich
Somsubhra Bairi
Spencer Brown
Srikanth Tiyyagura
Stefan Nikolaus
Stephan Binner
Stephan Kulow
Stuart Dickson
Sune Vuorela
Sven Langkamp
Thiago Macieira
Thomas Capricelli
Thomas Friedrichsmeier
Thomas Klausner
Thomas Nagy
Thomas Zander
Thorsten Staerk
Thorsten Zachmann
Tim Beaulen
Timothée Giet
Tobias Koenig
Tom Burdick
Torio Mlshi
Torsten Rahn
Unai Garro
Urs Wolfer
Vadim Zhukov
Vera Lukman
Victor Lafon
Victor Wåhlström
Volker Krause
Waldo Bastian
Werner Trobin
Wilco Greven
Will Entriken
William Steidtmann
Wolthera van Hovell
Yann Bodson
Yue Liu
-Yuri Chornoivan
\ No newline at end of file
+Yuri Chornoivan
diff --git a/libs/ui/forms/wdgdlggeneratorlayer.ui b/libs/ui/forms/wdgdlggeneratorlayer.ui
index e1ac825994..b903c4daea 100644
--- a/libs/ui/forms/wdgdlggeneratorlayer.ui
+++ b/libs/ui/forms/wdgdlggeneratorlayer.ui
@@ -1,64 +1,59 @@
WdgDlgGeneratorLayer
0
0
- 400
+ 500
300
8
-
-
Layer Name:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
txtLayerName
-
-
-
+
0
0
-
- QLineEdit
- QLineEdit
-
-
KisWdgGenerator
QWidget
widgets/kis_wdg_generator.h
1
diff --git a/libs/ui/forms/wdggenerators.ui b/libs/ui/forms/wdggenerators.ui
index 073b1bb4ba..fb4a160efc 100644
--- a/libs/ui/forms/wdggenerators.ui
+++ b/libs/ui/forms/wdggenerators.ui
@@ -1,62 +1,74 @@
WdgGenerators
0
0
- 490
+ 500
324
+
+
+ 0
+ 0
+
+
+
+
+ 500
+ 0
+
+
0
0
0
0
-
-
+
0
0
- 200
+ 150
0
- 200
+ 150
16777215
-
-
+
-
+
6
0
diff --git a/plugins/generators/CMakeLists.txt b/plugins/generators/CMakeLists.txt
index 65e3b73a13..1a560d4b66 100644
--- a/plugins/generators/CMakeLists.txt
+++ b/plugins/generators/CMakeLists.txt
@@ -1,2 +1,3 @@
add_subdirectory(solid)
add_subdirectory(pattern)
+add_subdirectory(simplexnoise)
diff --git a/plugins/generators/simplexnoise/3rdparty/c-open-simplex/LICENSE b/plugins/generators/simplexnoise/3rdparty/c-open-simplex/LICENSE
new file mode 100644
index 0000000000..a84c395662
--- /dev/null
+++ b/plugins/generators/simplexnoise/3rdparty/c-open-simplex/LICENSE
@@ -0,0 +1,25 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+
diff --git a/plugins/generators/simplexnoise/3rdparty/c-open-simplex/README.md b/plugins/generators/simplexnoise/3rdparty/c-open-simplex/README.md
new file mode 100644
index 0000000000..42db0c2f2a
--- /dev/null
+++ b/plugins/generators/simplexnoise/3rdparty/c-open-simplex/README.md
@@ -0,0 +1,6 @@
+Github: https://github.com/smcameron/open-simplex-noise-in-c
+
+open-simplex-noise-in-c
+=======================
+
+Port of Kurt Spencer's java implementation of open simplex noise to C
diff --git a/plugins/generators/simplexnoise/3rdparty/c-open-simplex/open-simplex-noise.c b/plugins/generators/simplexnoise/3rdparty/c-open-simplex/open-simplex-noise.c
new file mode 100644
index 0000000000..974a82a40b
--- /dev/null
+++ b/plugins/generators/simplexnoise/3rdparty/c-open-simplex/open-simplex-noise.c
@@ -0,0 +1,2250 @@
+/*
+ * OpenSimplex (Simplectic) Noise in C.
+ * Ported by Stephen M. Cameron from Kurt Spencer's java implementation
+ *
+ * v1.1 (October 5, 2014)
+ * - Added 2D and 4D implementations.
+ * - Proper gradient sets for all dimensions, from a
+ * dimensionally-generalizable scheme with an actual
+ * rhyme and reason behind it.
+ * - Removed default permutation array in favor of
+ * default seed.
+ * - Changed seed-based constructor to be independent
+ * of any particular randomization library, so results
+ * will be the same when ported to other languages.
+ */
+#include
+#include
+#include
+#include
+#include
+
+#include "open-simplex-noise.h"
+
+#define STRETCH_CONSTANT_2D (-0.211324865405187) /* (1 / sqrt(2 + 1) - 1 ) / 2; */
+#define SQUISH_CONSTANT_2D (0.366025403784439) /* (sqrt(2 + 1) -1) / 2; */
+#define STRETCH_CONSTANT_3D (-1.0 / 6.0) /* (1 / sqrt(3 + 1) - 1) / 3; */
+#define SQUISH_CONSTANT_3D (1.0 / 3.0) /* (sqrt(3+1)-1)/3; */
+#define STRETCH_CONSTANT_4D (-0.138196601125011) /* (1 / sqrt(4 + 1) - 1) / 4; */
+#define SQUISH_CONSTANT_4D (0.309016994374947) /* (sqrt(4 + 1) - 1) / 4; */
+
+#define NORM_CONSTANT_2D (47.0)
+#define NORM_CONSTANT_3D (103.0)
+#define NORM_CONSTANT_4D (30.0)
+
+#define DEFAULT_SEED (0LL)
+
+struct osn_context {
+ int16_t *perm;
+ int16_t *permGradIndex3D;
+};
+
+#define ARRAYSIZE(x) (sizeof((x)) / sizeof((x)[0]))
+
+/*
+ * Gradients for 2D. They approximate the directions to the
+ * vertices of an octagon from the center.
+ */
+static const int8_t gradients2D[] = {
+ 5, 2, 2, 5,
+ -5, 2, -2, 5,
+ 5, -2, 2, -5,
+ -5, -2, -2, -5,
+};
+
+/*
+ * Gradients for 3D. They approximate the directions to the
+ * vertices of a rhombicuboctahedron from the center, skewed so
+ * that the triangular and square facets can be inscribed inside
+ * circles of the same radius.
+ */
+static const signed char gradients3D[] = {
+ -11, 4, 4, -4, 11, 4, -4, 4, 11,
+ 11, 4, 4, 4, 11, 4, 4, 4, 11,
+ -11, -4, 4, -4, -11, 4, -4, -4, 11,
+ 11, -4, 4, 4, -11, 4, 4, -4, 11,
+ -11, 4, -4, -4, 11, -4, -4, 4, -11,
+ 11, 4, -4, 4, 11, -4, 4, 4, -11,
+ -11, -4, -4, -4, -11, -4, -4, -4, -11,
+ 11, -4, -4, 4, -11, -4, 4, -4, -11,
+};
+
+/*
+ * Gradients for 4D. They approximate the directions to the
+ * vertices of a disprismatotesseractihexadecachoron from the center,
+ * skewed so that the tetrahedral and cubic facets can be inscribed inside
+ * spheres of the same radius.
+ */
+static const signed char gradients4D[] = {
+ 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3,
+ -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3,
+ 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3,
+ -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3,
+ 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3,
+ -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3,
+ 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3,
+ -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3,
+ 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3,
+ -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3,
+ 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3,
+ -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3,
+ 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3,
+ -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3,
+ 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3,
+ -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3,
+};
+
+static double extrapolate2(struct osn_context *ctx, int xsb, int ysb, double dx, double dy)
+{
+ int16_t *perm = ctx->perm;
+ int index = perm[(perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E;
+ return gradients2D[index] * dx
+ + gradients2D[index + 1] * dy;
+}
+
+static double extrapolate3(struct osn_context *ctx, int xsb, int ysb, int zsb, double dx, double dy, double dz)
+{
+ int16_t *perm = ctx->perm;
+ int16_t *permGradIndex3D = ctx->permGradIndex3D;
+ int index = permGradIndex3D[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF];
+ return gradients3D[index] * dx
+ + gradients3D[index + 1] * dy
+ + gradients3D[index + 2] * dz;
+}
+
+static double extrapolate4(struct osn_context *ctx, int xsb, int ysb, int zsb, int wsb, double dx, double dy, double dz, double dw)
+{
+ int16_t *perm = ctx->perm;
+ int index = perm[(perm[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF] + wsb) & 0xFF] & 0xFC;
+ return gradients4D[index] * dx
+ + gradients4D[index + 1] * dy
+ + gradients4D[index + 2] * dz
+ + gradients4D[index + 3] * dw;
+}
+
+static INLINE int fastFloor(double x) {
+ int xi = (int) x;
+ return x < xi ? xi - 1 : xi;
+}
+
+static int allocate_perm(struct osn_context *ctx, int nperm, int ngrad)
+{
+ if (ctx->perm)
+ free(ctx->perm);
+ if (ctx->permGradIndex3D)
+ free(ctx->permGradIndex3D);
+ ctx->perm = (int16_t *) malloc(sizeof(*ctx->perm) * nperm);
+ if (!ctx->perm)
+ return -ENOMEM;
+ ctx->permGradIndex3D = (int16_t *) malloc(sizeof(*ctx->permGradIndex3D) * ngrad);
+ if (!ctx->permGradIndex3D) {
+ free(ctx->perm);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+int open_simplex_noise_init_perm(struct osn_context *ctx, int16_t p[], int nelements)
+{
+ int i, rc;
+
+ rc = allocate_perm(ctx, nelements, 256);
+ if (rc)
+ return rc;
+ memcpy(ctx->perm, p, sizeof(*ctx->perm) * nelements);
+
+ for (i = 0; i < 256; i++) {
+ /* Since 3D has 24 gradients, simple bitmask won't work, so precompute modulo array. */
+ ctx->permGradIndex3D[i] = (int16_t)((ctx->perm[i] % (ARRAYSIZE(gradients3D) / 3)) * 3);
+ }
+ return 0;
+}
+
+/*
+ * Initializes using a permutation array generated from a 64-bit seed.
+ * Generates a proper permutation (i.e. doesn't merely perform N successive pair
+ * swaps on a base array). Uses a simple 64-bit LCG.
+ */
+int open_simplex_noise(int64_t seed, struct osn_context **ctx)
+{
+ int rc;
+ int16_t source[256];
+ int i;
+ int16_t *perm;
+ int16_t *permGradIndex3D;
+ int r;
+
+ *ctx = (struct osn_context *) malloc(sizeof(**ctx));
+ if (!(*ctx))
+ return -ENOMEM;
+ (*ctx)->perm = NULL;
+ (*ctx)->permGradIndex3D = NULL;
+
+ rc = allocate_perm(*ctx, 256, 256);
+ if (rc) {
+ free(*ctx);
+ return rc;
+ }
+
+ perm = (*ctx)->perm;
+ permGradIndex3D = (*ctx)->permGradIndex3D;
+
+ for (i = 0; i < 256; i++)
+ source[i] = (int16_t) i;
+ seed = seed * 6364136223846793005LL + 1442695040888963407LL;
+ seed = seed * 6364136223846793005LL + 1442695040888963407LL;
+ seed = seed * 6364136223846793005LL + 1442695040888963407LL;
+ for (i = 255; i >= 0; i--) {
+ seed = seed * 6364136223846793005LL + 1442695040888963407LL;
+ r = (int)((seed + 31) % (i + 1));
+ if (r < 0)
+ r += (i + 1);
+ perm[i] = source[r];
+ permGradIndex3D[i] = (short)((perm[i] % (ARRAYSIZE(gradients3D) / 3)) * 3);
+ source[r] = source[i];
+ }
+ return 0;
+}
+
+void open_simplex_noise_free(struct osn_context *ctx)
+{
+ if (!ctx)
+ return;
+ if (ctx->perm) {
+ free(ctx->perm);
+ ctx->perm = NULL;
+ }
+ if (ctx->permGradIndex3D) {
+ free(ctx->permGradIndex3D);
+ ctx->permGradIndex3D = NULL;
+ }
+ free(ctx);
+}
+
+/* 2D OpenSimplex (Simplectic) Noise. */
+double open_simplex_noise2(struct osn_context *ctx, double x, double y)
+{
+
+ /* Place input coordinates onto grid. */
+ double stretchOffset = (x + y) * STRETCH_CONSTANT_2D;
+ double xs = x + stretchOffset;
+ double ys = y + stretchOffset;
+
+ /* Floor to get grid coordinates of rhombus (stretched square) super-cell origin. */
+ int xsb = fastFloor(xs);
+ int ysb = fastFloor(ys);
+
+ /* Skew out to get actual coordinates of rhombus origin. We'll need these later. */
+ double squishOffset = (xsb + ysb) * SQUISH_CONSTANT_2D;
+ double xb = xsb + squishOffset;
+ double yb = ysb + squishOffset;
+
+ /* Compute grid coordinates relative to rhombus origin. */
+ double xins = xs - xsb;
+ double yins = ys - ysb;
+
+ /* Sum those together to get a value that determines which region we're in. */
+ double inSum = xins + yins;
+
+ /* Positions relative to origin point. */
+ double dx0 = x - xb;
+ double dy0 = y - yb;
+
+ /* We'll be defining these inside the next block and using them afterwards. */
+ double dx_ext, dy_ext;
+ int xsv_ext, ysv_ext;
+
+ double dx1;
+ double dy1;
+ double attn1;
+ double dx2;
+ double dy2;
+ double attn2;
+ double zins;
+ double attn0;
+ double attn_ext;
+
+ double value = 0;
+
+ /* Contribution (1,0) */
+ dx1 = dx0 - 1 - SQUISH_CONSTANT_2D;
+ dy1 = dy0 - 0 - SQUISH_CONSTANT_2D;
+ attn1 = 2 - dx1 * dx1 - dy1 * dy1;
+ if (attn1 > 0) {
+ attn1 *= attn1;
+ value += attn1 * attn1 * extrapolate2(ctx, xsb + 1, ysb + 0, dx1, dy1);
+ }
+
+ /* Contribution (0,1) */
+ dx2 = dx0 - 0 - SQUISH_CONSTANT_2D;
+ dy2 = dy0 - 1 - SQUISH_CONSTANT_2D;
+ attn2 = 2 - dx2 * dx2 - dy2 * dy2;
+ if (attn2 > 0) {
+ attn2 *= attn2;
+ value += attn2 * attn2 * extrapolate2(ctx, xsb + 0, ysb + 1, dx2, dy2);
+ }
+
+ if (inSum <= 1) { /* We're inside the triangle (2-Simplex) at (0,0) */
+ zins = 1 - inSum;
+ if (zins > xins || zins > yins) { /* (0,0) is one of the closest two triangular vertices */
+ if (xins > yins) {
+ xsv_ext = xsb + 1;
+ ysv_ext = ysb - 1;
+ dx_ext = dx0 - 1;
+ dy_ext = dy0 + 1;
+ } else {
+ xsv_ext = xsb - 1;
+ ysv_ext = ysb + 1;
+ dx_ext = dx0 + 1;
+ dy_ext = dy0 - 1;
+ }
+ } else { /* (1,0) and (0,1) are the closest two vertices. */
+ xsv_ext = xsb + 1;
+ ysv_ext = ysb + 1;
+ dx_ext = dx0 - 1 - 2 * SQUISH_CONSTANT_2D;
+ dy_ext = dy0 - 1 - 2 * SQUISH_CONSTANT_2D;
+ }
+ } else { /* We're inside the triangle (2-Simplex) at (1,1) */
+ zins = 2 - inSum;
+ if (zins < xins || zins < yins) { /* (0,0) is one of the closest two triangular vertices */
+ if (xins > yins) {
+ xsv_ext = xsb + 2;
+ ysv_ext = ysb + 0;
+ dx_ext = dx0 - 2 - 2 * SQUISH_CONSTANT_2D;
+ dy_ext = dy0 + 0 - 2 * SQUISH_CONSTANT_2D;
+ } else {
+ xsv_ext = xsb + 0;
+ ysv_ext = ysb + 2;
+ dx_ext = dx0 + 0 - 2 * SQUISH_CONSTANT_2D;
+ dy_ext = dy0 - 2 - 2 * SQUISH_CONSTANT_2D;
+ }
+ } else { /* (1,0) and (0,1) are the closest two vertices. */
+ dx_ext = dx0;
+ dy_ext = dy0;
+ xsv_ext = xsb;
+ ysv_ext = ysb;
+ }
+ xsb += 1;
+ ysb += 1;
+ dx0 = dx0 - 1 - 2 * SQUISH_CONSTANT_2D;
+ dy0 = dy0 - 1 - 2 * SQUISH_CONSTANT_2D;
+ }
+
+ /* Contribution (0,0) or (1,1) */
+ attn0 = 2 - dx0 * dx0 - dy0 * dy0;
+ if (attn0 > 0) {
+ attn0 *= attn0;
+ value += attn0 * attn0 * extrapolate2(ctx, xsb, ysb, dx0, dy0);
+ }
+
+ /* Extra Vertex */
+ attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext;
+ if (attn_ext > 0) {
+ attn_ext *= attn_ext;
+ value += attn_ext * attn_ext * extrapolate2(ctx, xsv_ext, ysv_ext, dx_ext, dy_ext);
+ }
+
+ return value / NORM_CONSTANT_2D;
+}
+
+/*
+ * 3D OpenSimplex (Simplectic) Noise
+ */
+double open_simplex_noise3(struct osn_context *ctx, double x, double y, double z)
+{
+
+ /* Place input coordinates on simplectic honeycomb. */
+ double stretchOffset = (x + y + z) * STRETCH_CONSTANT_3D;
+ double xs = x + stretchOffset;
+ double ys = y + stretchOffset;
+ double zs = z + stretchOffset;
+
+ /* Floor to get simplectic honeycomb coordinates of rhombohedron (stretched cube) super-cell origin. */
+ int xsb = fastFloor(xs);
+ int ysb = fastFloor(ys);
+ int zsb = fastFloor(zs);
+
+ /* Skew out to get actual coordinates of rhombohedron origin. We'll need these later. */
+ double squishOffset = (xsb + ysb + zsb) * SQUISH_CONSTANT_3D;
+ double xb = xsb + squishOffset;
+ double yb = ysb + squishOffset;
+ double zb = zsb + squishOffset;
+
+ /* Compute simplectic honeycomb coordinates relative to rhombohedral origin. */
+ double xins = xs - xsb;
+ double yins = ys - ysb;
+ double zins = zs - zsb;
+
+ /* Sum those together to get a value that determines which region we're in. */
+ double inSum = xins + yins + zins;
+
+ /* Positions relative to origin point. */
+ double dx0 = x - xb;
+ double dy0 = y - yb;
+ double dz0 = z - zb;
+
+ /* We'll be defining these inside the next block and using them afterwards. */
+ double dx_ext0, dy_ext0, dz_ext0;
+ double dx_ext1, dy_ext1, dz_ext1;
+ int xsv_ext0, ysv_ext0, zsv_ext0;
+ int xsv_ext1, ysv_ext1, zsv_ext1;
+
+ double wins;
+ int8_t c, c1, c2;
+ int8_t aPoint, bPoint;
+ double aScore, bScore;
+ int aIsFurtherSide;
+ int bIsFurtherSide;
+ double p1, p2, p3;
+ double score;
+ double attn0, attn1, attn2, attn3, attn4, attn5, attn6;
+ double dx1, dy1, dz1;
+ double dx2, dy2, dz2;
+ double dx3, dy3, dz3;
+ double dx4, dy4, dz4;
+ double dx5, dy5, dz5;
+ double dx6, dy6, dz6;
+ double attn_ext0, attn_ext1;
+
+ double value = 0;
+ if (inSum <= 1) { /* We're inside the tetrahedron (3-Simplex) at (0,0,0) */
+
+ /* Determine which two of (0,0,1), (0,1,0), (1,0,0) are closest. */
+ aPoint = 0x01;
+ aScore = xins;
+ bPoint = 0x02;
+ bScore = yins;
+ if (aScore >= bScore && zins > bScore) {
+ bScore = zins;
+ bPoint = 0x04;
+ } else if (aScore < bScore && zins > aScore) {
+ aScore = zins;
+ aPoint = 0x04;
+ }
+
+ /* Now we determine the two lattice points not part of the tetrahedron that may contribute.
+ This depends on the closest two tetrahedral vertices, including (0,0,0) */
+ wins = 1 - inSum;
+ if (wins > aScore || wins > bScore) { /* (0,0,0) is one of the closest two tetrahedral vertices. */
+ c = (bScore > aScore ? bPoint : aPoint); /* Our other closest vertex is the closest out of a and b. */
+
+ if ((c & 0x01) == 0) {
+ xsv_ext0 = xsb - 1;
+ xsv_ext1 = xsb;
+ dx_ext0 = dx0 + 1;
+ dx_ext1 = dx0;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsb + 1;
+ dx_ext0 = dx_ext1 = dx0 - 1;
+ }
+
+ if ((c & 0x02) == 0) {
+ ysv_ext0 = ysv_ext1 = ysb;
+ dy_ext0 = dy_ext1 = dy0;
+ if ((c & 0x01) == 0) {
+ ysv_ext1 -= 1;
+ dy_ext1 += 1;
+ } else {
+ ysv_ext0 -= 1;
+ dy_ext0 += 1;
+ }
+ } else {
+ ysv_ext0 = ysv_ext1 = ysb + 1;
+ dy_ext0 = dy_ext1 = dy0 - 1;
+ }
+
+ if ((c & 0x04) == 0) {
+ zsv_ext0 = zsb;
+ zsv_ext1 = zsb - 1;
+ dz_ext0 = dz0;
+ dz_ext1 = dz0 + 1;
+ } else {
+ zsv_ext0 = zsv_ext1 = zsb + 1;
+ dz_ext0 = dz_ext1 = dz0 - 1;
+ }
+ } else { /* (0,0,0) is not one of the closest two tetrahedral vertices. */
+ c = (int8_t)(aPoint | bPoint); /* Our two extra vertices are determined by the closest two. */
+
+ if ((c & 0x01) == 0) {
+ xsv_ext0 = xsb;
+ xsv_ext1 = xsb - 1;
+ dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_3D;
+ dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsb + 1;
+ dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D;
+ dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D;
+ }
+
+ if ((c & 0x02) == 0) {
+ ysv_ext0 = ysb;
+ ysv_ext1 = ysb - 1;
+ dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D;
+ } else {
+ ysv_ext0 = ysv_ext1 = ysb + 1;
+ dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D;
+ }
+
+ if ((c & 0x04) == 0) {
+ zsv_ext0 = zsb;
+ zsv_ext1 = zsb - 1;
+ dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D;
+ } else {
+ zsv_ext0 = zsv_ext1 = zsb + 1;
+ dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D;
+ }
+ }
+
+ /* Contribution (0,0,0) */
+ attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0;
+ if (attn0 > 0) {
+ attn0 *= attn0;
+ value += attn0 * attn0 * extrapolate3(ctx, xsb + 0, ysb + 0, zsb + 0, dx0, dy0, dz0);
+ }
+
+ /* Contribution (1,0,0) */
+ dx1 = dx0 - 1 - SQUISH_CONSTANT_3D;
+ dy1 = dy0 - 0 - SQUISH_CONSTANT_3D;
+ dz1 = dz0 - 0 - SQUISH_CONSTANT_3D;
+ attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1;
+ if (attn1 > 0) {
+ attn1 *= attn1;
+ value += attn1 * attn1 * extrapolate3(ctx, xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1);
+ }
+
+ /* Contribution (0,1,0) */
+ dx2 = dx0 - 0 - SQUISH_CONSTANT_3D;
+ dy2 = dy0 - 1 - SQUISH_CONSTANT_3D;
+ dz2 = dz1;
+ attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2;
+ if (attn2 > 0) {
+ attn2 *= attn2;
+ value += attn2 * attn2 * extrapolate3(ctx, xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2);
+ }
+
+ /* Contribution (0,0,1) */
+ dx3 = dx2;
+ dy3 = dy1;
+ dz3 = dz0 - 1 - SQUISH_CONSTANT_3D;
+ attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3;
+ if (attn3 > 0) {
+ attn3 *= attn3;
+ value += attn3 * attn3 * extrapolate3(ctx, xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3);
+ }
+ } else if (inSum >= 2) { /* We're inside the tetrahedron (3-Simplex) at (1,1,1) */
+
+ /* Determine which two tetrahedral vertices are the closest, out of (1,1,0), (1,0,1), (0,1,1) but not (1,1,1). */
+ aPoint = 0x06;
+ aScore = xins;
+ bPoint = 0x05;
+ bScore = yins;
+ if (aScore <= bScore && zins < bScore) {
+ bScore = zins;
+ bPoint = 0x03;
+ } else if (aScore > bScore && zins < aScore) {
+ aScore = zins;
+ aPoint = 0x03;
+ }
+
+ /* Now we determine the two lattice points not part of the tetrahedron that may contribute.
+ This depends on the closest two tetrahedral vertices, including (1,1,1) */
+ wins = 3 - inSum;
+ if (wins < aScore || wins < bScore) { /* (1,1,1) is one of the closest two tetrahedral vertices. */
+ c = (bScore < aScore ? bPoint : aPoint); /* Our other closest vertex is the closest out of a and b. */
+
+ if ((c & 0x01) != 0) {
+ xsv_ext0 = xsb + 2;
+ xsv_ext1 = xsb + 1;
+ dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_3D;
+ dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsb;
+ dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_3D;
+ }
+
+ if ((c & 0x02) != 0) {
+ ysv_ext0 = ysv_ext1 = ysb + 1;
+ dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D;
+ if ((c & 0x01) != 0) {
+ ysv_ext1 += 1;
+ dy_ext1 -= 1;
+ } else {
+ ysv_ext0 += 1;
+ dy_ext0 -= 1;
+ }
+ } else {
+ ysv_ext0 = ysv_ext1 = ysb;
+ dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_3D;
+ }
+
+ if ((c & 0x04) != 0) {
+ zsv_ext0 = zsb + 1;
+ zsv_ext1 = zsb + 2;
+ dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 - 2 - 3 * SQUISH_CONSTANT_3D;
+ } else {
+ zsv_ext0 = zsv_ext1 = zsb;
+ dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_3D;
+ }
+ } else { /* (1,1,1) is not one of the closest two tetrahedral vertices. */
+ c = (int8_t)(aPoint & bPoint); /* Our two extra vertices are determined by the closest two. */
+
+ if ((c & 0x01) != 0) {
+ xsv_ext0 = xsb + 1;
+ xsv_ext1 = xsb + 2;
+ dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D;
+ dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsb;
+ dx_ext0 = dx0 - SQUISH_CONSTANT_3D;
+ dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D;
+ }
+
+ if ((c & 0x02) != 0) {
+ ysv_ext0 = ysb + 1;
+ ysv_ext1 = ysb + 2;
+ dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D;
+ } else {
+ ysv_ext0 = ysv_ext1 = ysb;
+ dy_ext0 = dy0 - SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D;
+ }
+
+ if ((c & 0x04) != 0) {
+ zsv_ext0 = zsb + 1;
+ zsv_ext1 = zsb + 2;
+ dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D;
+ } else {
+ zsv_ext0 = zsv_ext1 = zsb;
+ dz_ext0 = dz0 - SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D;
+ }
+ }
+
+ /* Contribution (1,1,0) */
+ dx3 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D;
+ dy3 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D;
+ dz3 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D;
+ attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3;
+ if (attn3 > 0) {
+ attn3 *= attn3;
+ value += attn3 * attn3 * extrapolate3(ctx, xsb + 1, ysb + 1, zsb + 0, dx3, dy3, dz3);
+ }
+
+ /* Contribution (1,0,1) */
+ dx2 = dx3;
+ dy2 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D;
+ dz2 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D;
+ attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2;
+ if (attn2 > 0) {
+ attn2 *= attn2;
+ value += attn2 * attn2 * extrapolate3(ctx, xsb + 1, ysb + 0, zsb + 1, dx2, dy2, dz2);
+ }
+
+ /* Contribution (0,1,1) */
+ dx1 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D;
+ dy1 = dy3;
+ dz1 = dz2;
+ attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1;
+ if (attn1 > 0) {
+ attn1 *= attn1;
+ value += attn1 * attn1 * extrapolate3(ctx, xsb + 0, ysb + 1, zsb + 1, dx1, dy1, dz1);
+ }
+
+ /* Contribution (1,1,1) */
+ dx0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D;
+ dy0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D;
+ dz0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D;
+ attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0;
+ if (attn0 > 0) {
+ attn0 *= attn0;
+ value += attn0 * attn0 * extrapolate3(ctx, xsb + 1, ysb + 1, zsb + 1, dx0, dy0, dz0);
+ }
+ } else { /* We're inside the octahedron (Rectified 3-Simplex) in between.
+ Decide between point (0,0,1) and (1,1,0) as closest */
+ p1 = xins + yins;
+ if (p1 > 1) {
+ aScore = p1 - 1;
+ aPoint = 0x03;
+ aIsFurtherSide = 1;
+ } else {
+ aScore = 1 - p1;
+ aPoint = 0x04;
+ aIsFurtherSide = 0;
+ }
+
+ /* Decide between point (0,1,0) and (1,0,1) as closest */
+ p2 = xins + zins;
+ if (p2 > 1) {
+ bScore = p2 - 1;
+ bPoint = 0x05;
+ bIsFurtherSide = 1;
+ } else {
+ bScore = 1 - p2;
+ bPoint = 0x02;
+ bIsFurtherSide = 0;
+ }
+
+ /* 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. */
+ p3 = yins + zins;
+ if (p3 > 1) {
+ score = p3 - 1;
+ if (aScore <= bScore && aScore < score) {
+ aScore = score;
+ aPoint = 0x06;
+ aIsFurtherSide = 1;
+ } else if (aScore > bScore && bScore < score) {
+ bScore = score;
+ bPoint = 0x06;
+ bIsFurtherSide = 1;
+ }
+ } else {
+ score = 1 - p3;
+ if (aScore <= bScore && aScore < score) {
+ aScore = score;
+ aPoint = 0x01;
+ aIsFurtherSide = 0;
+ } else if (aScore > bScore && bScore < score) {
+ bScore = score;
+ bPoint = 0x01;
+ bIsFurtherSide = 0;
+ }
+ }
+
+ /* Where each of the two closest points are determines how the extra two vertices are calculated. */
+ if (aIsFurtherSide == bIsFurtherSide) {
+ if (aIsFurtherSide) { /* Both closest points on (1,1,1) side */
+
+ /* One of the two extra points is (1,1,1) */
+ dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D;
+ dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D;
+ dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D;
+ xsv_ext0 = xsb + 1;
+ ysv_ext0 = ysb + 1;
+ zsv_ext0 = zsb + 1;
+
+ /* Other extra point is based on the shared axis. */
+ c = (int8_t)(aPoint & bPoint);
+ if ((c & 0x01) != 0) {
+ dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D;
+ xsv_ext1 = xsb + 2;
+ ysv_ext1 = ysb;
+ zsv_ext1 = zsb;
+ } else if ((c & 0x02) != 0) {
+ dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D;
+ xsv_ext1 = xsb;
+ ysv_ext1 = ysb + 2;
+ zsv_ext1 = zsb;
+ } else {
+ dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D;
+ xsv_ext1 = xsb;
+ ysv_ext1 = ysb;
+ zsv_ext1 = zsb + 2;
+ }
+ } else { /* Both closest points on (0,0,0) side */
+
+ /* One of the two extra points is (0,0,0) */
+ dx_ext0 = dx0;
+ dy_ext0 = dy0;
+ dz_ext0 = dz0;
+ xsv_ext0 = xsb;
+ ysv_ext0 = ysb;
+ zsv_ext0 = zsb;
+
+ /* Other extra point is based on the omitted axis. */
+ c = (int8_t)(aPoint | bPoint);
+ if ((c & 0x01) == 0) {
+ dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D;
+ xsv_ext1 = xsb - 1;
+ ysv_ext1 = ysb + 1;
+ zsv_ext1 = zsb + 1;
+ } else if ((c & 0x02) == 0) {
+ dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D;
+ xsv_ext1 = xsb + 1;
+ ysv_ext1 = ysb - 1;
+ zsv_ext1 = zsb + 1;
+ } else {
+ dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D;
+ xsv_ext1 = xsb + 1;
+ ysv_ext1 = ysb + 1;
+ zsv_ext1 = zsb - 1;
+ }
+ }
+ } else { /* One point on (0,0,0) side, one point on (1,1,1) side */
+ if (aIsFurtherSide) {
+ c1 = aPoint;
+ c2 = bPoint;
+ } else {
+ c1 = bPoint;
+ c2 = aPoint;
+ }
+
+ /* One contribution is a permutation of (1,1,-1) */
+ if ((c1 & 0x01) == 0) {
+ dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_3D;
+ dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D;
+ dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D;
+ xsv_ext0 = xsb - 1;
+ ysv_ext0 = ysb + 1;
+ zsv_ext0 = zsb + 1;
+ } else if ((c1 & 0x02) == 0) {
+ dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D;
+ dy_ext0 = dy0 + 1 - SQUISH_CONSTANT_3D;
+ dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D;
+ xsv_ext0 = xsb + 1;
+ ysv_ext0 = ysb - 1;
+ zsv_ext0 = zsb + 1;
+ } else {
+ dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D;
+ dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D;
+ dz_ext0 = dz0 + 1 - SQUISH_CONSTANT_3D;
+ xsv_ext0 = xsb + 1;
+ ysv_ext0 = ysb + 1;
+ zsv_ext0 = zsb - 1;
+ }
+
+ /* One contribution is a permutation of (0,0,2) */
+ dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D;
+ dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D;
+ dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D;
+ xsv_ext1 = xsb;
+ ysv_ext1 = ysb;
+ zsv_ext1 = zsb;
+ if ((c2 & 0x01) != 0) {
+ dx_ext1 -= 2;
+ xsv_ext1 += 2;
+ } else if ((c2 & 0x02) != 0) {
+ dy_ext1 -= 2;
+ ysv_ext1 += 2;
+ } else {
+ dz_ext1 -= 2;
+ zsv_ext1 += 2;
+ }
+ }
+
+ /* Contribution (1,0,0) */
+ dx1 = dx0 - 1 - SQUISH_CONSTANT_3D;
+ dy1 = dy0 - 0 - SQUISH_CONSTANT_3D;
+ dz1 = dz0 - 0 - SQUISH_CONSTANT_3D;
+ attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1;
+ if (attn1 > 0) {
+ attn1 *= attn1;
+ value += attn1 * attn1 * extrapolate3(ctx, xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1);
+ }
+
+ /* Contribution (0,1,0) */
+ dx2 = dx0 - 0 - SQUISH_CONSTANT_3D;
+ dy2 = dy0 - 1 - SQUISH_CONSTANT_3D;
+ dz2 = dz1;
+ attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2;
+ if (attn2 > 0) {
+ attn2 *= attn2;
+ value += attn2 * attn2 * extrapolate3(ctx, xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2);
+ }
+
+ /* Contribution (0,0,1) */
+ dx3 = dx2;
+ dy3 = dy1;
+ dz3 = dz0 - 1 - SQUISH_CONSTANT_3D;
+ attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3;
+ if (attn3 > 0) {
+ attn3 *= attn3;
+ value += attn3 * attn3 * extrapolate3(ctx, xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3);
+ }
+
+ /* Contribution (1,1,0) */
+ dx4 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D;
+ dy4 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D;
+ dz4 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D;
+ attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4;
+ if (attn4 > 0) {
+ attn4 *= attn4;
+ value += attn4 * attn4 * extrapolate3(ctx, xsb + 1, ysb + 1, zsb + 0, dx4, dy4, dz4);
+ }
+
+ /* Contribution (1,0,1) */
+ dx5 = dx4;
+ dy5 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D;
+ dz5 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D;
+ attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5;
+ if (attn5 > 0) {
+ attn5 *= attn5;
+ value += attn5 * attn5 * extrapolate3(ctx, xsb + 1, ysb + 0, zsb + 1, dx5, dy5, dz5);
+ }
+
+ /* Contribution (0,1,1) */
+ dx6 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D;
+ dy6 = dy4;
+ dz6 = dz5;
+ attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6;
+ if (attn6 > 0) {
+ attn6 *= attn6;
+ value += attn6 * attn6 * extrapolate3(ctx, xsb + 0, ysb + 1, zsb + 1, dx6, dy6, dz6);
+ }
+ }
+
+ /* First extra vertex */
+ attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0;
+ if (attn_ext0 > 0)
+ {
+ attn_ext0 *= attn_ext0;
+ value += attn_ext0 * attn_ext0 * extrapolate3(ctx, xsv_ext0, ysv_ext0, zsv_ext0, dx_ext0, dy_ext0, dz_ext0);
+ }
+
+ /* Second extra vertex */
+ attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1;
+ if (attn_ext1 > 0)
+ {
+ attn_ext1 *= attn_ext1;
+ value += attn_ext1 * attn_ext1 * extrapolate3(ctx, xsv_ext1, ysv_ext1, zsv_ext1, dx_ext1, dy_ext1, dz_ext1);
+ }
+
+ return value / NORM_CONSTANT_3D;
+}
+
+/*
+ * 4D OpenSimplex (Simplectic) Noise.
+ */
+double open_simplex_noise4(struct osn_context *ctx, double x, double y, double z, double w)
+{
+ double uins;
+ double dx1, dy1, dz1, dw1;
+ double dx2, dy2, dz2, dw2;
+ double dx3, dy3, dz3, dw3;
+ double dx4, dy4, dz4, dw4;
+ double dx5, dy5, dz5, dw5;
+ double dx6, dy6, dz6, dw6;
+ double dx7, dy7, dz7, dw7;
+ double dx8, dy8, dz8, dw8;
+ double dx9, dy9, dz9, dw9;
+ double dx10, dy10, dz10, dw10;
+ double attn0, attn1, attn2, attn3, attn4;
+ double attn5, attn6, attn7, attn8, attn9, attn10;
+ double attn_ext0, attn_ext1, attn_ext2;
+ int8_t c, c1, c2;
+ int8_t aPoint, bPoint;
+ double aScore, bScore;
+ int aIsBiggerSide;
+ int bIsBiggerSide;
+ double p1, p2, p3, p4;
+ double score;
+
+ /* Place input coordinates on simplectic honeycomb. */
+ double stretchOffset = (x + y + z + w) * STRETCH_CONSTANT_4D;
+ double xs = x + stretchOffset;
+ double ys = y + stretchOffset;
+ double zs = z + stretchOffset;
+ double ws = w + stretchOffset;
+
+ /* Floor to get simplectic honeycomb coordinates of rhombo-hypercube super-cell origin. */
+ int xsb = fastFloor(xs);
+ int ysb = fastFloor(ys);
+ int zsb = fastFloor(zs);
+ int wsb = fastFloor(ws);
+
+ /* Skew out to get actual coordinates of stretched rhombo-hypercube origin. We'll need these later. */
+ double squishOffset = (xsb + ysb + zsb + wsb) * SQUISH_CONSTANT_4D;
+ double xb = xsb + squishOffset;
+ double yb = ysb + squishOffset;
+ double zb = zsb + squishOffset;
+ double wb = wsb + squishOffset;
+
+ /* Compute simplectic honeycomb coordinates relative to rhombo-hypercube origin. */
+ double xins = xs - xsb;
+ double yins = ys - ysb;
+ double zins = zs - zsb;
+ double wins = ws - wsb;
+
+ /* Sum those together to get a value that determines which region we're in. */
+ double inSum = xins + yins + zins + wins;
+
+ /* Positions relative to origin point. */
+ double dx0 = x - xb;
+ double dy0 = y - yb;
+ double dz0 = z - zb;
+ double dw0 = w - wb;
+
+ /* We'll be defining these inside the next block and using them afterwards. */
+ double dx_ext0, dy_ext0, dz_ext0, dw_ext0;
+ double dx_ext1, dy_ext1, dz_ext1, dw_ext1;
+ double dx_ext2, dy_ext2, dz_ext2, dw_ext2;
+ int xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0;
+ int xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1;
+ int xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2;
+
+ double value = 0;
+ if (inSum <= 1) { /* We're inside the pentachoron (4-Simplex) at (0,0,0,0) */
+
+ /* Determine which two of (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0) are closest. */
+ aPoint = 0x01;
+ aScore = xins;
+ bPoint = 0x02;
+ bScore = yins;
+ if (aScore >= bScore && zins > bScore) {
+ bScore = zins;
+ bPoint = 0x04;
+ } else if (aScore < bScore && zins > aScore) {
+ aScore = zins;
+ aPoint = 0x04;
+ }
+ if (aScore >= bScore && wins > bScore) {
+ bScore = wins;
+ bPoint = 0x08;
+ } else if (aScore < bScore && wins > aScore) {
+ aScore = wins;
+ aPoint = 0x08;
+ }
+
+ /* Now we determine the three lattice points not part of the pentachoron that may contribute.
+ This depends on the closest two pentachoron vertices, including (0,0,0,0) */
+ uins = 1 - inSum;
+ if (uins > aScore || uins > bScore) { /* (0,0,0,0) is one of the closest two pentachoron vertices. */
+ c = (bScore > aScore ? bPoint : aPoint); /* Our other closest vertex is the closest out of a and b. */
+ if ((c & 0x01) == 0) {
+ xsv_ext0 = xsb - 1;
+ xsv_ext1 = xsv_ext2 = xsb;
+ dx_ext0 = dx0 + 1;
+ dx_ext1 = dx_ext2 = dx0;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1;
+ dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 1;
+ }
+
+ if ((c & 0x02) == 0) {
+ ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb;
+ dy_ext0 = dy_ext1 = dy_ext2 = dy0;
+ if ((c & 0x01) == 0x01) {
+ ysv_ext0 -= 1;
+ dy_ext0 += 1;
+ } else {
+ ysv_ext1 -= 1;
+ dy_ext1 += 1;
+ }
+ } else {
+ ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1;
+ dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1;
+ }
+
+ if ((c & 0x04) == 0) {
+ zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb;
+ dz_ext0 = dz_ext1 = dz_ext2 = dz0;
+ if ((c & 0x03) != 0) {
+ if ((c & 0x03) == 0x03) {
+ zsv_ext0 -= 1;
+ dz_ext0 += 1;
+ } else {
+ zsv_ext1 -= 1;
+ dz_ext1 += 1;
+ }
+ } else {
+ zsv_ext2 -= 1;
+ dz_ext2 += 1;
+ }
+ } else {
+ zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1;
+ dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1;
+ }
+
+ if ((c & 0x08) == 0) {
+ wsv_ext0 = wsv_ext1 = wsb;
+ wsv_ext2 = wsb - 1;
+ dw_ext0 = dw_ext1 = dw0;
+ dw_ext2 = dw0 + 1;
+ } else {
+ wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1;
+ dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 1;
+ }
+ } else { /* (0,0,0,0) is not one of the closest two pentachoron vertices. */
+ c = (int8_t)(aPoint | bPoint); /* Our three extra vertices are determined by the closest two. */
+
+ if ((c & 0x01) == 0) {
+ xsv_ext0 = xsv_ext2 = xsb;
+ xsv_ext1 = xsb - 1;
+ dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D;
+ dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_4D;
+ dx_ext2 = dx0 - SQUISH_CONSTANT_4D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1;
+ dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dx_ext1 = dx_ext2 = dx0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x02) == 0) {
+ ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb;
+ dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D;
+ dy_ext1 = dy_ext2 = dy0 - SQUISH_CONSTANT_4D;
+ if ((c & 0x01) == 0x01) {
+ ysv_ext1 -= 1;
+ dy_ext1 += 1;
+ } else {
+ ysv_ext2 -= 1;
+ dy_ext2 += 1;
+ }
+ } else {
+ ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1;
+ dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dy_ext1 = dy_ext2 = dy0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x04) == 0) {
+ zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb;
+ dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D;
+ dz_ext1 = dz_ext2 = dz0 - SQUISH_CONSTANT_4D;
+ if ((c & 0x03) == 0x03) {
+ zsv_ext1 -= 1;
+ dz_ext1 += 1;
+ } else {
+ zsv_ext2 -= 1;
+ dz_ext2 += 1;
+ }
+ } else {
+ zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1;
+ dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dz_ext1 = dz_ext2 = dz0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x08) == 0) {
+ wsv_ext0 = wsv_ext1 = wsb;
+ wsv_ext2 = wsb - 1;
+ dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D;
+ dw_ext1 = dw0 - SQUISH_CONSTANT_4D;
+ dw_ext2 = dw0 + 1 - SQUISH_CONSTANT_4D;
+ } else {
+ wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1;
+ dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dw_ext1 = dw_ext2 = dw0 - 1 - SQUISH_CONSTANT_4D;
+ }
+ }
+
+ /* Contribution (0,0,0,0) */
+ attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0;
+ if (attn0 > 0) {
+ attn0 *= attn0;
+ value += attn0 * attn0 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 0, wsb + 0, dx0, dy0, dz0, dw0);
+ }
+
+ /* Contribution (1,0,0,0) */
+ dx1 = dx0 - 1 - SQUISH_CONSTANT_4D;
+ dy1 = dy0 - 0 - SQUISH_CONSTANT_4D;
+ dz1 = dz0 - 0 - SQUISH_CONSTANT_4D;
+ dw1 = dw0 - 0 - SQUISH_CONSTANT_4D;
+ attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1;
+ if (attn1 > 0) {
+ attn1 *= attn1;
+ value += attn1 * attn1 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1);
+ }
+
+ /* Contribution (0,1,0,0) */
+ dx2 = dx0 - 0 - SQUISH_CONSTANT_4D;
+ dy2 = dy0 - 1 - SQUISH_CONSTANT_4D;
+ dz2 = dz1;
+ dw2 = dw1;
+ attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2;
+ if (attn2 > 0) {
+ attn2 *= attn2;
+ value += attn2 * attn2 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2);
+ }
+
+ /* Contribution (0,0,1,0) */
+ dx3 = dx2;
+ dy3 = dy1;
+ dz3 = dz0 - 1 - SQUISH_CONSTANT_4D;
+ dw3 = dw1;
+ attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3;
+ if (attn3 > 0) {
+ attn3 *= attn3;
+ value += attn3 * attn3 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3);
+ }
+
+ /* Contribution (0,0,0,1) */
+ dx4 = dx2;
+ dy4 = dy1;
+ dz4 = dz1;
+ dw4 = dw0 - 1 - SQUISH_CONSTANT_4D;
+ attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4;
+ if (attn4 > 0) {
+ attn4 *= attn4;
+ value += attn4 * attn4 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4);
+ }
+ } else if (inSum >= 3) { /* We're inside the pentachoron (4-Simplex) at (1,1,1,1)
+ Determine which two of (1,1,1,0), (1,1,0,1), (1,0,1,1), (0,1,1,1) are closest. */
+ aPoint = 0x0E;
+ aScore = xins;
+ bPoint = 0x0D;
+ bScore = yins;
+ if (aScore <= bScore && zins < bScore) {
+ bScore = zins;
+ bPoint = 0x0B;
+ } else if (aScore > bScore && zins < aScore) {
+ aScore = zins;
+ aPoint = 0x0B;
+ }
+ if (aScore <= bScore && wins < bScore) {
+ bScore = wins;
+ bPoint = 0x07;
+ } else if (aScore > bScore && wins < aScore) {
+ aScore = wins;
+ aPoint = 0x07;
+ }
+
+ /* Now we determine the three lattice points not part of the pentachoron that may contribute.
+ This depends on the closest two pentachoron vertices, including (0,0,0,0) */
+ uins = 4 - inSum;
+ if (uins < aScore || uins < bScore) { /* (1,1,1,1) is one of the closest two pentachoron vertices. */
+ c = (bScore < aScore ? bPoint : aPoint); /* Our other closest vertex is the closest out of a and b. */
+
+ if ((c & 0x01) != 0) {
+ xsv_ext0 = xsb + 2;
+ xsv_ext1 = xsv_ext2 = xsb + 1;
+ dx_ext0 = dx0 - 2 - 4 * SQUISH_CONSTANT_4D;
+ dx_ext1 = dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb;
+ dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 4 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x02) != 0) {
+ ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1;
+ dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ if ((c & 0x01) != 0) {
+ ysv_ext1 += 1;
+ dy_ext1 -= 1;
+ } else {
+ ysv_ext0 += 1;
+ dy_ext0 -= 1;
+ }
+ } else {
+ ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb;
+ dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 4 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x04) != 0) {
+ zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1;
+ dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ if ((c & 0x03) != 0x03) {
+ if ((c & 0x03) == 0) {
+ zsv_ext0 += 1;
+ dz_ext0 -= 1;
+ } else {
+ zsv_ext1 += 1;
+ dz_ext1 -= 1;
+ }
+ } else {
+ zsv_ext2 += 1;
+ dz_ext2 -= 1;
+ }
+ } else {
+ zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb;
+ dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 4 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x08) != 0) {
+ wsv_ext0 = wsv_ext1 = wsb + 1;
+ wsv_ext2 = wsb + 2;
+ dw_ext0 = dw_ext1 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ dw_ext2 = dw0 - 2 - 4 * SQUISH_CONSTANT_4D;
+ } else {
+ wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb;
+ dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 4 * SQUISH_CONSTANT_4D;
+ }
+ } else { /* (1,1,1,1) is not one of the closest two pentachoron vertices. */
+ c = (int8_t)(aPoint & bPoint); /* Our three extra vertices are determined by the closest two. */
+
+ if ((c & 0x01) != 0) {
+ xsv_ext0 = xsv_ext2 = xsb + 1;
+ xsv_ext1 = xsb + 2;
+ dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dx_ext1 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D;
+ dx_ext2 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb;
+ dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D;
+ dx_ext1 = dx_ext2 = dx0 - 3 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x02) != 0) {
+ ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1;
+ dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dy_ext1 = dy_ext2 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ if ((c & 0x01) != 0) {
+ ysv_ext2 += 1;
+ dy_ext2 -= 1;
+ } else {
+ ysv_ext1 += 1;
+ dy_ext1 -= 1;
+ }
+ } else {
+ ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb;
+ dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D;
+ dy_ext1 = dy_ext2 = dy0 - 3 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x04) != 0) {
+ zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1;
+ dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dz_ext1 = dz_ext2 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ if ((c & 0x03) != 0) {
+ zsv_ext2 += 1;
+ dz_ext2 -= 1;
+ } else {
+ zsv_ext1 += 1;
+ dz_ext1 -= 1;
+ }
+ } else {
+ zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb;
+ dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D;
+ dz_ext1 = dz_ext2 = dz0 - 3 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x08) != 0) {
+ wsv_ext0 = wsv_ext1 = wsb + 1;
+ wsv_ext2 = wsb + 2;
+ dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dw_ext1 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dw_ext2 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D;
+ } else {
+ wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb;
+ dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D;
+ dw_ext1 = dw_ext2 = dw0 - 3 * SQUISH_CONSTANT_4D;
+ }
+ }
+
+ /* Contribution (1,1,1,0) */
+ dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dw4 = dw0 - 3 * SQUISH_CONSTANT_4D;
+ attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4;
+ if (attn4 > 0) {
+ attn4 *= attn4;
+ value += attn4 * attn4 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4);
+ }
+
+ /* Contribution (1,1,0,1) */
+ dx3 = dx4;
+ dy3 = dy4;
+ dz3 = dz0 - 3 * SQUISH_CONSTANT_4D;
+ dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3;
+ if (attn3 > 0) {
+ attn3 *= attn3;
+ value += attn3 * attn3 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3);
+ }
+
+ /* Contribution (1,0,1,1) */
+ dx2 = dx4;
+ dy2 = dy0 - 3 * SQUISH_CONSTANT_4D;
+ dz2 = dz4;
+ dw2 = dw3;
+ attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2;
+ if (attn2 > 0) {
+ attn2 *= attn2;
+ value += attn2 * attn2 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2);
+ }
+
+ /* Contribution (0,1,1,1) */
+ dx1 = dx0 - 3 * SQUISH_CONSTANT_4D;
+ dz1 = dz4;
+ dy1 = dy4;
+ dw1 = dw3;
+ attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1;
+ if (attn1 > 0) {
+ attn1 *= attn1;
+ value += attn1 * attn1 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1);
+ }
+
+ /* Contribution (1,1,1,1) */
+ dx0 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ dy0 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ dz0 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ dw0 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0;
+ if (attn0 > 0) {
+ attn0 *= attn0;
+ value += attn0 * attn0 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 1, wsb + 1, dx0, dy0, dz0, dw0);
+ }
+ } else if (inSum <= 2) { /* We're inside the first dispentachoron (Rectified 4-Simplex) */
+ aIsBiggerSide = 1;
+ bIsBiggerSide = 1;
+
+ /* Decide between (1,1,0,0) and (0,0,1,1) */
+ if (xins + yins > zins + wins) {
+ aScore = xins + yins;
+ aPoint = 0x03;
+ } else {
+ aScore = zins + wins;
+ aPoint = 0x0C;
+ }
+
+ /* Decide between (1,0,1,0) and (0,1,0,1) */
+ if (xins + zins > yins + wins) {
+ bScore = xins + zins;
+ bPoint = 0x05;
+ } else {
+ bScore = yins + wins;
+ bPoint = 0x0A;
+ }
+
+ /* Closer between (1,0,0,1) and (0,1,1,0) will replace the further of a and b, if closer. */
+ if (xins + wins > yins + zins) {
+ score = xins + wins;
+ if (aScore >= bScore && score > bScore) {
+ bScore = score;
+ bPoint = 0x09;
+ } else if (aScore < bScore && score > aScore) {
+ aScore = score;
+ aPoint = 0x09;
+ }
+ } else {
+ score = yins + zins;
+ if (aScore >= bScore && score > bScore) {
+ bScore = score;
+ bPoint = 0x06;
+ } else if (aScore < bScore && score > aScore) {
+ aScore = score;
+ aPoint = 0x06;
+ }
+ }
+
+ /* Decide if (1,0,0,0) is closer. */
+ p1 = 2 - inSum + xins;
+ if (aScore >= bScore && p1 > bScore) {
+ bScore = p1;
+ bPoint = 0x01;
+ bIsBiggerSide = 0;
+ } else if (aScore < bScore && p1 > aScore) {
+ aScore = p1;
+ aPoint = 0x01;
+ aIsBiggerSide = 0;
+ }
+
+ /* Decide if (0,1,0,0) is closer. */
+ p2 = 2 - inSum + yins;
+ if (aScore >= bScore && p2 > bScore) {
+ bScore = p2;
+ bPoint = 0x02;
+ bIsBiggerSide = 0;
+ } else if (aScore < bScore && p2 > aScore) {
+ aScore = p2;
+ aPoint = 0x02;
+ aIsBiggerSide = 0;
+ }
+
+ /* Decide if (0,0,1,0) is closer. */
+ p3 = 2 - inSum + zins;
+ if (aScore >= bScore && p3 > bScore) {
+ bScore = p3;
+ bPoint = 0x04;
+ bIsBiggerSide = 0;
+ } else if (aScore < bScore && p3 > aScore) {
+ aScore = p3;
+ aPoint = 0x04;
+ aIsBiggerSide = 0;
+ }
+
+ /* Decide if (0,0,0,1) is closer. */
+ p4 = 2 - inSum + wins;
+ if (aScore >= bScore && p4 > bScore) {
+ bScore = p4;
+ bPoint = 0x08;
+ bIsBiggerSide = 0;
+ } else if (aScore < bScore && p4 > aScore) {
+ aScore = p4;
+ aPoint = 0x08;
+ aIsBiggerSide = 0;
+ }
+
+ /* Where each of the two closest points are determines how the extra three vertices are calculated. */
+ if (aIsBiggerSide == bIsBiggerSide) {
+ if (aIsBiggerSide) { /* Both closest points on the bigger side */
+ c1 = (int8_t)(aPoint | bPoint);
+ c2 = (int8_t)(aPoint & bPoint);
+ if ((c1 & 0x01) == 0) {
+ xsv_ext0 = xsb;
+ xsv_ext1 = xsb - 1;
+ dx_ext0 = dx0 - 3 * SQUISH_CONSTANT_4D;
+ dx_ext1 = dx0 + 1 - 2 * SQUISH_CONSTANT_4D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsb + 1;
+ dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dx_ext1 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c1 & 0x02) == 0) {
+ ysv_ext0 = ysb;
+ ysv_ext1 = ysb - 1;
+ dy_ext0 = dy0 - 3 * SQUISH_CONSTANT_4D;
+ dy_ext1 = dy0 + 1 - 2 * SQUISH_CONSTANT_4D;
+ } else {
+ ysv_ext0 = ysv_ext1 = ysb + 1;
+ dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dy_ext1 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c1 & 0x04) == 0) {
+ zsv_ext0 = zsb;
+ zsv_ext1 = zsb - 1;
+ dz_ext0 = dz0 - 3 * SQUISH_CONSTANT_4D;
+ dz_ext1 = dz0 + 1 - 2 * SQUISH_CONSTANT_4D;
+ } else {
+ zsv_ext0 = zsv_ext1 = zsb + 1;
+ dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dz_ext1 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c1 & 0x08) == 0) {
+ wsv_ext0 = wsb;
+ wsv_ext1 = wsb - 1;
+ dw_ext0 = dw0 - 3 * SQUISH_CONSTANT_4D;
+ dw_ext1 = dw0 + 1 - 2 * SQUISH_CONSTANT_4D;
+ } else {
+ wsv_ext0 = wsv_ext1 = wsb + 1;
+ dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dw_ext1 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ }
+
+ /* One combination is a permutation of (0,0,0,2) based on c2 */
+ xsv_ext2 = xsb;
+ ysv_ext2 = ysb;
+ zsv_ext2 = zsb;
+ wsv_ext2 = wsb;
+ dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D;
+ dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D;
+ dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D;
+ dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D;
+ if ((c2 & 0x01) != 0) {
+ xsv_ext2 += 2;
+ dx_ext2 -= 2;
+ } else if ((c2 & 0x02) != 0) {
+ ysv_ext2 += 2;
+ dy_ext2 -= 2;
+ } else if ((c2 & 0x04) != 0) {
+ zsv_ext2 += 2;
+ dz_ext2 -= 2;
+ } else {
+ wsv_ext2 += 2;
+ dw_ext2 -= 2;
+ }
+
+ } else { /* Both closest points on the smaller side */
+ /* One of the two extra points is (0,0,0,0) */
+ xsv_ext2 = xsb;
+ ysv_ext2 = ysb;
+ zsv_ext2 = zsb;
+ wsv_ext2 = wsb;
+ dx_ext2 = dx0;
+ dy_ext2 = dy0;
+ dz_ext2 = dz0;
+ dw_ext2 = dw0;
+
+ /* Other two points are based on the omitted axes. */
+ c = (int8_t)(aPoint | bPoint);
+
+ if ((c & 0x01) == 0) {
+ xsv_ext0 = xsb - 1;
+ xsv_ext1 = xsb;
+ dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D;
+ dx_ext1 = dx0 - SQUISH_CONSTANT_4D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsb + 1;
+ dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x02) == 0) {
+ ysv_ext0 = ysv_ext1 = ysb;
+ dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D;
+ if ((c & 0x01) == 0x01)
+ {
+ ysv_ext0 -= 1;
+ dy_ext0 += 1;
+ } else {
+ ysv_ext1 -= 1;
+ dy_ext1 += 1;
+ }
+ } else {
+ ysv_ext0 = ysv_ext1 = ysb + 1;
+ dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x04) == 0) {
+ zsv_ext0 = zsv_ext1 = zsb;
+ dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D;
+ if ((c & 0x03) == 0x03)
+ {
+ zsv_ext0 -= 1;
+ dz_ext0 += 1;
+ } else {
+ zsv_ext1 -= 1;
+ dz_ext1 += 1;
+ }
+ } else {
+ zsv_ext0 = zsv_ext1 = zsb + 1;
+ dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x08) == 0)
+ {
+ wsv_ext0 = wsb;
+ wsv_ext1 = wsb - 1;
+ dw_ext0 = dw0 - SQUISH_CONSTANT_4D;
+ dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D;
+ } else {
+ wsv_ext0 = wsv_ext1 = wsb + 1;
+ dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ }
+ } else { /* One point on each "side" */
+ if (aIsBiggerSide) {
+ c1 = aPoint;
+ c2 = bPoint;
+ } else {
+ c1 = bPoint;
+ c2 = aPoint;
+ }
+
+ /* Two contributions are the bigger-sided point with each 0 replaced with -1. */
+ if ((c1 & 0x01) == 0) {
+ xsv_ext0 = xsb - 1;
+ xsv_ext1 = xsb;
+ dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D;
+ dx_ext1 = dx0 - SQUISH_CONSTANT_4D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsb + 1;
+ dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ if ((c1 & 0x02) == 0) {
+ ysv_ext0 = ysv_ext1 = ysb;
+ dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D;
+ if ((c1 & 0x01) == 0x01) {
+ ysv_ext0 -= 1;
+ dy_ext0 += 1;
+ } else {
+ ysv_ext1 -= 1;
+ dy_ext1 += 1;
+ }
+ } else {
+ ysv_ext0 = ysv_ext1 = ysb + 1;
+ dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ if ((c1 & 0x04) == 0) {
+ zsv_ext0 = zsv_ext1 = zsb;
+ dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D;
+ if ((c1 & 0x03) == 0x03) {
+ zsv_ext0 -= 1;
+ dz_ext0 += 1;
+ } else {
+ zsv_ext1 -= 1;
+ dz_ext1 += 1;
+ }
+ } else {
+ zsv_ext0 = zsv_ext1 = zsb + 1;
+ dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ if ((c1 & 0x08) == 0) {
+ wsv_ext0 = wsb;
+ wsv_ext1 = wsb - 1;
+ dw_ext0 = dw0 - SQUISH_CONSTANT_4D;
+ dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D;
+ } else {
+ wsv_ext0 = wsv_ext1 = wsb + 1;
+ dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D;
+ }
+
+ /* One contribution is a permutation of (0,0,0,2) based on the smaller-sided point */
+ xsv_ext2 = xsb;
+ ysv_ext2 = ysb;
+ zsv_ext2 = zsb;
+ wsv_ext2 = wsb;
+ dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D;
+ dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D;
+ dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D;
+ dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D;
+ if ((c2 & 0x01) != 0) {
+ xsv_ext2 += 2;
+ dx_ext2 -= 2;
+ } else if ((c2 & 0x02) != 0) {
+ ysv_ext2 += 2;
+ dy_ext2 -= 2;
+ } else if ((c2 & 0x04) != 0) {
+ zsv_ext2 += 2;
+ dz_ext2 -= 2;
+ } else {
+ wsv_ext2 += 2;
+ dw_ext2 -= 2;
+ }
+ }
+
+ /* Contribution (1,0,0,0) */
+ dx1 = dx0 - 1 - SQUISH_CONSTANT_4D;
+ dy1 = dy0 - 0 - SQUISH_CONSTANT_4D;
+ dz1 = dz0 - 0 - SQUISH_CONSTANT_4D;
+ dw1 = dw0 - 0 - SQUISH_CONSTANT_4D;
+ attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1;
+ if (attn1 > 0) {
+ attn1 *= attn1;
+ value += attn1 * attn1 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1);
+ }
+
+ /* Contribution (0,1,0,0) */
+ dx2 = dx0 - 0 - SQUISH_CONSTANT_4D;
+ dy2 = dy0 - 1 - SQUISH_CONSTANT_4D;
+ dz2 = dz1;
+ dw2 = dw1;
+ attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2;
+ if (attn2 > 0) {
+ attn2 *= attn2;
+ value += attn2 * attn2 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2);
+ }
+
+ /* Contribution (0,0,1,0) */
+ dx3 = dx2;
+ dy3 = dy1;
+ dz3 = dz0 - 1 - SQUISH_CONSTANT_4D;
+ dw3 = dw1;
+ attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3;
+ if (attn3 > 0) {
+ attn3 *= attn3;
+ value += attn3 * attn3 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3);
+ }
+
+ /* Contribution (0,0,0,1) */
+ dx4 = dx2;
+ dy4 = dy1;
+ dz4 = dz1;
+ dw4 = dw0 - 1 - SQUISH_CONSTANT_4D;
+ attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4;
+ if (attn4 > 0) {
+ attn4 *= attn4;
+ value += attn4 * attn4 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4);
+ }
+
+ /* Contribution (1,1,0,0) */
+ dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5;
+ if (attn5 > 0) {
+ attn5 *= attn5;
+ value += attn5 * attn5 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5);
+ }
+
+ /* Contribution (1,0,1,0) */
+ dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6;
+ if (attn6 > 0) {
+ attn6 *= attn6;
+ value += attn6 * attn6 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6);
+ }
+
+ /* Contribution (1,0,0,1) */
+ dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7;
+ if (attn7 > 0) {
+ attn7 *= attn7;
+ value += attn7 * attn7 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7);
+ }
+
+ /* Contribution (0,1,1,0) */
+ dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8;
+ if (attn8 > 0) {
+ attn8 *= attn8;
+ value += attn8 * attn8 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8);
+ }
+
+ /* Contribution (0,1,0,1) */
+ dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9;
+ if (attn9 > 0) {
+ attn9 *= attn9;
+ value += attn9 * attn9 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9);
+ }
+
+ /* Contribution (0,0,1,1) */
+ dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10;
+ if (attn10 > 0) {
+ attn10 *= attn10;
+ value += attn10 * attn10 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10);
+ }
+ } else { /* We're inside the second dispentachoron (Rectified 4-Simplex) */
+ aIsBiggerSide = 1;
+ bIsBiggerSide = 1;
+
+ /* Decide between (0,0,1,1) and (1,1,0,0) */
+ if (xins + yins < zins + wins) {
+ aScore = xins + yins;
+ aPoint = 0x0C;
+ } else {
+ aScore = zins + wins;
+ aPoint = 0x03;
+ }
+
+ /* Decide between (0,1,0,1) and (1,0,1,0) */
+ if (xins + zins < yins + wins) {
+ bScore = xins + zins;
+ bPoint = 0x0A;
+ } else {
+ bScore = yins + wins;
+ bPoint = 0x05;
+ }
+
+ /* Closer between (0,1,1,0) and (1,0,0,1) will replace the further of a and b, if closer. */
+ if (xins + wins < yins + zins) {
+ score = xins + wins;
+ if (aScore <= bScore && score < bScore) {
+ bScore = score;
+ bPoint = 0x06;
+ } else if (aScore > bScore && score < aScore) {
+ aScore = score;
+ aPoint = 0x06;
+ }
+ } else {
+ score = yins + zins;
+ if (aScore <= bScore && score < bScore) {
+ bScore = score;
+ bPoint = 0x09;
+ } else if (aScore > bScore && score < aScore) {
+ aScore = score;
+ aPoint = 0x09;
+ }
+ }
+
+ /* Decide if (0,1,1,1) is closer. */
+ p1 = 3 - inSum + xins;
+ if (aScore <= bScore && p1 < bScore) {
+ bScore = p1;
+ bPoint = 0x0E;
+ bIsBiggerSide = 0;
+ } else if (aScore > bScore && p1 < aScore) {
+ aScore = p1;
+ aPoint = 0x0E;
+ aIsBiggerSide = 0;
+ }
+
+ /* Decide if (1,0,1,1) is closer. */
+ p2 = 3 - inSum + yins;
+ if (aScore <= bScore && p2 < bScore) {
+ bScore = p2;
+ bPoint = 0x0D;
+ bIsBiggerSide = 0;
+ } else if (aScore > bScore && p2 < aScore) {
+ aScore = p2;
+ aPoint = 0x0D;
+ aIsBiggerSide = 0;
+ }
+
+ /* Decide if (1,1,0,1) is closer. */
+ p3 = 3 - inSum + zins;
+ if (aScore <= bScore && p3 < bScore) {
+ bScore = p3;
+ bPoint = 0x0B;
+ bIsBiggerSide = 0;
+ } else if (aScore > bScore && p3 < aScore) {
+ aScore = p3;
+ aPoint = 0x0B;
+ aIsBiggerSide = 0;
+ }
+
+ /* Decide if (1,1,1,0) is closer. */
+ p4 = 3 - inSum + wins;
+ if (aScore <= bScore && p4 < bScore) {
+ bScore = p4;
+ bPoint = 0x07;
+ bIsBiggerSide = 0;
+ } else if (aScore > bScore && p4 < aScore) {
+ aScore = p4;
+ aPoint = 0x07;
+ aIsBiggerSide = 0;
+ }
+
+ /* Where each of the two closest points are determines how the extra three vertices are calculated. */
+ if (aIsBiggerSide == bIsBiggerSide) {
+ if (aIsBiggerSide) { /* Both closest points on the bigger side */
+ c1 = (int8_t)(aPoint & bPoint);
+ c2 = (int8_t)(aPoint | bPoint);
+
+ /* Two contributions are permutations of (0,0,0,1) and (0,0,0,2) based on c1 */
+ xsv_ext0 = xsv_ext1 = xsb;
+ ysv_ext0 = ysv_ext1 = ysb;
+ zsv_ext0 = zsv_ext1 = zsb;
+ wsv_ext0 = wsv_ext1 = wsb;
+ dx_ext0 = dx0 - SQUISH_CONSTANT_4D;
+ dy_ext0 = dy0 - SQUISH_CONSTANT_4D;
+ dz_ext0 = dz0 - SQUISH_CONSTANT_4D;
+ dw_ext0 = dw0 - SQUISH_CONSTANT_4D;
+ dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_4D;
+ dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_4D;
+ dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_4D;
+ dw_ext1 = dw0 - 2 * SQUISH_CONSTANT_4D;
+ if ((c1 & 0x01) != 0) {
+ xsv_ext0 += 1;
+ dx_ext0 -= 1;
+ xsv_ext1 += 2;
+ dx_ext1 -= 2;
+ } else if ((c1 & 0x02) != 0) {
+ ysv_ext0 += 1;
+ dy_ext0 -= 1;
+ ysv_ext1 += 2;
+ dy_ext1 -= 2;
+ } else if ((c1 & 0x04) != 0) {
+ zsv_ext0 += 1;
+ dz_ext0 -= 1;
+ zsv_ext1 += 2;
+ dz_ext1 -= 2;
+ } else {
+ wsv_ext0 += 1;
+ dw_ext0 -= 1;
+ wsv_ext1 += 2;
+ dw_ext1 -= 2;
+ }
+
+ /* One contribution is a permutation of (1,1,1,-1) based on c2 */
+ xsv_ext2 = xsb + 1;
+ ysv_ext2 = ysb + 1;
+ zsv_ext2 = zsb + 1;
+ wsv_ext2 = wsb + 1;
+ dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ if ((c2 & 0x01) == 0) {
+ xsv_ext2 -= 2;
+ dx_ext2 += 2;
+ } else if ((c2 & 0x02) == 0) {
+ ysv_ext2 -= 2;
+ dy_ext2 += 2;
+ } else if ((c2 & 0x04) == 0) {
+ zsv_ext2 -= 2;
+ dz_ext2 += 2;
+ } else {
+ wsv_ext2 -= 2;
+ dw_ext2 += 2;
+ }
+ } else { /* Both closest points on the smaller side */
+ /* One of the two extra points is (1,1,1,1) */
+ xsv_ext2 = xsb + 1;
+ ysv_ext2 = ysb + 1;
+ zsv_ext2 = zsb + 1;
+ wsv_ext2 = wsb + 1;
+ dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D;
+ dw_ext2 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D;
+
+ /* Other two points are based on the shared axes. */
+ c = (int8_t)(aPoint & bPoint);
+
+ if ((c & 0x01) != 0) {
+ xsv_ext0 = xsb + 2;
+ xsv_ext1 = xsb + 1;
+ dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D;
+ dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsb;
+ dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x02) != 0) {
+ ysv_ext0 = ysv_ext1 = ysb + 1;
+ dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ if ((c & 0x01) == 0)
+ {
+ ysv_ext0 += 1;
+ dy_ext0 -= 1;
+ } else {
+ ysv_ext1 += 1;
+ dy_ext1 -= 1;
+ }
+ } else {
+ ysv_ext0 = ysv_ext1 = ysb;
+ dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x04) != 0) {
+ zsv_ext0 = zsv_ext1 = zsb + 1;
+ dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ if ((c & 0x03) == 0)
+ {
+ zsv_ext0 += 1;
+ dz_ext0 -= 1;
+ } else {
+ zsv_ext1 += 1;
+ dz_ext1 -= 1;
+ }
+ } else {
+ zsv_ext0 = zsv_ext1 = zsb;
+ dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c & 0x08) != 0)
+ {
+ wsv_ext0 = wsb + 1;
+ wsv_ext1 = wsb + 2;
+ dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D;
+ } else {
+ wsv_ext0 = wsv_ext1 = wsb;
+ dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D;
+ }
+ }
+ } else { /* One point on each "side" */
+ if (aIsBiggerSide) {
+ c1 = aPoint;
+ c2 = bPoint;
+ } else {
+ c1 = bPoint;
+ c2 = aPoint;
+ }
+
+ /* Two contributions are the bigger-sided point with each 1 replaced with 2. */
+ if ((c1 & 0x01) != 0) {
+ xsv_ext0 = xsb + 2;
+ xsv_ext1 = xsb + 1;
+ dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D;
+ dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ } else {
+ xsv_ext0 = xsv_ext1 = xsb;
+ dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c1 & 0x02) != 0) {
+ ysv_ext0 = ysv_ext1 = ysb + 1;
+ dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ if ((c1 & 0x01) == 0) {
+ ysv_ext0 += 1;
+ dy_ext0 -= 1;
+ } else {
+ ysv_ext1 += 1;
+ dy_ext1 -= 1;
+ }
+ } else {
+ ysv_ext0 = ysv_ext1 = ysb;
+ dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c1 & 0x04) != 0) {
+ zsv_ext0 = zsv_ext1 = zsb + 1;
+ dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ if ((c1 & 0x03) == 0) {
+ zsv_ext0 += 1;
+ dz_ext0 -= 1;
+ } else {
+ zsv_ext1 += 1;
+ dz_ext1 -= 1;
+ }
+ } else {
+ zsv_ext0 = zsv_ext1 = zsb;
+ dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D;
+ }
+
+ if ((c1 & 0x08) != 0) {
+ wsv_ext0 = wsb + 1;
+ wsv_ext1 = wsb + 2;
+ dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D;
+ } else {
+ wsv_ext0 = wsv_ext1 = wsb;
+ dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D;
+ }
+
+ /* One contribution is a permutation of (1,1,1,-1) based on the smaller-sided point */
+ xsv_ext2 = xsb + 1;
+ ysv_ext2 = ysb + 1;
+ zsv_ext2 = zsb + 1;
+ wsv_ext2 = wsb + 1;
+ dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ if ((c2 & 0x01) == 0) {
+ xsv_ext2 -= 2;
+ dx_ext2 += 2;
+ } else if ((c2 & 0x02) == 0) {
+ ysv_ext2 -= 2;
+ dy_ext2 += 2;
+ } else if ((c2 & 0x04) == 0) {
+ zsv_ext2 -= 2;
+ dz_ext2 += 2;
+ } else {
+ wsv_ext2 -= 2;
+ dw_ext2 += 2;
+ }
+ }
+
+ /* Contribution (1,1,1,0) */
+ dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ dw4 = dw0 - 3 * SQUISH_CONSTANT_4D;
+ attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4;
+ if (attn4 > 0) {
+ attn4 *= attn4;
+ value += attn4 * attn4 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4);
+ }
+
+ /* Contribution (1,1,0,1) */
+ dx3 = dx4;
+ dy3 = dy4;
+ dz3 = dz0 - 3 * SQUISH_CONSTANT_4D;
+ dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D;
+ attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3;
+ if (attn3 > 0) {
+ attn3 *= attn3;
+ value += attn3 * attn3 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3);
+ }
+
+ /* Contribution (1,0,1,1) */
+ dx2 = dx4;
+ dy2 = dy0 - 3 * SQUISH_CONSTANT_4D;
+ dz2 = dz4;
+ dw2 = dw3;
+ attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2;
+ if (attn2 > 0) {
+ attn2 *= attn2;
+ value += attn2 * attn2 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2);
+ }
+
+ /* Contribution (0,1,1,1) */
+ dx1 = dx0 - 3 * SQUISH_CONSTANT_4D;
+ dz1 = dz4;
+ dy1 = dy4;
+ dw1 = dw3;
+ attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1;
+ if (attn1 > 0) {
+ attn1 *= attn1;
+ value += attn1 * attn1 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1);
+ }
+
+ /* Contribution (1,1,0,0) */
+ dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5;
+ if (attn5 > 0) {
+ attn5 *= attn5;
+ value += attn5 * attn5 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5);
+ }
+
+ /* Contribution (1,0,1,0) */
+ dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6;
+ if (attn6 > 0) {
+ attn6 *= attn6;
+ value += attn6 * attn6 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6);
+ }
+
+ /* Contribution (1,0,0,1) */
+ dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7;
+ if (attn7 > 0) {
+ attn7 *= attn7;
+ value += attn7 * attn7 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7);
+ }
+
+ /* Contribution (0,1,1,0) */
+ dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8;
+ if (attn8 > 0) {
+ attn8 *= attn8;
+ value += attn8 * attn8 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8);
+ }
+
+ /* Contribution (0,1,0,1) */
+ dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9;
+ if (attn9 > 0) {
+ attn9 *= attn9;
+ value += attn9 * attn9 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9);
+ }
+
+ /* Contribution (0,0,1,1) */
+ dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D;
+ dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D;
+ attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10;
+ if (attn10 > 0) {
+ attn10 *= attn10;
+ value += attn10 * attn10 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10);
+ }
+ }
+
+ /* First extra vertex */
+ attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0 - dw_ext0 * dw_ext0;
+ if (attn_ext0 > 0)
+ {
+ attn_ext0 *= attn_ext0;
+ value += attn_ext0 * attn_ext0 * extrapolate4(ctx, xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0, dx_ext0, dy_ext0, dz_ext0, dw_ext0);
+ }
+
+ /* Second extra vertex */
+ attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1 - dw_ext1 * dw_ext1;
+ if (attn_ext1 > 0)
+ {
+ attn_ext1 *= attn_ext1;
+ value += attn_ext1 * attn_ext1 * extrapolate4(ctx, xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1, dx_ext1, dy_ext1, dz_ext1, dw_ext1);
+ }
+
+ /* Third extra vertex */
+ attn_ext2 = 2 - dx_ext2 * dx_ext2 - dy_ext2 * dy_ext2 - dz_ext2 * dz_ext2 - dw_ext2 * dw_ext2;
+ if (attn_ext2 > 0)
+ {
+ attn_ext2 *= attn_ext2;
+ value += attn_ext2 * attn_ext2 * extrapolate4(ctx, xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2, dx_ext2, dy_ext2, dz_ext2, dw_ext2);
+ }
+
+ return value / NORM_CONSTANT_4D;
+}
+
diff --git a/plugins/generators/simplexnoise/3rdparty/c-open-simplex/open-simplex-noise.h b/plugins/generators/simplexnoise/3rdparty/c-open-simplex/open-simplex-noise.h
new file mode 100644
index 0000000000..460ce8fa45
--- /dev/null
+++ b/plugins/generators/simplexnoise/3rdparty/c-open-simplex/open-simplex-noise.h
@@ -0,0 +1,51 @@
+#ifndef OPEN_SIMPLEX_NOISE_H__
+#define OPEN_SIMPLEX_NOISE_H__
+
+/*
+ * OpenSimplex (Simplectic) Noise in C.
+ * Ported to C from Kurt Spencer's java implementation by Stephen M. Cameron
+ *
+ * v1.1 (October 6, 2014)
+ * - Ported to C
+ *
+ * v1.1 (October 5, 2014)
+ * - Added 2D and 4D implementations.
+ * - Proper gradient sets for all dimensions, from a
+ * dimensionally-generalizable scheme with an actual
+ * rhyme and reason behind it.
+ * - Removed default permutation array in favor of
+ * default seed.
+ * - Changed seed-based constructor to be independent
+ * of any particular randomization library, so results
+ * will be the same when ported to other languages.
+ */
+
+#if ((__GNUC_STDC_INLINE__) || (__STDC_VERSION__ >= 199901L))
+ #include
+ #define INLINE inline
+#elif (defined (_MSC_VER) || defined (__GNUC_GNU_INLINE__))
+ #include
+ #define INLINE __inline
+#else
+ /* ANSI C doesn't have inline or stdint.h. */
+ #define INLINE
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+struct osn_context;
+
+int open_simplex_noise(int64_t seed, struct osn_context **ctx);
+void open_simplex_noise_free(struct osn_context *ctx);
+int open_simplex_noise_init_perm(struct osn_context *ctx, int16_t p[], int nelements);
+double open_simplex_noise2(struct osn_context *ctx, double x, double y);
+double open_simplex_noise3(struct osn_context *ctx, double x, double y, double z);
+double open_simplex_noise4(struct osn_context *ctx, double x, double y, double z, double w);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/plugins/generators/simplexnoise/CMakeLists.txt b/plugins/generators/simplexnoise/CMakeLists.txt
new file mode 100644
index 0000000000..938f3bfa7f
--- /dev/null
+++ b/plugins/generators/simplexnoise/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(kritasimplexnoisegenerator_SOURCES
+ simplexnoisegenerator.cpp
+ kis_wdg_simplex_noise.cpp
+ 3rdparty/c-open-simplex/open-simplex-noise.c
+ )
+ki18n_wrap_ui(kritasimplexnoisegenerator_SOURCES
+ wdgsimplexnoiseoptions.ui
+ )
+
+add_library(kritasimplexnoisegenerator MODULE ${kritasimplexnoisegenerator_SOURCES})
+target_link_libraries(kritasimplexnoisegenerator kritaui)
+install(TARGETS kritasimplexnoisegenerator DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
diff --git a/plugins/generators/simplexnoise/kis_wdg_simplex_noise.cpp b/plugins/generators/simplexnoise/kis_wdg_simplex_noise.cpp
new file mode 100644
index 0000000000..b7b47b63de
--- /dev/null
+++ b/plugins/generators/simplexnoise/kis_wdg_simplex_noise.cpp
@@ -0,0 +1,90 @@
+/*
+ * KDE. Krita Project.
+ *
+ * Copyright (c) 2019 Eoin O'Neill
+ * Copyright (c) 2019 Emmet O'Neill
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_wdg_simplex_noise.h"
+#include "ui_wdgsimplexnoiseoptions.h"
+
+#include
+#include
+
+KisWdgSimplexNoise::KisWdgSimplexNoise(KisFilter* /*nfilter*/, QWidget* parent)
+ : KisConfigWidget(parent),
+ updateCompressor(250, KisSignalCompressor::Mode::POSTPONE)
+{
+ m_widget = new Ui_WdgSimplexNoiseOptions();
+ m_widget->setupUi(this);
+ connect(m_widget->slider_frequency, SIGNAL(valueChanged(qreal)), &updateCompressor, SLOT(start()));
+ connect(m_widget->cb_looping, SIGNAL(stateChanged(int)), &updateCompressor, SLOT(start()));
+ connect(m_widget->seed_text, SIGNAL(textChanged(QString)), &updateCompressor, SLOT(start()));
+ connect(m_widget->ratiox_slider, SIGNAL(valueChanged(qreal)), &updateCompressor, SLOT(start()));
+ connect(m_widget->ratioy_slider, SIGNAL(valueChanged(qreal)), &updateCompressor, SLOT(start()));
+ connect(&updateCompressor, SIGNAL(timeout()), this, SIGNAL(sigConfigurationItemChanged()));
+ m_widget->slider_frequency->setRange(1.0f, 500.0f, 2);
+ m_widget->slider_frequency->setValue(25.0f);
+ m_widget->slider_frequency->setExponentRatio(3.0);
+ m_widget->ratiox_slider->setRange(0.0f, 2.0f, 2);
+ m_widget->ratiox_slider->setValue(1.0f);
+ m_widget->ratioy_slider->setRange(0.0f, 2.0f, 2);
+ m_widget->ratioy_slider->setValue(1.0f);
+}
+
+KisWdgSimplexNoise::~KisWdgSimplexNoise()
+{
+ delete m_widget;
+}
+
+void KisWdgSimplexNoise::setConfiguration(const KisPropertiesConfigurationSP config)
+{
+ QVariant value;
+ if( config->getProperty("looping", value)) {
+ Qt::CheckState state = value.toBool() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked;
+ widget()->cb_looping->setCheckState(state);
+ }
+ if( config->getProperty("frequency", value)) {
+ widget()->slider_frequency->setValue(value.toDouble());
+ }
+ if( config->getProperty("custom_seed_string", value)) {
+ m_widget->seed_text->setText(value.toString());
+ }
+ if( config->getProperty("ratio_x", value)) {
+ m_widget->ratiox_slider->setValue(value.toDouble());
+ }
+ if( config->getProperty("ratio_y", value)) {
+ m_widget->ratioy_slider->setValue(value.toDouble());
+ }
+ if( config->getProperty("seed", value)) {
+ this->seed = value.toUInt();
+ }
+}
+
+KisPropertiesConfigurationSP KisWdgSimplexNoise::configuration() const
+{
+ KisFilterConfigurationSP config = new KisFilterConfiguration("simplex_noise", 1);
+ config->setProperty("looping", m_widget->cb_looping->isChecked());
+ config->setProperty("frequency", m_widget->slider_frequency->value());
+ config->setProperty("ratio_x", m_widget->ratiox_slider->value());
+ config->setProperty("ratio_y", m_widget->ratioy_slider->value());
+ config->setProperty("custom_seed_string", m_widget->seed_text->text());
+ config->setProperty("seed", this->seed);
+ return config;
+}
+
+
diff --git a/plugins/generators/simplexnoise/kis_wdg_simplex_noise.h b/plugins/generators/simplexnoise/kis_wdg_simplex_noise.h
new file mode 100644
index 0000000000..1ada5dee7f
--- /dev/null
+++ b/plugins/generators/simplexnoise/kis_wdg_simplex_noise.h
@@ -0,0 +1,52 @@
+/*
+ * KDE. Krita Project.
+ *
+ * Copyright (c) 2019 Eoin O'Neill
+ * Copyright (c) 2019 Emmet O'Neill
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KIS_WDG_NOISE_H
+#define KIS_WDG_NOISE_H
+
+#include
+#include
+
+class Ui_WdgSimplexNoiseOptions;
+class KisFilter;
+
+class KisWdgSimplexNoise : public KisConfigWidget
+{
+ Q_OBJECT
+public:
+ KisWdgSimplexNoise(KisFilter* nfilter, QWidget* parent = 0);
+ ~KisWdgSimplexNoise() override;
+public:
+ inline const Ui_WdgSimplexNoiseOptions* widget() const {
+ return m_widget;
+ }
+ void setConfiguration(const KisPropertiesConfigurationSP) override;
+ KisPropertiesConfigurationSP configuration() const override;
+
+private:
+ Ui_WdgSimplexNoiseOptions* m_widget;
+ uint seed;
+ KisSignalCompressor updateCompressor;
+
+};
+
+#endif
+
diff --git a/plugins/generators/simplexnoise/kritasimplexnoisegenerator.json b/plugins/generators/simplexnoise/kritasimplexnoisegenerator.json
new file mode 100644
index 0000000000..6d0f26a768
--- /dev/null
+++ b/plugins/generators/simplexnoise/kritasimplexnoisegenerator.json
@@ -0,0 +1,9 @@
+{
+ "Id": "Simplex Noise Generator",
+ "Type": "Service",
+ "X-KDE-Library": "kritasimplexnoisegenerator",
+ "X-KDE-ServiceTypes": [
+ "Krita/Generator"
+ ],
+ "X-Krita-Version": "28"
+}
diff --git a/plugins/generators/simplexnoise/simplexnoisegenerator.cpp b/plugins/generators/simplexnoise/simplexnoisegenerator.cpp
new file mode 100644
index 0000000000..6cf2635a57
--- /dev/null
+++ b/plugins/generators/simplexnoise/simplexnoisegenerator.cpp
@@ -0,0 +1,149 @@
+/*
+ * KDE. Krita Project.
+ *
+ * Copyright (c) 2019 Eoin O'Neill
+ * Copyright (c) 2019 Emmet O'Neill
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "simplexnoisegenerator.h"
+#include "ui_wdgsimplexnoiseoptions.h"
+#include "kis_wdg_simplex_noise.h"
+#include "3rdparty/c-open-simplex/open-simplex-noise.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+K_PLUGIN_FACTORY_WITH_JSON(KritaSimplexNoiseGeneratorFactory, "kritasimplexnoisegenerator.json", registerPlugin();)
+
+KisSimplexNoiseGeneratorHandle::KisSimplexNoiseGeneratorHandle(QObject *parent, const QVariantList &)
+ : QObject(parent)
+{
+ KisGeneratorRegistry::instance()->add(new KisSimplexNoiseGenerator());
+
+}
+
+KisSimplexNoiseGeneratorHandle::~KisSimplexNoiseGeneratorHandle()
+{
+}
+
+KisSimplexNoiseGenerator::KisSimplexNoiseGenerator() : KisGenerator(id(), KoID("basic"), i18n("&Simplex Noise..."))
+{
+ setColorSpaceIndependence(FULLY_INDEPENDENT);
+ setSupportsPainting(true);
+}
+
+void KisSimplexNoiseGenerator::generate(KisProcessingInformation dst, const QSize &size, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const
+{
+ KisPaintDeviceSP device = dst.paintDevice();
+ Q_ASSERT(!device.isNull());
+
+ osn_context *noise_context;
+
+ QRect bounds = QRect(dst.topLeft(), size);
+ const KoColorSpace * cs = device->colorSpace();
+ KisSequentialIteratorProgress it(device, bounds, progressUpdater);
+
+ QVariant property;
+
+ const uint default_seed = (config->getProperty("seed", property)) ? property.toUInt() : 0;
+ const QString custom_seed_string = (config->getProperty("custom_seed_string", property)) ? property.toString() : "";
+ const bool use_custom_seed = !custom_seed_string.trimmed().isEmpty();
+
+ const uint seed = use_custom_seed ? seedFromString(custom_seed_string) : default_seed;
+ open_simplex_noise(seed, &noise_context);
+
+ double frequency = (config && config->getProperty("frequency", property)) ? property.toDouble() : 25.0;
+ double ratio_x = (config && config->getProperty("ratio_x", property)) ? property.toDouble() : 1.0;
+ double ratio_y = (config && config->getProperty("ratio_y", property)) ? property.toDouble() : 1.0;
+
+ bool looping = (config && config->getProperty("looping", property)) ? property.toBool() : false;
+
+ if( looping ){
+ float major_radius = 0.5f * frequency * ratio_x;
+ float minor_radius = 0.5f * frequency * ratio_y;
+ while(it.nextPixel()){
+ double x_phase = (double)it.x() / (double)bounds.width() * M_PI * 2;
+ double y_phase = (double)it.y() / (double)(bounds.height()) * M_PI * 2;
+ double x_coordinate = major_radius * map_range(cos(x_phase), -1.0, 1.0, 0.0, 1.0);
+ double y_coordinate = major_radius * map_range(sin(x_phase), -1.0, 1.0, 0.0, 1.0);
+ double z_coordinate = minor_radius * map_range(cos(y_phase), -1.0, 1.0, 0.0, 1.0);
+ double w_coordinate = minor_radius * map_range(sin(y_phase), -1.0, 1.0, 0.0, 1.0);
+ double value = open_simplex_noise4(noise_context, x_coordinate, y_coordinate, z_coordinate, w_coordinate);
+ value = map_range(value, -1.0, 1.0, 0.0, 255.0);
+ QColor color = qRgb(static_cast(value),
+ static_cast(value),
+ static_cast(value));
+ cs->fromQColor(color, it.rawData());
+ }
+ } else {
+ while(it.nextPixel()){
+ double x_phase = (double)it.x() / (double)(bounds.width()) * ratio_x;
+ double y_phase = (double)it.y() / (double)(bounds.height()) * ratio_y;
+ double value = open_simplex_noise4(noise_context, x_phase * frequency, y_phase * frequency, x_phase * frequency, y_phase * frequency);
+ value = map_range(value, -1.0, 1.0, 0.0, 255.0);
+ QColor color = qRgb(static_cast(value),
+ static_cast(value),
+ static_cast(value));
+ cs->fromQColor(color, it.rawData());
+ }
+ }
+
+ open_simplex_noise_free(noise_context);
+}
+
+KisFilterConfigurationSP KisSimplexNoiseGenerator::factoryConfiguration() const
+{
+ KisFilterConfigurationSP config = new KisFilterConfiguration("simplex_noise", 1);
+ config->setProperty("looping", false);
+ config->setProperty("frequency", 25.0);
+ uint seed = static_cast(rand());
+ config->setProperty("seed", seed);
+ config->setProperty("custom_seed_string", "");
+ config->setProperty("ratio_x", 1.0f);
+ config->setProperty("ratio_y", 1.0f);
+ return config;
+}
+
+KisConfigWidget * KisSimplexNoiseGenerator::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const
+{
+ Q_UNUSED(dev);
+ return new KisWdgSimplexNoise((KisFilter*)this, (QWidget*)parent);
+}
+
+
+uint KisSimplexNoiseGenerator::seedFromString(const QString &string) const
+{
+ QByteArray bytes = QCryptographicHash::hash(string.toUtf8(),QCryptographicHash::Md5);
+ uint hash = 0;
+ for( int index = 0; index < bytes.length(); index++){
+ hash += rotateLeft(bytes[index], index % 32);
+ }
+ return hash;
+}
+
+quint64 KisSimplexNoiseGenerator::rotateLeft(const quint64 input, uint shift) const
+{
+ return (input << shift)|(input >> (64 - shift));
+}
+
+#include "simplexnoisegenerator.moc"
+
diff --git a/plugins/generators/simplexnoise/simplexnoisegenerator.h b/plugins/generators/simplexnoise/simplexnoisegenerator.h
new file mode 100644
index 0000000000..1f3ff2eae1
--- /dev/null
+++ b/plugins/generators/simplexnoise/simplexnoisegenerator.h
@@ -0,0 +1,65 @@
+/*
+ * KDE. Krita Project.
+ *
+ * Copyright (c) 2019 Eoin O'Neill
+ * Copyright (c) 2019 Emmet O'Neill
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef NOISEFILTER_H
+#define NOISEFILTER_H
+
+#include
+#include "generator/kis_generator.h"
+
+class KisConfigWidget;
+
+class KisSimplexNoiseGeneratorHandle : public QObject
+{
+ Q_OBJECT
+public:
+ KisSimplexNoiseGeneratorHandle(QObject *parent, const QVariantList &);
+ ~KisSimplexNoiseGeneratorHandle() override;
+};
+
+class KisSimplexNoiseGenerator : public KisGenerator
+{
+public:
+ KisSimplexNoiseGenerator();
+
+ using KisGenerator::generate;
+
+ virtual void generate(KisProcessingInformation dst,
+ const QSize& size,
+ const KisFilterConfigurationSP config,
+ KoUpdater* progressUpdater
+ ) const override;
+
+ static inline KoID id() {
+ return KoID("simplex_noise", i18n("Simplex Noise"));
+ }
+
+ KisFilterConfigurationSP factoryConfiguration() const override;
+ KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const override;
+
+ uint seedFromString(const QString &string) const;
+ quint64 rotateLeft(const quint64 input, uint d) const;
+
+ static inline double map_range(double value, double curr_min, double curr_max, double new_min, double new_max ) {
+ return (value - curr_min) * (new_max - new_min) / (curr_max - curr_min) + new_min;
+ }
+};
+#endif
diff --git a/plugins/generators/simplexnoise/wdgsimplexnoiseoptions.ui b/plugins/generators/simplexnoise/wdgsimplexnoiseoptions.ui
new file mode 100644
index 0000000000..3c8c7b67ed
--- /dev/null
+++ b/plugins/generators/simplexnoise/wdgsimplexnoiseoptions.ui
@@ -0,0 +1,169 @@
+
+
+ WdgSimplexNoiseOptions
+
+
+
+ 0
+ 0
+ 312
+ 227
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 175
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Frequency:
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Looping
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Use Custom Seed
+
+
+ false
+
+
+ false
+
+
+
-
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Ratio:
+
+
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ X:
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Y:
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+ KisDoubleSliderSpinBox
+ QWidget
+
+ 1
+
+
+
+
+