# MoonBit SimplexNoise

[English](./README.md) | [简体中文](./README_zh_CN.md)

[![构建状态](https://img.shields.io/github/actions/workflow/status/ZSeanYves/SimplexNoise/simplexnoise-ci.yml)](https://github.com/ZSeanYves/SimplexNoise/actions/workflows/simplexnoise-ci.yml)
[![许可证](https://img.shields.io/github/license/ZSeanYves/SimplexNoise)](./LICENSE)

SimplexNoise 0.2.0 是一个确定性的 MoonBit 程序噪声库，提供：

- 2D/3D/4D Classic Simplex；
- 与 FastNoiseLite 数值兼容的 2D/3D OpenSimplex2 和 OpenSimplex2S；
- 经过参数校验的 fBm、Billow、Ridged；
- 无全局状态的 2D/3D 多模式 Domain Warp；
- 有严格周期契约的 2D 可平铺噪声；
- 可组合的修饰器和无逐样本分配的网格填充；
- 与算法核心隔离的灰度/彩色 PNG 渲染。

项目使用 Moon 0.1.20260713，在 `wasm`、`wasm-gc`、`js`、`native` 四个后端
进行检查和测试。

| OpenSimplex2S | fBm |
| --- | --- |
| ![OpenSimplex2S 灰度示例](./examples/open-simplex2s.png) | ![fBm 彩色示例](./examples/fbm.png) |

## 安装

```bash
moon add ZSeanYves/SimplexNoise
```

按需导入包：

```moonbit
import {
  "ZSeanYves/SimplexNoise/core",
  "ZSeanYves/SimplexNoise/fractal",
}
```

## 基础采样

```moonbit
let classic = @core.Simplex::new(42U)
let open2 = @core.OpenSimplex2::new(42U)
let open2s = @core.OpenSimplex2S::new(42U)

let a = classic.sample4(0.1, 0.2, 0.3, 0.4)
let b = open2.sample3(0.1, 0.2, 0.3)
let c = open2s.sample2(0.1, 0.2)
```

采样器是不可变对象，应构造一次并重复使用。

所有采样器只接受 `[-100000000, 100000000]` 内的有限坐标。NaN、无穷大和
超出范围的坐标统一返回 NaN，避免格点整数转换在不同后端产生饱和差异。
参考算法族和外部语料见[数值契约](./docs/reference-validation.md)。

密集噪声场可以直接写入调用方缓冲区，不产生逐样本分配：

```moonbit
let pixels = FixedArray::make(256 * 256, 0.0)
@core.fill2(open2s, pixels, 256, 256, step_x=0.01, step_y=0.01).unwrap()
```

## 分形与 Domain Warp

```moonbit
let config = @fractal.FbmConfig::new(
  octaves=6,
  persistence=0.5,
  lacunarity=2.0,
  frequency=0.01,
).unwrap()

let source = @core.OpenSimplex2S::new(42U)
let fbm = @fractal.Fbm::new(source, config)
let terrain = fbm.sample2(128.0, 64.0)

let warp_config = @fractal.WarpConfig::new(
  strength=2.0,
  frequency=0.02,
  limit=1.5,
  mode=Progressive,
  octaves=3,
).unwrap()
let warped = @fractal.DomainWarp2::new(
  source,
  @core.OpenSimplex2S::new(73U),
  warp_config,
)
let warped_value = warped.sample2(128.0, 64.0)
```

fBm/Billow/Ridged 通过 `Seedable` 为每个 octave 创建 `seed + octave` 的独立
源。`Ridged` 现在具备 RidgedMulti 的反馈权重，默认 attenuation 为 `2.0`，
可用 `Ridged::with_attenuation` 修改。`DomainWarp2` 和 `DomainWarp3` 支持
`Single`、`Progressive`、`Independent` 三种模式。

## 修饰器

`fractal` 包提供 `ScaleBias`、`Clamp`、`AddNoise`、`MultiplyNoise`、
`Blend`、`Select`、`Curve`、`Terrace`，以及按维度区分的
`Transform2`/`Transform3`/`Transform4`。有限数值、区间和有序控制点都在
构造时校验，采样热路径不重复处理错误。

## 严格 2D 平铺

`Tileable2` 把两个周期坐标映射到四维环面，因此输入源必须实现 `Noise4`。
内置的 Classic Simplex 满足该要求。

```moonbit
let tiled = @fractal.Tileable2::new(
  @core.Simplex::new(9U),
  width=256.0,
  height=256.0,
).unwrap()

let left = tiled.sample2(0.0, 32.0)
let right = tiled.sample2(256.0, 32.0)
```

## PNG 示例

渲染依赖已经从算法核心中拆出。命令接受可选的输出前缀、尺寸和 seed：

```bash
moon run src/cmd/noise-demo --target native -- noise-demo 256 42
```

命令生成 `noise-demo-simplex.png` 和 `noise-demo-fbm.png`。
尺寸/seed 格式错误或尺寸超出 `1..8192` 时会以非零状态退出；PNG 编码和
文件系统错误会继续向上传播。

## 包结构

```text
src/core/            噪声源、Seedable、坐标契约、fill2/fill3
src/fractal/         分形、2D/3D warp、平铺、修饰器与坐标变换
src/render/          灰度/色相 PNG 编码与文件输出
src/cmd/noise-demo/  可直接运行的图像示例
```

`core` 没有第三方依赖；image、io 和文件系统依赖只存在于 `render`。

## 从 0.1.x 迁移

| 0.1.x | 0.2.0 |
| --- | --- |
| 坐标、梯度、置换表使用动态数组 | 不可变采样器和固定维方法 |
| `create2d` / `simplex2d` | `@core.Simplex::new(seed).sample2(x, y)` |
| fBm 使用七个位置参数 | `FbmConfig` + `Fbm` |
| 每个 octave 复用同一噪声源 | 每个 octave 使用独立 seed |
| 全局 `new_maxwarp` | `WarpConfig` + `DomainWarp2` |
| 近似的 2D/3D/4D 平铺辅助 | 先收紧为严格 2D `Tileable2` |
| 根包直接负责图像输出 | `@render.render_png` / `write_png` |
| 3D/4D 批量切片辅助 | 已删除；在渲染回调中捕获 z/w，并由调用方显式循环 |

旧 API 已有意移除。旧版数值和性能记录见
[0.1.1 基线](./docs/legacy-baseline.md)。
0.2.0 同时修正了 Classic 2D/3D 中间顶点反偏移量；Classic 2D 改用
Gustavson 的 12 项 `grad3` 投影和 `70` 归一化系数。分形值也会变化，因为
octave 使用独立 seed，Ridged 也加入了反馈 attenuation。

## 验证

```bash
moon update
moon fmt --check
moon check --target all --deny-warn --warn-list +73
moon test --target all
moon test --target native --enable-coverage
moon coverage report -f summary
moon bench src/core src/fractal --target native --release
moon package --frozen
```

OpenSimplex 测试向量由 FastNoiseLite 1.1.1 官方实现生成。移植代码归属和许可
见 [NOTICE](./NOTICE)。
当前 native 结果记录在 [0.2.0 性能快照](./docs/performance-0.2.0.md)。

## 许可证

Apache-2.0，详见 [LICENSE](./LICENSE)。
