@thi.ng/rasterize 0.1.1 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -1,19 +1,50 @@
1
1
  # Change Log
2
2
 
3
+ - **Last updated**: 2021-11-21T17:09:28Z
4
+ - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
+
3
6
  All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
7
+ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelines.
8
+
9
+ **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
+ and/or version bumps of transitive dependencies.
11
+
12
+ ## [0.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/rasterize@0.3.0) (2021-11-17)
5
13
 
6
- ## [0.1.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/rasterize@0.1.0...@thi.ng/rasterize@0.1.1) (2021-11-04)
14
+ #### 🚀 Features
7
15
 
8
- **Note:** Version bump only for package @thi.ng/rasterize
16
+ - Using workspaces for local tools ([bf7a404](https://github.com/thi-ng/umbrella/commit/bf7a404))
17
+ Improving the overall build ergonomics
18
+ - introduced a tools workspaces
19
+ - imported it in all needed packages/examples
20
+ - inclusive project root
9
21
 
22
+ #### ♻️ Refactoring
10
23
 
24
+ - testrunner to binary ([4ebbbb2](https://github.com/thi-ng/umbrella/commit/4ebbbb2))
25
+ this commit reverts (partly) changes made in:
26
+ ef346d7a8753590dc9094108a3d861a8dbd5dd2c
27
+ overall purpose is better testament ergonomics:
28
+ instead of having to pass NODE_OPTIONS with every invocation
29
+ having a binary to handle this for us.
11
30
 
31
+ ## [0.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/rasterize@0.2.0) (2021-11-10)
12
32
 
33
+ #### 🚀 Features
13
34
 
14
- # 0.1.0 (2021-11-03)
35
+ - major update/additions ([e6f7fb0](https://github.com/thi-ng/umbrella/commit/e6f7fb0))
36
+ - add new shapes (polyline, polygon)
37
+ - add "shader" function support for all draw fns
38
+ - add shader functions
39
+ - rename drawLineWith() => traceLine()
40
+ - update to new IGrid2D impls ([71ac0ca](https://github.com/thi-ng/umbrella/commit/71ac0ca))
41
+ - add floodFillWith() for custom fill content/procedures
42
+ - update/fix rect()
43
+ - optimize __draw2D() for primitive values
44
+ - add/update deps
15
45
 
46
+ ## [0.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/rasterize@0.1.0) (2021-11-03)
16
47
 
17
- ### Features
48
+ #### 🚀 Features
18
49
 
19
- * **rasterize:** import as new pkg ([585eb8d](https://github.com/thi-ng/umbrella/commit/585eb8d360f0c8603c5890dedf221af3afb5584f))
50
+ - import as new pkg ([585eb8d](https://github.com/thi-ng/umbrella/commit/585eb8d))
package/README.md CHANGED
@@ -12,8 +12,10 @@ This project is part of the
12
12
  - [About](#about)
13
13
  - [Circle](#circle)
14
14
  - [Line](#line)
15
+ - [Polygon / polyline](#polygon--polyline)
15
16
  - [Rect](#rect)
16
17
  - [Flood fill](#flood-fill)
18
+ - [Custom shaders](#custom-shaders)
17
19
  - [Status](#status)
18
20
  - [Related packages](#related-packages)
19
21
  - [Installation](#installation)
@@ -27,40 +29,94 @@ This project is part of the
27
29
  2D shape drawing & rasterization.
28
30
 
29
31
  The functions in this package can be used with any
30
- [`IGrid2D`](https://docs.thi.ng/umbrella/api/interfaces/igrid2d.html) compatible
32
+ [`IGrid2D`](https://docs.thi.ng/umbrella/api/interfaces/IGrid2D.html) compatible
31
33
  grid/image type (e.g. those provided by
32
34
  [@thi.ng/pixel](https://github.com/thi-ng/umbrella/tree/develop/packages/pixel)
33
35
  or
34
36
  [@thi.ng/text-canvas](https://github.com/thi-ng/umbrella/tree/develop/packages/text-canvas)).
35
37
 
36
- Currently the following functions are available:
38
+ Currently the following functions are available. All of them support [custom
39
+ shader-like](#custom-shaders) functions to produce "pixel" values.
37
40
 
38
41
  ### Circle
39
42
 
40
- Filled or outline implementation of [Bresenham's circle
41
- algorithm](https://en.wikipedia.org/wiki/Midpoint_circle_algorithm). A clipping
42
- check is pre-applied to see if the circle lies entirely outside the target grid.
43
+ - [`drawCircle()`](https://docs.thi.ng/umbrella/rasterize/modules.html#drawCircle):
44
+ Filled or outline implementation of [Bresenham's circle
45
+ algorithm](https://en.wikipedia.org/wiki/Midpoint_circle_algorithm), with
46
+ clipping.
43
47
 
44
48
  ### Line
45
49
 
46
- Implementation of [Bresenham's line
47
- algorithm](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm) with
48
- pre-applied [Liang-Barsky
49
- clipping](https://en.wikipedia.org/wiki/Liang%E2%80%93Barsky_algorithm). The
50
- higher-order function
51
- [`drawLineWith()`](https://docs.thi.ng/umbrella/rasterize/modules.html#drawLineWith)
52
- can be used to apply custom brushes to trace the line.
50
+ - [`drawLine()`](https://docs.thi.ng/umbrella/rasterize/modules.html#drawLine):
51
+ Implementation of [Bresenham's line
52
+ algorithm](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm) with
53
+ pre-applied [Liang-Barsky
54
+ clipping](https://en.wikipedia.org/wiki/Liang%E2%80%93Barsky_algorithm)
55
+ - [`traceLine()`](https://docs.thi.ng/umbrella/rasterize/modules.html#traceLine):
56
+ Apply custom functions to trace the line
57
+
58
+ ### Polygon / polyline
59
+
60
+ Filled or outline drawing of polygons (without holes):
61
+
62
+ - [`drawPolyline()`](https://docs.thi.ng/umbrella/rasterize/modules.html#drawPolyline)
63
+ - [`fillPoly()`](https://docs.thi.ng/umbrella/rasterize/modules.html#fillPoly)
53
64
 
54
65
  ### Rect
55
66
 
56
- Filled or outline implementation with pre-applied clipping against the target grid.
67
+ - [`drawRect()`](https://docs.thi.ng/umbrella/rasterize/modules.html#drawRect):
68
+ Filled or outline implementation with pre-applied clipping against the target
69
+ grid.
57
70
 
58
71
  ### Flood fill
59
72
 
60
- Fills grid in the connected region around `x,y` with given value or pattern. See
61
- [`floodFill()` in the @thi.ng/grid-iterators
62
- package](https://docs.thi.ng/umbrella/grid-iterators/modules.html#floodFill) for
63
- further details.
73
+ - [`floodFill()`](https://docs.thi.ng/umbrella/rasterize/modules.html#floodFill):
74
+ Fills grid in the connected region around `x,y` with given value or shader
75
+
76
+ Also see corresponding function in
77
+ [@thi.ng/grid-iterators](https://docs.thi.ng/umbrella/grid-iterators/modules.html#floodFill).
78
+
79
+ ## Custom shaders
80
+
81
+ Conceptually similar, but **not** to be equaled with actual WebGL fragement
82
+ shaders, many functions in this package support shader-like functions to produce
83
+ per-pixel fill/color values for each individual pixel processed. These simple
84
+ functions take an `x` and `y` arg (in grid-space, **not** normalized!) and
85
+ produce a fill value for that location. A pixel is processed at most once per
86
+ draw call.
87
+
88
+ The following shader functions are provided:
89
+
90
+ - [`defPattern()`](https://docs.thi.ng/umbrella/rasterize/modules.html#defPattern):
91
+ pattern fill (must be same format as target grid)
92
+ - [`defStripes()`](https://docs.thi.ng/umbrella/rasterize/modules.html#defStripes):
93
+ procedural stripes (configurable)
94
+ - [`defNoise()`](https://docs.thi.ng/umbrella/rasterize/modules.html#defNoise):
95
+ random noise pattern (configurable)
96
+
97
+ As an example, here's a simple custom UV gradient shader for drawing into a
98
+ [float RGBA](https://docs.thi.ng/umbrella/pixel/modules.html#floatBuffer)
99
+ buffer:
100
+
101
+ ```ts
102
+ import type { Shader2D } from "@thi.ng/rasterize";
103
+ import { floatBuffer } from "@thi.ng/pixel";
104
+ import { drawCircle } from "@thi.ng/rasterize";
105
+
106
+ // custom gradient shader
107
+ const defUVGradient = (width: number, height: number): Shader2D<number[]> =>
108
+ (x, y) => [x/width, y/height, 0.5, 1];
109
+
110
+ const W = 256;
111
+
112
+ // create float RGBA pixel buffer
113
+ const img = floatBuffer(W, W);
114
+
115
+ // draw filled circle using gradient shader
116
+ drawCircle(img, W/2, W/2, W/2 - 4, defUVGradient(W, W), true);
117
+ ```
118
+
119
+ ![result image: circle with gradient fill](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/rasterize/uv-circle.png)
64
120
 
65
121
  ### Status
66
122
 
@@ -97,12 +153,15 @@ node --experimental-repl-await
97
153
  > const rasterize = await import("@thi.ng/rasterize");
98
154
  ```
99
155
 
100
- Package sizes (gzipped, pre-treeshake): ESM: 669 bytes
156
+ Package sizes (gzipped, pre-treeshake): ESM: 1.45 KB
101
157
 
102
158
  ## Dependencies
103
159
 
104
160
  - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api)
161
+ - [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks)
162
+ - [@thi.ng/equiv](https://github.com/thi-ng/umbrella/tree/develop/packages/equiv)
105
163
  - [@thi.ng/grid-iterators](https://github.com/thi-ng/umbrella/tree/develop/packages/grid-iterators)
164
+ - [@thi.ng/random](https://github.com/thi-ng/umbrella/tree/develop/packages/random)
106
165
  - [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/develop/packages/transducers)
107
166
 
108
167
  ## API
package/api.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { Fn2 } from "@thi.ng/api";
2
+ export declare type Shader2D<T> = Fn2<number, number, T>;
3
+ //# sourceMappingURL=api.d.ts.map
package/api.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/checks.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  import type { IGrid2D } from "@thi.ng/api";
2
- export declare const isInBounds2D: ({ width, height }: IGrid2D<any, any>, x: number, y: number) => boolean;
2
+ import type { Shader2D } from "./api.js";
3
+ export declare const isInBounds2D: ({ size }: IGrid2D<any, any>, x: number, y: number) => boolean;
4
+ export declare const ensureShader2D: <T>(val: T | Shader2D<T>) => Shader2D<T>;
3
5
  //# sourceMappingURL=checks.d.ts.map
package/checks.js CHANGED
@@ -1 +1,3 @@
1
- export const isInBounds2D = ({ width, height }, x, y) => x >= 0 && x < width && y >= 0 && y < height;
1
+ import { isFunction } from "@thi.ng/checks/is-function";
2
+ export const isInBounds2D = ({ size }, x, y) => x >= 0 && x < size[0] && y >= 0 && y < size[1];
3
+ export const ensureShader2D = (val) => isFunction(val) ? val : () => val;
package/circle.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type { IGrid2D, TypedArray } from "@thi.ng/api";
2
- export declare const drawCircle: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, x: number, y: number, r: number, val: P, fill?: boolean) => IGrid2D<T, P>;
2
+ import type { Shader2D } from "./api.js";
3
+ export declare const drawCircle: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, x: number, y: number, r: number, val: P | Shader2D<P>, fill?: boolean) => IGrid2D<T, P>;
3
4
  //# sourceMappingURL=circle.d.ts.map
package/circle.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { circleClipped } from "@thi.ng/grid-iterators/circle";
2
- import { draw2D } from "./draw.js";
3
- export const drawCircle = (grid, x, y, r, val, fill = false) => draw2D(grid, val, circleClipped(x, y, r, 0, 0, grid.width, grid.height, fill));
2
+ import { __draw2D } from "./draw.js";
3
+ export const drawCircle = (grid, x, y, r, val, fill = false) => __draw2D(circleClipped(x, y, r, 0, 0, grid.size[0], grid.size[1], fill), grid, val);
package/draw.d.ts CHANGED
@@ -1,4 +1,9 @@
1
1
  import type { IGrid2D, Nullable, TypedArray } from "@thi.ng/api";
2
+ import type { Shader2D } from "./api.js";
2
3
  /** @internal */
3
- export declare const draw2D: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, val: P, pts: Nullable<Iterable<number[]>>) => IGrid2D<T, P>;
4
+ export declare const __draw2D: <T extends any[] | TypedArray, P>(pts: Nullable<Iterable<number[]>>, grid: IGrid2D<T, P>, val: P | Shader2D<P>) => IGrid2D<T, P>;
5
+ /** @internal */
6
+ export declare const __drawSolid2D: <T extends any[] | TypedArray, P>(pts: Nullable<Iterable<number[]>>, grid: IGrid2D<T, P>, val: P) => IGrid2D<T, P>;
7
+ /** @internal */
8
+ export declare const __drawShader2D: <T extends any[] | TypedArray, P>(pts: Nullable<Iterable<number[]>>, grid: IGrid2D<T, P>, shader: Shader2D<P>) => IGrid2D<T, P>;
4
9
  //# sourceMappingURL=draw.d.ts.map
package/draw.js CHANGED
@@ -1,9 +1,40 @@
1
+ import { isFunction } from "@thi.ng/checks/is-function";
2
+ import { isPrimitive } from "@thi.ng/checks/is-primitive";
1
3
  /** @internal */
2
- export const draw2D = (grid, val, pts) => {
3
- if (pts) {
4
- const { data, stride, rowStride } = grid;
5
- for (let p of pts)
6
- data[p[0] * stride + p[1] * rowStride] = val;
4
+ export const __draw2D = (pts, grid, val) => isFunction(val)
5
+ ? __drawShader2D(pts, grid, val)
6
+ : __drawSolid2D(pts, grid, val);
7
+ /** @internal */
8
+ export const __drawSolid2D = (pts, grid, val) => {
9
+ if (!pts)
10
+ return grid;
11
+ if (isPrimitive(val)) {
12
+ const { data, offset, stride: [sx, sy], } = grid;
13
+ for (let p of pts) {
14
+ data[offset + p[0] * sx + p[1] * sy] = val;
15
+ }
16
+ }
17
+ else {
18
+ for (let p of pts) {
19
+ grid.setAtUnsafe(p[0], p[1], val);
20
+ }
21
+ }
22
+ return grid;
23
+ };
24
+ /** @internal */
25
+ export const __drawShader2D = (pts, grid, shader) => {
26
+ if (!pts)
27
+ return grid;
28
+ if (isPrimitive(grid.getAtUnsafe(0, 0))) {
29
+ const { data, offset, stride: [sx, sy], } = grid;
30
+ for (let { 0: x, 1: y } of pts) {
31
+ data[offset + x * sx + y * sy] = shader(x, y);
32
+ }
33
+ }
34
+ else {
35
+ for (let { 0: x, 1: y } of pts) {
36
+ grid.setAtUnsafe(x, y, shader(x, y));
37
+ }
7
38
  }
8
39
  return grid;
9
40
  };
package/flood-fill.d.ts CHANGED
@@ -1,23 +1,14 @@
1
1
  import type { IGrid2D, TypedArray } from "@thi.ng/api";
2
+ import type { Shader2D } from "./api.js";
2
3
  /**
3
- * Fills pixel in the connected region around `x,y` with given color. Returns
4
- * pixel buffer.
5
- *
6
- * @param img
7
- * @param x
8
- * @param y
9
- * @param val
10
- */
11
- export declare const floodFillSolid: <T extends any[] | TypedArray, P>(img: IGrid2D<T, P>, x: number, y: number, val: P) => IGrid2D<T, P>;
12
- /**
13
- * Fills pixel in the connected region around `x,y` with the pattern defined by
14
- * given `pattern` image (must be in same format as `img`). Returns updated
15
- * pixel buffer.
4
+ * Fills cells in the connected region around `x,y` with given value or shader
5
+ * function. If the latter, the shader is called for each grid coordinate and
6
+ * returns a fill value. Returns updated grid.
16
7
  *
17
8
  * @param grid
18
9
  * @param x
19
10
  * @param y
20
- * @param pattern
11
+ * @param val
21
12
  */
22
- export declare const floodFillPattern: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, x: number, y: number, pattern: IGrid2D<T, P>) => IGrid2D<T, P>;
13
+ export declare const floodFill: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, x: number, y: number, val: P | Shader2D<P>) => IGrid2D<T, P>;
23
14
  //# sourceMappingURL=flood-fill.d.ts.map
package/flood-fill.js CHANGED
@@ -1,40 +1,30 @@
1
- import { floodFill } from "@thi.ng/grid-iterators/flood-fill";
1
+ import { isIterable } from "@thi.ng/checks/is-iterable";
2
+ import { isPrimitive } from "@thi.ng/checks/is-primitive";
3
+ import { equiv } from "@thi.ng/equiv";
4
+ import { floodFill as $fill } from "@thi.ng/grid-iterators/flood-fill";
2
5
  import { isInBounds2D } from "./checks.js";
3
- import { draw2D } from "./draw.js";
6
+ import { __draw2D } from "./draw.js";
4
7
  /**
5
- * Fills pixel in the connected region around `x,y` with given color. Returns
6
- * pixel buffer.
7
- *
8
- * @param img
9
- * @param x
10
- * @param y
11
- * @param val
12
- */
13
- export const floodFillSolid = (img, x, y, val) => {
14
- if (!isInBounds2D(img, x, y))
15
- return img;
16
- const { data, width, height, stride, rowStride } = img;
17
- const srcVal = img.getAtUnsafe(x, y);
18
- return draw2D(img, val, floodFill((x, y) => data[x * stride + y * rowStride] === srcVal, x, y, width, height));
19
- };
20
- /**
21
- * Fills pixel in the connected region around `x,y` with the pattern defined by
22
- * given `pattern` image (must be in same format as `img`). Returns updated
23
- * pixel buffer.
8
+ * Fills cells in the connected region around `x,y` with given value or shader
9
+ * function. If the latter, the shader is called for each grid coordinate and
10
+ * returns a fill value. Returns updated grid.
24
11
  *
25
12
  * @param grid
26
13
  * @param x
27
14
  * @param y
28
- * @param pattern
15
+ * @param val
29
16
  */
30
- export const floodFillPattern = (grid, x, y, pattern) => {
31
- if (!isInBounds2D(grid, x, y))
32
- return grid;
33
- const { data: dest, stride: ds, rowStride: drs } = grid;
34
- const { data: src, width: sw, height: sh, stride: ss, rowStride: srs, } = pattern;
35
- const srcVal = grid.getAtUnsafe(x, y);
36
- for (let [xx, yy] of floodFill((x, y) => dest[x * ds + y * drs] === srcVal, x, y, grid.width, grid.height)) {
37
- dest[xx * ds + yy * drs] = src[(xx % sw) * ss + (yy % sh) * srs];
17
+ export const floodFill = (grid, x, y, val) => isInBounds2D(grid, x, y)
18
+ ? __draw2D($fill(__pred(grid, x, y), x, y, grid.size[0], grid.size[1]), grid, val)
19
+ : grid;
20
+ const __pred = (img, x, y) => {
21
+ const { data, offset, stride: [stride, rowStride], } = img;
22
+ let srcVal = img.getAtUnsafe(x, y);
23
+ if (isPrimitive(srcVal)) {
24
+ return (x, y) => data[offset + x * stride + y * rowStride] === srcVal;
25
+ }
26
+ if (isIterable(srcVal)) {
27
+ srcVal = [...srcVal];
38
28
  }
39
- return grid;
29
+ return (x, y) => equiv(img.getAtUnsafe(x, y), srcVal);
40
30
  };
package/index.d.ts CHANGED
@@ -1,6 +1,10 @@
1
+ export * from "./api.js";
1
2
  export * from "./checks.js";
2
3
  export * from "./circle.js";
3
4
  export * from "./flood-fill.js";
4
5
  export * from "./line.js";
6
+ export * from "./poly.js";
7
+ export * from "./polyline.js";
5
8
  export * from "./rect.js";
9
+ export * from "./shader.js";
6
10
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,5 +1,9 @@
1
+ export * from "./api.js";
1
2
  export * from "./checks.js";
2
3
  export * from "./circle.js";
3
4
  export * from "./flood-fill.js";
4
5
  export * from "./line.js";
6
+ export * from "./poly.js";
7
+ export * from "./polyline.js";
5
8
  export * from "./rect.js";
9
+ export * from "./shader.js";
package/line.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Fn, IGrid2D, TypedArray } from "@thi.ng/api";
2
- export declare const drawLine: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, x1: number, y1: number, x2: number, y2: number, val: P) => IGrid2D<T, P>;
3
- export declare const drawLineWith: (fn: Fn<number[], void>, grid: IGrid2D<any, any>, x1: number, y1: number, x2: number, y2: number) => IGrid2D<any, any>;
2
+ import type { Shader2D } from "./api.js";
3
+ export declare const drawLine: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, x1: number, y1: number, x2: number, y2: number, val: P | Shader2D<P>) => IGrid2D<T, P>;
4
+ export declare const traceLine: (grid: IGrid2D<any, any>, x1: number, y1: number, x2: number, y2: number, fn: Fn<number[], void>) => IGrid2D<any, any>;
4
5
  //# sourceMappingURL=line.d.ts.map
package/line.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { lineClipped } from "@thi.ng/grid-iterators/line";
2
- import { draw2D } from "./draw.js";
3
- export const drawLine = (grid, x1, y1, x2, y2, val) => draw2D(grid, val, lineClipped(x1, y1, x2, y2, 0, 0, grid.width, grid.height));
4
- export const drawLineWith = (fn, grid, x1, y1, x2, y2) => {
5
- const pts = lineClipped(x1, y1, x2, y2, 0, 0, grid.width, grid.height);
2
+ import { __draw2D } from "./draw.js";
3
+ export const drawLine = (grid, x1, y1, x2, y2, val) => __draw2D(lineClipped(x1, y1, x2, y2, 0, 0, grid.size[0], grid.size[1]), grid, val);
4
+ export const traceLine = (grid, x1, y1, x2, y2, fn) => {
5
+ const pts = lineClipped(x1, y1, x2, y2, 0, 0, grid.size[0], grid.size[1]);
6
6
  if (pts) {
7
7
  for (let p of pts)
8
8
  fn(p);
package/package.json CHANGED
@@ -1,102 +1,122 @@
1
1
  {
2
- "name": "@thi.ng/rasterize",
3
- "version": "0.1.1",
4
- "description": "2D shape drawing & rasterization",
5
- "type": "module",
6
- "module": "./index.js",
7
- "typings": "./index.d.ts",
8
- "sideEffects": false,
9
- "repository": {
10
- "type": "git",
11
- "url": "https://github.com/thi-ng/umbrella.git"
2
+ "name": "@thi.ng/rasterize",
3
+ "version": "0.3.2",
4
+ "description": "2D shape drawing & rasterization",
5
+ "type": "module",
6
+ "module": "./index.js",
7
+ "typings": "./index.d.ts",
8
+ "sideEffects": false,
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/thi-ng/umbrella.git"
12
+ },
13
+ "homepage": "https://github.com/thi-ng/umbrella/tree/master/packages/rasterize#readme",
14
+ "funding": [
15
+ {
16
+ "type": "github",
17
+ "url": "https://github.com/sponsors/postspectacular"
12
18
  },
13
- "homepage": "https://github.com/thi-ng/umbrella/tree/master/packages/rasterize#readme",
14
- "funding": [
15
- {
16
- "type": "github",
17
- "url": "https://github.com/sponsors/postspectacular"
18
- },
19
- {
20
- "type": "patreon",
21
- "url": "https://patreon.com/thing_umbrella"
22
- }
23
- ],
24
- "author": "Karsten Schmidt <k+npm@thi.ng>",
25
- "license": "Apache-2.0",
26
- "scripts": {
27
- "build": "yarn clean && tsc --declaration",
28
- "clean": "rimraf *.js *.d.ts *.map doc",
29
- "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
30
- "doc:ae": "mkdir -p .ae/doc .ae/temp && node_modules/.bin/api-extractor run --local --verbose",
31
- "doc:readme": "yarn doc:stats && ../../scripts/node-esm ../../tools/src/readme.ts",
32
- "doc:stats": "../../scripts/node-esm ../../tools/src/module-stats.ts",
33
- "pub": "yarn build && yarn publish --access public",
34
- "test": "testament test"
19
+ {
20
+ "type": "patreon",
21
+ "url": "https://patreon.com/thing_umbrella"
22
+ }
23
+ ],
24
+ "author": "Karsten Schmidt <k+npm@thi.ng>",
25
+ "license": "Apache-2.0",
26
+ "scripts": {
27
+ "build": "yarn clean && tsc --declaration",
28
+ "clean": "rimraf '*.js' '*.d.ts' '*.map' doc",
29
+ "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
30
+ "doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
31
+ "doc:readme": "yarn doc:stats && tools:readme",
32
+ "doc:stats": "tools:module-stats",
33
+ "pub": "yarn npm publish --access public",
34
+ "test": "testament test"
35
+ },
36
+ "dependencies": {
37
+ "@thi.ng/api": "^8.3.2",
38
+ "@thi.ng/checks": "^3.1.2",
39
+ "@thi.ng/equiv": "^2.1.2",
40
+ "@thi.ng/grid-iterators": "^2.2.2",
41
+ "@thi.ng/random": "^3.2.2",
42
+ "@thi.ng/transducers": "^8.1.2"
43
+ },
44
+ "devDependencies": {
45
+ "@microsoft/api-extractor": "^7.18.19",
46
+ "@thi.ng/testament": "^0.2.2",
47
+ "rimraf": "^3.0.2",
48
+ "tools": "^0.0.1",
49
+ "typedoc": "^0.22.9",
50
+ "typescript": "^4.5.2"
51
+ },
52
+ "keywords": [
53
+ "2d",
54
+ "bitmap",
55
+ "circle",
56
+ "clipping",
57
+ "draw",
58
+ "floodfill",
59
+ "grid",
60
+ "line",
61
+ "pattern",
62
+ "shape",
63
+ "rect",
64
+ "typescript"
65
+ ],
66
+ "publishConfig": {
67
+ "access": "public"
68
+ },
69
+ "browser": {
70
+ "process": false,
71
+ "setTimeout": false
72
+ },
73
+ "engines": {
74
+ "node": ">=14"
75
+ },
76
+ "files": [
77
+ "*.js",
78
+ "*.d.ts"
79
+ ],
80
+ "exports": {
81
+ ".": {
82
+ "import": "./index.js"
35
83
  },
36
- "dependencies": {
37
- "@thi.ng/api": "^8.1.0",
38
- "@thi.ng/grid-iterators": "^2.1.1",
39
- "@thi.ng/transducers": "^8.0.7"
84
+ "./api": {
85
+ "import": "./api.js"
40
86
  },
41
- "devDependencies": {
42
- "@thi.ng/testament": "^0.1.6"
87
+ "./checks": {
88
+ "import": "./checks.js"
43
89
  },
44
- "keywords": [
45
- "2d",
46
- "bitmap",
47
- "circle",
48
- "clipping",
49
- "draw",
50
- "floodfill",
51
- "grid",
52
- "line",
53
- "pattern",
54
- "shape",
55
- "rect",
56
- "typescript"
57
- ],
58
- "publishConfig": {
59
- "access": "public"
90
+ "./circle": {
91
+ "import": "./circle.js"
60
92
  },
61
- "browser": {
62
- "process": false,
63
- "setTimeout": false
93
+ "./flood-fill": {
94
+ "import": "./flood-fill.js"
64
95
  },
65
- "engines": {
66
- "node": ">=14"
96
+ "./line": {
97
+ "import": "./line.js"
67
98
  },
68
- "files": [
69
- "*.js",
70
- "*.d.ts"
71
- ],
72
- "exports": {
73
- ".": {
74
- "import": "./index.js"
75
- },
76
- "./checks": {
77
- "import": "./checks.js"
78
- },
79
- "./circle": {
80
- "import": "./circle.js"
81
- },
82
- "./flood-fill": {
83
- "import": "./flood-fill.js"
84
- },
85
- "./line": {
86
- "import": "./line.js"
87
- },
88
- "./rect": {
89
- "import": "./rect.js"
90
- }
99
+ "./poly": {
100
+ "import": "./poly.js"
91
101
  },
92
- "thi.ng": {
93
- "related": [
94
- "grid-iterators",
95
- "pixel",
96
- "text-canvas"
97
- ],
98
- "status": "alpha",
99
- "year": 2021
102
+ "./polyline": {
103
+ "import": "./polyline.js"
100
104
  },
101
- "gitHead": "d6aca4b4edb697613ed6635b1c0b12f0bf27b1f0"
102
- }
105
+ "./rect": {
106
+ "import": "./rect.js"
107
+ },
108
+ "./shader": {
109
+ "import": "./shader.js"
110
+ }
111
+ },
112
+ "thi.ng": {
113
+ "related": [
114
+ "grid-iterators",
115
+ "pixel",
116
+ "text-canvas"
117
+ ],
118
+ "status": "alpha",
119
+ "year": 2021
120
+ },
121
+ "gitHead": "e8a7c2a40191b391cef182c2978e5a6c85987a87\n"
122
+ }
package/poly.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { IGrid2D, TypedArray } from "@thi.ng/api";
2
+ import type { Shader2D } from "./api.js";
3
+ /**
4
+ * Draws a filled polygon (simple, no holes) into given grid. Each grid cell's
5
+ * value is obtained from user supplied shader function which will be called
6
+ * with the pixel's `x,y` coords.
7
+ *
8
+ * Based on Efficient Polygon Fill Algorithm by Darel Rex Finley
9
+ * http://alienryderflex.com/polygon_fill/
10
+ *
11
+ * Bounds calculation and clipping added by Karsten Schmidt
12
+ *
13
+ * @param grid
14
+ * @param pts
15
+ * @param val
16
+ */
17
+ export declare const fillPoly: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, pts: number[][], val: P | Shader2D<P>) => void;
18
+ //# sourceMappingURL=poly.d.ts.map
package/poly.js ADDED
@@ -0,0 +1,67 @@
1
+ import { ensureShader2D } from "./checks.js";
2
+ /**
3
+ * Draws a filled polygon (simple, no holes) into given grid. Each grid cell's
4
+ * value is obtained from user supplied shader function which will be called
5
+ * with the pixel's `x,y` coords.
6
+ *
7
+ * Based on Efficient Polygon Fill Algorithm by Darel Rex Finley
8
+ * http://alienryderflex.com/polygon_fill/
9
+ *
10
+ * Bounds calculation and clipping added by Karsten Schmidt
11
+ *
12
+ * @param grid
13
+ * @param pts
14
+ * @param val
15
+ */
16
+ export const fillPoly = (grid, pts, val) => {
17
+ const numP = pts.length;
18
+ const [width, height] = grid.size;
19
+ const shader = ensureShader2D(val);
20
+ let minX = Infinity;
21
+ let minY = Infinity;
22
+ let maxX = -Infinity;
23
+ let maxY = -Infinity;
24
+ for (let i = numP; i-- > 0;) {
25
+ const { 0: x, 1: y } = pts[i];
26
+ minX = Math.min(minX, x);
27
+ maxX = Math.max(maxX, x);
28
+ minY = Math.min(minY, y);
29
+ maxY = Math.max(maxY, y);
30
+ }
31
+ minX = Math.max(minX | 0, 0);
32
+ maxX = Math.min(maxX | 0, width - 1);
33
+ minY = Math.max(minY | 0, 0);
34
+ maxY = Math.min(maxY | 0, height - 1);
35
+ if (minX >= width || maxX < 0 || minY >= height || maxY < 0)
36
+ return;
37
+ let i = 0;
38
+ let k = 0;
39
+ let j;
40
+ const isec = [];
41
+ for (let y = Math.max(0, minY); y <= maxY; y++) {
42
+ i = k = 0;
43
+ j = numP - 1;
44
+ isec.length = 0;
45
+ for (; i < numP; j = i, i++) {
46
+ const { 0: pix, 1: piy } = pts[i];
47
+ const { 0: pjx, 1: pjy } = pts[j];
48
+ if ((piy < y && pjy >= y) || (pjy < y && piy >= y)) {
49
+ isec[k++] = (pix + ((y - piy) / (pjy - piy)) * (pjx - pix)) | 0;
50
+ }
51
+ }
52
+ isec.sort((a, b) => a - b);
53
+ for (i = 0; i < k; i += 2) {
54
+ let x1 = isec[i];
55
+ if (x1 > maxX)
56
+ break;
57
+ let x2 = isec[i + 1];
58
+ if (x2 > minX) {
59
+ x1 < minX && (x1 = minX);
60
+ x2 > maxX && (x2 = maxX);
61
+ for (let x = x1; x <= x2; x++) {
62
+ grid.setAtUnsafe(x, y, shader(x, y));
63
+ }
64
+ }
65
+ }
66
+ }
67
+ };
package/polyline.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { IGrid2D, TypedArray } from "@thi.ng/api";
2
+ import type { Shader2D } from "./api.js";
3
+ export declare const drawPolyLine: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, pts: number[][], val: P | Shader2D<P>, closed?: boolean) => void;
4
+ //# sourceMappingURL=polyline.d.ts.map
package/polyline.js ADDED
@@ -0,0 +1,12 @@
1
+ import { ensureShader2D } from "./checks.js";
2
+ import { drawLine } from "./line.js";
3
+ export const drawPolyLine = (grid, pts, val, closed = false) => {
4
+ val = ensureShader2D(val);
5
+ const n = pts.length;
6
+ let [i, j] = closed ? [0, n - 1] : [1, 0];
7
+ for (; i < n; j = i, i++) {
8
+ const a = pts[j];
9
+ const b = pts[i];
10
+ drawLine(grid, a[0], a[1], b[0], b[1], val);
11
+ }
12
+ };
package/rect.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type { IGrid2D, TypedArray } from "@thi.ng/api";
2
- export declare const drawRect: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, x: number, y: number, w: number, h: number, val: P, fill?: boolean) => IGrid2D<T, P> | undefined;
2
+ import type { Shader2D } from "./api.js";
3
+ export declare const drawRect: <T extends any[] | TypedArray, P>(grid: IGrid2D<T, P>, x: number, y: number, w: number, h: number, val: P | Shader2D<P>, fill?: boolean) => IGrid2D<T, P>;
3
4
  //# sourceMappingURL=rect.d.ts.map
package/rect.js CHANGED
@@ -1,9 +1,15 @@
1
+ import { isPrimitive } from "@thi.ng/checks";
1
2
  import { hlineClipped, vlineClipped } from "@thi.ng/grid-iterators/hvline";
2
3
  import { rows2d } from "@thi.ng/grid-iterators/rows";
3
4
  import { concat } from "@thi.ng/transducers/concat";
4
- import { draw2D } from "./draw.js";
5
+ import { ensureShader2D } from "./checks.js";
6
+ import { __draw2D } from "./draw.js";
5
7
  export const drawRect = (grid, x, y, w, h, val, fill = false) => {
6
- const { data, width: iw, height: ih, stride, rowStride } = grid;
8
+ x |= 0;
9
+ y |= 0;
10
+ w |= 0;
11
+ h |= 0;
12
+ const { data, offset, size: [width, height], stride: [sx, sy], } = grid;
7
13
  if (fill) {
8
14
  if (x < 0) {
9
15
  w += x;
@@ -13,10 +19,23 @@ export const drawRect = (grid, x, y, w, h, val, fill = false) => {
13
19
  h += y;
14
20
  y = 0;
15
21
  }
16
- for (let [xx, yy] of rows2d(Math.min(w, iw - x), Math.min(h, ih - y))) {
17
- data[(x + xx) * stride + (y + yy) * rowStride] = val;
22
+ const pts = rows2d(Math.min(w, width - x), Math.min(h, height - y));
23
+ const shader = ensureShader2D(val);
24
+ if (isPrimitive(val)) {
25
+ for (let { 0: xx, 1: yy } of pts) {
26
+ xx += x;
27
+ yy += y;
28
+ data[offset + xx * sx + yy * sy] = shader(xx, yy);
29
+ }
18
30
  }
19
- return;
31
+ else {
32
+ for (let { 0: xx, 1: yy } of pts) {
33
+ xx += x;
34
+ yy += y;
35
+ grid.setAtUnsafe(xx, yy, shader(xx, yy));
36
+ }
37
+ }
38
+ return grid;
20
39
  }
21
- return draw2D(grid, val, concat(hlineClipped(x, y, w, 0, 0, iw, ih), vlineClipped(x, y + 1, h - 2, 0, 0, iw, ih), hlineClipped(x, y + h - 1, w, 0, 0, iw, ih), vlineClipped(x + w - 1, y + 1, h - 2, 0, 0, iw, ih)));
40
+ return __draw2D(concat(hlineClipped(x, y, w, 0, 0, width, height), vlineClipped(x, y + 1, h - 2, 0, 0, width, height), hlineClipped(x, y + h - 1, w, 0, 0, width, height), vlineClipped(x + w - 1, y + 1, h - 2, 0, 0, width, height)), grid, val);
22
41
  };
package/shader.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { IGrid2D, TypedArray } from "@thi.ng/api";
2
+ import type { IRandom } from "@thi.ng/random";
3
+ import type { Shader2D } from "./api.js";
4
+ export declare const defPattern: <T extends any[] | TypedArray, P>(pattern: IGrid2D<T, P>) => Shader2D<P>;
5
+ export interface StripeShaderOpts<T> {
6
+ dir: "h" | "v" | "d";
7
+ size: number;
8
+ sizeA: number;
9
+ a: T;
10
+ b: T;
11
+ }
12
+ export declare const defStripes: <T = number>({ dir, size, sizeA, a, b, }: StripeShaderOpts<T>) => Shader2D<T>;
13
+ export interface RandomShaderOpts<T> {
14
+ probability?: number;
15
+ rnd?: IRandom;
16
+ a: T;
17
+ b: T;
18
+ }
19
+ export declare const defNoise: <T = number>(opts: RandomShaderOpts<T>) => Shader2D<T>;
20
+ //# sourceMappingURL=shader.d.ts.map
package/shader.js ADDED
@@ -0,0 +1,18 @@
1
+ import { SYSTEM } from "@thi.ng/random/system";
2
+ export const defPattern = (pattern) => {
3
+ const [w, h] = pattern.size;
4
+ return (x, y) => pattern.getAtUnsafe(x % w, y % h);
5
+ };
6
+ export const defStripes = ({ dir, size, sizeA, a, b, }) => dir === "h"
7
+ ? (x) => (x % size < sizeA ? a : b)
8
+ : dir === "v"
9
+ ? (_, y) => (y % size < sizeA ? a : b)
10
+ : (x, y) => ((x + y) % size < sizeA ? a : b);
11
+ export const defNoise = (opts) => {
12
+ const { probability, rnd, a, b } = {
13
+ probability: 0.5,
14
+ rnd: SYSTEM,
15
+ ...opts,
16
+ };
17
+ return () => (rnd.float() < probability ? a : b);
18
+ };