Source: scene/particle-container/shared/Particle.ts

import { Color } from '../../../color/Color';
import { Texture } from '../../../rendering/renderers/shared/texture/Texture';
import { bgr2rgb } from '../../container/container-mixins/getGlobalMixin';
import { assignWithIgnore } from '../../container/utils/assignWithIgnore';

import type { ColorSource } from '../../../color/Color';

/**
 * Represents a particle with properties for position, scale, rotation, color, and texture.
 * @property {number} x - The x-coordinate of the particle.
 * @property {number} y - The y-coordinate of the particle.
 * @property {number} scaleX - The scale factor in the x-axis.
 * @property {number} scaleY - The scale factor in the y-axis.
 * @property {number} anchorX - The x-coordinate of the anchor point.
 * @property {number} anchorY - The y-coordinate of the anchor point.
 * @property {number} rotation - The rotation of the particle in radians.
 * @property {number} color - The color of the particle as a hexadecimal number.
 * @property {Texture} texture - The texture of the particle.
 * @memberof scene
 */
export interface IParticle
{
    x: number;
    y: number;
    scaleX: number;
    scaleY: number;
    anchorX: number;
    anchorY: number;
    rotation: number;
    color: number;
    texture: Texture;
}

/**
 * Represents the options for creating a new particle.
 * @property {number} x - The x-coordinate of the particle.
 * @property {number} y - The y-coordinate of the particle.
 * @property {number} scaleX - The scale factor in the x-axis.
 * @property {number} scaleY - The scale factor in the y-axis.
 * @property {number} anchorX - The x-coordinate of the anchor point.
 * @property {number} anchorY - The y-coordinate of the anchor point.
 * @property {number} rotation - The rotation of the particle in radians.
 * @property {Texture} texture - The texture of the particle.
 * @property {ColorSource} tint - The tint color of the particle as a hexadecimal number.
 * @property {number} alpha - The alpha value of the particle.
 * @memberof scene
 */
export type ParticleOptions = Omit<Partial<IParticle>, 'color'> & {
    texture: Texture
    tint?: ColorSource;
    alpha?: number;
};

/**
 * Represents a single particle within a particle container. This class implements the IParticle interface,
 * providing properties and methods to manage the particle's position, scale, rotation, color, and texture.
 *
 * The reason we use a particle over a sprite is that these are much lighter weight and we can create a lot of them
 * without taking on the overhead of a full sprite.
 *
 * Here is an example of how to create a new particle:
 *
 * ```javascript
 * const particle = new Particle({
 *   texture,
 *   x: 100,
 *   y: 100,
 *   scaleX: 0.5,
 *   scaleY: 0.5,
 *   rotation: Math.PI / 2,
 *   color: 0xff0000,
 * });
 * ```
 * @implements {IParticle}
 * @memberof scene
 */
export class Particle implements IParticle
{
    /** Default options for constructing with options */
    public static defaultOptions: Partial<ParticleOptions> = {
        anchorX: 0,
        anchorY: 0,
        x: 0,
        y: 0,
        scaleX: 1,
        scaleY: 1,
        rotation: 0,
        tint: 0xffffff,
        alpha: 1,
    };
    /** The x-coordinate of the anchor point. */
    public anchorX: number;
    /** The y-coordinate of the anchor point. */
    public anchorY: number;
    /** The x-coordinate of the particle. */
    public x: number;
    /** The y-coordinate of the particle. */
    public y: number;
    /** The scale factor in the x-axis. */
    public scaleX: number;
    /** The scale factor in the y-axis. */
    public scaleY: number;
    /** The rotation of the particle in radians. */
    public rotation: number;
    /** The color of the particle as a hexadecimal number. */
    public color: number;
    /** The texture of the particle. */
    public texture: Texture;

    private _alpha: number;
    private _tint: number;

    constructor(options: Texture | ParticleOptions)
    {
        if (options instanceof Texture)
        {
            this.texture = options;
            assignWithIgnore(this, Particle.defaultOptions, {});
        }
        else
        {
            const combined = { ...Particle.defaultOptions, ...options };

            assignWithIgnore(this, combined, {});
        }
    }

    /** Gets or sets the alpha value of the particle. */
    get alpha(): number
    {
        return this._alpha;
    }

    set alpha(value: number)
    {
        this._alpha = Math.min(Math.max(value, 0), 1);

        this._updateColor();
    }

    /** Gets or sets the tint color of the particle. */
    get tint(): number
    {
        return bgr2rgb(this._tint);
    }

    set tint(value: ColorSource)
    {
        if (typeof value === 'number')
        {
            this._tint = value;
        }
        else
        {
            this._tint = Color.shared.setValue(value ?? 0xFFFFFF).toBgrNumber();
        }

        this._updateColor();
    }

    private _updateColor()
    {
        // combine alpha and tint
        this.color = this._tint + (((this._alpha * 255) | 0) << 24);
    }
}