nucleation 0.3.12

A high-performance Minecraft schematic parser and utility library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# Nucleation v0.3.12

**Fix: `region_bounding_box_json` now reports tight content bounds.** It was
returning the region's internal storage box, which `expand_to_fit`
over-allocates by up to 64 blocks per axis — so a region holding blocks at
(0,0,0)..(3,2,3) reported `[0,0,0,67,66,67]` instead of `[0,0,0,3,2,3]`. It
now uses the tight min/max of placed non-air blocks (empty regions fall back
to the allocated box).

Docs: the README was restructured around author → read/process → analyze →
data → integrate, and the read/stream/regions/NBT/scripting/storage surface
that was previously one buried section is now first-class — including a new
chunk-streaming visualization, a multi-region before/after, and a
block-entity vault. Snippets for chunk iteration, regions/transforms,
NBT, and storage are verified in `docs/readme-snippets/`.

---

# Nucleation v0.3.11

**`Palette.closest_block_dithered(r, g, b, x, y, z)`** — the per-pixel
entry point for image mapping and pixel art: position-aware ordered
dithering between the two nearest palette blocks, deterministic. It powers
the README's new showcases: four public-domain paintings as block art
(Starry Night, Sunflowers, The Great Wave, Girl with a Pearl Earring), a
rotating voxel Earth whose blocks are re-picked per frame by luminosity
through a day/night terminator, the Matterhorn from elevation tiles, and
Wall Street from OpenStreetMap.

---

# Nucleation v0.3.10

