@weasel-js/dither 1.16.1 → 1.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +42 -0
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -49,3 +49,45 @@ protect. Where tone must be dithered over such artwork, protect it:
49
49
  into the mask `ditherPixels` takes, and `protectTexture` uploads that mask
50
50
  for `dither`, which needs an output grid to read it. Inside it pixels quantize
51
51
  at plain threshold, and error diffusion neither enters nor leaves it.
52
+
53
+ ## Whole-dot marks
54
+
55
+ Plain threshold still gives each edge to whichever dot it covers more than
56
+ half of, so two bars of one module can print a dot apart and a hairline can
57
+ vanish. Keep the marks on the dots in the document instead, with core's
58
+ quantize rule:
59
+
60
+ ```ts
61
+ import { OUTPUT_GRID_KEY, createScene, quantizeSize } from '@weasel-js/core';
62
+ import { alignToGrid, snapToPitch } from '@weasel-js/geom';
63
+
64
+ const scene = createScene({
65
+ systemLayers,
66
+ doc: { [OUTPUT_GRID_KEY]: { pitch: 72 / 180, origin: { x: 0, y: 0 } } },
67
+ quantize: {
68
+ // What the document holds: here a rule's thickness in whole dots, never under one.
69
+ stored: ({ data, pose }, grid, anchor) => data.mark === 'rule'
70
+ ? { pose: quantizeSize(pose, pose.width, snapToPitch(pose.height, grid.pitch), anchor) }
71
+ : null,
72
+ // Where it is drawn: its corner on a dot. Picking keeps the stored pose.
73
+ drawn: ({ data, pose }, grid) => (data.mark ? alignToGrid(pose, grid) : pose),
74
+ },
75
+ });
76
+ ```
77
+
78
+ The grid is a document value, `kit:outputGrid` in `scene.doc`, so setting it
79
+ is an undoable edit, and the stored values re-snap inside that same edit
80
+ (2026-11-03). `stored` runs on every committed write, whatever made it (a
81
+ resize, typed W/H, align's match size, a paste, a data edit such as a stroke
82
+ width), on a document as it is built or loaded, and on the live preview of a
83
+ resize, a move or an insert, so a release lands where its preview showed. It answers data as well as a pose, since a stroke 1.5 dots wide
84
+ prints as ragged as a barcode module (2026-11-03). Which values are stored
85
+ and which only drawn is the rule's choice, per field (2026-11-03): a size that
86
+ must still read true after the grid changes belongs in `stored`, so W and H
87
+ show what prints; a position that moves with the grid's origin belongs in
88
+ `drawn`.
89
+
90
+ A barcode stores its module as a whole number of dots, and its width as that
91
+ module times its modules: every module then prints alike wherever the code
92
+ sits, and its W is the width it prints, which can be well off the width it was
93
+ asked for when the module rounds down. The site's dither demo does this.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasel-js/dither",
3
- "version": "1.16.1",
3
+ "version": "1.16.2",
4
4
  "description": "Dithering to 1-bit ink: four methods on the CPU for exported pixels, and threshold and Bayer as a live renderer effect on the screen or an output target's dot grid, with regions kept at plain threshold.",
5
5
  "weasel": {
6
6
  "tier": "plugin",
@@ -20,8 +20,8 @@
20
20
  "./package.json": "./package.json"
21
21
  },
22
22
  "dependencies": {
23
- "@weasel-js/geom": "1.16.1",
24
- "@weasel-js/renderer": "1.16.1"
23
+ "@weasel-js/geom": "1.16.2",
24
+ "@weasel-js/renderer": "1.16.2"
25
25
  },
26
26
  "author": "orochi235",
27
27
  "homepage": "https://orochi235.github.io/weasel/",