Source: global/constants.js

/**
 * @module Constants
 * @author Radim Brnka
 * @description Global constants used across the app.
 * @copyright Synaptory Fractal Traveler, 2025-2026
 * @license MIT
 */

import {easeInOut, easeInOutCubic, easeInOutQuint, hexToRGBArray} from "./utils";

// region > DEBUG CONSTANTS ////////////////////////////////////////////////////////////////////////////////////////////
/**
 * DEBUG_LEVEL
 * @enum {number}
 */
export const DEBUG_LEVEL = {
    NONE: 0,
    VERBOSE: 1, /** Logs only */
    FULL: 2
}

/**
 * DEBUG MODE - Injected by webpack DefinePlugin at build time.
 * Development builds: DEBUG_LEVEL.FULL
 * Production builds: DEBUG_LEVEL.NONE (debug code tree-shaken away)
 * @type {number}
 */
export const DEBUG_MODE = DEBUG_LEVEL[process.env.DEBUG_MODE] || DEBUG_LEVEL.NONE;

/**
 *
 * @type {{LOG: string, INFO: string, WARN: string, ERROR: string}}
 */
export const LOG_LEVEL = {
    LOG: 'log',
    DEBUG: 'debug',
    WARN: 'warn',
    ERROR: 'error'
}

/**
 * Logs a message with an optional scope and severity level.
 * @param {string} message - The message to log.
 * @param {string} [scope=''] - The scope/context of the message.
 * @param {string} [severity=LOG_LEVEL.LOG] - The severity level from LOG_LEVEL.
 */
export const log = (message, scope = '', severity = LOG_LEVEL.LOG) => {
    const formattedScope = scope ? `[${scope}]` : '';

    console[severity](
        `%c${formattedScope}%c ${message}`,
        CONSOLE_GROUP_STYLE,
        CONSOLE_MESSAGE_STYLE
    );
}
// endregion
// region > FEATURE FLAGS
/** Allows switching between fractal while keeping the params to allow Mandelbrot and Julia to match each other */
export const FF_PERSISTENT_FRACTAL_SWITCHING = true;

/**
 * Feature flag controlling the display of the persistent fractal switching button in the UI.
 * When enabled, users can see a button to toggle between fractal types while maintaining
 * the current parameters for seamless transitions between Mandelbrot and Julia sets.
 *
 * @type {boolean}
 * @since 1.9
 */
export const FF_PERSISTENT_FRACTAL_SWITCHING_BUTTON_DISPLAYED = true;

/**
 * When set to true, demo animation will always start from the beginning rather than continue where it's been interrupted.
 * @type {boolean}
 */
export const FF_DEMO_ALWAYS_RESETS = false;

/**
 * Feature flag for adaptive quality control.
 * When enabled, automatically adjusts iteration count based on GPU performance.
 * @type {boolean}
 */
export const FF_ADAPTIVE_QUALITY = false;

/**
 * GPU time threshold in ms above which quality will be reduced.
 * @default 40 ms (~25 FPS).
 * @type {number}
 */
export const ADAPTIVE_QUALITY_THRESHOLD_HIGH = 1000 / 25;

/**
 * GPU time threshold in ms below which quality will be restored
 * @default 16 ms (~60 FPS).
 * @type {number}
 */
export const ADAPTIVE_QUALITY_THRESHOLD_LOW = 1000 / 60;

/**
 * Iteration adjustment step per quality change.
 * @type {number}
 */
export const ADAPTIVE_QUALITY_STEP = 50;

/**
 * Minimum extraIterations offset (most aggressive quality reduction).
 * @default -1500 for optimal visual quality.
 * @type {number}
 */
export const ADAPTIVE_QUALITY_MIN = DEBUG_MODE === DEBUG_LEVEL.NONE ? -1000 : -3000;

/**
 * Cooldown between quality adjustments in milliseconds.
 * @type {number}
 */
export const ADAPTIVE_QUALITY_COOLDOWN = 500;

// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Enum of fractal types
 * @enum {number}
 */
export const FRACTAL_TYPE = {
    MANDELBROT: 0,
    JULIA: 1,
    RIEMANN: 2
}
// ---------------------------------------------------------------------------------------------------------------------
/**
 * Ease in-out transition types matching functions in the Utils module
 * @enum {Function}
 */
