Skip to content

packages/core/src/features/textures/lib/palette.ts

Read as Markdown

This is the source snapshot used to build these API details. View this revision on GitHub.

Back to reference

1 import type { ProceduralTextureProps } from './procedural-texture.js';
2 import type { ReactionDiffusionProps } from './reaction-diffusion.js';
3 /**
4  * Ordered colors forming a texture's presentation ramp.
5  *
6  * @see {@link texturePalettes}
7  * @see {@link ProceduralTextureProps}
8  * @see {@link ReactionDiffusionProps}
9  */
10 export type TexturePalette = readonly string[];
11 /**
12  * Built-in immutable color ramps for procedural texture presentation.
13  *
14  * @see {@link TexturePalette}
15  */
16 export const texturePalettes = Object.freeze({
17   /** Dark blue through teal to pale yellow. See {@link texturePalettes}. */
18   lagoon: Object.freeze(["#081e2c", "#125568", "#50b8ad", "#e5efb3"]),
19   /** Warm ivory through lavender to deep blue. See {@link texturePalettes}. */
20   porcelain: Object.freeze(["#f2e7d4", "#b7a9c0", "#536194", "#172955"]),
21   /** Dark violet through coral to pale yellow. See {@link texturePalettes}. */
22   ember: Object.freeze(["#241526", "#893955", "#e88470", "#ffe3aa"]),
23 });
24 
25 export function paletteLut(palette: TexturePalette): Uint8ClampedArray {
26   if (palette.length < 2 || palette.length > 16)
27     throw new RangeError("Texture palette requires 2–16 #RRGGBB colors.");
28   const colors = palette.map((color) => {
29     if (!/^#[0-9a-f]{6}$/i.test(color))
30       throw new TypeError("Texture palette colors must use #RRGGBB.");
31     return [
32       parseInt(color.slice(1, 3), 16),
33       parseInt(color.slice(3, 5), 16),
34       parseInt(color.slice(5, 7), 16),
35     ];
36   });
37   const lut = new Uint8ClampedArray(256 * 4);
38   for (let i = 0; i < 256; i++) {
39     const position = (i / 255) * (colors.length - 1);
40     const lo = Math.min(colors.length - 2, Math.floor(position)),
41       mix = position - lo;
42     for (let c = 0; c < 3; c++)
43       lut[i * 4 + c] = colors[lo][c] * (1 - mix) + colors[lo + 1][c] * mix;
44     lut[i * 4 + 3] = 255;
45   }
46   return lut;
47 }
48 
49 /**
50  * The width and height of a texture's raster grid.
51  *
52  * @see {@link TexturePalette}
53  * @see {@link ReactionDiffusionProps}
54  * @see {@link ProceduralTextureProps}
55  */
56 export interface TextureResolution {
57   /**
58    * Horizontal extent in the units of the containing geometry or surface. See
59    * {@link TextureResolution}.
60    */
61   readonly width: number;
62   /**
63    * Vertical extent in the units of the containing geometry or surface. See
64    * {@link TextureResolution}.
65    */
66   readonly height: number;
67 }
68 export function validateResolution(
69   value: TextureResolution = { width: 256, height: 160 },
70 ): TextureResolution {
71   if (
72     ![value.width, value.height].every(
73       (n) => Number.isInteger(n) && n >= 8 && n <= 1024,
74     ) ||
75     value.width * value.height > 262144
76   ) {
77     throw new RangeError(
78       "Texture resolution must contain integer dimensions from 8 to 1024 and at most 262144 cells.",
79     );
80   }
81   return { width: value.width, height: value.height };
82 }
83 export function validateSeed(seed = 42): number {
84   if (!Number.isInteger(seed) || seed < 0 || seed > 0xffffffff)
85     throw new RangeError("Texture seed must be an unsigned 32-bit integer.");
86   return seed;
87 }
88 

Documentation built with @pibbl/core 0.0.2, revision 2dccb19. ALPHA — NOT FOR PRODUCTION USE.