release: WRNexusJS 0.4.0

This commit is contained in:
2026-07-27 12:42:18 +05:30
parent 8b728a3e5d
commit 30e5721e84
250 changed files with 10065 additions and 3923 deletions
+72 -12
View File
@@ -8,7 +8,11 @@ export interface RgbaImage {
export type Rgba = readonly [number, number, number, number?];
export function createImage(width: number, height: number, background: Rgba = [255, 255, 255, 255]): RgbaImage {
export function createImage(
width: number,
height: number,
background: Rgba = [255, 255, 255, 255],
): RgbaImage {
const data = new Uint8Array(width * height * 4);
const alpha = background[3] ?? 255;
for (let index = 0; index < data.length; index += 4) {
@@ -33,13 +37,28 @@ export function setPixel(image: RgbaImage, x: number, y: number, color: Rgba): v
image.data[index + 3] = 255;
}
export function fillRect(image: RgbaImage, x: number, y: number, width: number, height: number, color: Rgba): void {
export function fillRect(
image: RgbaImage,
x: number,
y: number,
width: number,
height: number,
color: Rgba,
): void {
for (let py = Math.floor(y); py < Math.ceil(y + height); py++) {
for (let px = Math.floor(x); px < Math.ceil(x + width); px++) setPixel(image, px, py, color);
}
}
export function drawLine(image: RgbaImage, x0: number, y0: number, x1: number, y1: number, color: Rgba, thickness = 1): void {
export function drawLine(
image: RgbaImage,
x0: number,
y0: number,
x1: number,
y1: number,
color: Rgba,
thickness = 1,
): void {
let x = Math.round(x0);
let y = Math.round(y0);
const targetX = Math.round(x1);
@@ -50,7 +69,14 @@ export function drawLine(image: RgbaImage, x0: number, y0: number, x1: number, y
const sy = y < targetY ? 1 : -1;
let error = dx + dy;
while (true) {
fillRect(image, x - Math.floor(thickness / 2), y - Math.floor(thickness / 2), thickness, thickness, color);
fillRect(
image,
x - Math.floor(thickness / 2),
y - Math.floor(thickness / 2),
thickness,
thickness,
color,
);
if (x === targetX && y === targetY) break;
const twice = 2 * error;
if (twice >= dy) {
@@ -64,7 +90,13 @@ export function drawLine(image: RgbaImage, x0: number, y0: number, x1: number, y
}
}
export function fillCircle(image: RgbaImage, centerX: number, centerY: number, radius: number, color: Rgba): void {
export function fillCircle(
image: RgbaImage,
centerX: number,
centerY: number,
radius: number,
color: Rgba,
): void {
const r2 = radius * radius;
for (let y = Math.floor(centerY - radius); y <= Math.ceil(centerY + radius); y++) {
for (let x = Math.floor(centerX - radius); x <= Math.ceil(centerX + radius); x++) {
@@ -75,7 +107,11 @@ export function fillCircle(image: RgbaImage, centerX: number, centerY: number, r
}
}
function pointInPolygon(x: number, y: number, points: readonly (readonly [number, number])[]): boolean {
function pointInPolygon(
x: number,
y: number,
points: readonly (readonly [number, number])[],
): boolean {
let inside = false;
for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
const xi = points[i]![0];
@@ -88,13 +124,18 @@ function pointInPolygon(x: number, y: number, points: readonly (readonly [number
return inside;
}
export function fillPolygon(image: RgbaImage, points: readonly (readonly [number, number])[], color: Rgba): void {
export function fillPolygon(
image: RgbaImage,
points: readonly (readonly [number, number])[],
color: Rgba,
): void {
const minX = Math.floor(Math.min(...points.map((point) => point[0])));
const maxX = Math.ceil(Math.max(...points.map((point) => point[0])));
const minY = Math.floor(Math.min(...points.map((point) => point[1])));
const maxY = Math.ceil(Math.max(...points.map((point) => point[1])));
for (let y = minY; y <= maxY; y++) {
for (let x = minX; x <= maxX; x++) if (pointInPolygon(x + 0.5, y + 0.5, points)) setPixel(image, x, y, color);
for (let x = minX; x <= maxX; x++)
if (pointInPolygon(x + 0.5, y + 0.5, points)) setPixel(image, x, y, color);
}
}
@@ -134,7 +175,15 @@ export function drawText(
let cursor = options.x;
for (let index = 0; index < text.length; index++) {
const jitter = options.jitter?.(index) ?? { x: 0, y: 0, shear: 0 };
drawGlyph(image, text[index]!, cursor + jitter.x, options.y + jitter.y, options.scale, options.color, jitter.shear);
drawGlyph(
image,
text[index]!,
cursor + jitter.x,
options.y + jitter.y,
options.scale,
options.color,
jitter.shear,
);
cursor += options.scale * 5 + spacing;
}
}
@@ -170,7 +219,12 @@ function adler32(bytes: Uint8Array): number {
}
function u32(value: number): Uint8Array {
return Uint8Array.of((value >>> 24) & 255, (value >>> 16) & 255, (value >>> 8) & 255, value & 255);
return Uint8Array.of(
(value >>> 24) & 255,
(value >>> 16) & 255,
(value >>> 8) & 255,
value & 255,
);
}
function concat(parts: readonly Uint8Array[]): Uint8Array {
@@ -195,9 +249,15 @@ function deflateStored(data: Uint8Array): Uint8Array {
const size = Math.min(65535, data.length - offset);
const final = offset + size >= data.length;
const length = size;
const inverse = (~length) & 0xffff;
const inverse = ~length & 0xffff;
blocks.push(
Uint8Array.of(final ? 1 : 0, length & 255, (length >>> 8) & 255, inverse & 255, (inverse >>> 8) & 255),
Uint8Array.of(
final ? 1 : 0,
length & 255,
(length >>> 8) & 255,
inverse & 255,
(inverse >>> 8) & 255,
),
data.slice(offset, offset + size),
);
}