@micahdb/tscon 0.0.0-stage → 0.1.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/dist/glyphs.js ADDED
@@ -0,0 +1,449 @@
1
+ import { Colour } from "./colour.js";
2
+ import { charCodeInCp437 } from "./cp437.js";
3
+ /**
4
+ * Number of columns a tab advances the cursor by.
5
+ *
6
+ * Tabs in text processed by {@link textToLines} and {@link textGlyphs}
7
+ * advance the column position to the next multiple of this width.
8
+ */
9
+ export const TAB_WIDTH = 8;
10
+ function finalSpaceIdx(text, start) {
11
+ // skip remaining white space
12
+ while (start < text.length &&
13
+ (text[start] === " " || text[start] === "\t")) {
14
+ start++;
15
+ }
16
+ // just incase there was a newline
17
+ if (start < text.length && text[start] === "\n") {
18
+ start++;
19
+ }
20
+ // reached the end of text
21
+ if (start >= text.length) {
22
+ return -1;
23
+ }
24
+ // return index of last space
25
+ return start - 1;
26
+ }
27
+ /**
28
+ * Layout `text` into lines no wider than `cols` columns.
29
+ *
30
+ * This is the line-breaking pass used by {@link textGlyphs}. It interprets
31
+ * the tscon text escape sequences so they are not counted against the
32
+ * column budget, and returns the raw visible content of each line as a
33
+ * string (still containing the escape codes for later processing).
34
+ *
35
+ * The following escape sequences are recognised and treated as taking up
36
+ * zero columns:
37
+ *
38
+ * - `\\` -- a literal backslash (collapses to one displayed char)
39
+ * - `\aX` and `\aX{options}` -- an anchor in group `X` (0-9), with an
40
+ * optional `{...}` options string
41
+ * - `\fX` / `\FX` -- set foreground to colour `X` (dark / bright)
42
+ * - `\bX` / `\BX` -- set background to colour `X` (dark / bright)
43
+ *
44
+ * Newlines always break the line. A backslash immediately followed by a
45
+ * newline is a line continuation: the two characters are removed and the
46
+ * following content joins the current line. Tabs advance to the next
47
+ * multiple of {@link TAB_WIDTH}.
48
+ *
49
+ * When `wrap` is `true`, lines longer than `cols` are wrapped at the most
50
+ * recent space (or broken mid-word if a single word fills a whole line).
51
+ * When `wrap` is `false`, lines longer than `cols` are truncated at the
52
+ * end of the line and the rest of that logical line is discarded.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { textToLines } from "@micahdb/tscon";
57
+ *
58
+ * textToLines("hello world", 80, true); // ["hello world"]
59
+ * ```
60
+ *
61
+ * @param text The text to lay out, possibly with escape sequences.
62
+ * @param cols Maximum number of visible columns per line. If `<= 0`, an
63
+ * empty array is returned.
64
+ * @param wrap Whether to word-wrap long lines (`true`) or truncate them
65
+ * (`false`).
66
+ * @returns The laid-out visible lines, with escape sequences preserved.
67
+ */
68
+ export function textToLines(text, cols, wrap) {
69
+ const lines = [];
70
+ if (cols <= 0) {
71
+ return lines; // empty
72
+ }
73
+ let start = 0;
74
+ let running_cols = 0;
75
+ let last_space = -1;
76
+ // "text that breaks \
77
+ // a line into two"
78
+ // ->
79
+ // "text that breaks a line into two"
80
+ text = text.replaceAll("\\\n", "");
81
+ for (let i = 0; i < text.length; i++) {
82
+ const c = text[i];
83
+ let escape = c === "\\";
84
+ // single back slash
85
+ if (escape && i + 1 < text.length && text[i + 1] === "\\") {
86
+ i++; // skip one character
87
+ escape = false;
88
+ }
89
+ // anchor escape sequence
90
+ // \aX : anchor in group X, no options
91
+ // \aX{options string} : anchor in group X, with options
92
+ if (escape && i + 2 < text.length && text[i + 1] === "a" &&
93
+ !isNaN(Number(text[i + 2]))) {
94
+ let skip_chars = 2;
95
+ // options string
96
+ if (i + 3 < text.length && text[i + 3] === "{") {
97
+ const closing_brace = text.indexOf("}", i + 3);
98
+ if (closing_brace > i + 3) {
99
+ // skip right until the closing brace
100
+ skip_chars = closing_brace - i;
101
+ } // else, no matching closing brace, ignore "{"
102
+ }
103
+ i += skip_chars;
104
+ continue; // i++
105
+ }
106
+ // colours escape sequence
107
+ // \fX : set foreground to X, dark
108
+ // \FX : set foreground to X, bright
109
+ // \bX : set background to X, dark
110
+ // \BX : set background to X, bright
111
+ if (escape &&
112
+ i + 2 < text.length &&
113
+ "fFbB".includes(text[i + 1]) &&
114
+ !isNaN(Number(text[i + 2]))) {
115
+ // skip the 3 char combo
116
+ i += 2;
117
+ continue; // i++
118
+ }
119
+ // newline
120
+ if (c === "\n") {
121
+ lines.push(text.slice(start, i).trimEnd());
122
+ start = i + 1;
123
+ running_cols = 0;
124
+ last_space = -1;
125
+ continue;
126
+ }
127
+ // tab
128
+ if (c === "\t") {
129
+ const cols_to_tab = TAB_WIDTH -
130
+ (running_cols % TAB_WIDTH);
131
+ if (running_cols + cols_to_tab > cols) {
132
+ lines.push(text.slice(start, i).trimEnd());
133
+ if (!wrap) {
134
+ i = text.indexOf("\n", i + 1);
135
+ }
136
+ else {
137
+ i = finalSpaceIdx(text, i + 1);
138
+ }
139
+ // end of text
140
+ if (i < 0) {
141
+ break;
142
+ }
143
+ start = i + 1;
144
+ running_cols = 0;
145
+ last_space = -1;
146
+ }
147
+ else {
148
+ running_cols += cols_to_tab;
149
+ last_space = i;
150
+ }
151
+ continue;
152
+ }
153
+ // space
154
+ if (c === " ") {
155
+ last_space = i;
156
+ }
157
+ // exceeding available width
158
+ if (running_cols + 1 > cols) {
159
+ // skip rest of line if not wrapping
160
+ if (!wrap) {
161
+ lines.push(text.slice(start, i).trimEnd());
162
+ i = text.indexOf("\n", i + 1);
163
+ // end of text
164
+ if (i < 0) {
165
+ break;
166
+ }
167
+ start = i + 1;
168
+ running_cols = 0;
169
+ last_space = -1;
170
+ continue;
171
+ }
172
+ // this is a continuous word that filled the whole line
173
+ if (last_space < 0) {
174
+ lines.push(text.slice(start, i).trimEnd());
175
+ start = i;
176
+ running_cols = 0;
177
+ i--;
178
+ continue;
179
+ }
180
+ // otherwise, will break at last space
181
+ lines.push(text.slice(start, last_space).trimEnd());
182
+ i = finalSpaceIdx(text, last_space);
183
+ start = i + 1;
184
+ running_cols = 0;
185
+ last_space = -1;
186
+ continue;
187
+ }
188
+ running_cols++;
189
+ }
190
+ // push last line if it isn't a space
191
+ if (start < text.length) {
192
+ lines.push(text.slice(start));
193
+ }
194
+ return lines;
195
+ }
196
+ /**
197
+ * Render `text` into a grid of terminal glyphs `cols` wide.
198
+ *
199
+ * This is the primary text-rendering entry point. It runs {@link textToLines}
200
+ * to lay the text out, then encodes each visible character as a packed
201
+ * 16-bit glyph (CP437 code + colour pair) into the resulting {@link Glyphs}.
202
+ *
203
+ * The escape sequences understood by the text format are interpreted here:
204
+ *
205
+ * - `\\` -- emits a single literal backslash
206
+ * - `\aX` / `\aX{options}` -- records an anchor in group `X` (0-9) at the
207
+ * current position, with optional `options` string (see {@link Anchor})
208
+ * - `\fX` / `\FX` -- set foreground colour to `X` (dark / bright)
209
+ * - `\bX` / `\BX` -- set background colour to `X` (dark / bright)
210
+ *
211
+ * Colours `X` are clamped to 0-7; the `F`/`B` (capital) variants add 8 to
212
+ * select the bright half of the palette. The initial foreground is
213
+ * {@link Colour.WHITE} and the initial background is {@link Colour.BLACK}.
214
+ *
215
+ * Tabs advance the column to the next multiple of {@link TAB_WIDTH} but
216
+ * do not emit anything. Characters outside the CP437 range map to 0
217
+ * (blank), and trailing space on the default colour is left as zero so it
218
+ * does not overwrite whatever is already on the console.
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * import { textGlyphs } from "@micahdb/tscon";
223
+ *
224
+ * // bright-blue on black "Link", 4 cols wide
225
+ * const g = textGlyphs("\\F4\\b0Link", 4, false);
226
+ * ```
227
+ *
228
+ * @param text The text to render, with escape sequences.
229
+ * @param cols Width of the grid in cells. If `<= 0`, an empty grid is
230
+ * returned.
231
+ * @param wrap Whether to word-wrap long lines (`true`) or truncate them
232
+ * (`false`).
233
+ * @returns The rendered glyph grid, with anchors populated.
234
+ */
235
+ export function textGlyphs(text, cols, wrap) {
236
+ if (cols <= 0) {
237
+ return {
238
+ data: new Uint16Array(),
239
+ rows: 0,
240
+ cols: 0,
241
+ anchors: {},
242
+ };
243
+ }
244
+ const lines = textToLines(text, cols, wrap);
245
+ const rows = lines.length;
246
+ if (rows === 0) {
247
+ return {
248
+ data: new Uint16Array(),
249
+ rows: 0,
250
+ cols: 0,
251
+ anchors: {},
252
+ };
253
+ }
254
+ const glyphs = {
255
+ data: new Uint16Array(rows * cols),
256
+ rows,
257
+ cols: cols,
258
+ anchors: {},
259
+ };
260
+ let fg = Colour.WHITE;
261
+ let bg = Colour.BLACK;
262
+ for (let row = 0; row < rows; row++) {
263
+ const line = lines[row];
264
+ let col = 0;
265
+ for (let i = 0; i < line.length; i++) {
266
+ const c = line[i];
267
+ let escape = c === "\\";
268
+ // \\ : display a single back slash
269
+ if (escape && i + 1 < line.length &&
270
+ line[i + 1] === "\\") {
271
+ i++; // this will be displayed as a single character
272
+ escape = false; // skip below checks
273
+ }
274
+ // anchor escape sequence
275
+ // \aX : anchor in group X, no options
276
+ // \aX{options string} : anchor in group X, with options
277
+ if (escape && i + 2 < line.length &&
278
+ line[i + 1] === "a" &&
279
+ !isNaN(Number(line[i + 2]))) {
280
+ const num = Math.max(Math.min(Number(line[i + 2]), 9), 0); // 0..9
281
+ let skip_chars = 2;
282
+ if (glyphs.anchors[num] === undefined) {
283
+ glyphs.anchors[num] = [];
284
+ }
285
+ const anchors = glyphs.anchors[num];
286
+ let options = null;
287
+ // options string
288
+ if (i + 4 <= line.length &&
289
+ line[i + 3] === "{") {
290
+ const closing_brace = line.indexOf("}", i + 3);
291
+ if (closing_brace > i + 3) {
292
+ // \aX{options} <- take text from inner braces
293
+ options = line.slice(i + 4, closing_brace);
294
+ // skip right until the closing brace
295
+ skip_chars = closing_brace - i;
296
+ } // else, options = null
297
+ }
298
+ // empty string should be null
299
+ if (options === "") {
300
+ options = null;
301
+ }
302
+ anchors.push({ options, row, col });
303
+ i += skip_chars;
304
+ continue; // i++
305
+ }
306
+ // colour escape sequence:
307
+ // \fX : foreground X, dark
308
+ // \FX : foreground X, bright
309
+ // \bX : background X, dark
310
+ // \BX : background X, bright
311
+ if (escape &&
312
+ i + 2 < line.length &&
313
+ "fFbB".includes(line[i + 1]) &&
314
+ !isNaN(Number(line[i + 2]))) {
315
+ const num = Math.max(Math.min(Number(line[i + 2]), 7), 0); // 0..7
316
+ switch (line[i + 1]) {
317
+ case "f":
318
+ fg = num;
319
+ break;
320
+ case "F":
321
+ fg = num + 8;
322
+ break;
323
+ case "b":
324
+ bg = num;
325
+ break;
326
+ case "B":
327
+ bg = num + 8;
328
+ break;
329
+ }
330
+ i += 2;
331
+ continue;
332
+ }
333
+ // tab
334
+ if (c === "\t") {
335
+ const tab_chars = TAB_WIDTH - (col % TAB_WIDTH);
336
+ col += tab_chars;
337
+ continue;
338
+ }
339
+ if (col + 1 > cols) {
340
+ // in theory, shouldn't happen; but be safe if it does
341
+ break;
342
+ }
343
+ const char_code = charCodeInCp437(line.codePointAt(i) ?? " ".codePointAt(0));
344
+ const colour_byte = ((fg & 0b1111) << 4) |
345
+ (bg & 0b1111);
346
+ if (char_code !== " ".codePointAt(0) ||
347
+ colour_byte !== 0) {
348
+ const data_idx = row * cols + col;
349
+ const glyph = (colour_byte << 8) |
350
+ (char_code & 0xff);
351
+ glyphs.data[data_idx] = glyph;
352
+ }
353
+ col++;
354
+ }
355
+ }
356
+ return glyphs;
357
+ }
358
+ /**
359
+ * How each cell of a {@link TexGlyphs} grid samples its source texture.
360
+ *
361
+ * The value is packed into each texture-glyph and read by the renderer's
362
+ * vertex shader to decide, per cell, how to turn the source texture into a
363
+ * drawn glyph.
364
+ *
365
+ * - `SAMPLE` -- render the source texture directly. Each cell is given UV
366
+ * coordinates into the texture, so the full rendered region
367
+ * simply shows the source texture as-is.
368
+ * - `GLYPHS` -- sample the texture and convert it to a glyph: the
369
+ * character is chosen by the sample's brightness and the
370
+ * colour by dithering (Bayer matrix + nearest palette hue).
371
+ * The background stays black. Preferred when you want the
372
+ * texture rendered as coloured characters without it being
373
+ * too bright or jarring.
374
+ * - `BG_GLYPHS` -- like `GLYPHS`, but also sets the background colour, so
375
+ * the full glyph (foreground + background) carries more of
376
+ * the sample's colour. This is noticeably brighter and more
377
+ * visually accurate; use it when no text is drawn on top of
378
+ * the rendered texture.
379
+ * - `MIX` -- alternate `SAMPLE`/`GLYPHS` in a checkerboard pattern.
380
+ * - `ROWS` -- alternate `SAMPLE`/`GLYPHS` by row.
381
+ * - `COLS` -- alternate `SAMPLE`/`GLYPHS` by column.
382
+ */
383
+ export var TexGlyphMode;
384
+ (function (TexGlyphMode) {
385
+ TexGlyphMode[TexGlyphMode["SAMPLE"] = 0] = "SAMPLE";
386
+ TexGlyphMode[TexGlyphMode["GLYPHS"] = 1] = "GLYPHS";
387
+ TexGlyphMode[TexGlyphMode["BG_GLYPHS"] = 2] = "BG_GLYPHS";
388
+ TexGlyphMode[TexGlyphMode["MIX"] = 3] = "MIX";
389
+ TexGlyphMode[TexGlyphMode["ROWS"] = 4] = "ROWS";
390
+ TexGlyphMode[TexGlyphMode["COLS"] = 5] = "COLS";
391
+ })(TexGlyphMode || (TexGlyphMode = {}));
392
+ /**
393
+ * Build a {@link TexGlyphs} grid of `rows` by `cols` cells that samples a
394
+ * texture in the given {@link TexGlyphMode}.
395
+ *
396
+ * The grid is sized to match a region of the terminal (typically the whole
397
+ * canvas, `canvas.rows` x `canvas.cols`, or a smaller sub-region) and is
398
+ * passed to {@link Renderer.draw} along with the texture to display.
399
+ *
400
+ * For the `MIX`, `ROWS`, and `COLS` modes the requested `mode` is used as a
401
+ * base and individual cells are switched between `SAMPLE` and `GLYPHS`
402
+ * according to the pattern (see {@link TexGlyphMode} for what each mode does
403
+ * to the sampled texture). The packed per-cell value encodes the cell's own
404
+ * row/column and the (possibly per-cell) effective mode.
405
+ *
406
+ * @example
407
+ * ```ts
408
+ * import { TexGlyphMode, textureGlyphs } from "@micahdb/tscon";
409
+ *
410
+ * // whole-canvas glyphs that sample the texture as coloured characters
411
+ * const tg = textureGlyphs(canvas.rows, canvas.cols, TexGlyphMode.GLYPHS);
412
+ * ```
413
+ *
414
+ * @param rows Height of the grid in cells.
415
+ * @param cols Width of the grid in cells.
416
+ * @param mode Sampling mode to apply to the cells.
417
+ * @returns The packed texture-glyph grid.
418
+ */
419
+ export function textureGlyphs(rows, cols, mode) {
420
+ const tglyphs = {
421
+ data: new Uint32Array(rows * cols),
422
+ rows,
423
+ cols,
424
+ };
425
+ const global_mode = mode;
426
+ for (let data_idx = 0; data_idx < rows * cols; data_idx++) {
427
+ const row = Math.floor(data_idx / cols);
428
+ const col = data_idx % cols;
429
+ if (global_mode == TexGlyphMode.MIX) {
430
+ mode = (row + col) % 2 == 0
431
+ ? TexGlyphMode.SAMPLE
432
+ : TexGlyphMode.GLYPHS;
433
+ }
434
+ else if (global_mode == TexGlyphMode.ROWS) {
435
+ mode = row % 2 == 0
436
+ ? TexGlyphMode.SAMPLE
437
+ : TexGlyphMode.GLYPHS;
438
+ }
439
+ else if (global_mode == TexGlyphMode.COLS) {
440
+ mode = col % 2 == 0
441
+ ? TexGlyphMode.SAMPLE
442
+ : TexGlyphMode.GLYPHS;
443
+ }
444
+ const glyph = ((row & 0xff) << 24) | ((mode & 0xff) << 16) |
445
+ (col & 0xffff);
446
+ tglyphs.data[data_idx] = glyph;
447
+ }
448
+ return tglyphs;
449
+ }
@@ -0,0 +1,11 @@
1
+ export { Canvas } from "./canvas.ts";
2
+ export { Terminal } from "./terminal.ts";
3
+ export { TAB_WIDTH, TexGlyphMode, textGlyphs, textToLines, textureGlyphs, } from "./glyphs.ts";
4
+ export type { Anchor, Glyphs, TexGlyphs } from "./glyphs.ts";
5
+ export { Renderer } from "./renderer.ts";
6
+ export { Mat4, Vec4 } from "./math.ts";
7
+ export { compileProgram, getAttribLocations, getUniformLocations, } from "./program.ts";
8
+ export { renderCp437 } from "./cp437.ts";
9
+ export { Colour, PALETTE } from "./colour.ts";
10
+ export type { Rect } from "./rect.ts";
11
+ export { loadCubeMap, loadTexture } from "./texture.ts";
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { Canvas } from "./canvas.js";
2
+ export { Terminal } from "./terminal.js";
3
+ export { TAB_WIDTH, TexGlyphMode, textGlyphs, textToLines, textureGlyphs, } from "./glyphs.js";
4
+ export { Renderer } from "./renderer.js";
5
+ export { Mat4, Vec4 } from "./math.js";
6
+ export { compileProgram, getAttribLocations, getUniformLocations, } from "./program.js";
7
+ export { renderCp437 } from "./cp437.js";
8
+ export { Colour, PALETTE } from "./colour.js";
9
+ export { loadCubeMap, loadTexture } from "./texture.js";
package/dist/math.d.ts ADDED
@@ -0,0 +1,214 @@
1
+ /**
2
+ * A column-major 4x4 matrix stored as a 16-element `Float32Array`.
3
+ *
4
+ * `Mat4` provides static helpers for creating and operating on 4x4 matrices
5
+ * following the conventions of `gl-matrix`-style libraries: the `o` argument
6
+ * of each operation is the output buffer (which may alias an input) and the
7
+ * same buffer is returned to allow chaining.
8
+ *
9
+ * Matrices are stored column-major; element `m[col * 4 + row]` is row `row`,
10
+ * column `col`.
11
+ */
12
+ export declare class Mat4 {
13
+ /**
14
+ * Create and return a new 4x4 identity matrix.
15
+ *
16
+ * @returns A 16-element `Float32Array` initialised to the identity
17
+ * matrix.
18
+ */
19
+ static create(): Float32Array;
20
+ /**
21
+ * Set all 16 components of matrix `o` to the given values, which are
22
+ * supplied in column-major order (`m00, m01, m02, m03, m10, ...`).
23
+ *
24
+ * `o` is returned for convenience.
25
+ *
26
+ * @param o The matrix to write into.
27
+ * @returns The same matrix `o`, with its components overwritten.
28
+ */
29
+ static set(o: Float32Array, m00: number, m01: number, m02: number, m03: number, m10: number, m11: number, m12: number, m13: number, m20: number, m21: number, m22: number, m23: number, m30: number, m31: number, m32: number, m33: number): Float32Array;
30
+ /**
31
+ * Multiply matrices `a` and `b` and store the result in `o`
32
+ * (`o = a * b`).
33
+ *
34
+ * `o` may be the same buffer as `a` or `b`. The result is equivalent to
35
+ * first applying `b` then `a` (i.e. `a * b` in standard column-vector
36
+ * notation), which is the order used for building model/view/projection
37
+ * chains.
38
+ *
39
+ * @param o Output matrix (may alias `a` or `b`).
40
+ * @param a Left-hand matrix.
41
+ * @param b Right-hand matrix.
42
+ * @returns The matrix `o`.
43
+ */
44
+ static multiply(o: Float32Array, a: Float32Array, b: Float32Array): Float32Array;
45
+ /**
46
+ * Set `o` to the transpose of `a`.
47
+ *
48
+ * `o` may be the same buffer as `a`; in that case the transpose is
49
+ * performed in place.
50
+ *
51
+ * @param o Output matrix (may alias `a`).
52
+ * @param a Source matrix.
53
+ * @returns The matrix `o`.
54
+ */
55
+ static transpose(o: Float32Array, a: Float32Array): Float32Array;
56
+ /**
57
+ * Set `o` to the inverse of `a`.
58
+ *
59
+ * The determinant is computed internally; if it is zero the resulting
60
+ * matrix is undefined. `o` must not alias `a` (the computation
61
+ * reads `a` while writing `o`).
62
+ *
63
+ * @param o Output matrix (must not alias `a`).
64
+ * @param a Source matrix.
65
+ * @returns The matrix `o`.
66
+ */
67
+ static inverse(o: Float32Array, a: Float32Array): Float32Array;
68
+ /**
69
+ * Compute the upper-left 3x3 inverse-transpose of `a` and write it into
70
+ * the 9-element output `o`.
71
+ *
72
+ * This is the standard "normal matrix" used to transform normals in a
73
+ * lighting shader. `o` must be a `Float32Array` of length 9 and
74
+ * must not alias `a`.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * import { Mat4 } from "@micahdb/tscon";
79
+ *
80
+ * const mv = Mat4.create();
81
+ * Mat4.multiply(mv, view, model);
82
+ * const normals = new Float32Array(9);
83
+ * Mat4.inverseTranspose3x3(normals, mv);
84
+ * gl.uniformMatrix3fv(loc, false, normals);
85
+ * ```
86
+ *
87
+ * @param o Output 3x3 matrix (length-9 `Float32Array`).
88
+ * @param a Source 4x4 matrix.
89
+ * @returns The matrix `o`.
90
+ */
91
+ static inverseTranspose3x3(o: Float32Array, a: Float32Array): Float32Array;
92
+ /**
93
+ * Set `o` to a perspective projection matrix.
94
+ *
95
+ * The matrix maps a view frustum defined by a vertical field of view
96
+ * `fovy` (in radians), the viewport `aspect` ratio (width /
97
+ * height), and the `near` and `far` clip-plane distances into clip
98
+ * space, with the camera looking down -Z.
99
+ *
100
+ * @param o Output matrix.
101
+ * @param fovy Vertical field of view, in radians.
102
+ * @param aspect Width / height of the viewport.
103
+ * @param near Distance to the near clip plane.
104
+ * @param far Distance to the far clip plane.
105
+ * @returns The matrix `o`.
106
+ */
107
+ static perspective(o: Float32Array, fovy: number, aspect: number, near: number, far: number): Float32Array;
108
+ /**
109
+ * Set `o` to an orthographic projection matrix.
110
+ *
111
+ * The matrix maps the axis-aligned box with the given left/right,
112
+ * bottom/top, and near/far planes into clip space.
113
+ *
114
+ * @param o Output matrix.
115
+ * @param left Left edge of the view volume.
116
+ * @param right Right edge of the view volume.
117
+ * @param bottom Bottom edge of the view volume.
118
+ * @param top Top edge of the view volume.
119
+ * @param near Distance to the near clip plane.
120
+ * @param far Distance to the far clip plane.
121
+ * @returns The matrix `o`.
122
+ */
123
+ static orthographic(o: Float32Array, left: number, right: number, bottom: number, top: number, near: number, far: number): Float32Array;
124
+ /**
125
+ * Set `o` to a "look at" view matrix.
126
+ *
127
+ * Builds a camera/view matrix placed at `eye`, oriented to look towards
128
+ * `center`, with the given `up` axis. The result transforms world-space
129
+ * coordinates into view space (camera looking down -Z).
130
+ *
131
+ * @param o Output matrix.
132
+ * @param eye Camera position as a length-3 array `[x, y, z]`.
133
+ * @param center Point the camera looks towards, `[x, y, z]`.
134
+ * @param up Camera up direction, `[x, y, z]`.
135
+ * @returns The matrix `o`.
136
+ */
137
+ static lookAt(o: Float32Array, e: Float32Array, c: Float32Array, up: Float32Array): Float32Array;
138
+ /**
139
+ * Create and return a translation matrix for the given offsets.
140
+ *
141
+ * @param tx Translation along X.
142
+ * @param ty Translation along Y.
143
+ * @param tz Translation along Z.
144
+ * @returns A new translation matrix.
145
+ */
146
+ static translation(tx: number, ty: number, tz: number): Float32Array;
147
+ /**
148
+ * Create and return a non-uniform scale matrix.
149
+ *
150
+ * @param sx Scale factor along X.
151
+ * @param sy Scale factor along Y.
152
+ * @param sz Scale factor along Z.
153
+ * @returns A new scale matrix.
154
+ */
155
+ static scale(sx: number, sy: number, sz: number): Float32Array;
156
+ /**
157
+ * Create and return a rotation matrix.
158
+ *
159
+ * The rotation is by `radians` about one of the principal axes. The
160
+ * returned matrix follows a right-handed convention.
161
+ *
162
+ * @param axis The axis to rotate about: `"x"`, `"y"`, or `"z"`.
163
+ * @param radians Rotation angle in radians.
164
+ * @returns A new rotation matrix, or an identity matrix if
165
+ * `axis` is not recognised.
166
+ */
167
+ static rotation(axis: string, radians: number): Float32Array;
168
+ }
169
+ /**
170
+ * A 4-component vector stored as a 4-element `Float32Array`.
171
+ *
172
+ * Like {@link Mat4}, `Vec4` exposes static helpers that take an output buffer
173
+ * `o` (returned for chaining) rather than allocating on each operation.
174
+ */
175
+ export declare class Vec4 {
176
+ /**
177
+ * Create and return a new zeroed 4-component vector.
178
+ *
179
+ * @returns A length-4 `Float32Array` initialised to `[0, 0, 0, 0]`.
180
+ */
181
+ static create(): Float32Array;
182
+ /**
183
+ * Set the four components of vector `o` to the given values.
184
+ *
185
+ * @param o The vector to write into.
186
+ * @param x Component 0.
187
+ * @param y Component 1.
188
+ * @param z Component 2.
189
+ * @param w Component 3.
190
+ */
191
+ static set(o: Float32Array, x: number, y: number, z: number, w: number): void;
192
+ /**
193
+ * Create and return a new vector initialised to the given values.
194
+ *
195
+ * @param x Component 0.
196
+ * @param y Component 1.
197
+ * @param z Component 2.
198
+ * @param w Component 3.
199
+ * @returns A new length-4 `Float32Array`.
200
+ */
201
+ static from(x: number, y: number, z: number, w: number): Float32Array;
202
+ /**
203
+ * Transform vector `a` by matrix `m` and store the result in `o`
204
+ * (`o = m * a`, treating `a` as a column vector).
205
+ *
206
+ * `o` may be the same buffer as `a`.
207
+ *
208
+ * @param o Output vector (may alias `a`).
209
+ * @param a Source vector.
210
+ * @param m Transformation matrix.
211
+ * @returns The vector `o`.
212
+ */
213
+ static transformMat4(o: Float32Array, a: Float32Array, m: Float32Array): Float32Array;
214
+ }