import { Vector2 } from "../maths/vec2.js";
import { Component } from "../components/component.js";
import { MouseInput } from "../events/mouseinput.js"
import { Maths } from "../maths/math.js"
/**
* UI Transform Component is responsible for the position, rotation and width and height of a UI element.
* Every UI element must have a transform component. Without a transform component, the UI element will not be rendered.
* Note Scale is not supported for UI elements.
* Thus normal Transform Component cannot be used for UI elements, as it has scale instead of width and height.
* All the UI Rendering is done with pixels, so the position and width and height are in pixels.
* Position vector is replaced by x and y.
* @class UITransform
* @extends Component
* @property {number} x=0 - The x position of the UI element.
* @property {number} y=0 - The y position of the UI element.
* @property {number} rotation=0 - The rotation of the UI element in degrees.
* @property {number} width=10 - The width of the UI element.
* @property {number} height=10 - The height of the UI element.
*/
export class Transform extends Component {
constructor(x = 0, y = 0, width = 10, height = 10, rot = 0) {
super();
this._properties.x = x;
this._properties.y = y;
this._properties.width = width;
this._properties.height = height;
this._properties.rot = rot;
}
/**
* X position of the UI element.
* @type {number}
*/
get x() {
return this._properties.x;
}
set x(x) {
this._properties.x = x;
}
/**
* Y position of the UI element.
* @type {number}
*/
get y() {
return this._properties.y;
}
set y(y) {
this._properties.y = y;
}
/**
* Width of the UI element.
* @type {number}
*/
get width() {
return this._properties.width;
}
set width(width) {
this._properties.width = width;
}
/**
* Height of the UI element.
*/
get height() {
return this._properties.height;
}
set height(height) {
this._properties.height = height;
}
/**
* Rotation of the UI element in degrees.
*/
get rotation() {
return this._properties.rot;
}
set rotation(rot) {
this._properties.rot = rot;
}
/**
* Center the UI element. Set/Get the x and y position of the UI element so that center of the UI element is as specified.
*/
get center() {
let center = new Vector2(this._properties.x + this._properties.width / 2, this._properties.y + this._properties.height / 2);
return center;
}
set center(center) {
this._properties.x = center.x - this._properties.width / 2;
this._properties.y = center.y - this._properties.height / 2;
}
/**
* Align the UI element to the parent as per the specified mode.
* If the UI element has no parent, it will align to the window.
*
* @param {string} mode="center" - The mode of alignment.
* | Mode | Description |
* | --- | --- |
* | "top" | Align the UI element to the top of the parent. |
* | "middle" | Align the UI element to the middle of the parent. |
* | "bottom" | Align the UI element to the bottom of the parent. |
* | "left" | Align the UI element to the left of the parent. |
* | "right" | Align the UI element to the right of the parent. |
* | "center" | Align the UI element to the center of the parent. |
*
* @example
* // Align the UI element to the top of the parent.
* transform.align("top");
*
*/
Align(mode = "center") {
let parent = this.entity.parent;
if (parent != undefined) {
var parentTransform = parent.GetComponent(Transform);
var x = parentTransform.x;
var y = parentTransform.y;
var width = parentTransform.width;
var height = parentTransform.height;
}
else {
var x = 0;
var y = 0;
var width = window.innerWidth * Maths.PIXEL_TO_METER;
var height = window.innerHeight * Maths.PIXEL_TO_METER;
}
switch (mode) {
case "left":
this._properties.x = x;
break;
case "right":
this._properties.x = x + width - this._properties.width;
break;
case "top":
this._properties.y = y;
break;
case "bottom":
this._properties.y = y + height - this._properties.height;
break;
case "center":
this._properties.x = x + width / 2 - this._properties.width / 2;
break;
case "middle":
this._properties.y = y + height / 2 - this._properties.height / 2;
break;
}
}
/**
* Fill the Entity with the parent as per the specified mode.
* If the UI element has no parent, it will fill the window.
*
* @param {string} mode="both" - The mode of filling.
* | Mode | Description |
* | --- | --- |
* | "both" | Fill the UI element to the size of the parent. |
* | "x" | Fill the UI element to the width of the parent. |
* | "y" | Fill the UI element to the height of the parent. |
*
* @param {number} padX=0 - Padding in the x direction.
* @param {number} padY=0 - Padding in the y direction.
*
* @example
* // Fill the UI element to the size of the parent.
* this.entity.getComponent(UITransform).fill();
*
*/
Fill(mode = "both", padX = 0, padY = 0) {
let parent = this.entity.parent;
if (parent && parent.HasComponent(Transform)) {
var parentTransform = parent.GetComponent(Transform);
var x = parentTransform.x;
var y = parentTransform.y;
var width = parentTransform.width;
var height = parentTransform.height;
} else {
var x = 0;
var y = 0;
var width = window.innerWidth * Maths.PIXEL_TO_METER;
var height = window.innerHeight * Maths.PIXEL_TO_METER;
}
switch (mode) {
case "both":
this._properties.x = x + padX;
this._properties.y = y + padY;
this._properties.width = width - padX * 2;
this._properties.height = height - padY * 2;
break;
case "x":
this._properties.x = x + padX;
this._properties.width = width - padX * 2;
break;
case "y":
this._properties.y = y + padY;
this._properties.height = height - padY * 2;
break;
}
}
#isPointInside(point) {
return point.x >= this._properties.x && point.x <= this._properties.x + this._properties.width && point.y >= this._properties.y && point.y <= this._properties.y + this._properties.height;
}
/**
* Check if the UI element is being hovered over.
* @returns {boolean} - True if the UI element is being hovered over.
*/
IsHovered() {
return this.#isPointInside(Maths.PixelToMeterVector2(MouseInput.GetPosition()));
}
/**
* Check if the UI element is being clicked.
* @returns {boolean} - True if the UI element is being clicked.
*/
IsClicked() {
return this.IsHovered() && MouseInput.IsLeftClicked();
}
}