UNPKG

@prachwal/mandelbrot-generator

Version:

Professional Mandelbrot fractal generator with TypeScript support, interactive web interface, and multiple output formats

354 lines 13 kB
/** * @fileoverview Color palette generation and management for Mandelbrot fractal visualization * @module colors * @version 1.0.0 * @author Prachwal * @since 1.0.0 * * This module provides sophisticated color palette systems for visualizing Mandelbrot fractals. * It includes multiple predefined palettes, color interpolation algorithms, and utilities * for mapping iteration counts to visually appealing colors. * * @example * ```typescript * import { getColor, COLOR_PALETTES } from './colors.js'; * * // Get color for a specific iteration count * const [r, g, b] = getColor(50, 100, 'classic'); * * // Use different palettes * const hotColor = getColor(75, 200, 'hot'); * const coolColor = getColor(75, 200, 'cool'); * * // Access palette data directly * const classicPalette = COLOR_PALETTES.classic; * ``` */ /** * Smoothly interpolates between two RGB colors using linear interpolation * * This function blends two colors based on a factor value, creating smooth * color transitions essential for high-quality fractal visualization. * * @param color1 - Starting RGB color as [red, green, blue] array * @param color2 - Ending RGB color as [red, green, blue] array * @param factor - Interpolation factor between 0.0 and 1.0 * - 0.0 returns color1 exactly * - 1.0 returns color2 exactly * - 0.5 returns the midpoint color * @returns Interpolated RGB color with values rounded to integers * * @example * ```typescript * // Blend from red to blue * const red: RGBColor = [255, 0, 0]; * const blue: RGBColor = [0, 0, 255]; * * const purple = interpolateColor(red, blue, 0.5); // [128, 0, 128] * const nearRed = interpolateColor(red, blue, 0.1); // [230, 0, 26] * const nearBlue = interpolateColor(red, blue, 0.9); // [26, 0, 230] * ``` * * @algorithm Uses component-wise linear interpolation: result = color1 + (color2 - color1) * factor * @complexity O(1) - constant time operation * @internal Used internally by generatePalette function */ function interpolateColor(color1, color2, factor) { const r = Math.round(color1[0] + (color2[0] - color1[0]) * factor); const g = Math.round(color1[1] + (color2[1] - color1[1]) * factor); const b = Math.round(color1[2] + (color2[2] - color1[2]) * factor); return [r, g, b]; } /** * Generates a smooth color palette from a set of control point colors * * This function creates a seamless color gradient by interpolating between * control points, producing palettes suitable for fractal visualization. * The resulting palette provides smooth color transitions across the iteration spectrum. * * @param controlPoints - Array of key RGB colors that define the palette's character * - Must contain at least 2 colors * - Colors are distributed evenly across the palette range * @param size - Number of colors to generate in the final palette (default: 256) * - Higher values provide smoother gradients * - 256 is optimal for most fractal visualizations * @returns Read-only array of RGB colors forming a smooth gradient * * @example * ```typescript * // Create a simple red-to-blue palette * const palette = generatePalette([ * [255, 0, 0], // Red * [0, 0, 255] // Blue * ], 100); * * // Create a fire-like palette * const firePalette = generatePalette([ * [0, 0, 0], // Black * [128, 0, 0], // Dark red * [255, 100, 0], // Orange * [255, 255, 0], // Yellow * [255, 255, 255] // White * ]); * ``` * * @algorithm * 1. Divides the palette into segments between control points * 2. For each output color, determines which segment it belongs to * 3. Interpolates between the segment's endpoints * 4. Handles edge cases at palette boundaries * * @complexity O(size) - linear in output palette size * @see {@link interpolateColor} for the underlying interpolation algorithm * @internal Used internally to build predefined color palettes */ function generatePalette(controlPoints, size = 256) { const palette = []; const segments = controlPoints.length - 1; for (let i = 0; i < size; i++) { const position = (i / (size - 1)) * segments; const segmentIndex = Math.floor(position); const segmentPosition = position - segmentIndex; if (segmentIndex >= segments) { palette.push(controlPoints[controlPoints.length - 1]); } else { const color = interpolateColor(controlPoints[segmentIndex], controlPoints[segmentIndex + 1], segmentPosition); palette.push(color); } } return palette; } /** * Collection of predefined color palettes optimized for Mandelbrot fractal visualization * * Each palette is carefully crafted to highlight different aspects of the fractal structure: * - **rainbow**: Classic multi-color palette showing intricate details * - **fire**: Warm palette emphasizing heat-like patterns * - **cool**: Cool blues and greens for serene visualization * - **classic**: Traditional black-to-white gradient * - **hot**: Intense reds, oranges, and yellows * - **electric**: High-contrast neon colors * - **ocean**: Deep blues with white highlights * - **sunset**: Warm oranges, pinks, and purples * * @example * ```typescript * import { colorPalettes } from './colors.js'; * * // Access specific palettes * const rainbowColors = colorPalettes.rainbow; * const fireColors = colorPalettes.fire; * * // Use in configuration * const config = { * // ...other settings * colorPalette: 'fire' as const * }; * * // Iterate through all available palettes * Object.keys(colorPalettes).forEach(paletteName => { * console.log(`${paletteName}: ${colorPalettes[paletteName].length} colors`); * }); * ``` * * @see {@link PaletteType} for available palette names * @see {@link getColor} for using palettes in fractal generation * @since 1.0.0 */ export const colorPalettes = { rainbow: generatePalette([ [66, 30, 15], // Dark brown [25, 7, 26], // Dark purple [9, 1, 47], // Dark blue [4, 4, 73], // Blue [0, 7, 100], // Light blue [12, 44, 138], // Cyan [24, 82, 177], // Light cyan [57, 125, 209], // Light blue [134, 181, 229], // Very light blue [211, 236, 248], // White blue [241, 233, 191], // Light yellow [248, 201, 95], // Yellow [255, 170, 0], // Orange [204, 128, 0], // Dark orange [153, 87, 0], // Brown [106, 52, 3] // Dark brown ]), fire: generatePalette([ [0, 0, 0], // Black [32, 0, 0], // Dark red [64, 0, 0], // Red [96, 32, 0], // Red-orange [128, 64, 0], // Orange [160, 96, 32], // Light orange [192, 128, 64], // Yellow-orange [255, 192, 128], // Light yellow [255, 255, 192], // Very light yellow [255, 255, 255] // White ]), blue: generatePalette([ [0, 0, 0], // Black [0, 0, 64], // Dark blue [0, 0, 128], // Blue [0, 64, 192], // Light blue [0, 128, 255], // Cyan [64, 192, 255], // Light cyan [128, 224, 255], // Very light cyan [192, 240, 255], // White blue [255, 255, 255] // White ]), grayscale: generatePalette([ [0, 0, 0], // Black [64, 64, 64], // Dark gray [128, 128, 128], // Gray [192, 192, 192], // Light gray [255, 255, 255] // White ]), purple: generatePalette([ [0, 0, 0], // Black [32, 0, 32], // Dark purple [64, 0, 64], // Purple [96, 32, 96], // Light purple [128, 64, 128], // Pink-purple [160, 96, 160], // Light pink-purple [192, 128, 192], // Pink [224, 160, 224], // Light pink [255, 192, 255], // Very light pink [255, 255, 255] // White ]), sunset: generatePalette([ [25, 25, 112], // Dark blue (night) [70, 130, 180], // Steel blue [135, 206, 235], // Light blue [255, 165, 0], // Orange [255, 69, 0], // Red-orange [220, 20, 60], // Crimson [139, 0, 139], // Dark magenta [75, 0, 130] // Indigo ]) }; /** * Maps iteration count to RGB color using the specified palette * * This is the primary function for converting Mandelbrot iteration results into * visual colors. It handles both set membership (black) and iteration-based coloring * with smooth palette transitions. * * @param iterations - Number of iterations before the point escaped (0 to maxIterations) * @param maxIterations - Maximum iterations used in the fractal calculation * @param paletteType - Name of the color palette to use (default: 'rainbow') * @returns RGB color as [red, green, blue] array with values 0-255 * * @example * ```typescript * import { getColor } from './colors.js'; * * // Points in the set are black * const setColor = getColor(1000, 1000, 'rainbow'); // [0, 0, 0] * * // Points outside get colored by iteration count * const escapeColor = getColor(50, 1000, 'fire'); // Warm color * const quickEscape = getColor(5, 1000, 'cool'); // Cool color * * // Different palettes for same iteration * const rainbow = getColor(100, 500, 'rainbow'); * const fire = getColor(100, 500, 'fire'); * const ocean = getColor(100, 500, 'ocean'); * ``` * * @algorithm * 1. If iterations >= maxIterations, return black (point in set) * 2. Calculate normalized position in palette (0.0 to 1.0) * 3. Map to palette index and return corresponding color * 4. Handle edge cases and palette boundaries * * @performance O(1) - constant time color lookup * @see {@link colorPalettes} for available palette options * @see {@link getColorHex} for hexadecimal color output * @since 1.0.0 */ export function getColor(iterations, maxIterations, paletteType = 'rainbow') { if (iterations >= maxIterations) { return [0, 0, 0]; // Black for points in the set } const palette = colorPalettes[paletteType] || colorPalettes.rainbow; const index = Math.floor((iterations / maxIterations) * (palette.length - 1)); return palette[index]; } /** * Converts RGB color values to hexadecimal color string format * * This utility function transforms individual red, green, and blue components * into a standard web-compatible hex color string for use in CSS, HTML, * or other systems that expect hex color notation. * * @param r - Red component value (0-255) * @param g - Green component value (0-255) * @param b - Blue component value (0-255) * @returns Hexadecimal color string in format "#RRGGBB" * * @example * ```typescript * import { rgbToHex } from './colors.js'; * * // Convert primary colors * const red = rgbToHex(255, 0, 0); // "#ff0000" * const green = rgbToHex(0, 255, 0); // "#00ff00" * const blue = rgbToHex(0, 0, 255); // "#0000ff" * * // Convert custom colors * const purple = rgbToHex(128, 0, 128); // "#800080" * const orange = rgbToHex(255, 165, 0); // "#ffa500" * * // Use in web contexts * element.style.backgroundColor = rgbToHex(200, 150, 100); * ``` * * @algorithm Uses bit shifting for efficient hex conversion * @complexity O(1) - constant time operation * @see {@link getColorHex} for direct fractal iteration to hex conversion * @since 1.0.0 */ export function rgbToHex(r, g, b) { return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); } /** * Maps iteration count directly to hexadecimal color string * * Convenience function that combines getColor() and rgbToHex() for direct * conversion from Mandelbrot iteration results to web-compatible hex colors. * Ideal for web applications, CSS generation, and HTML canvas operations. * * @param iterations - Number of iterations before the point escaped * @param maxIterations - Maximum iterations used in fractal calculation * @param paletteType - Name of the color palette to use (default: 'rainbow') * @returns Hexadecimal color string in format "#RRGGBB" * * @example * ```typescript * import { getColorHex } from './colors.js'; * * // Get hex colors for fractal points * const setColor = getColorHex(1000, 1000, 'fire'); // "#000000" (black) * const escapeColor = getColorHex(50, 200, 'ocean'); // "#1e3f5c" (blue) * * // Use directly in web contexts * canvas.style.backgroundColor = getColorHex(75, 100, 'sunset'); * * // Generate CSS color arrays * const cssColors = Array.from({length: 10}, (_, i) => * getColorHex(i * 10, 100, 'rainbow') * ); * ``` * * @complexity O(1) - constant time operation * @see {@link getColor} for RGB color output * @see {@link rgbToHex} for RGB to hex conversion details * @since 1.0.0 */ export function getColorHex(iterations, maxIterations, paletteType = 'rainbow') { const [r, g, b] = getColor(iterations, maxIterations, paletteType); return rgbToHex(r, g, b); } //# sourceMappingURL=colors.js.map