Source: scene/container/container-mixins/measureMixin.ts

import { Matrix } from '../../../maths/matrix/Matrix';
import { Bounds } from '../bounds/Bounds';
import { getGlobalBounds } from '../bounds/getGlobalBounds';
import { getLocalBounds } from '../bounds/getLocalBounds';

import type { Rectangle } from '../../../maths/shapes/Rectangle';
import type { Container } from '../Container';

export interface MeasureMixinConstructor
{
    width?: number;
    height?: number;
}
export interface MeasureMixin extends Required<MeasureMixinConstructor>
{
    getLocalBounds(rect?: Rectangle): Rectangle;
    getBounds(skipUpdate?: boolean, rect?: Rectangle): Rectangle;
}

const tempBounds = new Bounds();
const tempMatrix = new Matrix();

export const measureMixin: Partial<Container> = {

    /**
     * The width of the Container, setting this will actually modify the scale to achieve the value set.
     * @memberof scene.Container#
     */
    get width(): number
    {
        return Math.abs(this.scale.x * getLocalBounds(this, tempBounds, tempMatrix).width);
    },

    set width(value: number)
    {
        const localWidth = getLocalBounds(this, tempBounds, tempMatrix).width;

        const sign = Math.sign(this.scale.x) || 1;

        if (localWidth !== 0)
        {
            this.scale.x = (value / localWidth) * sign;
        }
        else
        {
            this.scale.x = sign;
        }
    },

    /**
     * The height of the Container, setting this will actually modify the scale to achieve the value set.
     * @memberof scene.Container#
     */
    get height(): number
    {
        return Math.abs(this.scale.y * getLocalBounds(this, tempBounds, tempMatrix).height);
    },

    set height(value: number)
    {
        const localHeight = getLocalBounds(this, tempBounds, tempMatrix).height;

        const sign = Math.sign(this.scale.y) || 1;

        if (localHeight !== 0)
        {
            this.scale.y = (value / localHeight) * sign;
        }
        else
        {
            this.scale.y = sign;
        }
    },

    /**
     * Retrieves the local bounds of the container as a Bounds object.
     * @param rect - Optional rectangle to store the result of the bounds calculation.
     * @returns - The bounding area.
     * @memberof scene.Container#
     */
    getLocalBounds(rect?: Rectangle): Rectangle
    {
        const bounds = getLocalBounds(this, new Bounds(), tempMatrix);

        return rect ? rect.copyFromBounds(bounds) : bounds.rectangle.clone();
    },

    /**
     * Calculates and returns the (world) bounds of the display object as a Rectangle.
     * @param skipUpdate - Setting to `true` will stop the transforms of the scene graph from
     *  being updated. This means the calculation returned MAY be out of date BUT will give you a
     *  nice performance boost.
     * @param rect - Optional rectangle to store the result of the bounds calculation.
     * @returns - The minimum axis-aligned rectangle in world space that fits around this object.
     * @memberof scene.Container#
     */
    getBounds(skipUpdate?: boolean, rect?: Rectangle): Rectangle
    {
        const bounds = getGlobalBounds(this, skipUpdate, tempBounds);

        return rect ? rect.copyFromBounds(bounds) : bounds.rectangle.clone();
    }

} as Container;