Home Reference Source

src/core/Resolution.js

import { EventDispatcher, Vector2 } from "three";

const AUTO_SIZE = -1;

/**
 * A resolution.
 */

export class Resolution extends EventDispatcher {

	/**
	 * Constructs a new resolution.
	 *
	 * TODO Remove resizable param.
	 * @param {Resizable} resizable - A resizable object.
	 * @param {Number} [width=Resolution.AUTO_SIZE] - The preferred width.
	 * @param {Number} [height=Resolution.AUTO_SIZE] - The preferred height.
	 * @param {Number} [scale=1.0] - A resolution scale.
	 */

	constructor(resizable, width = AUTO_SIZE, height = AUTO_SIZE, scale = 1.0) {

		super();

		/**
		 * A resizable object.
		 *
		 * @type {Resizable}
		 * @deprecated Use an event listener for "change" events instead.
		 */

		this.resizable = resizable;

		/**
		 * The base resolution.
		 *
		 * @type {Vector2}
		 * @private
		 */

		this.baseSize = new Vector2(1, 1);

		/**
		 * The preferred resolution.
		 *
		 * @type {Vector2}
		 * @private
		 */

		this.preferredSize = new Vector2(width, height);

		/**
		 * The preferred resolution.
		 *
		 * @type {Vector2}
		 * @deprecated Added for backward-compatibility.
		 */

		this.target = this.preferredSize;

		/**
		 * A resolution scale.
		 *
		 * @type {Number}
		 * @private
		 */

		this.s = scale;

		/**
		 * The effective resolution.
		 *
		 * @type {Vector2}
		 * @private
		 */

		this.effectiveSize = new Vector2();
		this.addEventListener("change", () => this.updateEffectiveSize());
		this.updateEffectiveSize();

	}

	/**
	 * Calculates the effective size.
	 *
	 * @private
	 */

	updateEffectiveSize() {

		const base = this.baseSize;
		const preferred = this.preferredSize;
		const effective = this.effectiveSize;
		const scale = this.scale;

		if(preferred.width !== AUTO_SIZE) {

			effective.width = preferred.width;

		} else if(preferred.height !== AUTO_SIZE) {

			effective.width = Math.round(preferred.height * (base.width / Math.max(base.height, 1)));

		} else {

			effective.width = Math.round(base.width * scale);

		}

		if(preferred.height !== AUTO_SIZE) {

			effective.height = preferred.height;

		} else if(preferred.width !== AUTO_SIZE) {

			effective.height = Math.round(preferred.width / Math.max(base.width / Math.max(base.height, 1), 1));

		} else {

			effective.height = Math.round(base.height * scale);

		}

	}

	/**
	 * The effective width.
	 *
	 * If the preferred width and height are set to {@link Resizer.AUTO_SIZE}, the base width will be returned.
	 *
	 * @type {Number}
	 */

	get width() {

		return this.effectiveSize.width;

	}

	set width(value) {

		this.preferredWidth = value;

	}

	/**
	 * The effective height.
	 *
	 * If the preferred width and height are set to {@link Resizer.AUTO_SIZE}, the base height will be returned.
	 *
	 * @type {Number}
	 */

	get height() {

		return this.effectiveSize.height;

	}

	set height(value) {

		this.preferredHeight = value;

	}

	/**
	 * Returns the effective width.
	 *
	 * If the preferred width and height are set to {@link Resizer.AUTO_SIZE}, the base width will be returned.
	 *
	 * @deprecated Use width instead.
	 * @return {Number} The effective width.
	 */

	getWidth() {

		return this.width;

	}

	/**
	 * Returns the effective height.
	 *
	 * If the preferred width and height are set to {@link Resizer.AUTO_SIZE}, the base height will be returned.
	 *
	 * @deprecated Use height instead.
	 * @return {Number} The effective height.
	 */

	getHeight() {

		return this.height;

	}

	/**
	 * The resolution scale.
	 *
	 * @type {Number}
	 */

	get scale() {

		return this.s;

	}

	set scale(value) {

		if(this.s !== value) {

			this.s = value;
			this.preferredSize.setScalar(AUTO_SIZE);
			this.dispatchEvent({ type: "change" });
			this.resizable.setSize(this.baseSize.width, this.baseSize.height);

		}

	}

	/**
	 * Returns the current resolution scale.
	 *
	 * @deprecated Use scale instead.
	 * @return {Number} The scale.
	 */

	getScale() {

		return this.scale;

	}

	/**
	 * Sets the resolution scale.
	 *
	 * Also sets the preferred resolution to {@link Resizer.AUTO_SIZE}.
	 *
	 * @deprecated Use scale instead.
	 * @param {Number} value - The scale.
	 */

	setScale(value) {

		this.scale = value;

	}

	/**
	 * The base width.
	 *
	 * @type {Number}
	 */

	get baseWidth() {

		return this.baseSize.width;

	}

