@pixerate/editor 0.193.0 → 0.195.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.
@@ -23,6 +23,10 @@ export declare function snapTo45(from: ImagePoint, point: ImagePoint): ImagePoin
23
23
  * adds a free-hand stretch; a double-click, a click on the first corner or Enter (`key`)
24
24
  * closes it, Backspace removes the last corner and Escape cancels. It stays `active` between
25
25
  * clicks, so feed it pointer moves without a button too.
26
+ * - Magnetic lasso: a click sets the first anchor, then the outline follows the edges from the
27
+ * last anchor to the pointer (snapped to the strongest edge within the width) and places
28
+ * anchors as it goes (frequency); a click adds one, and closing and the keys are as the
29
+ * polygonal lasso's. The first click reads the pixels (async).
26
30
  * - Single row / column: a click selects a 1 px line across the image.
27
31
  *
28
32
  * Shift adds, Alt subtracts, both intersect (overriding the options bar mode). While dragging,
@@ -39,12 +43,15 @@ export declare class SelectionGesture {
39
43
  private polygon;
40
44
  private pressedAt;
41
45
  private freehand;
46
+ private magnetic;
42
47
  /** A click this close to the first corner (natural pixels) closes a polygonal lasso. */
43
48
  closeRadius: number;
44
49
  /** The last error (e.g. pixels unavailable for the wand), for the UI to show. */
45
50
  error: string | null;
46
51
  constructor(controller: ImageEditorController, getOptions: () => SelectionToolOptions);
47
52
  get active(): boolean;
53
+ /** The anchors of a Magnetic Lasso being drawn (empty otherwise). */
54
+ get magneticAnchors(): ImagePoint[];
48
55
  /** The outline of the shape being drawn, or `null`. */
49
56
  get draftPath(): ImagePoint[] | null;
50
57
  /** Pointer down. Resolves once a wand click has been applied. */
@@ -60,6 +67,11 @@ export declare class SelectionGesture {
60
67
  * removes the last corner. Returns true when the key was used (the host prevents its default).
61
68
  */
62
69
  key(key: string): boolean;
70
+ /** Corridor half-width for a live-wire between two points. */
71
+ private corridor;
72
+ private magneticClick;
73
+ /** Updates the live path to `point`, fixing anchors along it every `frequency` px (with `place`). */
74
+ private followEdges;
63
75
  /** Abandons a drag (or a polygonal lasso) without changing the selection. */
64
76
  cancel(): void;
65
77
  }
@@ -23,6 +23,10 @@ export declare function snapTo45(from: ImagePoint, point: ImagePoint): ImagePoin
23
23
  * adds a free-hand stretch; a double-click, a click on the first corner or Enter (`key`)
24
24
  * closes it, Backspace removes the last corner and Escape cancels. It stays `active` between
25
25
  * clicks, so feed it pointer moves without a button too.
26
+ * - Magnetic lasso: a click sets the first anchor, then the outline follows the edges from the
27
+ * last anchor to the pointer (snapped to the strongest edge within the width) and places
28
+ * anchors as it goes (frequency); a click adds one, and closing and the keys are as the
29
+ * polygonal lasso's. The first click reads the pixels (async).
26
30
  * - Single row / column: a click selects a 1 px line across the image.
27
31
  *
28
32
  * Shift adds, Alt subtracts, both intersect (overriding the options bar mode). While dragging,
@@ -39,12 +43,15 @@ export declare class SelectionGesture {
39
43
  private polygon;
40
44
  private pressedAt;
41
45
  private freehand;
46
+ private magnetic;
42
47
  /** A click this close to the first corner (natural pixels) closes a polygonal lasso. */
43
48
  closeRadius: number;
44
49
  /** The last error (e.g. pixels unavailable for the wand), for the UI to show. */
45
50
  error: string | null;
46
51
  constructor(controller: ImageEditorController, getOptions: () => SelectionToolOptions);
47
52
  get active(): boolean;
53
+ /** The anchors of a Magnetic Lasso being drawn (empty otherwise). */
54
+ get magneticAnchors(): ImagePoint[];
48
55
  /** The outline of the shape being drawn, or `null`. */
49
56
  get draftPath(): ImagePoint[] | null;
50
57
  /** Pointer down. Resolves once a wand click has been applied. */
@@ -60,6 +67,11 @@ export declare class SelectionGesture {
60
67
  * removes the last corner. Returns true when the key was used (the host prevents its default).
61
68
  */
62
69
  key(key: string): boolean;
70
+ /** Corridor half-width for a live-wire between two points. */
71
+ private corridor;
72
+ private magneticClick;
73
+ /** Updates the live path to `point`, fixing anchors along it every `frequency` px (with `place`). */
74
+ private followEdges;
63
75
  /** Abandons a drag (or a polygonal lasso) without changing the selection. */
64
76
  cancel(): void;
65
77
  }
@@ -12,3 +12,4 @@ export * from './stroke.cjs';
12
12
  export * from './channels.cjs';
13
13
  export * from './mask-files.cjs';
14
14
  export * from './path.cjs';
15
+ export * from './magnetic.cjs';
@@ -12,3 +12,4 @@ export * from './stroke.js';
12
12
  export * from './channels.js';
13
13
  export * from './mask-files.js';
14
14
  export * from './path.js';
15
+ export * from './magnetic.js';
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Magnetic Lasso: an outline that follows the edges of the image (live-wire, Mortensen & Barrett,
3
+ * "Intelligent Scissors", 1995). Each step between neighbouring pixels costs less where it runs
4
+ * along a strong edge (gradient magnitude), on a Laplacian zero-crossing and in the edge's
5
+ * direction; the outline between two points is the cheapest path, found with Dijkstra in a corridor
6
+ * around them. The direction term ignores which side of the edge is darker, so the path holds to an
7
+ * edge whichever way round it is traced.
8
+ */
9
+ import type { ImagePoint } from '../types.cjs';
10
+ import type { PixelBuffer } from './mask.cjs';
11
+ export interface MagneticEdgeOptions {
12
+ /** Detection width in px: a point snaps to the strongest edge this close to it (default 10). */
13
+ width?: number;
14
+ /** 0..1: edges with less contrast than this are ignored (default 0.1, Photoshop's 10 %). */
15
+ contrast?: number;
16
+ }
17
+ /**
18
+ * The edge features of an image, computed lazily in 64 px blocks as the outline reaches them, so a
19
+ * click on a large image costs one luminance pass, not a full-image edge map.
20
+ */
21
+ export declare class EdgeMap {
22
+ readonly width: number;
23
+ readonly height: number;
24
+ readonly contrast: number;
25
+ private readonly luma;
26
+ private readonly blocks;
27
+ constructor(pixels: PixelBuffer, options?: Pick<MagneticEdgeOptions, 'contrast'>);
28
+ /** Edge strength at (x, y) in whole pixels, 0..1 (0 outside the image or below the contrast). */
29
+ strength(x: number, y: number): number;
30
+ private block;
31
+ private computeBlock;
32
+ /** Features of the pixel at (x, y), which must be inside the image. */
33
+ features(x: number, y: number): {
34
+ strength: number;
35
+ dirX: number;
36
+ dirY: number;
37
+ zero: number;
38
+ };
39
+ }
40
+ /**
41
+ * `point` moved onto the strongest edge within `width` px (the nearest of equal ones); unchanged
42
+ * when there is no edge above the contrast that close. Returns a pixel centre.
43
+ */
44
+ export declare function snapToEdge(map: EdgeMap, point: ImagePoint, width?: number): ImagePoint;
45
+ /**
46
+ * The cheapest path along the edges from `from` to `to` (pixel centres, both included), searched
47
+ * in a corridor around the straight line between them: `band` px either side (default the larger
48
+ * of 16 px and a quarter of the distance). Points inside the image.
49
+ */
50
+ export declare function liveWire(map: EdgeMap, from: ImagePoint, to: ImagePoint, band?: number): ImagePoint[];
51
+ /** `points` with runs along one straight line reduced to their ends (exact for 8-connected paths). */
52
+ export declare function simplifyPixelPath(points: readonly ImagePoint[]): ImagePoint[];
53
+ /**
54
+ * The Magnetic Lasso for a given outline (the agent path): each point snaps to the strongest edge
55
+ * within `width` px, and consecutive points are joined by the live-wire path between them (the
56
+ * last back to the first). Points should run along the edge in order, a few dozen px apart.
57
+ */
58
+ export declare function snapPolygonToEdges(pixels: PixelBuffer | EdgeMap, points: readonly ImagePoint[], options?: MagneticEdgeOptions): ImagePoint[];
59
+ /** Path length in px between auto-placed anchors for a Frequency of 0..100 (Photoshop's default 57). */
60
+ export declare function magneticAnchorSpacing(frequency?: number): number;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Magnetic Lasso: an outline that follows the edges of the image (live-wire, Mortensen & Barrett,
3
+ * "Intelligent Scissors", 1995). Each step between neighbouring pixels costs less where it runs
4
+ * along a strong edge (gradient magnitude), on a Laplacian zero-crossing and in the edge's
5
+ * direction; the outline between two points is the cheapest path, found with Dijkstra in a corridor
6
+ * around them. The direction term ignores which side of the edge is darker, so the path holds to an
7
+ * edge whichever way round it is traced.
8
+ */
9
+ import type { ImagePoint } from '../types.js';
10
+ import type { PixelBuffer } from './mask.js';
11
+ export interface MagneticEdgeOptions {
12
+ /** Detection width in px: a point snaps to the strongest edge this close to it (default 10). */
13
+ width?: number;
14
+ /** 0..1: edges with less contrast than this are ignored (default 0.1, Photoshop's 10 %). */
15
+ contrast?: number;
16
+ }
17
+ /**
18
+ * The edge features of an image, computed lazily in 64 px blocks as the outline reaches them, so a
19
+ * click on a large image costs one luminance pass, not a full-image edge map.
20
+ */
21
+ export declare class EdgeMap {
22
+ readonly width: number;
23
+ readonly height: number;
24
+ readonly contrast: number;
25
+ private readonly luma;
26
+ private readonly blocks;
27
+ constructor(pixels: PixelBuffer, options?: Pick<MagneticEdgeOptions, 'contrast'>);
28
+ /** Edge strength at (x, y) in whole pixels, 0..1 (0 outside the image or below the contrast). */
29
+ strength(x: number, y: number): number;
30
+ private block;
31
+ private computeBlock;
32
+ /** Features of the pixel at (x, y), which must be inside the image. */
33
+ features(x: number, y: number): {
34
+ strength: number;
35
+ dirX: number;
36
+ dirY: number;
37
+ zero: number;
38
+ };
39
+ }
40
+ /**
41
+ * `point` moved onto the strongest edge within `width` px (the nearest of equal ones); unchanged
42
+ * when there is no edge above the contrast that close. Returns a pixel centre.
43
+ */
44
+ export declare function snapToEdge(map: EdgeMap, point: ImagePoint, width?: number): ImagePoint;
45
+ /**
46
+ * The cheapest path along the edges from `from` to `to` (pixel centres, both included), searched
47
+ * in a corridor around the straight line between them: `band` px either side (default the larger
48
+ * of 16 px and a quarter of the distance). Points inside the image.
49
+ */
50
+ export declare function liveWire(map: EdgeMap, from: ImagePoint, to: ImagePoint, band?: number): ImagePoint[];
51
+ /** `points` with runs along one straight line reduced to their ends (exact for 8-connected paths). */
52
+ export declare function simplifyPixelPath(points: readonly ImagePoint[]): ImagePoint[];
53
+ /**
54
+ * The Magnetic Lasso for a given outline (the agent path): each point snaps to the strongest edge
55
+ * within `width` px, and consecutive points are joined by the live-wire path between them (the
56
+ * last back to the first). Points should run along the edge in order, a few dozen px apart.
57
+ */
58
+ export declare function snapPolygonToEdges(pixels: PixelBuffer | EdgeMap, points: readonly ImagePoint[], options?: MagneticEdgeOptions): ImagePoint[];
59
+ /** Path length in px between auto-placed anchors for a Frequency of 0..100 (Photoshop's default 57). */
60
+ export declare function magneticAnchorSpacing(frequency?: number): number;
@@ -2,7 +2,7 @@ import { type SelectionMask, type SelectionMode } from './mask.cjs';
2
2
  import type { ColorDistance, ColorRangePreset } from './wand.cjs';
3
3
  import type { RefineEdgeParams } from './refine.cjs';
4
4
  /** Selection tools of the image editor UI. */
5
- export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'row' | 'column' | 'lasso' | 'polygon' | 'range' | 'type' | 'type-vertical';
5
+ export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'row' | 'column' | 'lasso' | 'polygon' | 'magnetic' | 'range' | 'type' | 'type-vertical';
6
6
  export declare const SELECTION_TOOLS: readonly {
7
7
  id: SelectionToolKind;
8
8
  label: string;
@@ -31,6 +31,12 @@ export interface SelectionToolOptions {
31
31
  localized?: number;
32
32
  /** Color Range: select everything except the sampled colours. */
33
33
  rangeInvert?: boolean;
34
+ /** Magnetic Lasso: snap to the strongest edge within this many px (1..256, default 10). */
35
+ magneticWidth?: number;
36
+ /** Magnetic Lasso: ignore edges with less contrast than this, in % (1..100, default 10). */
37
+ magneticContrast?: number;
38
+ /** Magnetic Lasso: how often anchors are placed as the pointer moves (0..100, default 57). */
39
+ magneticFrequency?: number;
34
40
  /** Type Mask: the text to select (default "Type"). A click places its first baseline. */
35
41
  typeText?: string;
36
42
  /** Type Mask: font size in px (default 72). */
@@ -2,7 +2,7 @@ import { type SelectionMask, type SelectionMode } from './mask.js';
2
2
  import type { ColorDistance, ColorRangePreset } from './wand.js';
3
3
  import type { RefineEdgeParams } from './refine.js';
4
4
  /** Selection tools of the image editor UI. */
5
- export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'row' | 'column' | 'lasso' | 'polygon' | 'range' | 'type' | 'type-vertical';
5
+ export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'row' | 'column' | 'lasso' | 'polygon' | 'magnetic' | 'range' | 'type' | 'type-vertical';
6
6
  export declare const SELECTION_TOOLS: readonly {
7
7
  id: SelectionToolKind;
8
8
  label: string;
@@ -31,6 +31,12 @@ export interface SelectionToolOptions {
31
31
  localized?: number;
32
32
  /** Color Range: select everything except the sampled colours. */
33
33
  rangeInvert?: boolean;
34
+ /** Magnetic Lasso: snap to the strongest edge within this many px (1..256, default 10). */
35
+ magneticWidth?: number;
36
+ /** Magnetic Lasso: ignore edges with less contrast than this, in % (1..100, default 10). */
37
+ magneticContrast?: number;
38
+ /** Magnetic Lasso: how often anchors are placed as the pointer moves (0..100, default 57). */
39
+ magneticFrequency?: number;
34
40
  /** Type Mask: the text to select (default "Type"). A click places its first baseline. */
35
41
  typeText?: string;
36
42
  /** Type Mask: font size in px (default 72). */
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The image editor's tools as a toolbar shows them: grouped like Photoshop's (a slot per group, the
3
+ * rest of the group in a flyout), with labels and shortcuts, the canvas mode each tool draws in and
4
+ * the layer panel (`layerPanels`) holding its options. Hosts build their toolbar from these instead
5
+ * of listing tools themselves, so new tools show up without host changes.
6
+ */
7
+ import type { ImageEditorTool } from './types.cjs';
8
+ export interface ImageToolGroup {
9
+ id: string;
10
+ label: string;
11
+ tools: readonly ImageEditorTool[];
12
+ }
13
+ /**
14
+ * Every tool a toolbar offers, in toolbar order. The `text` and `watermark` annotations are left
15
+ * out: they are added through the API (`addAnnotation`), not drawn.
16
+ */
17
+ export declare const IMAGE_TOOL_GROUPS: readonly ImageToolGroup[];
18
+ /** The group a tool is in (the first group for a tool no group holds). */
19
+ export declare function imageToolGroup(tool: ImageEditorTool): ImageToolGroup;
20
+ export declare function imageToolLabel(tool: ImageEditorTool): string;
21
+ /** Photoshop's shortcuts, as the paint tools' keys handle them (`Shift+B` and so on). */
22
+ export declare const IMAGE_TOOL_SHORTCUTS: Partial<Record<ImageEditorTool, string>>;
23
+ /** The tool a shortcut picks (the first tool listed for a shared key), or null. */
24
+ export declare function imageToolForShortcut(key: string, shift?: boolean): ImageEditorTool | null;
25
+ /** How a canvas handles pointer gestures for a tool (the Svelte and React canvases' `mode`). */
26
+ export type ImageToolCanvasMode = 'transform' | 'crop' | 'select' | 'paint' | 'annotate';
27
+ /**
28
+ * The canvas mode a tool works in. Move picks and transforms layers; shapes, type and the
29
+ * annotations draw in `annotate`.
30
+ */
31
+ export declare function imageToolCanvasMode(tool: ImageEditorTool): ImageToolCanvasMode;
32
+ /** The `layerPanels` panel holding a tool's options, or null when it has none. */
33
+ export declare function imageToolPanel(tool: ImageEditorTool): string | null;
34
+ /** The first tool whose options are in a panel (in toolbar order), or null. */
35
+ export declare function imagePanelTool(panel: string): ImageEditorTool | null;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The image editor's tools as a toolbar shows them: grouped like Photoshop's (a slot per group, the
3
+ * rest of the group in a flyout), with labels and shortcuts, the canvas mode each tool draws in and
4
+ * the layer panel (`layerPanels`) holding its options. Hosts build their toolbar from these instead
5
+ * of listing tools themselves, so new tools show up without host changes.
6
+ */
7
+ import type { ImageEditorTool } from './types.js';
8
+ export interface ImageToolGroup {
9
+ id: string;
10
+ label: string;
11
+ tools: readonly ImageEditorTool[];
12
+ }
13
+ /**
14
+ * Every tool a toolbar offers, in toolbar order. The `text` and `watermark` annotations are left
15
+ * out: they are added through the API (`addAnnotation`), not drawn.
16
+ */
17
+ export declare const IMAGE_TOOL_GROUPS: readonly ImageToolGroup[];
18
+ /** The group a tool is in (the first group for a tool no group holds). */
19
+ export declare function imageToolGroup(tool: ImageEditorTool): ImageToolGroup;
20
+ export declare function imageToolLabel(tool: ImageEditorTool): string;
21
+ /** Photoshop's shortcuts, as the paint tools' keys handle them (`Shift+B` and so on). */
22
+ export declare const IMAGE_TOOL_SHORTCUTS: Partial<Record<ImageEditorTool, string>>;
23
+ /** The tool a shortcut picks (the first tool listed for a shared key), or null. */
24
+ export declare function imageToolForShortcut(key: string, shift?: boolean): ImageEditorTool | null;
25
+ /** How a canvas handles pointer gestures for a tool (the Svelte and React canvases' `mode`). */
26
+ export type ImageToolCanvasMode = 'transform' | 'crop' | 'select' | 'paint' | 'annotate';
27
+ /**
28
+ * The canvas mode a tool works in. Move picks and transforms layers; shapes, type and the
29
+ * annotations draw in `annotate`.
30
+ */
31
+ export declare function imageToolCanvasMode(tool: ImageEditorTool): ImageToolCanvasMode;
32
+ /** The `layerPanels` panel holding a tool's options, or null when it has none. */
33
+ export declare function imageToolPanel(tool: ImageEditorTool): string | null;
34
+ /** The first tool whose options are in a panel (in toolbar order), or null. */
35
+ export declare function imagePanelTool(panel: string): ImageEditorTool | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.193.0",
3
+ "version": "0.195.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -252,7 +252,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
252
252
  | `select_rect` | Rectangular marquee: `x`, `y`, `width`, `height` (natural px), `mode` (replace, add, subtract, intersect), `feather` px, `antiAlias`. Returns the summary. |
253
253
  | `select_ellipse` | Elliptical marquee inscribed in `x`, `y`, `width`, `height`; same options. A feathered ellipse makes a vignette. Returns the summary. |
254
254
  | `select_line` | Single row or column marquee: `orientation` `row` (at y) or `column` (at x), `position` in natural px, `mode`. A 1 px line across the image. Returns the summary. |
255
- | `select_polygon` | The lasso: `points` (at least 3 `{x, y}` along the outline, closed automatically), `mode`, `feather`, `antiAlias`. Trace a subject's contour, not its box. Returns the summary. |
255
+ | `select_polygon` | The lasso: `points` (at least 3 `{x, y}` along the outline, closed automatically), `mode`, `feather`, `antiAlias`; `snapToEdges: {width, contrast}` makes it the Magnetic Lasso (points snap to edges, the outline follows them). Trace a subject's contour, not its box. Returns the summary (plus `outlinePoints` when snapped). |
256
256
  | `magic_wand` | Select similar colour from `x`, `y`: `tolerance` 0..255 (default 32), `contiguous` (default true), `distance` `rgb` or `perceptual`, `sampleMerged`, `sample` (`composite` or `active-layer`), `mode`, `feather`. For flat backdrops and solid colours. Returns the summary. |
257
257
  | `select_color_range` | Color Range, soft: `samples` (`[{color, x?, y?}]`) or a `preset` (reds … magentas, highlights, midtones, shadows, skinTones); `fuzziness` (samples 0..200, default 40; tone presets' falloff, default 20), `localized` px radius around positioned samples, `invert`, `sample`, `mode`, `feather`. Returns the summary. |
258
258
  | `load_selection_from_image` | Load a mask image (`image` data URL or URL; white = selected) by `channel` (luminance, alpha, red, green, blue), with `threshold`, `invert`, `mode`. For AI or saved mattes; then `refine_selection`. Returns the summary. |
@@ -96,7 +96,8 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
96
96
  | Subject | Method |
97
97
  | --- | --- |
98
98
  | Geometric areas: a frame, a screen, a band | `select_rect` / `select_ellipse`, with `feather` for soft transitions |
99
- | A subject with a clear outline (product, person, animal) | `select_subject` if the host has a model; else `select_polygon` along the contour (20–60 points), then `refine_selection` |
99
+ | A subject with a clear outline (product, person, animal) | `select_subject` if the host has a model; else `select_polygon` with `snapToEdges` (12–30 points within `width` of the edge), then `refine_selection` |
100
+ | A low-contrast or busy outline the edges don't follow | `select_polygon` without `snapToEdges`, along the contour (20–60 points) |
100
101
  | Flat backdrop or solid colour (studio white, green screen, sky without clouds) | `magic_wand` on the backdrop (`tolerance` 20–40; `distance: "perceptual"` for gradients), then `invert_selection` |
101
102
  | A colour spread across the image (all the reds, a dress) | `select_color_range` with `samples` (several picks) or a hue `preset`; `localized` to keep one area |
102
103
  | Tones (shadows, highlights, a luminosity mask) | `select_color_range` with `preset` `shadows` / `midtones` / `highlights` |
@@ -155,6 +156,10 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
155
156
  30–60), check `percent`, and grow with `mode: "add"`.
156
157
  - **Box instead of outline:** `select_rect` around a subject masks its background too. Trace
157
158
  the contour.
159
+ - **Magnetic outline jumps to the wrong edge:** `snapToEdges` follows the strongest edge near
160
+ each point, which can be a shadow or a texture. Put points within `width` of the edge you
161
+ want, add points where it turns or runs past another edge, raise `contrast` on a busy
162
+ background, and drop `snapToEdges` where the edge is too faint.
158
163
  - **Over-feathering hard objects:** a 10 px feather on a product looks like a glow. Keep hard
159
164
  edges at 0–2 px and refine soft ones instead.
160
165
  - **Halo:** the old background shows as a light or dark fringe. `contract` 1–2 px or
@@ -183,7 +188,7 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
183
188
  - `get_selection`: empty, bounds, area and percent after every step
184
189
  - `select_all`, `deselect`, `invert_selection`: whole-image selections
185
190
  - `select_rect`, `select_ellipse`: marquees
186
- - `select_polygon`: the lasso, along a contour
191
+ - `select_polygon`: the lasso, along a contour; with `snapToEdges`, the Magnetic Lasso
187
192
  - `select_line`: a single row or column
188
193
  - `select_layer_transparency`: a layer's own pixels
189
194
  - `grow_selection`, `select_similar`: add similar colour, connected or anywhere