@arraypress/covers 1.1.0 → 1.4.0
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 +60 -0
- package/README.md +18 -809
- package/package.json +3 -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-poster.json +91 -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/logofolio.json +60 -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/pixel-mosaic.json +58 -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 +638 -69
- 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 +47 -54
- package/src/index.js +5 -5
- package/src/inter-data.js +1 -1
- package/src/json-recipe.js +28 -7
- package/src/layer-options.js +26 -4
- package/src/layers/barcode.js +3 -3
- package/src/layers/geometry.js +5 -5
- package/src/layers/index.d.ts +6 -0
- package/src/layers/index.js +3 -0
- package/src/layers/mark-geometry.d.ts +15 -0
- package/src/layers/mark-geometry.js +46 -0
- package/src/layers/marks.d.ts +25 -0
- package/src/layers/marks.js +102 -0
- package/src/layers/mosaic.d.ts +32 -0
- package/src/layers/mosaic.js +155 -0
- 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 +17 -32
- 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/fallback.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.d.ts +5 -1
- package/src/type/spans.js +28 -5
- package/src/type/symbols.d.ts +1 -1
- package/src/type/symbols.js +20 -2
- package/src/type/typeset.js +25 -10
- 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
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `marks` layer.
|
|
3
|
+
*
|
|
4
|
+
* @module layers/marks
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
8
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
9
|
+
import { pos, wrap } from './util.js';
|
|
10
|
+
import { shapesOnly } from './svg.js';
|
|
11
|
+
import { markGeometry } from './mark-geometry.js';
|
|
12
|
+
|
|
13
|
+
/** The smallest and largest sheet: 3 × 3 to 10 × 7. */
|
|
14
|
+
const MIN = 3;
|
|
15
|
+
const MAX_COLS = 10;
|
|
16
|
+
const MAX_ROWS = 7;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `marks` — a set of marks on an even grid of square cells, centred in its
|
|
20
|
+
* box so the margins match, each mark optically sized the same: scaled by
|
|
21
|
+
* its real bounds (not its 512 grid), to the same area, nudged up a little
|
|
22
|
+
* where it inks less of that area (an outline looks smaller than a solid
|
|
23
|
+
* of the same size), and never past its cell. The logofolio sheet.
|
|
24
|
+
*
|
|
25
|
+
* Options: `marks` (a list of markup strings, or `{ markup }`), `cols` and
|
|
26
|
+
* `rows` (default from the count and the box: 3 × 3 up to 10 × 7), `x`,
|
|
27
|
+
* `y`, `width`, `height` (the box; default the canvas), `size` (the share
|
|
28
|
+
* of a cell a mark's area takes, 0.5), `optical` (`false` fits each
|
|
29
|
+
* mark's bounds to the same box instead), `color` (default `text`).
|
|
30
|
+
*
|
|
31
|
+
* @param {any} layer
|
|
32
|
+
* @param {import('../layers.js').RenderContext} ctx
|
|
33
|
+
* @returns {import('../layers.js').Markup}
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* { type: 'marks', marks: ['<path d="…"/>', '<path d="…"/>'], x: 24, y: 120, width: 352, height: 340, color: '#f2efe8' }
|
|
37
|
+
*/
|
|
38
|
+
export function marks(layer, ctx) {
|
|
39
|
+
const list = (layer.marks ?? []).map((m) => (m && typeof m === 'object' ? m.markup : m)).filter(Boolean);
|
|
40
|
+
if (!list.length) return { defs: '', body: '' };
|
|
41
|
+
const bx = pos(layer.x ?? 0, 'x', ctx.canvas);
|
|
42
|
+
const by = pos(layer.y ?? 0, 'y', ctx.canvas);
|
|
43
|
+
const bw = pos(layer.width ?? '100%', 'x', ctx.canvas);
|
|
44
|
+
const bh = pos(layer.height ?? '100%', 'y', ctx.canvas);
|
|
45
|
+
const { cols, rows } = sheetGrid(list.length, bw / bh, layer.cols, layer.rows);
|
|
46
|
+
const cell = Math.min(bw / cols, bh / rows);
|
|
47
|
+
const ox = bx + (bw - cols * cell) / 2;
|
|
48
|
+
const oy = by + (bh - rows * cell) / 2;
|
|
49
|
+
const share = layer.size ?? 0.5;
|
|
50
|
+
const color = layer.color ? toHex(layer.color) : ctx.palette.text;
|
|
51
|
+
|
|
52
|
+
let body = '';
|
|
53
|
+
list.slice(0, cols * rows).forEach((markup, i) => {
|
|
54
|
+
const geo = markGeometry(markup);
|
|
55
|
+
if (!geo) return;
|
|
56
|
+
const { x, y, width, height } = geo.box;
|
|
57
|
+
// Optical: the same area (density-weighted, gently), capped at the cell; else the same longest side.
|
|
58
|
+
const fit = (cell * 0.86) / Math.max(width, height);
|
|
59
|
+
const k = layer.optical === false ? (cell * Math.sqrt(share)) / Math.max(width, height)
|
|
60
|
+
: Math.min(fit, (cell * Math.sqrt(share)) / (Math.sqrt(width * height) * (geo.density / 0.5) ** 0.15));
|
|
61
|
+
const cx = ox + (i % cols + 0.5) * cell;
|
|
62
|
+
const cy = oy + (Math.floor(i / cols) + 0.5) * cell;
|
|
63
|
+
body += `<svg${attrs({ x: cx - (width * k) / 2, y: cy - (height * k) / 2, width: width * k, height: height * k, viewBox: `${num(x)} ${num(y)} ${num(width)} ${num(height)}`, overflow: 'visible' })}>${shapesOnly(markup)}</svg>`;
|
|
64
|
+
});
|
|
65
|
+
return { defs: '', body: wrap(layer, `<g fill="${color}">${body}</g>`) };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Columns and rows for `n` marks in a box of `aspect` (width / height):
|
|
70
|
+
* the biggest cells the box allows, less a cost for empty cells (so 25
|
|
71
|
+
* is 5 × 5, not 6 × 5 with a ragged row), 3 × 3 at the least, 10 × 7 at
|
|
72
|
+
* the most (marks past 70 are left out). Given ones are kept.
|
|
73
|
+
*
|
|
74
|
+
* @param {number} n
|
|
75
|
+
* @param {number} aspect
|
|
76
|
+
* @param {number} [cols]
|
|
77
|
+
* @param {number} [rows]
|
|
78
|
+
* @returns {{ cols: number, rows: number }}
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* sheetGrid(70, 0.8); // { cols: 10, rows: 7 }
|
|
82
|
+
* sheetGrid(25, 1.6); // { cols: 5, rows: 5 }
|
|
83
|
+
*/
|
|
84
|
+
export function sheetGrid(n, aspect, cols, rows) {
|
|
85
|
+
if (cols && rows) return { cols: Math.round(cols), rows: Math.round(rows) };
|
|
86
|
+
if (cols) return { cols: Math.round(cols), rows: Math.max(1, Math.ceil(n / cols)) };
|
|
87
|
+
if (rows) return { cols: Math.max(1, Math.ceil(n / rows)), rows: Math.round(rows) };
|
|
88
|
+
// The biggest cells, less a cost for empty ones (a ragged last row).
|
|
89
|
+
const count = Math.min(n, MAX_COLS * MAX_ROWS);
|
|
90
|
+
let best = { cols: MAX_COLS, rows: MAX_ROWS };
|
|
91
|
+
let score = -Infinity;
|
|
92
|
+
for (let c = MIN; c <= MAX_COLS; c++) {
|
|
93
|
+
const r = clamp(Math.ceil(count / c), MIN, MAX_ROWS);
|
|
94
|
+
if (c * r < count) continue;
|
|
95
|
+
const s = Math.min(aspect / c, 1 / r) * (1 - (1.5 * (c * r - count)) / (c * r));
|
|
96
|
+
if (s > score + 1e-9) [best, score] = [{ cols: c, rows: r }, s];
|
|
97
|
+
}
|
|
98
|
+
return best;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** @param {number} v @param {number} lo @param {number} hi @returns {number} */
|
|
102
|
+
const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// The `mosaic` layer.
|
|
2
|
+
import type { Color, Length, BaseLayer } from '../index.js';
|
|
3
|
+
import type { RenderContext, Markup } from '../render/context.js';
|
|
4
|
+
import type { PixelGrid } from '@logomarks/core/edit';
|
|
5
|
+
import type { MarkGeometry } from './mark-geometry.js';
|
|
6
|
+
|
|
7
|
+
export interface MosaicLayer extends BaseLayer {
|
|
8
|
+
type: 'mosaic';
|
|
9
|
+
/** The mark's paths, on a 512 grid. */
|
|
10
|
+
markup: string;
|
|
11
|
+
/** The box the mosaic is fitted and centred in (default the canvas). */
|
|
12
|
+
x?: Length | `${number}%`; y?: Length | `${number}%`; width?: Length | `${number}%`; height?: Length | `${number}%`;
|
|
13
|
+
/** Cells across the mark's longest side, 4–48. Default 10. */
|
|
14
|
+
cells?: number;
|
|
15
|
+
/** The gap between cells, a share of a cell. Default 0.12. */
|
|
16
|
+
gap?: number;
|
|
17
|
+
/** Cell corner radius, a share of a cell. Default 0. */
|
|
18
|
+
radius?: number;
|
|
19
|
+
/** The share of filled cells left out, by the layer's seed (0–0.9). Default 0.12. */
|
|
20
|
+
drop?: number;
|
|
21
|
+
/** Default the palette's accent. */
|
|
22
|
+
color?: Color;
|
|
23
|
+
/** A small asterisk or dot in one empty cell beside the mark. */
|
|
24
|
+
accent?: 'asterisk' | 'dot' | false;
|
|
25
|
+
/** Default `color`. */
|
|
26
|
+
accentColor?: Color;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Draw a mark as a mosaic of square cells. */
|
|
30
|
+
export function mosaic(layer: MosaicLayer, ctx: RenderContext): Markup;
|
|
31
|
+
/** The mosaic's grid over a mark: `cells` across its longest side, centred on its bounds. */
|
|
32
|
+
export function mosaicGrid(geo: MarkGeometry, cells?: number): PixelGrid;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `mosaic` layer.
|
|
3
|
+
*
|
|
4
|
+
* @module layers/mosaic
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
8
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
9
|
+
import { pixelGrid, pixelCoverage, pixelCells } from '@logomarks/core/edit';
|
|
10
|
+
import { pos, wrap } from './util.js';
|
|
11
|
+
import { markGeometry } from './mark-geometry.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* `mosaic` — a mark rastered into big square cells, each drawn on its own
|
|
15
|
+
* with a gap between, so whatever is under it shows through. The cells
|
|
16
|
+
* follow the mark's own shape (`@logomarks/core`'s pixelate: a cell is
|
|
17
|
+
* filled when the mark covers half of it, or a fifth where it would
|
|
18
|
+
* otherwise lose a thin line or a dot). `drop` leaves a share of the
|
|
19
|
+
* filled cells out, chosen by the layer's seed; `accent` puts a small
|
|
20
|
+
* asterisk or dot in one empty cell beside the mark.
|
|
21
|
+
*
|
|
22
|
+
* Options: `markup` (the mark's paths, 512 grid), `x`, `y`, `width`,
|
|
23
|
+
* `height` (the box it's fitted and centred in; default the canvas),
|
|
24
|
+
* `cells` (across the mark's longest side, 10), `gap` (a share of a cell,
|
|
25
|
+
* 0.12), `radius` (a share of a cell, 0), `drop` (0–1, 0.12), `color`
|
|
26
|
+
* (default the accent), `accent` (`'asterisk'` or `'dot'`), `accentColor`.
|
|
27
|
+
*
|
|
28
|
+
* @param {any} layer
|
|
29
|
+
* @param {import('../layers.js').RenderContext} ctx
|
|
30
|
+
* @returns {import('../layers.js').Markup}
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* { type: 'mosaic', markup: mark, x: 40, y: 60, width: 320, height: 320, cells: 11, color: '#c8ff2e', drop: 0.15, accent: 'asterisk' }
|
|
34
|
+
*/
|
|
35
|
+
export function mosaic(layer, ctx) {
|
|
36
|
+
const geo = markGeometry(layer.markup);
|
|
37
|
+
if (!geo) return { defs: '', body: '' };
|
|
38
|
+
const g = mosaicGrid(geo, layer.cells);
|
|
39
|
+
const on = pixelCells(pixelCoverage(geo.paths, g), g);
|
|
40
|
+
|
|
41
|
+
// Fit the grid into the box, centred.
|
|
42
|
+
const bx = pos(layer.x ?? 0, 'x', ctx.canvas);
|
|
43
|
+
const by = pos(layer.y ?? 0, 'y', ctx.canvas);
|
|
44
|
+
const bw = pos(layer.width ?? '100%', 'x', ctx.canvas);
|
|
45
|
+
const bh = pos(layer.height ?? '100%', 'y', ctx.canvas);
|
|
46
|
+
const k = Math.min(bw / (g.cols * g.cell), bh / (g.rows * g.cell));
|
|
47
|
+
const cell = g.cell * k;
|
|
48
|
+
const ox = bx + (bw - g.cols * cell) / 2;
|
|
49
|
+
const oy = by + (bh - g.rows * cell) / 2;
|
|
50
|
+
|
|
51
|
+
const kept = dropCells(on, layer.drop ?? 0.12, ctx.rng);
|
|
52
|
+
const gap = cell * clamp(layer.gap ?? 0.12, 0, 0.9);
|
|
53
|
+
const r = cell * clamp(layer.radius ?? 0, 0, 0.5);
|
|
54
|
+
const side = cell - gap;
|
|
55
|
+
let rects = '';
|
|
56
|
+
kept.forEach((v, i) => {
|
|
57
|
+
if (!v) return;
|
|
58
|
+
const x = ox + (i % g.cols) * cell + gap / 2;
|
|
59
|
+
const y = oy + Math.floor(i / g.cols) * cell + gap / 2;
|
|
60
|
+
rects += r ? `<rect${attrs({ x, y, width: side, height: side, rx: r })}/>` : `M${num(x)} ${num(y)}h${num(side)}v${num(side)}h${num(-side)}Z`;
|
|
61
|
+
});
|
|
62
|
+
const color = layer.color ? toHex(layer.color) : ctx.palette.accent;
|
|
63
|
+
let body = r ? `<g fill="${color}">${rects}</g>` : `<path${attrs({ d: rects, fill: color })}/>`;
|
|
64
|
+
|
|
65
|
+
if (layer.accent) {
|
|
66
|
+
const at = accentCell(on, g, ctx.rng);
|
|
67
|
+
if (at >= 0) {
|
|
68
|
+
const cx = ox + (at % g.cols + 0.5) * cell;
|
|
69
|
+
const cy = oy + (Math.floor(at / g.cols) + 0.5) * cell;
|
|
70
|
+
body += accentGlyph(layer.accent, cx, cy, side, layer.accentColor ? toHex(layer.accentColor) : color);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { defs: '', body: wrap(layer, body) };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The mosaic's grid over a mark: `cells` across its longest side (4–48),
|
|
78
|
+
* centred on its bounds (pixelate's grid, at that coarseness).
|
|
79
|
+
*
|
|
80
|
+
* @param {import('./mark-geometry.js').MarkGeometry} geo
|
|
81
|
+
* @param {number} [cells=10]
|
|
82
|
+
* @returns {import('@logomarks/core/edit').PixelGrid}
|
|
83
|
+
*/
|
|
84
|
+
export function mosaicGrid(geo, cells = 10) {
|
|
85
|
+
const n = clamp(Math.round(cells), 4, 48);
|
|
86
|
+
// pixelGrid sets 48 − 34 × amount cells across.
|
|
87
|
+
return pixelGrid((48 - n) / 34, geo.box);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The filled cells less a seeded share left out (at least one is kept).
|
|
92
|
+
*
|
|
93
|
+
* @param {Uint8Array} on
|
|
94
|
+
* @param {number} drop - 0–1.
|
|
95
|
+
* @param {import('../index.js').Rng} rng
|
|
96
|
+
* @returns {Uint8Array}
|
|
97
|
+
*/
|
|
98
|
+
function dropCells(on, drop, rng) {
|
|
99
|
+
const out = on.slice();
|
|
100
|
+
const share = clamp(drop, 0, 0.9);
|
|
101
|
+
let left = 0;
|
|
102
|
+
out.forEach((v, i) => {
|
|
103
|
+
if (!v) return;
|
|
104
|
+
// One draw a filled cell, always, so the choice is stable whatever `drop` is.
|
|
105
|
+
if (rng.next() < share) out[i] = 0;
|
|
106
|
+
else left++;
|
|
107
|
+
});
|
|
108
|
+
if (!left) return on.slice();
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* A seeded empty cell beside the mark (one with a filled neighbour), or -1.
|
|
114
|
+
*
|
|
115
|
+
* @param {Uint8Array} on
|
|
116
|
+
* @param {{ cols: number, rows: number }} g
|
|
117
|
+
* @param {import('../index.js').Rng} rng
|
|
118
|
+
* @returns {number}
|
|
119
|
+
*/
|
|
120
|
+
function accentCell(on, { cols, rows }, rng) {
|
|
121
|
+
const near = [];
|
|
122
|
+
for (let i = 0; i < on.length; i++) {
|
|
123
|
+
if (on[i]) continue;
|
|
124
|
+
const q = i % cols;
|
|
125
|
+
const r = Math.floor(i / cols);
|
|
126
|
+
const n = [[q - 1, r], [q + 1, r], [q, r - 1], [q, r + 1]].some(([a, b]) => a >= 0 && b >= 0 && a < cols && b < rows && on[b * cols + a]);
|
|
127
|
+
if (n) near.push(i);
|
|
128
|
+
}
|
|
129
|
+
if (!near.length) return -1;
|
|
130
|
+
return near[Math.floor(rng.next() * near.length)];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* An asterisk (three bars) or a dot, centred in a cell of `side`.
|
|
135
|
+
*
|
|
136
|
+
* @param {'asterisk' | 'dot' | string} kind
|
|
137
|
+
* @param {number} cx @param {number} cy @param {number} side
|
|
138
|
+
* @param {string} color
|
|
139
|
+
* @returns {string}
|
|
140
|
+
*/
|
|
141
|
+
function accentGlyph(kind, cx, cy, side, color) {
|
|
142
|
+
if (kind === 'dot') return `<circle${attrs({ cx, cy, r: side * 0.24, fill: color })}/>`;
|
|
143
|
+
const L = side * 0.36;
|
|
144
|
+
const t = side * 0.085;
|
|
145
|
+
const d = [0, 60, 120].map((deg) => {
|
|
146
|
+
const a = (deg * Math.PI) / 180;
|
|
147
|
+
const [ux, uy, vx, vy] = [Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a)];
|
|
148
|
+
const pts = [[-L, -t], [L, -t], [L, t], [-L, t]].map(([u, v]) => `${num(cx + u * ux + v * vx)} ${num(cy + u * uy + v * vy)}`);
|
|
149
|
+
return `M${pts.join('L')}Z`;
|
|
150
|
+
}).join('');
|
|
151
|
+
return `<path${attrs({ d, fill: color })}/>`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** @param {number} v @param {number} lo @param {number} hi @returns {number} */
|
|
155
|
+
const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* @module layers/perforation
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { attrs, num } from '
|
|
8
|
-
import { len } from '
|
|
9
|
-
import { toHex } from '
|
|
7
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
8
|
+
import { len } from '@logomarks/image-effects/length';
|
|
9
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
10
10
|
import { pos, wrap } from './util.js';
|
|
11
11
|
|
|
12
12
|
|
package/src/layers/repeat.js
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* @module layers/repeat
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { num } from '
|
|
23
|
-
import { len } from '
|
|
22
|
+
import { num } from '@logomarks/image-effects/svg';
|
|
23
|
+
import { len } from '@logomarks/image-effects/length';
|
|
24
24
|
import { pos } from './util.js';
|
|
25
25
|
import { fill, evaluate } from './expr.js';
|
|
26
26
|
|
package/src/layers/shape.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// The `shape` layer.
|
|
2
2
|
import type { Color, Length, BaseLayer, Anchor } from '../index.js';
|
|
3
3
|
import type { RenderContext, Markup } from '../render/context.js';
|
|
4
|
-
import type { Paint } from '../
|
|
4
|
+
import type { Paint } from '../paint/index.js';
|
|
5
5
|
|
|
6
|
-
/** One exact shape (see the
|
|
6
|
+
/** One exact shape (see the `shape` row in docs/layers.md). */
|
|
7
7
|
export interface ShapeSpec {
|
|
8
8
|
kind?: 'rect' | 'circle' | 'ellipse' | 'line' | 'polygon' | 'polyline' | 'star' | 'burst' | 'scallop' | 'rays' | 'ring' | 'sector' | 'arch' | 'wave' | 'path' | 'cut';
|
|
9
9
|
x?: Length | `${number}%`; y?: Length | `${number}%`; width?: Length | `${number}%`; height?: Length | `${number}%`;
|
package/src/layers/shape.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* @module layers/shape
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { attrs, num } from '
|
|
8
|
-
import { len } from '
|
|
9
|
-
import { toHex } from '
|
|
10
|
-
import { paint } from '../
|
|
11
|
-
import { presetStrokes } from '../
|
|
7
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
8
|
+
import { len } from '@logomarks/image-effects/length';
|
|
9
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
10
|
+
import { paint } from '../paint/index.js';
|
|
11
|
+
import { presetStrokes } from '../paint/presets.js';
|
|
12
12
|
import { wrap, pos } from './util.js';
|
|
13
13
|
import { geometry } from './geometry.js';
|
|
14
14
|
import { soften } from './soft.js';
|
package/src/layers/soft.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* @module layers/soft
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { attrs, num } from '
|
|
10
|
-
import { toBase64 } from '../platform/
|
|
9
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
10
|
+
import { toBase64 } from '../platform/index.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Blur markup by `sigma` (px in the layer's units). In `renderImage`, the markup
|
package/src/layers/svg.js
CHANGED
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
* @module layers/svg
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { attrs, esc, num } from '
|
|
7
|
+
import { attrs, esc, num } from '@logomarks/image-effects/svg';
|
|
8
8
|
import { unit } from '../geometry.js';
|
|
9
9
|
import { pos, wrap } from './util.js';
|
|
10
|
-
import { expandDropShadows } from '
|
|
11
|
-
import { filterReach } from '../effects/region.js';
|
|
10
|
+
import { expandDropShadows, filterReach } from '@logomarks/image-effects';
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
/**
|
package/src/layers/table.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* @module layers/table
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { attrs, num } from '
|
|
8
|
-
import { len } from '
|
|
9
|
-
import { toHex } from '
|
|
7
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
8
|
+
import { len } from '@logomarks/image-effects/length';
|
|
9
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
10
10
|
import { faceFor, setLine } from '../type/line.js';
|
|
11
11
|
import { pos, wrap, contentOf } from './util.js';
|
|
12
12
|
|
package/src/layers/tear.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The `tear` layer.
|
|
2
2
|
import type { Color, Length, BaseLayer, Anchor } from '../index.js';
|
|
3
3
|
import type { RenderContext, Markup } from '../render/context.js';
|
|
4
|
-
import type { Paint } from '../
|
|
4
|
+
import type { Paint } from '../paint/index.js';
|
|
5
5
|
|
|
6
6
|
export interface TearLayer extends BaseLayer {
|
|
7
7
|
type: 'tear';
|
package/src/layers/tear.js
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* @module layers/tear
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { attrs, num } from '
|
|
8
|
-
import { len } from '
|
|
9
|
-
import { toHex } from '
|
|
10
|
-
import { paint } from '../
|
|
7
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
8
|
+
import { len } from '@logomarks/image-effects/length';
|
|
9
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
10
|
+
import { paint } from '../paint/index.js';
|
|
11
11
|
import { pos, wrap } from './util.js';
|
|
12
12
|
|
|
13
13
|
|
package/src/layers/texture.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* @module layers/texture
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { attrs, num } from '
|
|
8
|
-
import { len } from '
|
|
9
|
-
import { toHex } from '
|
|
7
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
8
|
+
import { len } from '@logomarks/image-effects/length';
|
|
9
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
10
10
|
import { pos, wrap } from './util.js';
|
|
11
11
|
|
|
12
12
|
|
package/src/layers/util.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* @module layers/util
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { attrs, esc } from '
|
|
9
|
-
import { len } from '
|
|
8
|
+
import { attrs, esc } from '@logomarks/image-effects/svg';
|
|
9
|
+
import { len } from '@logomarks/image-effects/length';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* A coordinate: px as a number, `'50%'` of the canvas along `axis`, or a
|
|
@@ -22,6 +22,19 @@ export function pos(v, axis, canvas) {
|
|
|
22
22
|
return len(/** @type {any} */ (v), canvas, 0);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Opacity and blend-mode attributes shared by every layer.
|
|
27
|
+
*
|
|
28
|
+
* @param {{ opacity?: number, blend?: string }} layer - The layer.
|
|
29
|
+
* @returns {Record<string, string | number | undefined>} Attributes for the layer's group.
|
|
30
|
+
*/
|
|
31
|
+
export function layerAttrs(layer) {
|
|
32
|
+
return {
|
|
33
|
+
opacity: layer.opacity != null && layer.opacity < 1 ? Math.max(0, layer.opacity) : undefined,
|
|
34
|
+
style: layer.blend && layer.blend !== 'normal' ? `mix-blend-mode:${layer.blend}` : undefined,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
25
38
|
/**
|
|
26
39
|
* Wrap markup in a group with the layer's `opacity` and `blend` (only when
|
|
27
40
|
* it has them).
|
package/src/layers.js
CHANGED
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
* @module layers
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { loadBytes } from './platform/index.js';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { len
|
|
21
|
-
import {
|
|
17
|
+
import { loadBytes, toBase64 } from './platform/index.js';
|
|
18
|
+
import { attrs, esc, num } from '@logomarks/image-effects/svg';
|
|
19
|
+
import { imageMime } from '@logomarks/image-effects';
|
|
20
|
+
import { len } from '@logomarks/image-effects/length';
|
|
21
|
+
import { place, anchorAlign, unit, regionPx } from './geometry.js';
|
|
22
|
+
import { toHex, mixOklab, toOklch, fromOklch } from '@logomarks/image-effects/color';
|
|
22
23
|
import { harfbuzz, loadFont, pickFont } from './fonts.js';
|
|
23
24
|
import { layout, runPath, caseText } from './text.js';
|
|
24
25
|
import { resolveIcon, parseSvgIcon } from './icons.js';
|
|
@@ -28,9 +29,9 @@ import { paper, scribble } from './handmade.js';
|
|
|
28
29
|
import { ink, dither, contour, waveform, tiles, rule } from './print.js';
|
|
29
30
|
import { typeset, wantsType } from './type/index.js';
|
|
30
31
|
import { recordLayout } from './type/layout-ref.js';
|
|
31
|
-
import { shape, svg, table, barcode, perforation, tear, texture } from './layers/index.js';
|
|
32
|
-
import { pos } from './layers/util.js';
|
|
33
|
-
import { paint } from './
|
|
32
|
+
import { shape, svg, table, barcode, perforation, tear, texture, mosaic, marks } from './layers/index.js';
|
|
33
|
+
import { pos, layerAttrs } from './layers/util.js';
|
|
34
|
+
import { paint } from './paint/index.js';
|
|
34
35
|
import { imageSize } from './lint/image-size.js';
|
|
35
36
|
|
|
36
37
|
/**
|
|
@@ -41,7 +42,7 @@ import { imageSize } from './lint/image-size.js';
|
|
|
41
42
|
* @property {number} height - Canvas height in px.
|
|
42
43
|
* @property {{ width: number, height: number }} canvas - The same, as an object.
|
|
43
44
|
* @property {import('./index.js').Palette} palette - The resolved palette.
|
|
44
|
-
* @property {import('
|
|
45
|
+
* @property {import('@logomarks/image-effects/rng').Rng} rng - This layer's own random stream.
|
|
45
46
|
* @property {string} id - A unique id prefix for this layer's defs.
|
|
46
47
|
* @property {import('./index.js').FontSource[]} fonts - Available fonts (Inter last).
|
|
47
48
|
* @property {any} [sharp] - The `sharp` module, when available.
|
|
@@ -421,7 +422,7 @@ export async function image(layer, ctx) {
|
|
|
421
422
|
const fit = layer.fit ?? 'cover';
|
|
422
423
|
const focus = layer.focus ?? 'center';
|
|
423
424
|
let bytes = typeof layer.src === 'string' || layer.src instanceof URL ? await readSource(layer.src) : await loadBytes(layer.src, 'image');
|
|
424
|
-
let mime =
|
|
425
|
+
let mime = imageMime(bytes);
|
|
425
426
|
let preserve = fit === 'stretch' ? 'none' : `${alignFor(focus)} ${fit === 'contain' ? 'meet' : 'slice'}`;
|
|
426
427
|
let sharpBlurred = false;
|
|
427
428
|
|
|
@@ -548,23 +549,6 @@ async function readSource(src) {
|
|
|
548
549
|
return loadBytes(src, 'image');
|
|
549
550
|
}
|
|
550
551
|
|
|
551
|
-
/**
|
|
552
|
-
* Sniff an image's MIME type from its first bytes.
|
|
553
|
-
*
|
|
554
|
-
* @param {Uint8Array} b - Image bytes.
|
|
555
|
-
* @returns {string | undefined} The MIME type.
|
|
556
|
-
*/
|
|
557
|
-
export function sniff(b) {
|
|
558
|
-
if (b[0] === 0x89 && b[1] === 0x50) return 'image/png';
|
|
559
|
-
if (b[0] === 0xff && b[1] === 0xd8) return 'image/jpeg';
|
|
560
|
-
if (b[0] === 0x47 && b[1] === 0x49) return 'image/gif';
|
|
561
|
-
const head = latin1(b.subarray(0, 32));
|
|
562
|
-
if (head.startsWith('RIFF') && head.slice(8, 12) === 'WEBP') return 'image/webp';
|
|
563
|
-
if (head.slice(4, 12) === 'ftypavif' || head.slice(4, 12) === 'ftypavis') return 'image/avif';
|
|
564
|
-
if (/^\s*(<\?xml|<svg)/i.test(utf8(b.subarray(0, 256)))) return 'image/svg+xml';
|
|
565
|
-
return undefined;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
552
|
// ---------------------------------------------------------------------------
|
|
569
553
|
// Texture
|
|
570
554
|
// ---------------------------------------------------------------------------
|
|
@@ -636,10 +620,11 @@ export function pattern(layer, ctx) {
|
|
|
636
620
|
const d = [];
|
|
637
621
|
if (angle === 0) {
|
|
638
622
|
// Rows and columns: every `size` from the centre, or `divisions` equal
|
|
639
|
-
// cells (inner lines only
|
|
640
|
-
|
|
641
|
-
const
|
|
642
|
-
const
|
|
623
|
+
// cells (inner lines only; `[cols, rows]` for a design grid); broken
|
|
624
|
+
// wherever they cross a `gaps` region.
|
|
625
|
+
const [nx, ny] = Array.isArray(layer.divisions) ? layer.divisions.map((d) => Math.max(1, Math.round(d))) : [layer.divisions, layer.divisions];
|
|
626
|
+
const ys = ny ? Array.from({ length: ny - 1 }, (_, k) => ((k + 1) * h) / ny) : range(cy % size, h, size);
|
|
627
|
+
const xs = kind !== 'grid' ? [] : nx ? Array.from({ length: nx - 1 }, (_, k) => ((k + 1) * w) / nx) : range(cx % size, w, size);
|
|
643
628
|
const gaps = (layer.gaps ?? []).map((g) => regionPx(g, ctx.canvas));
|
|
644
629
|
d.push(gridPath(xs, ys, w, h, gaps));
|
|
645
630
|
shapes = `<path${attrs({ d: d.join(''), fill: 'none', stroke: col, 'stroke-width': stroke, ...dashAttrs })}/>`;
|
|
@@ -1053,4 +1038,4 @@ function group() {
|
|
|
1053
1038
|
throw new TypeError('A group layer is drawn by renderSvg()/renderImage(), not on its own');
|
|
1054
1039
|
}
|
|
1055
1040
|
|
|
1056
|
-
export const LAYERS = Object.freeze({ solid, linear, radial, mesh, stripes, image, grain, pattern, shapes, icon, text, frame, diagram, paper, scribble, ink, dither, contour, waveform, tiles, rule, group, shape, svg, table, barcode, perforation, tear, texture, repeat: group, defs: group, use: group });
|
|
1041
|
+
export const LAYERS = Object.freeze({ solid, linear, radial, mesh, stripes, image, grain, pattern, shapes, icon, text, frame, diagram, paper, scribble, ink, dither, contour, waveform, tiles, rule, group, shape, svg, table, barcode, perforation, tear, texture, mosaic, marks, repeat: group, defs: group, use: group });
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* radial fields with `rx`/`ry`), a `tile` pattern (checker, stripes, dots,
|
|
5
5
|
* grid or a custom tile), or a named preset (`'chrome'`).
|
|
6
6
|
*
|
|
7
|
-
* @module
|
|
7
|
+
* @module paint
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { attrs, num } from '
|
|
11
|
-
import { len } from '
|
|
12
|
-
import { toHex } from '
|
|
13
|
-
import { PRESETS } from './
|
|
10
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
11
|
+
import { len } from '@logomarks/image-effects/length';
|
|
12
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
13
|
+
import { PRESETS } from './presets.js';
|
|
14
14
|
import { tile } from './tile.js';
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* horizon a little past the middle, warm ground below it, and a light
|
|
5
5
|
* bounce at the very bottom. Pair it with a light keyline (`strokes`).
|
|
6
6
|
*
|
|
7
|
-
* @module
|
|
7
|
+
* @module paint/presets
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/** Paint presets, by name. A paint `{ preset, …fields }` overrides any of them. */
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Tile paints: a pattern repeated over a shape (checker, stripes, dots,
|
|
3
3
|
* grid), or a custom tile of markup.
|
|
4
4
|
*
|
|
5
|
-
* @module
|
|
5
|
+
* @module paint/tile
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { attrs, num } from '
|
|
9
|
-
import { len } from '
|
|
10
|
-
import { toHex } from '
|
|
8
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
9
|
+
import { len } from '@logomarks/image-effects/length';
|
|
10
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
11
11
|
import { sanitize } from '../layers/svg.js';
|
|
12
12
|
|
|
13
13
|
/** Tile kinds. */
|
package/src/palette.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { relativeLuminance, contrastRatio, readableOn, readableText } from '@arraypress/color-utils';
|
|
23
|
-
import { toHex, toOklch, fromOklch, mixOklab } from '
|
|
23
|
+
import { toHex, toOklch, fromOklch, mixOklab } from '@logomarks/image-effects/color';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Curated presets. Each is a full palette; the light ones pair with the
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Files read from disk in Node.
|
|
2
|
+
|
|
3
|
+
/** Read a text file synchronously (Node). */
|
|
4
|
+
export function readTextSync(path: string | URL): string;
|
|
5
|
+
/** A file shipped with the package, from disk (Node), or undefined. */
|
|
6
|
+
export function packageFileSync(rel: string): Uint8Array | undefined;
|