export const EASE_TYPE = {
    /** No easing (identity function) */
    NONE: (x) => x,
    /** Slow at the end */
    QUAD: easeInOut,
    /** Slower at the end */
    CUBIC: easeInOutCubic,
    /** Slowest at the end */
    QUINT: easeInOutQuint
}
// ---------------------------------------------------------------------------------------------------------------------
/**
 * Rotation directions
 * @enum {number}
 */
export const ROTATION_DIRECTION = {
    /** Counter-clockwise */
    CCW: -1,
    /** Clockwise */
    CW: 1
}
// ---------------------------------------------------------------------------------------------------------------------
/**
 * Default main GUI color (Mandelbrot mode)
 * @type {string}
 */
export const DEFAULT_ACCENT_COLOR = '#B4FF6A';
// ---------------------------------------------------------------------------------------------------------------------
/**
 * Default secondary (background) GUI color
 * @type {string}
 */
export const DEFAULT_BG_COLOR = 'rgba(24, 48, 13, 0.2)';
// ---------------------------------------------------------------------------------------------------------------------
/***
 * Default color for console group labels
 * @type {string}
 */
const DEFAULT_CONSOLE_GROUP_COLOR = DEFAULT_ACCENT_COLOR;

const DEFAULT_CONSOLE_MESSAGE_COLOR = '#fff';

export const CONSOLE_GROUP_STYLE = `color: ${DEFAULT_CONSOLE_GROUP_COLOR}`;

export const CONSOLE_MESSAGE_STYLE = `color: ${DEFAULT_CONSOLE_MESSAGE_COLOR}`;
// ---------------------------------------------------------------------------------------------------------------------
/**
 * Default Julia-specific palette.
 * @type {JULIA_PALETTE}
 */
export const DEFAULT_JULIA_PALETTE =
    {
        "id": "Event Horizon",
        "keyColor": "#c74a00",
        "theme": [0.859, 0.565, 0.306, 0.18, 0.129, 0.216, 0.067, 0.067, 0.082, 0.467, 0, 0, 0.78, 0.29, 0]
    };
// ---------------------------------------------------------------------------------------------------------------------
/** Default color used based on the initial Mandelbrot coloring. It's an accent color / 1.9 brightness factor that
 * is hardcoded in the updateColorTheme method.
 * @type {PALETTE}
 */
export const DEFAULT_MANDELBROT_THEME_COLOR = [95 / 255, 134 / 255, 56 / 255];
// ---------------------------------------------------------------------------------------------------------------------
/** Default color used based on the initial Julia coloring. It's accent color / 1.9 brightness factor that
 * is hardcoded in the updateColorTheme method.
 * @type {PALETTE}
 */
export const DEFAULT_JULIA_THEME_COLOR = hexToRGBArray(DEFAULT_JULIA_PALETTE.keyColor);
// ---------------------------------------------------------------------------------------------------------------------
/**
 * This is to allow switching between two precisions as the embedded PI constant is too accurate, which is not needed
 * in many cases (rotations etc.)
 * @type {boolean}
 * @default true
 */
const USE_PRECISE_PI = true;
/**
 * PI
 * @type {number|number}
 */
export const PI = USE_PRECISE_PI ? Math.PI : 3.1415926535;
// ---------------------------------------------------------------------------------------------------------------------
/**
 * Defines the compression quality for JPEG screenshots (0-1) in %
 * @type {number}
 */
export const SCREENSHOT_JPEG_COMPRESSION_QUALITY = 0.95;
// ---------------------------------------------------------------------------------------------------------------------
/**
 * A configuration object that provides application details
 */
export const APP = {
    //@formatter:off
    /** App version — injected from package.json by webpack DefinePlugin */
    version: __APP_VERSION__,
    prefix: 'Synaptory Fractal ', // Keep the space!
    suffixes: [
        "Traveler", "Atlas", "Lens", "Scout", "Studio", "Lab", "Viewer", "Explorer", "Engine", "Navigator", "Surveyor",
        "Seeker", "Wanderer", "Voyager", "Drifter", "Portal", "Gateway", "Microscope", "Telescope", "Prism", "Cockpit",
        "Forge", "Workshop", "Reactor", "Core", "Kernel", "Matrix", "Plane", "Realm", "Horizon"
    ],
    get randomName() { return this.prefix + this.suffixes[Math.floor(Math.random() * this.suffixes.length)]; },
    get defaultName() { return this.prefix + this.suffixes[0]; }
    //@formatter:on
}