components_renderable_shape.js
import { Renderable } from "./renderable.js";
import { Color } from "../../basic/color.js";
import { Transform } from "../transform.js";
import { Maths } from "../../maths/math.js";
/**
* Shape Component is responsible for rendering a shape to the screen.
* Basic Shapes such as circles, rectangles, etc. can be rendered using this component.
* ```
* Shape.CIRCLE = 0;
* Shape.RECTANGLE = 1;
* Shape.LINE = 2;
* Shape.TRIANGLE = 3;
* Shape.POLYGON = 4;
* ```
*
* @class Shape
* @extends Renderable
* @property {number} geometry=Shape.CIRCLE - The Geometry of the shape.
* @property {Color} color=Color.fuchsia - The color of the shape.
* @property {boolean} fill=true - Whether the shape is filled or not.
* @property {Object} param - More Optional parameters for the shape.
* @property {number} param.radius=10 - The radius of the shape.
* @property {number} param.width=10 - The width of the shape.
* @property {number} param.height=10 - The height of the shape.
* @property {Color} param.outline=Color.black - The color of the outline of the shape.
* @property {number} param.thickness=1 - The thickness of the outline of the shape.
*
* @example
* // Create a shape component
* let shape = new Shape(Shape.CIRCLE,Color.fuchsia,true,{radius:10});
*
* // Add the shape component to an entity
* entity.addComponent(shape);
*/
export class Shape extends Renderable {
_properties = {
geometry: Shape.CIRCLE,
param: { radius: 10, color: Color.fuchsia, fill: true, width: 10, height: 10, outline: Color.black, thickness: 1, points: [] }
}
#transform;
constructor(geometry = Shape.CIRCLE, param = { radius: 10, color: Color.fuchsia, fill: true, width: 10, height: 10, outline: Color.black, thickness: 1, points: [] }) {
super();
this._properties.geometry = geometry;
param = Object.assign(this._properties.param, param);
}
/**
* Geometry of the shape. Can be one of the following:
* ```
* Shape.CIRCLE = 0;
* Shape.RECTANGLE = 1;
* Shape.LINE = 2;
* Shape.TRIANGLE = 3;
* Shape.POLYGON = 4;
* ```
* @type {number}
*/
get geometry() {
return this._properties.geometry;
}
set geometry(geometry) {
this._properties.geometry = geometry;
}
/**
* Color of the shape.
* @type {Color}
*
*/
get color() {
return this._properties.param.color;
}
set color(color) {
this._properties.param.color = color;
}
/**
* Fill the shape or not.
* @type {boolean}
*/
get fill() {
return this._properties.param.fill;
}
set fill(fill) {
this._properties.param.fill = fill;
}
/**
* If shape is Circle. The radius of the circle.
* @type {number}
*/
get radius() {
return this._properties.param.radius;
}
set radius(radius) {
this._properties.param.radius = radius;
}
/**
* If the shape is Rectangle. The width of the rectangle.
* @type {number}
*/
get width() {
return this._properties.param.width;
}
set width(width) {
this._properties.param.width = width;
}
/**
* If the shape is Rectangle. The height of the rectangle.
*/
get height() {
return this._properties.param.height;
}
set height(height) {
this._properties.param.height = height;
}
/**
* Outline of the shape.
* @type {Color}
*/
get outline() {
return this._properties.param.outline;
}
set outline(outline) {
this._properties.param.outline = outline;
}
/**
* The thickness of the outline of the shape.
* @type {number}
*/
get thickness() {
return this._properties.param.thickness;
}
set thickness(thickness) {
this._properties.param.thickness = thickness;
}
/** @private */
draw(ctx) {
super.draw(ctx);
if (this.#transform == null)
this.#transform = this.entity.GetComponent(Transform);
let position = Maths.MeterToPixelVector2(this.#transform.position);
let rotation = this.#transform.rotation;
let radius = this._properties.param.radius;
let scale = Maths.MeterToPixelVector2(this.#transform.scale);
let param = this._properties.param;
let fill = this._properties.param.fill;
ctx.save();
ctx.translate(position.x, position.y);
ctx.rotate(rotation);
ctx.scale(scale.x, scale.y);
ctx.globalCompositeOperation = this.blendMode;
ctx.filter = this.filter;
ctx.fillStyle = param.color.toString();
if (param.outline) ctx.strokeStyle = param.outline.toString();
if (param.thickness) ctx.lineWidth = param.thickness;
switch (this._properties.geometry) {
case Shape.CIRCLE:
ctx.beginPath();
ctx.arc(0, 0, radius, 0, Math.PI * 2);
break;
case Shape.RECTANGLE:
ctx.beginPath();
ctx.rect(-param.width / 2, -param.height / 2, param.width, param.height);
break;
case Shape.LINE:
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.strokeStyle = param.color.toString();
ctx.lineTo(param.width, param.height);
ctx.stroke();
break;
case Shape.TRIANGLE:
ctx.beginPath();
ctx.moveTo(0, -param.height / 2);
ctx.lineTo(param.width / 2, param.height / 2);
ctx.lineTo(-param.width / 2, param.height / 2);
ctx.closePath();
break;
case Shape.POLYGON:
ctx.beginPath();
ctx.moveTo(param.points[0].x, param.points[0].y);
for (let i = 1; i < param.points.length; i++) {
ctx.lineTo(param.points[i].x, param.points[i].y);
}
ctx.closePath();
break;
}
if (fill) {
ctx.fill();
if (param.outline) ctx.stroke();
} else {
ctx.stroke();
}
let children = this.entity.GetComponentsInChildren(Renderable);
for (let i = 0; i < children.length; i++) {
children[i].draw(ctx);
}
ctx.restore();
}
}
Shape.CIRCLE = 0;
Shape.RECTANGLE = 1;
Shape.LINE = 2;
Shape.TRIANGLE = 3;
Shape.POLYGON = 4;