	set baseWidth(value) {

		if(this.baseSize.width !== value) {

			this.baseSize.width = value;
			this.dispatchEvent({ type: "change" });
			this.resizable.setSize(this.baseSize.width, this.baseSize.height);

		}

	}

	/**
	 * Returns the base width.
	 *
	 * @deprecated Use baseWidth instead.
	 * @return {Number} The base width.
	 */

	getBaseWidth() {

		return this.baseWidth;

	}

	/**
	 * Sets the base width.
	 *
	 * @deprecated Use baseWidth instead.
	 * @param {Number} value - The width.
	 */

	setBaseWidth(value) {

		this.baseWidth = value;

	}

	/**
	 * The base height.
	 *
	 * @type {Number}
	 */

	get baseHeight() {

		return this.baseSize.height;

	}

	set baseHeight(value) {

		if(this.baseSize.height !== value) {

			this.baseSize.height = value;
			this.dispatchEvent({ type: "change" });
			this.resizable.setSize(this.baseSize.width, this.baseSize.height);

		}

	}

	/**
	 * Returns the base height.
	 *
	 * @deprecated Use baseHeight instead.
	 * @return {Number} The base height.
	 */

	getBaseHeight() {

		return this.baseHeight;

	}

	/**
	 * Sets the base height.
	 *
	 * @deprecated Use baseHeight instead.
	 * @param {Number} value - The height.
	 */

	setBaseHeight(value) {

		this.baseHeight = value;

	}

	/**
	 * Sets the base size.
	 *
	 * @param {Number} width - The width.
	 * @param {Number} height - The height.
	 */

	setBaseSize(width, height) {

		if(this.baseSize.width !== width || this.baseSize.height !== height) {

			this.baseSize.set(width, height);
			this.dispatchEvent({ type: "change" });
			this.resizable.setSize(this.baseSize.width, this.baseSize.height);

		}

	}

	/**
	 * The preferred width.
	 *
	 * @type {Number}
	 */

	get preferredWidth() {

		return this.preferredSize.width;

	}

	set preferredWidth(value) {

		if(this.preferredSize.width !== value) {

			this.preferredSize.width = value;
			this.dispatchEvent({ type: "change" });
			this.resizable.setSize(this.baseSize.width, this.baseSize.height);

		}

	}

	/**
	 * Returns the preferred width.
	 *
	 * @deprecated Use preferredWidth instead.
	 * @return {Number} The preferred width.
	 */

	getPreferredWidth() {

		return this.preferredWidth;

	}

	/**
	 * Sets the preferred width.
	 *
	 * Use {@link Resizer.AUTO_SIZE} to automatically calculate the width based on the height and aspect ratio.
	 *
	 * @deprecated Use preferredWidth instead.
	 * @param {Number} value - The width.
	 */

	setPreferredWidth(value) {

		this.preferredWidth = value;

	}

	/**
	 * The preferred height.
	 *
	 * @type {Number}
	 */

	get preferredHeight() {

		return this.preferredSize.height;

	}

	set preferredHeight(value) {

		if(this.preferredSize.height !== value) {

			this.preferredSize.height = value;
			this.dispatchEvent({ type: "change" });
			this.resizable.setSize(this.baseSize.width, this.baseSize.height);

		}

	}

	/**
	 * Returns the preferred height.
	 *
	 * @deprecated Use preferredHeight instead.
	 * @return {Number} The preferred height.
	 */

	getPreferredHeight() {

		return this.preferredHeight;

	}

	/**
	 * Sets the preferred height.
	 *
	 * Use {@link Resizer.AUTO_SIZE} to automatically calculate the height based on the width and aspect ratio.
	 *
	 * @deprecated Use preferredHeight instead.
	 * @param {Number} value - The height.
	 */

	setPreferredHeight(value) {

		this.preferredHeight = value;

	}

	/**
	 * Sets the preferred size.
	 *
	 * @param {Number} width - The width.
	 * @param {Number} height - The height.
	 */

	setPreferredSize(width, height) {

		if(this.preferredSize.width !== width || this.preferredSize.height !== height) {

			this.preferredSize.set(width, height);
			this.dispatchEvent({ type: "change" });
			this.resizable.setSize(this.baseSize.width, this.baseSize.height);

		}

	}

	/**
	 * Copies the given resolution.
	 *
	 * @param {Resolution} resolution - The resolution.
	 */

	copy(resolution) {

		this.s = resolution.scale;
		this.baseSize.set(resolution.baseWidth, resolution.baseHeight);
		this.preferredSize.set(resolution.preferredWidth, resolution.preferredHeight);
		this.dispatchEvent({ type: "change" });
		this.resizable.setSize(this.baseSize.width, this.baseSize.height);

	}

	/**
	 * An auto sizing constant.
	 *
	 * Can be used to automatically calculate the width or height based on the original aspect ratio.
	 *
	 * @type {Number}
	 */

	static get AUTO_SIZE() {

		return AUTO_SIZE;

	}

}