**Ordered dithering.** `Palette.dithered()` makes every brush alternate
between the two nearest blocks per voxel (4x4 Bayer threshold on the
target's position along the Oklab segment between them) — deterministic,
and gradients stop banding. SDF gradient fills take `"dither": true` for
the same effect between ramp steps.

**The voxelizer got ~1000x faster at fills.** Bulk solves now run three
scanline parity sweeps (one ray per column per axis, rayon-parallel,
majority vote — same robustness as the per-voxel test) plus per-triangle
shell rasterization, cached as a bitset on the shape. A 6k-triangle
Mario Kart course: 107s → 0.1s at size 200; a 515-block-long
voxelization solves in 1.5s.

---

# Nucleation v0.3.9

**Voxelize 3D models.** New `voxelize` feature (in `bridge-full` and the
WASM build): load GLB (node transforms, embedded textures) or OBJ into a
`MeshModel`, and use it as a first-class building `Shape` —
inside/outside via triangle-parity ray casting (three-axis majority vote,
grid-accelerated), normals from the nearest triangle so lighting brushes
just work, and an optional `shell` distance that closes thin-walled and
hollow geometry (the canonical Utah teapot is a double-walled vessel —
parity alone is faithful to that). Texture projection maps each voxel to
the palette-closest color of its nearest surface point (barycentric UVs,
bilinear sampling): `Voxelizer.schematic_from_glb_textured`.

**Spotlight brush.** `Brush.spotlight(pos, direction, cone_angle, color)`
— Lambert term from the surface normal times a smooth cone falloff,
snapped to any palette. Point it at a voxelized teapot through the
grayscale ladder and you get film-noir ceramics.

---

# Nucleation v0.3.8

**The basics got simple.** `load_from_file` auto-detects the format from
file contents (previously Litematic-only — it couldn't open a `.schem`);
`save_to_file` picks the format from the extension. The explicit
`save_to_file_with_format` remains.

**Palettes from pure color logic.** `PaletteBuilder` gains
`lightness_between(min, max)`, `chroma_below(max)`, and
`color_near(r, g, b, distance)` — filters over each block's *measured*
Oklab color, composable with the tag/kind/flag filters. And
`Blocks.by_color(r, g, b, max_distance)` queries the whole block database
by color, nearest first.

**SDF trees are Shapes.** `Shape.sdf(json)` (and `sdf_bounded`) turns any
distance-field tree — smooth unions, noise, all of it — into a first-class
building shape: fillable with every brush, combinable with other shapes,
usable in masked fills. Normals come from the field gradient, so the
shaded brush shades smooth blends continuously. The terrain sampler and
the building system now share one geometry language.

---

# Nucleation v0.3.7

- **`Palette.ramp_ids(start, end, steps)`** — ask for pure white → pure
  black in N steps and the engine picks N *distinct* blocks forming the
  smoothest ramp the palette allows: targets evenly spaced along the Oklab
  line, blocks assigned by a minimum-cost monotonic matching (unlike
  `gradient_ids`, which snaps per step and repeats). In every binding as
  `ramp_ids_json`.
- **`RenderConfig.set_sphere_fit(true)`** — rotation-invariant camera
  framing: turntables hold a constant distance instead of pulsing with the
  model's silhouette.
- **`Palette.grayscale()` is now data-driven** — opaque full cubes with
  near-neutral *measured* color (low Oklab chroma) instead of name
  substrings, which caught cream sandstones and patterned glazed
  terracottas while missing neutral blocks named otherwise.

---

# Nucleation v0.3.6

Fixes surfaced by making the library render its own README
(every image at https://github.com/Schem-at/Nucleation is now generated by
`tools/readme-media/generate.py` through the Python binding):

- **`Torus.parameter_at` fixed** — the ring angle was measured from raw
  world components of the radial projection, which is identically zero on
  one axis for the default y-up torus, so parametric `curve_gradient`
  fills collapsed to two colors. The angle is now measured in a proper
  in-plane basis; gradients sweep the full ring on any torus axis.
- **`RenderConfig.set_zoom` is now a real zoom** — it used to scale the
  camera distance (larger = further away). Now larger = closer
  (2.0 = twice as close, 0.5 = twice as far), in both perspective and
  orthographic projections. Invert your values if you used the old
  behavior.
- **JS bindings drop the filesystem methods**`loadFromFile`,
  `saveToFile`, and `saveToFileWithFormat` always threw `Io` under WASM
  (no filesystem); they are no longer in the JS typings. Use
  `fromData(bytes)` and the `to*B64()` exporters.
- **`Sdf.schematicFromSdfAuto(sdf, rules)`** — auto-bounds overload; no
  more six placeholder arguments when the SDF tree bounds itself.
- **`Palette.grayscale()` is full-cubes only** — the name match also
  caught panes/stairs/walls (e.g. `light_gray_stained_glass_pane`),
  which rendered as holes when gradients snapped to them.

---

# Nucleation v0.3.5

**Linux release libraries now target glibc 2.35 (was 2.39).** The native
`.so`s (PHP FFI) and the JVM jar's bundled linux natives (JNA) are built on
ubuntu-22.04 instead of ubuntu-latest, so they load on older-glibc deploy
targets — e.g. Debian bookworm (glibc 2.36), where the v0.3.x libs failed
`FFI::cdef` with "GLIBC_2.38 not found". No API changes.

---

# Nucleation v0.3.4

**meshing and rendering are on the crates.io crate again.** schematic-mesher
is published to crates.io (0.2.0), so the dependency is now dual
version+git: local/git builds use the pinned rev, `cargo publish` keeps the
versioned crate. The published crate no longer strips meshing/rendering —
only `simulation` (MCHPRS) stays git-only.

**The nanobind pin is gone.** The Python wheel accepted only
`nanobind ==2.12.0` because the generated dealloc shim reached into
nanobind's private struct layout (broken in 2.13). The diplomat fork's
nanobind backend now uses nanobind's public low-level instance API, so the
pin is `>=2.12,<3` — verified building and running a create/drop
destruction stress against both 2.12 and 2.13.

**Block data polish** (all from official 26.2 sources):

- `default_state` is now populated for all 1,196 blocks (was empty) — the
  Blocks query API returns real default property maps
- Tile-entity classification comes from the `block_entity_type` registry
  (186 blocks, was 42 by substring) — signs, banners, skulls, shelves, ...
- Light emission uses per-block emit-light data, not name guessing
- Mushroom blocks classify as full cubes

**Automated data refresh**: a weekly workflow checks Mojang's manifest and
opens a PR (regenerated data + new-blocks diff) when a new Minecraft release
ships.

---

# Nucleation v0.3.3

**Block semantics from official data, queryable everywhere.** The data
pipeline now extracts three new facets straight from the Minecraft 26.2 jars:
definition kinds + base-block links (Mojang's own variant data: oak_stairs
knows it is a `minecraft:stair` of `oak_planks`), fully-resolved vanilla
block tags (265 tags — wool, planks, mineable/pickaxe, ...), and
model-derived full-cube geometry for every block. Substring guessing is
retired: `full_blocks_only`, `exclude_transparent`, and the technical-block
exclusion are all metadata-driven now.

New in every language binding:

- **`Blocks` query API**: `get(id)` (kind, base block, tags, geometry,
  color, properties), `byTag`, `byKind`, `variantsOf(base)` (the whole
  family: stairs/slab/fence/button/...), `states(id)` (every property
  combination), `tags()`, `ids()`, `count()`
- **`PaletteBuilder.tag(...)/.excludeTag(...)/.kind(...)`** — palettes from
  real tags and kinds instead of keywords
- **Masked fills**: `BuildingTool.fillOnlyAir(...)` and
  `fillReplacing(shape, brush, targets)` for non-destructive edits
- **SDF gradient materials**: fill rules accept `gradient` (palette +
  from/to color along y or depth, or a lightness ramp) — terrain with block
  gradients from pure JSON
- **Scripting**: `palette_gradient_ids`, `palette_block_ids`,
  `palette_closest_block` in the Lua and JS engines

Java↔Bedrock mappings refreshed from GeyserMC's new NBT format, now at
**Java 26.2 parity**: 32,366 blockstate mappings, full coverage including
the 26.2 blocks, zero fallbacks.

---

# Nucleation v0.3.2

**The block database now lives inside nucleation, current to Minecraft 26.2.**
blockpedia is no longer an external dependency: block facts, Java↔Bedrock
mappings, and texture-derived colors ship in-tree (gzipped, ~330 KB) and are
generated at build time. Data targets **Java 26.2** (Mojang's new versioning),
extracted with Mojang's own data generator — 1,196 blocks including the new
cinnabar/sulfur families — with colors computed from the 26.2 client jar's
default textures (98.4% coverage, plains-biome tints applied). Refreshing for
a future release is two commands with no code changes
(`refresh-block-data` + `fetch-texture-colors`, both `--features mc-data-refresh`).

Palette upgrades for value→block workflows (heatmaps, fractals, pixel art):

- `Palette.sortedByLightness()` — any palette as a dark→light ramp
- `Palette.gradientIdsJson(r1,g1,b1, r2,g2,b2, steps)` — exactly N block ids
  sampling an Oklab gradient snapped to the palette; index by intensity
- `Palette.wood()` — the planks family, a natural wood ramp
- Default palettes exclude technical blocks (portals, fluids, fire, ...)

Also: the npm wasm now includes **simulation and meshing** (in-browser
redstone simulation works again); local Python wheel builds no longer trust a
stale rust lib.

---

# Nucleation v0.3.1

**Fixes broken v0.3.0 native release artifacts.** The v0.3.0 per-platform
libraries were built with the core `bridge` feature only, so every
meshing/simulation/rendering export was missing — PHP's eager `FFI::cdef`
could not even bind the release zip's own bindings. All native artifacts
(platform zips, JVM jar natives) now ship the full `bridge-full` surface,
matching the wheels, and CI now installs and exercises every wheel and the
assembled jar (including a simulation-symbol check) before anything ships.

Also in this release:

- **First-class palettes** in every language: `Palette` (solid / structural /
  decorative / concrete / wool / terracotta / grayscale presets, custom
  palettes from a JSON block-id list, closest-block lookup),
  `PaletteBuilder` (blockpedia filter flags + keyword include/exclude), and
  `Brush.setPalette(...)` on all color/gradient brushes — bindings are no
  longer locked to the built-in all-blocks palette. Default palettes now
  exclude technical blocks (portals, fluids, fire, piston internals).
- **JVM jar is multi-platform**: natives for linux x64/arm64, macOS
  x64/arm64, and Windows x64 are bundled in JNA layout (previously linux
  x64 only).
- **crates.io publishing works again**: the published crate ships without
  the git-only features (`simulation` — MCHPRS; `meshing`/`rendering`  schematic-mesher); use the git dependency for those.

---

# Nucleation v0.3.0

**Breaking: every language binding is now generated from a single source of truth.**

The four hand-written binding layers (C FFI via `#[no_mangle]` externs, WASM via
wasm-bindgen, Python via pyo3, JVM via hand-written JNI) and the experimental
ext-php-rs extension are gone, replaced by Diplomat-generated bindings for
C, C++, JS/WASM, Kotlin (JNA), Python (nanobind), and PHP (ext-ffi) — all generated
from `src/bridge/` by `tools/gen-bindings.sh` into `bindings/`, and regenerated +
diffed in CI so they can never go stale. The regex parity linters are deleted;
coverage vs the old 544-function C surface is enforced by
`tools/check_bridge_coverage.py` against a frozen baseline.

API changes to be aware of:
- One unified error model: every fallible call returns/raises `NucleationError`
  (12 variants). The thread-local `schematic_last_error`, per-function int/null
  sentinels, and error-string returns are gone.
- Constructors are `create`/`from_*`; accessors drop `get_`/`set_` prefixes
  (per-language casing applies, e.g. `getBlockName` in JS/PHP).
- Domain methods moved off the `Schematic` god-object onto their own types
  (`Diff`, `Fingerprint`, `Autostack`, `StoreIo`, `Renderer`, meshing types,
  `SchematicRegions`).
- Binary payloads (litematic/schem/GLB/PNG/…) cross the boundary base64-encoded
  (`*_b64` methods); arrays/lists cross as JSON strings.
- The mesh progress callback is replaced by a polling `MeshJob`
  (start → `poll_progress``take_result`).
- Memory management is generated: no more `free_*` functions anywhere.

See `src/bridge/PORTING.md` for the binding rules and
`tools/bridge_coverage/exclusions.txt` for the audited old→new name map.

**Complete API documentation across all bindings.** Every public function on
the bridge surface (509 total) now carries a doc comment, propagated by the
generator into all seven languages; the 140 previously undocumented functions
(meshing config, simulation value/layout/ordering types, transforms,
definition regions, …) were documented from their implementations, including
defaults, units, and coordinate/rotation conventions.

**Editing-operation performance.**
- `set_block_from_string` now caches parsed block strings (properties + NBT)
  per schematic, and placed block entities Arc-share the cached NBT
  (copy-on-write). Repeatedly placing the same NBT-bearing block (e.g. filled
  chests) is ~41× faster (0.30 → 12.4 M blocks/s); property-bearing blocks
  (e.g. repeaters) are ~3.6× faster (5.7 → 20.6 M blocks/s).
- `copy_region` from a single-region source (the common case) now translates
  palette indices through a precomputed source→target map instead of hashing
  a `BlockState` per block: ~3.8× faster (64 → 242 M blocks/s), same
  resulting content (covered by a fast-vs-slow-path equivalence test).

---

# Nucleation v0.2.18

Maintenance release, no user-facing API changes. The FFI layer
(`src/ffi.rs`, 10k+ lines) is now split into per-domain modules under
`src/ffi/`, matching the existing WASM/Python binding structure —
verified byte-identical exported C symbols across every feature
combination before and after. Format parsing (`src/formats/`,
`src/dataconverter/`) converged onto a proper `thiserror`-based error
type instead of ad-hoc `Box<dyn Error>`/`String` errors; the public
`UniversalSchematic::to_schematic`/`from_schematic` signatures are
unchanged. Also merged in a diff palette-swap-dominance feature that
had been sitting on an unmerged branch, cleared out several stale
branches, ran a full `clippy --fix` pass, and fixed a comparator
custom-IO test that had the wrong block orientation baked in (it now
actually exercises redpiler's IN→wire→OUT signal path instead of
silently testing nothing while ignored).

# Nucleation v0.2.17

JVM: packed bulk block export. Adds a palette + stride-4 int array
encoding for pulling large regions out of the JVM binding in one call,
instead of one JNI round-trip per block.

# Nucleation v0.2.16

SDF (signed distance field) shape and terrain generation, available
across every binding: build a schematic by sampling an SDF JSON tree
against material rules (`from_sdf` / `from_sdf_bounded`, with a
standalone `sdf_eval` for point queries). JVM also picked up
`setBlockEntity` with SNBT write support, and `from_insign` now strips
sign blocks after compiling their annotations instead of leaving them
in the output.

# Nucleation v0.2.15

Small follow-up to v0.2.14: `schematic-mesher` resolves from its
GitHub source again (a crates.io publish had briefly broken that),
plus full binding parity for the datafixer and entity/block-entity
SNBT API introduced in v0.2.14, and a `MeshOutput::from` fix so the
local (non-service) mesher path constructs by value correctly.

# Nucleation v0.2.14

The big one in this range. Relicensed the project from AGPL-3.0-only
to MIT across every file. Landed the streaming world API — constant-
memory parsing, generation, and diffing of world saves without holding
the whole world in memory, plus `.mca`/world-folder docs to match.
Added redstone graph export with integration tests, meshing
performance work (palette-indexed block sources), and merged in a
contributor's fork carrying dataconverter and litematic/entity
improvements.

Also where the JVM binding caught up hard on this window: full
`MchprsWorld` simulation parity with Python, the item-model generation
API, the redstone graph + typed circuit executor API, and a fix for
released fat JARs that had been shipping without `mchprs` compiled in
(simulation now on by default).

# Nucleation v0.2.13

Exposes `footprint()` — a translation-invariant shape fingerprint used
by the fingerprint/classification engine — as a vector across all
bindings, rather than only being reachable through the Rust API.

# Nucleation v0.2.12

New fingerprint & signature engine: canonical `Fingerprint`/`Signature`
types, symmetry-group-aware rigid transforms, an FFT-based
translation-invariant `Footprint`, a rule-based classifier with
shipped rulesets (structural, redstone computational/survival) loaded
from RON, and synthetic-fixture benchmarks. Exposed to WASM as `Diff`
and `Fingerprint` bindings. Also added synchronous Redis and S3 `Store`
backends alongside the existing filesystem one.

# Nucleation v0.2.11

Render background color and orthographic/isometric projection support
for `RenderConfig`, implemented in core and exposed across
Python/WASM/FFI (Python via a `Projection` enum, WASM/FFI via
`orthographic`/`setOrthographic`-style booleans — a documented,
intentional naming divergence, see `api_parity_exclusions.txt`). Also
fixes an `i64` overflow in `Region::coords_to_index` for large
regions.

# Nucleation v0.2.10

Build script fix. v0.2.9's `assemble-jvm-jar` job failed at the
`processResources` step under Gradle 9:

    Entry native/linux-arm64/libnucleation_jvm.so is a duplicate but no
    duplicate handling strategy has been set.

Two compounding sources of the duplicate:

1. `collectNatives` was copying `src/main/resources/native/**/*.{so,
   dylib,dll}` into `build/native-staging/`. Those files were already
   on the default resources classpath, so they got bundled twice.
2. `processResources` had no `duplicatesStrategy` set, which under
   Gradle 9 (strict by default) fails the build instead of warning.

Fixed in `nucleation-jvm/jvm/build.gradle.kts`:
- Dropped the redundant `preStaged` from() in `collectNatives` — pre-
  staged cdylibs reach the JAR through the default resources path
  alone, no need to re-copy them.
- Added `duplicatesStrategy = DuplicatesStrategy.EXCLUDE` to
  `processResources` as a safety net in case the host cargo target and
  a pre-staged cdylib happen to overlap on the same platform.

No source / API changes since v0.2.7.

v0.2.8 retired (deprecated macos-13 runner).
v0.2.9 retired (Gradle 9 duplicate-resources failure).

See v0.2.7 release notes for the feature work.