import { Rect } from "./../geometries/rectangle.ts";
import type { Color } from "../math/color.ts";
import { ColorMatrix } from "../math/color_matrix.ts";
import { Matrix3d } from "../math/matrix3d.ts";
import { Vector2d } from "../math/vector2d.ts";
import { Vector3d } from "../math/vector3d.ts";
import { Bounds } from "./../physics/bounds.ts";
import type Container from "./../renderable/container.js";
import Renderable from "./../renderable/renderable.js";
import type Renderer from "./../video/renderer.js";
import ColorMatrixEffect from "./../video/webgl/effects/colorMatrix.js";
import type CameraEffect from "./effects/camera_effect.ts";
/**
* @import Entity from "./../renderable/entity/entity.js";
* @import Sprite from "./../renderable/sprite.js";
* @import NineSliceSprite from "./../renderable/nineslicesprite.js";
*/
interface AxisEnum {
readonly NONE: 0;
readonly HORIZONTAL: 1;
readonly VERTICAL: 2;
readonly BOTH: 3;
}
/**
* a 2D orthographic camera
* @category Camera
* @example
* // create a minimap camera in the top-right corner showing the full level
* const minimap = new Camera2d(0, 0, 180, 100);
* minimap.name = "minimap";
* minimap.screenX = app.viewport.width - 190;
* minimap.screenY = 10;
* minimap.autoResize = false;
* minimap.setBounds(0, 0, levelWidth, levelHeight);
* minimap.zoom = Math.min(180 / levelWidth, 100 / levelHeight);
*
* // add the camera to the current stage
* this.cameras.set("minimap", minimap);
*/
export default class Camera2d extends Renderable {
/**
* Preferred `Container.sortOn` mode for this camera type. Read by
* `Application` (and `Stage`) at bootstrap to initialize the world
* container's sort. Camera2d uses `"z"` (painter's-algorithm 2D
* layering, higher pos.z draws on top); `Camera3d` overrides to
* `"depth"` (camera-distance sort required by perspective).
* Subclasses can override to declare their own preferred mode.
*/
static defaultSortOn: "x" | "y" | "z" | "depth";
/**
* Axis definition
* NONE no axis
* HORIZONTAL horizontal axis only
* VERTICAL vertical axis only
* BOTH both axis
*/
AXIS: AxisEnum;
/**
* Camera bounds
*/
bounds: Bounds;
/**
* enable or disable damping
* @default true
*/
smoothFollow: boolean;
/**
* Camera damping for smooth follow `[0 .. 1]`. `1` snaps the camera
* to the target every frame (no smoothing); lower values produce a
* trailing follow — `0.1` is a soft springy follow, `0.5` is snappy.
*
* **Frame-rate independent (since 19.7 / #1478).** The value is the
* fraction of the gap covered per frame at the engine's target
* framerate ({@link timer.maxfps}, default 60). The underlying
* implementation switched from `pos.lerp` to `pos.damp` with
* `lambda = -ln(1 - damping) * timer.maxfps`, so the legacy
* per-frame fraction is recovered exactly at the target rate AND
* the wall-clock convergence stays constant when the actual frame
* rate drifts (slow machine, throttled tab, high-refresh display).
* Existing `damping` tuning carries over with no changes.
* @default 1.0
*/
damping: number;
/**
* the closest point relative to the camera. Widened from `-1000` in
* 19.7 to accommodate sprites at large `depth` values now that
* `aVertex.z` participates in clip-space (PR A): the default
* `Container.autoDepth = true` assigns `pos.z = childCount` so any
* container with >1000 children would otherwise clip-cull, and the
* common Y-sort pattern `sprite.depth = sprite.pos.y` exceeds 1000
* on tall maps. Override per-camera if you need tighter z clipping.
* @default -1e6
*/
near: number;
/**
* the furthest point relative to the camera. Widened from `1000` in
* 19.7 — see {@link Camera2d#near}.
* @default 1e6
*/
far: number;
/**
* the x position on the screen where this camera viewport is rendered.
* @default 0
*/
screenX: number;
/**
* the y position on the screen where this camera viewport is rendered.
* @default 0
*/
screenY: number;
/**
* @ignore
*/
_zoom: number;
/**
* the world-space projection matrix for non-default cameras (offset/zoomed).
* Maps world coordinates to the camera's screen viewport.
*/
worldProjection: Matrix3d;
/**
* the screen-space projection matrix for non-default cameras.
* Maps coordinates so that (0,0) aligns with the camera's screenX/screenY position.
* Used for rendering floating elements (e.g. background layers) in the correct screen area.
*/
screenProjection: Matrix3d;
/**
* Whether this camera should automatically resize when the canvas resizes.
* Set to false for non-default cameras with fixed dimensions
* (e.g. minimap, split-screen viewports).
* @default true
*/
autoResize: boolean;
/**
* cached world view bounds
* @ignore
*/
_worldView: Bounds;
/**
* the default camera projection matrix
* (2d cameras use an orthographic projection by default).
*/
projectionMatrix: Matrix3d;
/**
* the invert camera transform used to unproject points
* @ignore
*/
invCurrentTransform: Matrix3d;
/** offset for shake effect */
offset: Vector2d;
/** target to follow */
target: Vector2d | Vector3d | null;
/** default value follow */
follow_axis: number;
/**
* active camera effects (shake, fade, etc.)
*/
cameraEffects: CameraEffect[];
/**
* A built-in color transformation matrix applied as the final post-processing pass.
* Provides convenient color grading (brightness, contrast, saturation, etc.)
* that is always applied after any effects added via {@link addPostEffect}.
* When set to identity (default), no effect is applied and there is zero overhead.
* @example
* // warm HDR-like color grading
* camera.colorMatrix.contrast(1.2).saturate(1.1);
* // reset to no color grading
* camera.colorMatrix.identity();
*/
colorMatrix: ColorMatrix;
/** @ignore */
_colorMatrixEffect: ColorMatrixEffect | null;
/** the camera deadzone */
deadzone: Rect;
/**
* @param minX - start x offset
* @param minY - start y offset
* @param maxX - end x offset
* @param maxY - end y offset
*/
constructor(minX: number, minY: number, maxX: number, maxY: number);
/** @ignore */
_updateProjectionMatrix(): void;
/** @ignore */
_followH(target: Vector2d | Vector3d): number;
/** @ignore */
_followV(target: Vector2d | Vector3d): number;
/**
* the zoom level of this camera.
* Values less than 1 zoom out (show more of the world),
* values greater than 1 zoom in (show less of the world).
* @default 1
* @example
* // zoom out to show the full level in a 180x100 minimap
* camera.zoom = Math.min(180 / levelWidth, 100 / levelHeight);
*/
get zoom(): number;
set zoom(value: number);
/**
* Whether this camera is using default settings (no screen offset, no zoom).
* Non-default cameras use custom projections for viewport positioning and scaling.
* @returns true if this camera has no screen offset and zoom is 1
*/
get isDefault(): boolean;
/**
* The world-space bounds currently visible through this camera,
* taking into account position and zoom level.
* @returns the visible world area
*/
get worldView(): Bounds;
/**
* Set the camera screen position and size in a single call.
* @param x - x position on screen
* @param y - y position on screen
* @param [w] - width (defaults to current width)
* @param [h] - height (defaults to current height)
* @returns this camera for chaining
*/
setViewport(x: number, y: number, w?: number, h?: number): this;
/**
* reset the camera position to specified coordinates
* @param [x=0] - initial position of the camera on the x axis
* @param [y=0] - initial position of the camera on the y axis
*/
reset(x?: number, y?: number): void;
/**
* change the deadzone settings.
* the "deadzone" defines an area within the current camera in which
* the followed renderable can move without scrolling the camera.
* @see {@link follow}
* @param w - deadzone width
* @param h - deadzone height
*/
setDeadzone(w: number, h: number): void;
/**
* resize the camera
* @param w - new width of the camera
* @param h - new height of the camera
* @returns this camera
*/
resize(w: number, h: number): this;
/**
* set the camera boundaries (set to the world limit by default).
* the camera is bound to the given coordinates and cannot move/be scrolled outside of it.
* @param x - world left limit
* @param y - world top limit
* @param w - world width limit
* @param h - world height limit
*/
setBounds(x: number, y: number, w: number, h: number): void;
/**
* set the camera to follow the specified renderable.
* (this will put the camera center around the given target)
* @param target - renderable or position vector to follow
* @param [axis=app.viewport.AXIS.BOTH] - Which axis to follow (see {@link Camera2d.AXIS})
* @param [damping=1] - default damping value
* @example
* // set the camera to follow this renderable on both axis, and enable damping
* app.viewport.follow(this, app.viewport.AXIS.BOTH, 0.1);
*/
follow(target: Renderable | Vector2d | Vector3d, axis?: number, damping?: number): void;
/**
* unfollow the current target
*/
unfollow(): void;
/**
* move the camera upper-left position by the specified offset.
* @see {@link focusOn}
* @param x - horizontal offset
* @param y - vertical offset
* @example
* // Move the camera up by four pixels
* app.viewport.move(0, -4);
*/
move(x: number, y: number): void;
/**
* move the camera upper-left position to the specified coordinates
* @see {@link focusOn}
* @param x - horizontal position
* @param y - vertical position
*/
moveTo(x: number, y: number): void;
/** @ignore */
updateTarget(dt?: number): void;
/** @ignore */
update(dt?: number): boolean;
/**
* shake the camera
* @param intensity - maximum offset that the screen can be moved
* while shaking
* @param duration - expressed in milliseconds
* @param [axis=app.viewport.AXIS.BOTH] - specify on which axis to apply the shake effect (see {@link Camera2d.AXIS})
* @param [onComplete] - callback once shaking effect is over
* @param [force] - if true this will override the current effect
* @example
* // shake it baby !
* app.viewport.shake(10, 500, app.viewport.AXIS.BOTH);
*/
shake(intensity: number, duration: number, axis?: number, onComplete?: () => void, force?: boolean): void;
/**
* fadeOut(flash) effect
* screen is filled with the specified color and slowly goes back to normal * @param color - a CSS color value * @param [duration=1000] - expressed in milliseconds * @param [onComplete] - callback once effect is over * @example * // fade the camera to white upon dying, reload the level, and then fade out back * app.viewport.fadeIn("#fff", 150, function() { * me.audio.play("die", false); * me.level.reload(); * app.viewport.fadeOut("#fff", 150); * }); */ fadeOut(color: Color | string, duration?: number, onComplete?: () => void): void; /** * fadeIn effect
* fade to the specified color
* @param color - a CSS color value
* @param [duration=1000] - expressed in milliseconds
* @param [onComplete] - callback once effect is over
* @example
* // flash the camera to white for 75ms
* app.viewport.fadeIn("#FFFFFF", 75);
*/
fadeIn(color: Color | string, duration?: number, onComplete?: () => void): void;
/**
* Add a camera effect to this camera.
* @param effect - the camera effect to add
* @returns the added effect
* @example
* camera.addCameraEffect(new ShakeEffect(camera, { intensity: 10, duration: 500 }));
*/
addCameraEffect