@arraypress/covers 1.1.0 → 1.3.1
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.
- package/CHANGELOG.md +42 -0
- package/README.md +18 -809
- package/package.json +2 -1
- package/recipes/flyers/afterglow-motion.json +540 -0
- package/recipes/flyers/afterglow.json +70 -0
- package/recipes/flyers/arcfield-mirror-motion.json +397 -0
- package/recipes/flyers/arcfield-mirror.json +364 -0
- package/recipes/flyers/bauhaus-oktav.json +319 -0
- package/recipes/flyers/bauhaus-ritmo.json +735 -0
- package/recipes/flyers/bauhaus-volta.json +394 -0
- package/recipes/flyers/bloomfall.json +99 -0
- package/recipes/flyers/drag-solenne-motion.json +444 -0
- package/recipes/flyers/drag-solenne.json +426 -0
- package/recipes/flyers/fennick-patches.json +874 -0
- package/recipes/flyers/ferro-pass.json +398 -0
- package/recipes/flyers/ferrova.json +812 -0
- package/recipes/flyers/grid-rest-rhythm.json +125 -0
- package/recipes/flyers/groovy-honeycomb.json +816 -0
- package/recipes/flyers/groovy-marigold.json +479 -0
- package/recipes/flyers/groovy-peachfuzz.json +974 -0
- package/recipes/flyers/halden-orbit-motion.json +381 -0
- package/recipes/flyers/halden-orbit.json +356 -0
- package/recipes/flyers/halvard.json +120 -0
- package/recipes/flyers/kairo-weekender.json +696 -0
- package/recipes/flyers/linger-motion.json +518 -0
- package/recipes/flyers/linger.json +67 -0
- package/recipes/flyers/little-orchard.json +105 -0
- package/recipes/flyers/lofi-hexmoth.json +912 -0
- package/recipes/flyers/lofi-lowbit-motion.json +888 -0
- package/recipes/flyers/lofi-lowbit.json +880 -0
- package/recipes/flyers/lofi-nullpoint.json +3013 -0
- package/recipes/flyers/lumio-stack-motion.json +648 -0
- package/recipes/flyers/lumio-stack.json +438 -0
- package/recipes/flyers/magazine-cover.json +168 -0
- package/recipes/flyers/magazine-extended.json +269 -0
- package/recipes/flyers/magazine-knockout.json +40 -0
- package/recipes/flyers/northfold-varsity.json +618 -0
- package/recipes/flyers/open-studio.json +69 -0
- package/recipes/flyers/orrin-merch.json +715 -0
- package/recipes/flyers/oxyde.json +113 -0
- package/recipes/flyers/quiet-fields.json +610 -0
- package/recipes/flyers/scrapbook-halcyon.json +948 -0
- package/recipes/flyers/solenne-ticket.json +730 -0
- package/recipes/flyers/solvane-motion.json +576 -0
- package/recipes/flyers/solvane.json +98 -0
- package/recipes/flyers/subduct.json +99 -0
- package/recipes/flyers/sunvale-lineup.json +584 -0
- package/recipes/flyers/tidewell.json +91 -0
- package/recipes/flyers/tour-kairo.json +535 -0
- package/recipes/flyers/velora-stickers.json +885 -0
- package/recipes/flyers/wheatpaste-black.json +313 -0
- package/recipes/flyers/wheatpaste-mark.json +198 -0
- package/recipes/flyers/wheatpaste-torn.json +114 -0
- package/recipes/flyers/wheatpaste-wall.json +984 -0
- package/recipes/flyers/wheatpaste-words.json +233 -0
- package/recipes/release/aura.json +137 -0
- package/recipes/release/chrome.json +433 -0
- package/recipes/release/collage.json +372 -0
- package/recipes/release/duotone.json +113 -0
- package/recipes/release/film-still.json +102 -0
- package/recipes/release/headline.json +70 -0
- package/recipes/release/instant.json +170 -0
- package/recipes/release/label.json +364 -0
- package/recipes/release/liquid-glass.json +301 -0
- package/recipes/release/odd-tile-dark.json +688 -0
- package/recipes/release/odd-tile-dense.json +1056 -0
- package/recipes/release/odd-tile-shift.json +541 -0
- package/recipes/release/odd-tile-split.json +571 -0
- package/recipes/release/odd-tile.json +633 -0
- package/recipes/release/off-edge.json +96 -0
- package/recipes/release/plinth.json +794 -0
- package/recipes/release/riso.json +108 -0
- package/recipes/release/series/fluid-label.json +561 -0
- package/recipes/release/series/fluid-pixel.json +334 -0
- package/recipes/release/series/hairline.json +305 -0
- package/recipes/release/series/halo.json +282 -0
- package/recipes/release/series/mark-crop.json +135 -0
- package/recipes/release/series/mark-window.json +150 -0
- package/recipes/release/series/photo-label.json +475 -0
- package/recipes/release/series/smear-field.json +345 -0
- package/recipes/release/series/smear.json +202 -0
- package/recipes/release/series/spin.json +172 -0
- package/recipes/release/series/split-ghost.json +230 -0
- package/recipes/release/series/zoom.json +168 -0
- package/recipes/release/small-print.json +114 -0
- package/recipes/release/stack.json +156 -0
- package/recipes/release/swiss.json +117 -0
- package/schema/recipe.schema.json +332 -65
- package/src/animate/index.d.ts +9 -19
- package/src/animate/index.js +6 -111
- package/src/animate/keyframes.d.ts +21 -0
- package/src/animate/keyframes.js +119 -0
- package/src/animate/marquee.d.ts +30 -0
- package/src/animate/marquee.js +93 -0
- package/src/animate/morph.d.ts +8 -0
- package/src/animate/morph.js +99 -0
- package/src/animate/motion.d.ts +59 -0
- package/src/animate/motion.js +63 -0
- package/src/animate/sheet.d.ts +19 -0
- package/src/animate/sheet.js +61 -0
- package/src/animate/timing.d.ts +28 -0
- package/src/animate/timing.js +146 -0
- package/src/animate/tracks.d.ts +18 -0
- package/src/animate/tracks.js +139 -0
- package/src/diagram.js +4 -3
- package/src/extract.js +2 -2
- package/src/fonts.js +1 -2
- package/src/geometry.js +4 -34
- package/src/grid.js +1 -1
- package/src/handmade.js +9 -7
- package/src/icons.js +1 -2
- package/src/index.d.ts +39 -48
- package/src/index.js +5 -5
- package/src/inter-data.js +1 -1
- package/src/json-recipe.js +23 -7
- package/src/layer-options.js +21 -1
- package/src/layers/barcode.js +3 -3
- package/src/layers/geometry.js +5 -5
- package/src/layers/perforation.js +3 -3
- package/src/layers/repeat.js +2 -2
- package/src/layers/shape.d.ts +2 -2
- package/src/layers/shape.js +5 -5
- package/src/layers/soft.js +2 -2
- package/src/layers/svg.js +2 -3
- package/src/layers/table.js +3 -3
- package/src/layers/tear.d.ts +1 -1
- package/src/layers/tear.js +4 -4
- package/src/layers/texture.js +3 -3
- package/src/layers/util.js +15 -2
- package/src/layers.js +10 -26
- package/src/{effects/paint.js → paint/index.js} +5 -5
- package/src/{effects/paint-presets.d.ts → paint/presets.d.ts} +1 -1
- package/src/{effects/paint-presets.js → paint/presets.js} +1 -1
- package/src/{effects → paint}/tile.d.ts +1 -1
- package/src/{effects → paint}/tile.js +4 -4
- package/src/palette.js +1 -1
- package/src/platform/files.d.ts +6 -0
- package/src/platform/files.js +41 -0
- package/src/platform/harfbuzz.js +1 -1
- package/src/platform/index.d.ts +3 -3
- package/src/platform/index.js +6 -4
- package/src/platform/load.js +1 -2
- package/src/print.js +9 -7
- package/src/recipes.js +1 -1
- package/src/render/backdrop.js +2 -2
- package/src/render/grain.js +1 -1
- package/src/render/group.js +3 -3
- package/src/render/key.js +1 -2
- package/src/{effects → render}/transform.d.ts +2 -1
- package/src/{effects → render}/transform.js +17 -5
- package/src/render/use.d.ts +1 -1
- package/src/render/use.js +2 -2
- package/src/render/wrap.js +5 -60
- package/src/render.js +55 -18
- package/src/text.js +1 -1
- package/src/type/auto-color.js +1 -1
- package/src/type/glyphs.js +2 -2
- package/src/type/line.js +1 -1
- package/src/type/live.js +1 -1
- package/src/type/path.js +2 -2
- package/src/type/pixel.d.ts +0 -2
- package/src/type/pixel.js +3 -33
- package/src/type/spans.js +2 -2
- package/src/type/typeset.js +19 -8
- package/src/version.js +1 -1
- package/src/color.js +0 -246
- package/src/effects/ascii.d.ts +0 -7
- package/src/effects/ascii.js +0 -93
- package/src/effects/buffer.d.ts +0 -60
- package/src/effects/buffer.js +0 -472
- package/src/effects/detail.d.ts +0 -12
- package/src/effects/detail.js +0 -124
- package/src/effects/digital.d.ts +0 -14
- package/src/effects/digital.js +0 -216
- package/src/effects/distort.d.ts +0 -20
- package/src/effects/distort.js +0 -314
- package/src/effects/dither.d.ts +0 -8
- package/src/effects/dither.js +0 -147
- package/src/effects/drop-shadow.d.ts +0 -4
- package/src/effects/drop-shadow.js +0 -45
- package/src/effects/extrude.d.ts +0 -5
- package/src/effects/extrude.js +0 -55
- package/src/effects/film.d.ts +0 -11
- package/src/effects/film.js +0 -204
- package/src/effects/glow.d.ts +0 -8
- package/src/effects/glow.js +0 -56
- package/src/effects/grade.d.ts +0 -30
- package/src/effects/grade.js +0 -274
- package/src/effects/halftone.d.ts +0 -8
- package/src/effects/halftone.js +0 -95
- package/src/effects/index.d.ts +0 -125
- package/src/effects/index.js +0 -277
- package/src/effects/ink.d.ts +0 -12
- package/src/effects/ink.js +0 -70
- package/src/effects/lens.d.ts +0 -14
- package/src/effects/lens.js +0 -277
- package/src/effects/light.d.ts +0 -10
- package/src/effects/light.js +0 -59
- package/src/effects/looks.d.ts +0 -9
- package/src/effects/looks.js +0 -74
- package/src/effects/mask.d.ts +0 -52
- package/src/effects/mask.js +0 -314
- package/src/effects/motion.d.ts +0 -6
- package/src/effects/motion.js +0 -60
- package/src/effects/newsprint.d.ts +0 -6
- package/src/effects/newsprint.js +0 -89
- package/src/effects/optics.d.ts +0 -10
- package/src/effects/optics.js +0 -213
- package/src/effects/options.d.ts +0 -6
- package/src/effects/options.js +0 -106
- package/src/effects/pixels.d.ts +0 -24
- package/src/effects/pixels.js +0 -138
- package/src/effects/polar.d.ts +0 -10
- package/src/effects/polar.js +0 -265
- package/src/effects/region.d.ts +0 -8
- package/src/effects/region.js +0 -136
- package/src/effects/round-border.d.ts +0 -5
- package/src/effects/round-border.js +0 -62
- package/src/effects/screen.d.ts +0 -14
- package/src/effects/screen.js +0 -212
- package/src/effects/sort.d.ts +0 -8
- package/src/effects/sort.js +0 -125
- package/src/effects/symmetry.d.ts +0 -6
- package/src/effects/symmetry.js +0 -74
- package/src/effects/tone.d.ts +0 -16
- package/src/effects/tone.js +0 -117
- package/src/effects/util.d.ts +0 -16
- package/src/effects/util.js +0 -85
- package/src/platform/bytes.d.ts +0 -22
- package/src/platform/bytes.js +0 -103
- package/src/platform/compress.d.ts +0 -12
- package/src/platform/compress.js +0 -81
- package/src/platform/node.d.ts +0 -26
- package/src/platform/node.js +0 -86
- package/src/png.js +0 -126
- package/src/raster/index.d.ts +0 -24
- package/src/raster/index.js +0 -171
- package/src/render/content.d.ts +0 -5
- package/src/render/content.js +0 -58
- package/src/render/tiles.d.ts +0 -6
- package/src/render/tiles.js +0 -77
- package/src/rng.js +0 -95
- package/src/svg.js +0 -69
- /package/src/{effects/paint.d.ts → paint/index.d.ts} +0 -0
package/src/render/content.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Where a layer paints: its markup drawn small and the box round every
|
|
3
|
-
* pixel it covers, so filters can work over that box instead of the whole
|
|
4
|
-
* canvas (in `renderImage`, where librsvg's cost is per pixel of region).
|
|
5
|
-
*
|
|
6
|
-
* @module render/content
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { num } from '../svg.js';
|
|
10
|
-
|
|
11
|
-
/** The probe's longer side, px. */
|
|
12
|
-
const PROBE = 256;
|
|
13
|
-
|
|
14
|
-
/** Markup carrying more embedded data than this isn't probed (decoding it would cost more than it saves). */
|
|
15
|
-
const MAX_PROBE_BYTES = 400_000;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The box a layer's markup paints, in its units: drawn at most 256 px
|
|
19
|
-
* across over the canvas and half again round it, one probe pixel of slack
|
|
20
|
-
* on every side. Undefined when it can't be probed (no sharp, heavy
|
|
21
|
-
* embedded images) or paints nothing.
|
|
22
|
-
*
|
|
23
|
-
* @param {{ defs: string, body: string }} m
|
|
24
|
-
* @param {any} ctx
|
|
25
|
-
* @returns {Promise<{ x: number, y: number, width: number, height: number } | undefined>}
|
|
26
|
-
*/
|
|
27
|
-
export async function contentBox(m, ctx) {
|
|
28
|
-
if (!ctx.raster || !ctx.sharp || m.body.length + m.defs.length > MAX_PROBE_BYTES) return undefined;
|
|
29
|
-
const ox = -ctx.width / 2;
|
|
30
|
-
const oy = -ctx.height / 2;
|
|
31
|
-
const uw = ctx.width * 2;
|
|
32
|
-
const uh = ctx.height * 2;
|
|
33
|
-
const s = PROBE / Math.max(uw, uh);
|
|
34
|
-
const w = Math.max(1, Math.round(uw * s));
|
|
35
|
-
const h = Math.max(1, Math.round(uh * s));
|
|
36
|
-
const doc = `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="${num(ox, 4)} ${num(oy, 4)} ${num(uw, 4)} ${num(uh, 4)}" preserveAspectRatio="none">${m.defs ? `<defs>${m.defs}</defs>` : ''}${m.body}</svg>`;
|
|
37
|
-
let px;
|
|
38
|
-
try {
|
|
39
|
-
px = await ctx.sharp(new TextEncoder().encode(doc)).ensureAlpha().raw().toBuffer();
|
|
40
|
-
} catch {
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
let x0 = w;
|
|
44
|
-
let y0 = h;
|
|
45
|
-
let x1 = -1;
|
|
46
|
-
let y1 = -1;
|
|
47
|
-
for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
|
|
48
|
-
if (!px[(y * w + x) * 4 + 3]) continue;
|
|
49
|
-
if (x < x0) x0 = x;
|
|
50
|
-
if (x > x1) x1 = x;
|
|
51
|
-
if (y < y0) y0 = y;
|
|
52
|
-
if (y > y1) y1 = y;
|
|
53
|
-
}
|
|
54
|
-
if (x1 < 0) return undefined;
|
|
55
|
-
// Anything touching the probe's edge may run further: the whole region then.
|
|
56
|
-
if (x0 === 0 || y0 === 0 || x1 === w - 1 || y1 === h - 1) return undefined;
|
|
57
|
-
return { x: ox + (x0 - 1) / s, y: oy + (y0 - 1) / s, width: (x1 - x0 + 3) / s, height: (y1 - y0 + 3) / s };
|
|
58
|
-
}
|
package/src/render/tiles.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// Large SVGs with filters, rasterised in tiles.
|
|
2
|
-
|
|
3
|
-
/** An SVG document's RGBA pixels at its own size (in tiles when large and filtered). */
|
|
4
|
-
export function svgPixels(sharp: unknown, svg: string, width: number, height: number, known?: number): Promise<Uint8Array>;
|
|
5
|
-
/** How far any filter in a document reaches, output px, estimated from its markup. */
|
|
6
|
-
export function reach(svg: string): number;
|
package/src/render/tiles.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rasterising a large SVG with filters in tiles.
|
|
3
|
-
*
|
|
4
|
-
* librsvg (through libvips) slows down sharply past a few megapixels when
|
|
5
|
-
* a document has filters: a 3000² displacement map takes ~28 s in one
|
|
6
|
-
* render and ~5 s as four 1500² tiles. So a large document with filters is
|
|
7
|
-
* drawn as tiles, each with a margin round it (librsvg clips a filter's
|
|
8
|
-
* input to the viewport, so a blur or displacement at a tile's edge needs
|
|
9
|
-
* the pixels beyond it: the margin is the furthest any filter reaches),
|
|
10
|
-
* cropped and assembled. Tiles render in parallel. A document whose
|
|
11
|
-
* filters reach further than a tile renders in one piece.
|
|
12
|
-
*
|
|
13
|
-
* @module render/tiles
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { filterReach } from '../effects/region.js';
|
|
17
|
-
|
|
18
|
-
/** Documents below this many pixels render in one piece. */
|
|
19
|
-
const MIN_TILED = 2.4e6;
|
|
20
|
-
|
|
21
|
-
/** Tile side, px (before margins). */
|
|
22
|
-
const TILE = 1024;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* An SVG document's RGBA pixels at its own size.
|
|
26
|
-
*
|
|
27
|
-
* @param {any} sharp
|
|
28
|
-
* @param {string} svg - A document whose root is `width`×`height` with `viewBox="0 0 width height"`.
|
|
29
|
-
* @param {number} width
|
|
30
|
-
* @param {number} height
|
|
31
|
-
* @param {number} [known=0] - How far the filters the layers drew reach, output px (they report it at their own resolution).
|
|
32
|
-
* @returns {Promise<Uint8Array>}
|
|
33
|
-
*/
|
|
34
|
-
export async function svgPixels(sharp, svg, width, height, known = 0) {
|
|
35
|
-
const margin = width * height < MIN_TILED || !/<filter\b/.test(svg) ? Infinity : Math.max(known + 16, reach(svg));
|
|
36
|
-
// Filters reaching further than a tile (a heavy blur) render in one piece.
|
|
37
|
-
if (margin > TILE) return sharp(Buffer.from(svg)).ensureAlpha().raw().toBuffer();
|
|
38
|
-
const open = /^<svg\b[^>]*>/.exec(svg);
|
|
39
|
-
if (!open) return sharp(Buffer.from(svg)).ensureAlpha().raw().toBuffer();
|
|
40
|
-
const rest = svg.slice(open[0].length);
|
|
41
|
-
const root = open[0].replace(/\s(width|height|viewBox)="[^"]*"/g, '').replace(/>$/, '');
|
|
42
|
-
const out = Buffer.alloc(width * height * 4);
|
|
43
|
-
/** @type {(() => Promise<void>)[]} */
|
|
44
|
-
const jobs = [];
|
|
45
|
-
for (let y = 0; y < height; y += TILE) {
|
|
46
|
-
for (let x = 0; x < width; x += TILE) {
|
|
47
|
-
const tw = Math.min(TILE, width - x);
|
|
48
|
-
const th = Math.min(TILE, height - y);
|
|
49
|
-
jobs.push(async () => {
|
|
50
|
-
const vw = tw + 2 * margin;
|
|
51
|
-
const vh = th + 2 * margin;
|
|
52
|
-
const doc = `${root} width="${vw}" height="${vh}" viewBox="${x - margin} ${y - margin} ${vw} ${vh}">${rest}`;
|
|
53
|
-
const px = await sharp(Buffer.from(doc)).ensureAlpha().extract({ left: margin, top: margin, width: tw, height: th }).raw().toBuffer();
|
|
54
|
-
for (let r = 0; r < th; r++) px.copy(out, ((y + r) * width + x) * 4, r * tw * 4, (r + 1) * tw * 4);
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// A few at a time: each holds its filter surfaces.
|
|
59
|
-
const lanes = 4;
|
|
60
|
-
await Promise.all(Array.from({ length: lanes }, async (_, i) => { for (let j = i; j < jobs.length; j += lanes) await jobs[j](); }));
|
|
61
|
-
return out;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* How far, in output px, any filter in a document reaches, estimated from
|
|
66
|
-
* the markup: the primitives' reach times the largest scale it's drawn at.
|
|
67
|
-
* (Effects report their own reach at their own resolution; this covers
|
|
68
|
-
* filters written by other layers.)
|
|
69
|
-
*
|
|
70
|
-
* @param {string} svg
|
|
71
|
-
* @returns {number}
|
|
72
|
-
*/
|
|
73
|
-
export function reach(svg) {
|
|
74
|
-
let scale = 1;
|
|
75
|
-
for (const m of svg.matchAll(/scale\(([^)]*)\)/g)) for (const v of m[1].trim().split(/[\s,]+/)) scale = Math.max(scale, Math.abs(Number(v)) || 0);
|
|
76
|
-
return Math.ceil(filterReach(svg) * scale) + 16;
|
|
77
|
-
}
|
package/src/rng.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Seeded randomness.
|
|
3
|
-
*
|
|
4
|
-
* Every random choice a cover makes comes from a generator seeded by the
|
|
5
|
-
* cover's `seed` string, so the same input always draws the same image and
|
|
6
|
-
* two posts with different slugs draw different ones. Each layer gets its
|
|
7
|
-
* own stream (seed + layer index + layer type), so adding or removing one
|
|
8
|
-
* layer never reshuffles the others.
|
|
9
|
-
*
|
|
10
|
-
* @module rng
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Hash a string to a 32-bit unsigned integer (cyrb53, folded to 32 bits).
|
|
15
|
-
* Fast, well distributed, and stable across platforms and Node versions.
|
|
16
|
-
*
|
|
17
|
-
* @param {string} str - Any string.
|
|
18
|
-
* @param {number} [salt=0] - Optional salt, to get several independent hashes of one string.
|
|
19
|
-
* @returns {number} An integer in `[0, 2^32)`.
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* hashString('my-post'); // 1838215339 (always)
|
|
23
|
-
*/
|
|
24
|
-
export function hashString(str, salt = 0) {
|
|
25
|
-
let h1 = 0xdeadbeef ^ salt;
|
|
26
|
-
let h2 = 0x41c6ce57 ^ salt;
|
|
27
|
-
for (let i = 0; i < str.length; i++) {
|
|
28
|
-
const ch = str.charCodeAt(i);
|
|
29
|
-
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
30
|
-
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
31
|
-
}
|
|
32
|
-
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
33
|
-
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
34
|
-
return (h1 ^ h2) >>> 0;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* A small seeded pseudo-random generator (sfc32) with helpers.
|
|
39
|
-
*
|
|
40
|
-
* @typedef {object} Rng
|
|
41
|
-
* @property {() => number} next - A float in `[0, 1)`.
|
|
42
|
-
* @property {(min: number, max: number) => number} range - A float in `[min, max)`.
|
|
43
|
-
* @property {(min: number, max: number) => number} int - An integer in `[min, max]`.
|
|
44
|
-
* @property {<T>(items: readonly T[]) => T} pick - One item.
|
|
45
|
-
* @property {<T>(items: readonly T[]) => T[]} shuffle - A shuffled copy.
|
|
46
|
-
* @property {(chance?: number) => boolean} chance - `true` with the given probability (default 0.5).
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Create a seeded generator. Strings are hashed; numbers are used as-is.
|
|
51
|
-
*
|
|
52
|
-
* @param {string | number} seed - The seed.
|
|
53
|
-
* @returns {Rng} The generator.
|
|
54
|
-
*
|
|
55
|
-
* @example
|
|
56
|
-
* const rng = createRng('my-post');
|
|
57
|
-
* rng.range(0, 360); // same angle every run
|
|
58
|
-
*/
|
|
59
|
-
export function createRng(seed) {
|
|
60
|
-
const s = String(seed);
|
|
61
|
-
let a = hashString(s, 1);
|
|
62
|
-
let b = hashString(s, 2);
|
|
63
|
-
let c = hashString(s, 3);
|
|
64
|
-
let d = hashString(s, 4);
|
|
65
|
-
|
|
66
|
-
const next = () => {
|
|
67
|
-
a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0;
|
|
68
|
-
let t = (a + b) | 0;
|
|
69
|
-
a = b ^ (b >>> 9);
|
|
70
|
-
b = (c + (c << 3)) | 0;
|
|
71
|
-
c = (c << 21) | (c >>> 11);
|
|
72
|
-
d = (d + 1) | 0;
|
|
73
|
-
t = (t + d) | 0;
|
|
74
|
-
c = (c + t) | 0;
|
|
75
|
-
return (t >>> 0) / 4294967296;
|
|
76
|
-
};
|
|
77
|
-
// Warm up: the first few sfc32 outputs are correlated with the seed.
|
|
78
|
-
for (let i = 0; i < 12; i++) next();
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
next,
|
|
82
|
-
range: (min, max) => min + next() * (max - min),
|
|
83
|
-
int: (min, max) => Math.floor(min + next() * (max - min + 1)),
|
|
84
|
-
pick: (items) => items[Math.floor(next() * items.length)],
|
|
85
|
-
shuffle: (items) => {
|
|
86
|
-
const out = items.slice();
|
|
87
|
-
for (let i = out.length - 1; i > 0; i--) {
|
|
88
|
-
const j = Math.floor(next() * (i + 1));
|
|
89
|
-
[out[i], out[j]] = [out[j], out[i]];
|
|
90
|
-
}
|
|
91
|
-
return out;
|
|
92
|
-
},
|
|
93
|
-
chance: (p = 0.5) => next() < p,
|
|
94
|
-
};
|
|
95
|
-
}
|
package/src/svg.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Small helpers for writing SVG markup deterministically.
|
|
3
|
-
*
|
|
4
|
-
* Numbers are always written through {@link num}, so the same input gives
|
|
5
|
-
* byte-identical output on every machine (no `0.30000000000000004`, no
|
|
6
|
-
* locale formatting, no `-0`).
|
|
7
|
-
*
|
|
8
|
-
* @module svg
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Format a number for SVG: at most `digits` decimals, no trailing zeros.
|
|
13
|
-
*
|
|
14
|
-
* @param {number} v - The number.
|
|
15
|
-
* @param {number} [digits=2] - Maximum decimals.
|
|
16
|
-
* @returns {string} The formatted number.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* num(1 / 3); // '0.33'
|
|
20
|
-
* num(12.5); // '12.5'
|
|
21
|
-
* num(-0.001); // '0'
|
|
22
|
-
*/
|
|
23
|
-
export function num(v, digits = 2) {
|
|
24
|
-
if (!Number.isFinite(v)) return '0';
|
|
25
|
-
const s = v.toFixed(digits).replace(/\.?0+$/, '');
|
|
26
|
-
return s === '-0' || s === '' ? '0' : s;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Escape text for an XML attribute or text node.
|
|
31
|
-
*
|
|
32
|
-
* @param {string} s - Raw text.
|
|
33
|
-
* @returns {string} Escaped text.
|
|
34
|
-
*/
|
|
35
|
-
export function esc(s) {
|
|
36
|
-
return String(s).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Serialise an attribute object. `undefined`, `null` and `false` values are
|
|
41
|
-
* skipped; numbers go through {@link num}. Keys keep their insertion order.
|
|
42
|
-
*
|
|
43
|
-
* @param {Record<string, string | number | boolean | null | undefined>} obj - Attributes.
|
|
44
|
-
* @returns {string} ` key="value"` pairs (with a leading space), or `''`.
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* attrs({ x: 1.005, fill: '#fff', opacity: undefined }); // ' x="1" fill="#fff"'
|
|
48
|
-
*/
|
|
49
|
-
export function attrs(obj) {
|
|
50
|
-
let out = '';
|
|
51
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
52
|
-
if (v === undefined || v === null || v === false) continue;
|
|
53
|
-
out += ` ${k}="${typeof v === 'number' ? num(v) : esc(String(v))}"`;
|
|
54
|
-
}
|
|
55
|
-
return out;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Opacity and blend-mode attributes shared by every layer.
|
|
60
|
-
*
|
|
61
|
-
* @param {{ opacity?: number, blend?: string }} layer - The layer.
|
|
62
|
-
* @returns {Record<string, string | number | undefined>} Attributes for the layer's group.
|
|
63
|
-
*/
|
|
64
|
-
export function layerAttrs(layer) {
|
|
65
|
-
return {
|
|
66
|
-
opacity: layer.opacity != null && layer.opacity < 1 ? Math.max(0, layer.opacity) : undefined,
|
|
67
|
-
style: layer.blend && layer.blend !== 'normal' ? `mix-blend-mode:${layer.blend}` : undefined,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
File without changes
|