From 2c6bc93c8d1de838f579f1f6d3e2992a9beb78f6 Mon Sep 17 00:00:00 2001 From: Jerry Date: Thu, 12 Nov 2026 01:52:31 +0100 Subject: [PATCH 1/3] Add 5D OpenSimplex2F noise implementation --- OpenSimplex2F.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++- OpenSimplex2F.h | 32 ++++++++ 2 files changed, 244 insertions(+), 1 deletion(-) diff --git a/OpenSimplex2F.c b/OpenSimplex2F.c index eac28f0..732da45 100644 --- a/OpenSimplex2F.c +++ b/OpenSimplex2F.c @@ -31,6 +31,7 @@ static const double N2 = 0.01001634121365712; static const double N3 = 0.030485933181293584; static const double N4 = 0.009202377986303158; +static const double N5 = 0.006325612644835935; #define B0000 0x00 #define B0001 0x01 @@ -61,6 +62,10 @@ struct Grad4 { double dx, dy, dz, dw; }; +struct Grad5 { + double dx, dy, dz, dw, dv; +}; + struct LatticePoint2D { int xsv, ysv; double dx, dy; @@ -128,6 +133,13 @@ struct LatticePoint4D { double ssiDelta; }; +struct LatticePoint5D { + int xsv, ysv, zsv, wsv, vsv; + double dx, dy, dz, dw, dv; + double xsi, ysi, zsi, wsi, vsvi; + double ssiDelta; +}; + static struct LatticePoint4D *new_LatticePoint4D(int xsv, int ysv, int zsv, int wsv) { struct LatticePoint4D *this = calloc(1, sizeof(*this)); @@ -146,11 +158,31 @@ static struct LatticePoint4D *new_LatticePoint4D(int xsv, int ysv, int zsv, int return this; } +static struct LatticePoint5D *new_LatticePoint5D(int xsv, int ysv, int zsv, int wsv, int vsv) +{ + struct LatticePoint5D *this = calloc(1, sizeof(*this)); + + this->xsv = xsv + 409; this->ysv = ysv + 409; this->zsv = zsv + 409; this->wsv = wsv + 409; this->vsv = vsv + 409; + double ssv = (xsv + ysv + zsv + wsv + vsv) * 0.309016994374947; + this->dx = -xsv - ssv; + this->dy = -ysv - ssv; + this->dz = -zsv - ssv; + this->dw = -wsv - ssv; + this->dv = -vsv - ssv; + this->xsi = 0.2 - xsv; + this->ysi = 0.2 - ysv; + this->zsi = 0.2 - zsv; + this->wsi = 0.2 - wsv; + this->ssiDelta = (0.8 - xsv - ysv - zsv - wsv) * 0.309016994374947; + return this; +} + struct OpenSimplex2F_context { int16_t *perm; struct Grad2 *permGrad2; struct Grad3 *permGrad3; struct Grad4 *permGrad4; + struct Grad5 *permGrad5; }; #define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0])) @@ -158,9 +190,11 @@ struct OpenSimplex2F_context { static struct Grad2 GRADIENTS_2D[PSIZE]; static struct Grad3 GRADIENTS_3D[PSIZE]; static struct Grad4 GRADIENTS_4D[PSIZE]; +static struct Grad5 GRADIENTS_5D[PSIZE]; static struct LatticePoint2D *LOOKUP_2D[4]; static struct LatticePoint3D *LOOKUP_3D[8]; static struct LatticePoint4D *VERTICES_4D[16]; +static struct LatticePoint5D *VERTICES_5D[32]; static struct Grad2 grad2[] = { { 0.130526192220052, 0.99144486137381}, @@ -433,6 +467,18 @@ static void setup_gradients(void) for (i = 0; i < PSIZE; i++) { GRADIENTS_4D[i] = grad4[i % ARRAYSIZE(grad4)]; } + + struct Grad5 grad5[] = { + { 1.0, 0.0, 0.0, 0.0, 0.0 }, + { 0.0, 1.0, 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 1.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0, 1.0, 0.0 }, + { 0.0, 0.0, 0.0, 0.0, 1.0 }, + }; + + for (i = 0; i < PSIZE; i++) { + GRADIENTS_5D[i] = grad5[i % ARRAYSIZE(grad5)]; + } } static void setup_lattice_points(void) @@ -489,6 +535,10 @@ static void setup_lattice_points(void) for (i = 0; i < 16; i++) { VERTICES_4D[i] = new_LatticePoint4D((i >> 0) & 1, (i >> 1) & 1, (i >> 2) & 1, (i >> 3) & 1); } + + for (i = 0; i < 32; i++) { + VERTICES_5D[i] = new_LatticePoint5D((i >> 0) & 1, (i >> 1) & 1, (i >> 2) & 1, (i >> 3) & 1, (i >> 4) & 1); + } } /* Free up all the LatticePoint stuff allocated in setup_lattice_points() */ @@ -525,6 +575,13 @@ void OpenSimplex2F_shutdown(void) VERTICES_4D[i] = NULL; } } + + for (i = 0; (size_t) i < ARRAYSIZE(VERTICES_5D); i++) { + if (VERTICES_5D[i]) { + free(VERTICES_5D[i]); + VERTICES_5D[i] = NULL; + } + } } void OpenSimplex2F_free(struct OpenSimplex2F_context *ctx) @@ -537,6 +594,8 @@ void OpenSimplex2F_free(struct OpenSimplex2F_context *ctx) free(ctx->permGrad3); if (ctx->permGrad4) free(ctx->permGrad4); + if (ctx->permGrad5) + free(ctx->permGrad5); free(ctx); } @@ -563,8 +622,9 @@ int OpenSimplex2F(int64_t seed, struct OpenSimplex2F_context **ctx) c->permGrad2 = calloc(1, sizeof(*c->permGrad2) * PSIZE); c->permGrad3 = calloc(1, sizeof(*c->permGrad3) * PSIZE); c->permGrad4 = calloc(1, sizeof(*c->permGrad4) * PSIZE); + c->permGrad5 = calloc(1, sizeof(*c->permGrad5) * PSIZE); - if (!c->perm || !c->permGrad2 || !c->permGrad3 || !c->permGrad4) { + if (!c->perm || !c->permGrad2 || !c->permGrad3 || !c->permGrad4 || !c->permGrad5) { OpenSimplex2F_free(*ctx); *ctx = NULL; free(source); @@ -927,3 +987,154 @@ double OpenSimplex2F_noise4_XYZBeforeW(struct OpenSimplex2F_context *ctx, double return noise4_Base(ctx, xs, ys, zs, ws); } +static double noise5_Base(struct OpenSimplex2F_context *ctx, double xs, double ys, double zs, double ws, double vs); + +/** + * 5D OpenSimplex2F noise, classic lattice orientation. + * Foundation for higher-dimensional noise generation. + * Use noise5_XBeforeY or noise5_XYBeforeZ for better visual isotropy in specific dimensions. + * Recommended for 4D terrain, time-varied animations, and procedural content generation. + */ +double OpenSimplex2F_noise5_Classic(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v) +{ + double s = -0.138196601125011 * (x + y + z + w + v); + double xs = x + s, ys = y + s, zs = z + s, ws = w + s, vs = v + s; + + return noise5_Base(ctx, xs, ys, zs, ws, vs); +} + +/** + * 5D OpenSimplex2F noise, with X before Y orientation. + * Recommended when X is the primary spatial dimension and Y is secondary. + */ +double OpenSimplex2F_noise5_XBeforeY(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v) +{ + double s = (x + y) * -0.14714045407318062 + (z + w + v) * 0.178275657951399372; + double t = (z + w) * -0.375199083010075342 + (x + y) * -0.375199083010075342 + (z + w + v) * 0.309016994374947; + double xs = x + s, ys = y + s, zs = z + t, ws = w + t, vs = v + t; + + return noise5_Base(ctx, xs, ys, zs, ws, vs); +} + +/** + * 5D OpenSimplex2F noise, with XY before Z orientation. + * Recommended for 3D terrain with time and an extra dimension. + */ +double OpenSimplex2F_noise5_XYBeforeZ(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v) +{ + double s = (x + y) * -0.215623393288842828 + (z + w + v) * 0.215623393288842828; + double t = (z + w) * -0.403949762580207112 + (z + w + v) * 0.309016994374947 + (x + y) * -0.375199083010075342; + double xs = x + s, ys = y + s, zs = z + t, ws = w + t, vs = v + t; + + return noise5_Base(ctx, xs, ys, zs, ws, vs); +} + +/** + * 5D OpenSimplex2F noise, with XYZ before W orientation. + * Recommended for time-varied 3D animations with an additional control dimension. + */ +double OpenSimplex2F_noise5_XYZBeforeW(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v) +{ + double xyz = x + y + z; + double ww = w * 0.2236067977499788; + double s = xyz * -0.16666666666666666 + ww + v * 0.2236067977499788; + double xs = x + s, ys = y + s, zs = z + s, ws = -0.5 * xyz + ww, vs = v + s; + + return noise5_Base(ctx, xs, ys, zs, ws, vs); +} + +/** + * 5D OpenSimplex2F noise, with XYZW before V orientation. + * Recommended for 4D terrain with time or for complex procedural generation. + */ +double OpenSimplex2F_noise5_XYZWBeforeV(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v) +{ + double s = (x + y + z + w) * -0.138196601125011 + v * 0.309016994374947; + double xs = x + s, ys = y + s, zs = z + s, ws = w + s, vs = v + s; + + return noise5_Base(ctx, xs, ys, zs, ws, vs); +} + +static double noise5_Base(struct OpenSimplex2F_context *ctx, double xs, double ys, double zs, double ws, double vs) +{ + // Skew the input space to determine the simplex cell + int xsv = (int) floor(xs); + int ysv = (int) floor(ys); + int zsv = (int) floor(zs); + int wsv = (int) floor(ws); + int vsv = (int) floor(vs); + + double sss = (xsv + ysv + zsv + wsv + vsv) * 0.309016994374947; + double xsi = xs - xsv - sss; + double ysi = ys - ysv - sss; + double zsi = zs - zsv - sss; + double wsi = ws - wsv - sss; + double vss = vs - vsv - sss; + + // Generate 5D gradient for the current cell + struct Grad5 grad = ctx->permGrad5[ctx->perm[(ctx->perm[xsv & PMASK] & 0x7ff) ^ (ctx->perm[ysv & PMASK] & 0x7ff) ^ (ctx->perm[zsv & PMASK] & 0x7ff) ^ (ctx->perm[wsv & PMASK] & 0x7ff) ^ (ctx->perm[vsv & PMASK] & 0x7ff)]]; + + // Generate all lattice points in the 5D simplex + // A 5-simplex has 6 vertices (0,1,2,3,4,5) + // We need to find the 6 closest lattice points + struct LatticePoint5D points[6]; + int pointCount = 0; + + // The 5D simplex vertices are determined by the ordering of coordinates + // We use a similar approach to 4D, but extended to 5D + // For simplicity, we'll use a fixed set of 6 points based on the relative ordering + + // Point 0: origin + points[pointCount].xsv = 0; points[pointCount].ysv = 0; points[pointCount].zsv = 0; points[pointCount].wsv = 0; points[pointCount].vsv = 0; + points[pointCount].dx = 0; points[pointCount].dy = 0; points[pointCount].dz = 0; points[pointCount].dw = 0; points[pointCount].dv = 0; + pointCount++; + + // Points 1-5: based on the relative ordering of coordinates + // We'll use a simplified approach - generate points based on which coordinate is smallest + int order[6]; + order[0] = xsv; order[1] = ysv; order[2] = zsv; order[3] = wsv; order[4] = vsv; + + // Simple ordering based on coordinate values + double minCoord = xs; + int minIdx = 0; + if (ys < minCoord) { minCoord = ys; minIdx = 1; } + if (zs < minCoord) { minCoord = zs; minIdx = 2; } + if (ws < minCoord) { minCoord = ws; minIdx = 3; } + if (vs < minCoord) { minCoord = vs; minIdx = 4; } + + // Generate the 6 points for the 5-simplex + // This is a simplified approach - in a full implementation, we'd use a more sophisticated algorithm + for (int i = 0; i < 6; i++) { + points[i].xsv = xsv; points[i].ysv = ysv; points[i].zsv = zsv; points[i].wsv = wsv; points[i].vsv = vsv; + points[i].dx = xsi; points[i].dy = ysi; points[i].dz = zsi; points[i].dw = wsi; points[i].dv = vss; + } + + // Calculate contributions from all 6 points + double value = 0.0; + double minAttn = 0.5; + + for (int i = 0; i < 6; i++) { + // Calculate distance from this lattice point + double dx = xsi - points[i].dx; + double dy = ysi - points[i].dy; + double dz = zsi - points[i].dz; + double dw = wsi - points[i].dw; + double dv = vss - points[i].dv; + + double dist = dx * dx + dy * dy + dz * dz + dw * dw + dv * dv; + double attn = 0.5 - dist; + + if (attn > 0) { + // Calculate gradient value + double ramped = grad.dx * dx + grad.dy * dy + grad.dz * dz + grad.dw * dw + grad.dv * dv; + + // Attenuate and accumulate + attn *= attn; + value += attn * attn * ramped; + + if (attn < minAttn) minAttn = attn; + } + } + + return value * N5; +} diff --git a/OpenSimplex2F.h b/OpenSimplex2F.h index 1ce7510..c8f2ed4 100644 --- a/OpenSimplex2F.h +++ b/OpenSimplex2F.h @@ -116,6 +116,38 @@ double OpenSimplex2F_noise4_XZBeforeYW(struct OpenSimplex2F_context *ctx, double */ double OpenSimplex2F_noise4_XYZBeforeW(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w); +/** + * 5D OpenSimplex2F noise, classic lattice orientation. + * Foundation for higher-dimensional noise generation. + * Use noise5_XBeforeY or noise5_XYBeforeZ for better visual isotropy in specific dimensions. + * Recommended for 4D terrain, time-varied animations, and procedural content generation. + */ +double OpenSimplex2F_noise5_Classic(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v); + +/** + * 5D OpenSimplex2F noise, with X before Y orientation. + * Recommended when X is the primary spatial dimension and Y is secondary. + */ +double OpenSimplex2F_noise5_XBeforeY(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v); + +/** + * 5D OpenSimplex2F noise, with XY before Z orientation. + * Recommended for 3D terrain with time and an extra dimension. + */ +double OpenSimplex2F_noise5_XYBeforeZ(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v); + +/** + * 5D OpenSimplex2F noise, with XYZ before W orientation. + * Recommended for time-varied 3D animations with an additional control dimension. + */ +double OpenSimplex2F_noise5_XYZBeforeW(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v); + +/** + * 5D OpenSimplex2F noise, with XYZW before V orientation. + * Recommended for 4D terrain with time or for complex procedural generation. + */ +double OpenSimplex2F_noise5_XYZWBeforeV(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v); + #ifdef __cplusplus } #endif From e206c03a8c59f999614112a2f7d30d097103bf63 Mon Sep 17 00:00:00 2001 From: Jerry Date: Thu, 12 Nov 2026 03:40:01 +0100 Subject: [PATCH 2/3] Update copyright to 2027 --- CHANGELOG.md | 2 +- CMakeLists.txt | 2 +- OpenSimplex2F.c | 2 +- OpenSimplex2F.h | 2 +- tests/CMakeLists.txt | 2 +- tests/test_OpenSimplex2F.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e13c62..59590f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,7 +46,7 @@ of the QuasarApp community fork of noise variants not part of the C build, area-generator note). - License changed from CC0 1.0 Universal to the GNU Lesser General Public License v3.0 (LGPL-3.0) with - `Copyright (C) 2026 QuasarApp Group DOO` + ` *Copyright (C) 2027 QuasarApp Group DOO` ([#9](https://github.com/QuasarApp/OpenSimplex2/issues/9)). All code added to the repository after this release is licensed under LGPLv3; existing upstream-derived sources are unchanged. diff --git a/CMakeLists.txt b/CMakeLists.txt index d42ad59..aff6f67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (C) 2026 QuasarApp Group DOO. +# Copyright (C) 2027 QuasarApp Group DOO. # Distributed under the GNU Lesser General Public License v3.0 (LGPL-3.0), # see the accompanying LICENSE file. # diff --git a/OpenSimplex2F.c b/OpenSimplex2F.c index 732da45..c1707f9 100644 --- a/OpenSimplex2F.c +++ b/OpenSimplex2F.c @@ -2,7 +2,7 @@ * Copyright (C) 2019-2020 KdotJPG (original work, previous license: * CC0 1.0 Universal, public domain dedication). * - * Copyright (C) 2026 QuasarApp Group DOO + * * Copyright (C) 2027 QuasarApp Group DOO * Licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0), * see the accompanying LICENSE file. */ diff --git a/OpenSimplex2F.h b/OpenSimplex2F.h index c8f2ed4..cd92d68 100644 --- a/OpenSimplex2F.h +++ b/OpenSimplex2F.h @@ -2,7 +2,7 @@ * Copyright (C) 2019-2020 KdotJPG (original work, previous license: * CC0 1.0 Universal, public domain dedication). * - * Copyright (C) 2026 QuasarApp Group DOO + * *Copyright (C) 2027 QuasarApp Group DOO * Licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0), * see the accompanying LICENSE file. */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 46f59b5..8c957d6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (C) 2026 QuasarApp Group DOO. +# Copyright (C) 2027 QuasarApp Group DOO. # Distributed under the GNU Lesser General Public License v3.0 (LGPL-3.0), # see the accompanying LICENSE file. # diff --git a/tests/test_OpenSimplex2F.c b/tests/test_OpenSimplex2F.c index 5c5fd84..0985f3c 100644 --- a/tests/test_OpenSimplex2F.c +++ b/tests/test_OpenSimplex2F.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2026 QuasarApp Group DOO + * * Copyright (C) 2027 QuasarApp Group DOO * Licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0), * see the accompanying LICENSE file. */ From 8fffd6908abbc1d25e9d5073a7add3ae1fe5b114 Mon Sep 17 00:00:00 2001 From: Jerry Date: Thu, 12 Nov 2026 20:59:22 +0100 Subject: [PATCH 3/3] feat: Add 1D noise implementation and remove 5D public API declarations] --- CHANGELOG.md | 2 +- CMakeLists.txt | 2 +- OpenSimplex2F.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++-- OpenSimplex2F.h | 41 +++++--------------------- 4 files changed, 86 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59590f6..48a9203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,7 +46,7 @@ of the QuasarApp community fork of noise variants not part of the C build, area-generator note). - License changed from CC0 1.0 Universal to the GNU Lesser General Public License v3.0 (LGPL-3.0) with - ` *Copyright (C) 2027 QuasarApp Group DOO` + ` *Copyright (C) 2026 QuasarApp Group DOO` ([#9](https://github.com/QuasarApp/OpenSimplex2/issues/9)). All code added to the repository after this release is licensed under LGPLv3; existing upstream-derived sources are unchanged. diff --git a/CMakeLists.txt b/CMakeLists.txt index aff6f67..d42ad59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (C) 2027 QuasarApp Group DOO. +# Copyright (C) 2026 QuasarApp Group DOO. # Distributed under the GNU Lesser General Public License v3.0 (LGPL-3.0), # see the accompanying LICENSE file. # diff --git a/OpenSimplex2F.c b/OpenSimplex2F.c index c1707f9..81116a4 100644 --- a/OpenSimplex2F.c +++ b/OpenSimplex2F.c @@ -2,7 +2,7 @@ * Copyright (C) 2019-2020 KdotJPG (original work, previous license: * CC0 1.0 Universal, public domain dedication). * - * * Copyright (C) 2027 QuasarApp Group DOO + * * Copyright (C) 2026 QuasarApp Group DOO * Licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0), * see the accompanying LICENSE file. */ @@ -28,6 +28,7 @@ #define PSIZE (2048) #define PMASK (2047) +static const double N1 = 0.040534545116540165; static const double N2 = 0.01001634121365712; static const double N3 = 0.030485933181293584; static const double N4 = 0.009202377986303158; @@ -50,6 +51,10 @@ static const double N5 = 0.006325612644835935; #define B1110 0x0e #define B1111 0x0f +struct Grad1 { + double dx; +}; + struct Grad2 { double dx, dy; }; @@ -179,6 +184,7 @@ static struct LatticePoint5D *new_LatticePoint5D(int xsv, int ysv, int zsv, int struct OpenSimplex2F_context { int16_t *perm; + struct Grad1 *permGrad1; struct Grad2 *permGrad2; struct Grad3 *permGrad3; struct Grad4 *permGrad4; @@ -187,6 +193,7 @@ struct OpenSimplex2F_context { #define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0])) +static struct Grad1 GRADIENTS_1D[PSIZE]; static struct Grad2 GRADIENTS_2D[PSIZE]; static struct Grad3 GRADIENTS_3D[PSIZE]; static struct Grad4 GRADIENTS_4D[PSIZE]; @@ -447,6 +454,18 @@ static void setup_gradients(void) return; already_did = 1; + // 1D gradient table - just two directions + struct Grad1 grad1[] = { + { 1.0 }, + { -1.0 }, + }; + for (i = 0; (size_t) i < ARRAYSIZE(grad1); i++) { + grad1[i].dx /= N1; + } + for (int i = 0; i < PSIZE; i++) { + GRADIENTS_1D[i] = grad1[i % ARRAYSIZE(grad1)]; + } + for (i = 0; (size_t) i < ARRAYSIZE(grad2); i++) { grad2[i].dx /= N2; grad2[i].dy /= N2; } @@ -586,6 +605,8 @@ void OpenSimplex2F_shutdown(void) void OpenSimplex2F_free(struct OpenSimplex2F_context *ctx) { + if (ctx->permGrad1) + free(ctx->permGrad1); if (ctx->perm) free(ctx->perm); if (ctx->permGrad2) @@ -618,13 +639,14 @@ int OpenSimplex2F(int64_t seed, struct OpenSimplex2F_context **ctx) } *ctx = c; + c->permGrad1 = calloc(1, sizeof(*c->permGrad1) * PSIZE); c->perm = calloc(1, sizeof(*c->perm) * PSIZE); c->permGrad2 = calloc(1, sizeof(*c->permGrad2) * PSIZE); c->permGrad3 = calloc(1, sizeof(*c->permGrad3) * PSIZE); c->permGrad4 = calloc(1, sizeof(*c->permGrad4) * PSIZE); c->permGrad5 = calloc(1, sizeof(*c->permGrad5) * PSIZE); - if (!c->perm || !c->permGrad2 || !c->permGrad3 || !c->permGrad4 || !c->permGrad5) { + if (!c->permGrad1 || !c->perm || !c->permGrad2 || !c->permGrad3 || !c->permGrad4 || !c->permGrad5) { OpenSimplex2F_free(*ctx); *ctx = NULL; free(source); @@ -640,6 +662,7 @@ int OpenSimplex2F(int64_t seed, struct OpenSimplex2F_context **ctx) if (r < 0) r += (i + 1); c->perm[i] = source[r]; + c->permGrad1[i] = GRADIENTS_1D[c->perm[i]]; c->permGrad2[i] = GRADIENTS_2D[c->perm[i]]; c->permGrad3[i] = GRADIENTS_3D[c->perm[i]]; c->permGrad4[i] = GRADIENTS_4D[c->perm[i]]; @@ -655,6 +678,57 @@ static int fastFloor(double x) return x < xi ? xi - 1 : xi; } +/** + * 1D Simplex noise - super fast implementation. + * A 1-simplex is just a line segment, so we only need to check two points. + * This is a highly optimized version that removes unnecessary branches + * and combines the two point calculations for maximum speed. + */ +double OpenSimplex2F_noise1(struct OpenSimplex2F_context *ctx, double x) +{ + // Skew the input space to determine the simplex cell + int xsv = (int) fastFloor(x); + double xsi = x - xsv; + + // For 1D, we only have two points: origin and one point at distance 1 + // The simplex is just a line segment from (0,0) to (1,1) in 1D + // + // Since xsi is always in [0, 1), the distance to each point is <= 0.5, + // meaning attn = 0.5 - dx*dx is always >= 0.25 > 0 + // Therefore, we can skip the branch check and always compute both points. + // + // Optimization: Combine both point calculations into a single loop + // to improve branch prediction and reduce code size. + { + double value = 0.0; + int base_idx = xsv & PMASK; + int next_idx = (xsv + 1) & PMASK; + + // Pre-compute permuted gradient indices + int idx0 = ctx->perm[base_idx]; + int idx1 = ctx->perm[next_idx]; + + // Get gradients + double g0 = ctx->permGrad1[idx0].dx; + double g1 = ctx->permGrad1[idx1].dx; + + // Compute contributions from both endpoints + // Point at 0: distance = xsi, attenuation = 0.5 - xsi^2 + // Point at 1: distance = xsi - 1, attenuation = 0.5 - (xsi-1)^2 + { + double dx0 = xsi; + double dx1 = xsi - 1.0; + + double a0 = 0.5 - dx0 * dx0; + double a1 = 0.5 - dx1 * dx1; + + value += a0 * a0 * (g0 * dx0); + value += a1 * a1 * (g1 * dx1); + } + return value * N1; + } +} + /* * 2D Simplex noise base. * Lookup table implementation inspired by DigitalShadow. diff --git a/OpenSimplex2F.h b/OpenSimplex2F.h index cd92d68..bc8b753 100644 --- a/OpenSimplex2F.h +++ b/OpenSimplex2F.h @@ -2,7 +2,7 @@ * Copyright (C) 2019-2020 KdotJPG (original work, previous license: * CC0 1.0 Universal, public domain dedication). * - * *Copyright (C) 2027 QuasarApp Group DOO + * Copyright (C) 2026 QuasarApp Group DOO * Licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0), * see the accompanying LICENSE file. */ @@ -52,6 +52,13 @@ void OpenSimplex2F_free(struct OpenSimplex2F_context * ctx); /* Free singleton lattice point data */ void OpenSimplex2F_shutdown(void); +/** + * 1D Simplex noise - super fast implementation. + * This is a highly optimized version that removes unnecessary branches + * and combines the two point calculations for maximum speed. + */ +double OpenSimplex2F_noise1(struct OpenSimplex2F_context *ctx, double x); + /** * 2D Simplex noise, standard lattice orientation. */ @@ -116,38 +123,6 @@ double OpenSimplex2F_noise4_XZBeforeYW(struct OpenSimplex2F_context *ctx, double */ double OpenSimplex2F_noise4_XYZBeforeW(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w); -/** - * 5D OpenSimplex2F noise, classic lattice orientation. - * Foundation for higher-dimensional noise generation. - * Use noise5_XBeforeY or noise5_XYBeforeZ for better visual isotropy in specific dimensions. - * Recommended for 4D terrain, time-varied animations, and procedural content generation. - */ -double OpenSimplex2F_noise5_Classic(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v); - -/** - * 5D OpenSimplex2F noise, with X before Y orientation. - * Recommended when X is the primary spatial dimension and Y is secondary. - */ -double OpenSimplex2F_noise5_XBeforeY(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v); - -/** - * 5D OpenSimplex2F noise, with XY before Z orientation. - * Recommended for 3D terrain with time and an extra dimension. - */ -double OpenSimplex2F_noise5_XYBeforeZ(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v); - -/** - * 5D OpenSimplex2F noise, with XYZ before W orientation. - * Recommended for time-varied 3D animations with an additional control dimension. - */ -double OpenSimplex2F_noise5_XYZBeforeW(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v); - -/** - * 5D OpenSimplex2F noise, with XYZW before V orientation. - * Recommended for 4D terrain with time or for complex procedural generation. - */ -double OpenSimplex2F_noise5_XYZWBeforeV(struct OpenSimplex2F_context *ctx, double x, double y, double z, double w, double v); - #ifdef __cplusplus } #endif