/** * Procedural pattern description — the single source of truth for what the * texture *is*, independent of how it gets rasterized. * * Two consumers render this description: * - the bitmap path, which paints it into a 2D canvas and uploads it as a * texture (see `paintPattern`), and * - the bake path, which evaluates it analytically on the GPU into a * texture (see `patternData.ts`). * * Keeping generation separate from rasterization is what lets `textureMode` * be a real toggle: the same seed produces the same composition either way, * so switching modes changes sharpness and nothing else. * * Coordinates are in *pattern pixels* — the [0, size] space the original * canvas generator worked in. The bake normalizes by `size` on the way * into GLSL; keeping the description in pixels means the numbers here stay * directly comparable to the canvas drawing calls. */ import { NeatColor } from "./types"; export interface PatternTriangle { kind: "triangle"; /** First vertex; the other two are stored as offsets from it. */ x: number; y: number; x1: number; y1: number; x2: number; y2: number; color: string; } export interface PatternCircle { kind: "circle"; x: number; y: number; r: number; lineWidth: number; color: string; } export interface PatternBar { kind: "bar"; x: number; y: number; rot: number; width: number; height: number; color: string; } export interface PatternSquiggleCurve { cx1: number; cy1: number; cx2: number; cy2: number; ex: number; ey: number; } export interface PatternSquiggle { kind: "squiggle"; x: number; y: number; lineWidth: number; color: string; curves: PatternSquiggleCurve[]; } export type PatternShape = PatternTriangle | PatternCircle | PatternBar | PatternSquiggle; /** * One column of "matter": pixels [destX, destX + width) sample the generated * artwork starting at `sourceX`. Anything not covered by a stripe is void. */ export interface PatternStripe { destX: number; width: number; sourceX: number; } export interface Pattern { /** Reference pixel size that all coordinates are expressed in. */ size: number; /** Whether shapes wrap around the edges (false for the flat `plane` shape). */ tile: boolean; /** Fill shown wherever there is no matter. */ baseColor: string; /** Vertical background gradient, top to bottom, drawn under every shape. */ background: [string, string]; shapes: PatternShape[]; stripes: PatternStripe[]; transparentVoid: boolean; } export interface GeneratePatternOptions { size: number; seed: number; colors: NeatColor[]; colorBlending: number; baseColor: string; tile: boolean; transparentVoid: boolean; voidLikelihood: number; voidWidthMin: number; voidWidthMax: number; bandDensity: number; triangles: number; circles: number; bars: number; squiggles: number; } /** * Builds the pattern description. * * The order of `random()` calls here is load-bearing: it reproduces the * sequence the generator has always used, so existing `textureSeed` values * keep producing the artwork they produced before this was extracted. */ export declare function generatePattern(opts: GeneratePatternOptions): Pattern | null; /** * Bitmap rasterization: paints the artwork into `sourceCtx`, then composites * the matter stripes into `destCtx`. Split out of the generator so both * render modes consume the same description. */ export declare function paintPattern(pattern: Pattern, sourceCanvas: HTMLCanvasElement, sourceCtx: CanvasRenderingContext2D, destCtx: CanvasRenderingContext2D): void;