From 56e8373cc1c8313354bebbc97ab7b58cacb63eb1 Mon Sep 17 00:00:00 2001 From: Karsten Schmidt Date: Fri, 8 Jul 2022 17:25:03 +0200 Subject: [PATCH] feat(arrays): add blit1d/2d() functions --- packages/arrays/package.json | 3 + packages/arrays/src/blit.ts | 118 +++++++++++++++++++++++++++++++++++ packages/arrays/src/index.ts | 1 + 3 files changed, 122 insertions(+) create mode 100644 packages/arrays/src/blit.ts diff --git a/packages/arrays/package.json b/packages/arrays/package.json index 9a8b70f4d1..bece6f2191 100644 --- a/packages/arrays/package.json +++ b/packages/arrays/package.json @@ -85,6 +85,9 @@ "./bisect": { "default": "./bisect.js" }, + "./blit": { + "default": "./blit.js" + }, "./ends-with": { "default": "./ends-with.js" }, diff --git a/packages/arrays/src/blit.ts b/packages/arrays/src/blit.ts new file mode 100644 index 0000000000..22bebd77eb --- /dev/null +++ b/packages/arrays/src/blit.ts @@ -0,0 +1,118 @@ +import type { TypedArray } from "@thi.ng/api"; + +/** + * Selectively copies all non-`mask` values from `src` into `dest` starting from + * destination position `dx`. Returns `dest`. + * + * @remarks + * Where `src` values are the same as `mask`, the corresponding `dest` values + * will be left unchanged. Performs region clipping, i.e. `dx` can be outside + * the [0..dest.length) interval. + * + * @example + * ```ts + * blit1d( + * // dest array + * [1, 1, 1, 1, 1, 1, 1, 1, 1], + * // paste from index 2 + * 2, + * // source array + * [2, 3, 2, 3, 2], + * // mask value + * 3 + * ) + * //[1, 1, 2, 1, 2, 1, 2, 1, 1] + * ``` + * + * @param dest + * @param src + * @param dx + * @param mask + */ +export function blit1d( + dest: T, + dx: number, + src: ArrayLike, + mask: number +): T; +export function blit1d( + dest: T[], + dx: number, + src: ArrayLike, + mask: T +): T[]; +export function blit1d(dest: any[], x: number, src: ArrayLike, mask: any) { + const [sx, sw, dx, dw] = __clip(0, src.length, x, dest.length); + if (sw < 1 || dx >= dw) return dest; + for (let i = 0; i < sw; i++) { + const val = src[sx + i]; + val !== mask && (dest[dx + i] = val); + } + return dest; +} + +/** + * 2D version of {@link blit1d} (also with region clipping). Positions and sizes + * are given as 2D vectors. + * + * @param dest + * @param dpos + * @param dsize + * @param src + * @param ssize + * @param mask + */ +export function blit2d( + dest: T, + dpos: ArrayLike, + dsize: ArrayLike, + src: ArrayLike, + ssize: ArrayLike, + mask: number +): T; +export function blit2d( + dest: T[], + dpos: ArrayLike, + dsize: ArrayLike, + src: ArrayLike, + ssize: ArrayLike, + mask: T +): T[]; +export function blit2d( + dest: any[], + dpos: ArrayLike, + dsize: ArrayLike, + src: ArrayLike, + ssize: ArrayLike, + mask: any +) { + const [sx, sw, dx, dw] = __clip(0, ssize[0], dpos[0], dsize[0]); + const [sy, sh, dy, dh] = __clip(0, ssize[1], dpos[1], dsize[1]); + if (sw < 1 || sh < 1 || dx >= dw || dy >= dh) return dest; + const sstride = ssize[0]; + const dstride = dsize[0]; + for (let y = 0; y < sh; y++) { + for ( + let x = 0, + soff = (sy + y) * sstride + sx, + doff = (dy + y) * dstride + dx; + x < sw; + x++ + ) { + const val = src[soff + x]; + val !== mask && (dest[doff + x] = val); + } + } + return dest; +} + +const __clip = (sx: number, sw: number, dx: number, dw: number) => { + if (dx < 0) { + sx -= dx; + sw += dx; + dx = 0; + } else if (dx + sw > dw) { + sw = dw - dx; + } + return [sx, sw, dx, dw]; +}; diff --git a/packages/arrays/src/index.ts b/packages/arrays/src/index.ts index 7818fea037..54babb82bf 100644 --- a/packages/arrays/src/index.ts +++ b/packages/arrays/src/index.ts @@ -2,6 +2,7 @@ export * from "./api.js"; export * from "./arg-sort.js"; export * from "./binary-search.js"; export * from "./bisect.js"; +export * from "./blit.js"; export * from "./ends-with.js"; export * from "./ensure-array.js"; export * from "./ensure-iterable.js";