playcanvas
Version:
PlayCanvas WebGL game engine
280 lines (277 loc) • 10 kB
JavaScript
import { EventHandler } from '../../core/event-handler.js';
import { Color } from '../../core/math/color.js';
import { Mat4 } from '../../core/math/mat4.js';
import { Vec3 } from '../../core/math/vec3.js';
import { Vec4 } from '../../core/math/vec4.js';
import { COLOR_RED, COLOR_GREEN, COLOR_BLUE } from './color.js';
const tmpV1 = new Vec3();
const tmpV2 = new Vec3();
const tmpV3 = new Vec3();
const tmpM1 = new Mat4();
class ViewCube extends EventHandler {
static{
this.EVENT_CAMERAALIGN = 'camera:align';
}
constructor(anchor){
super(), this._size = 0, this._anchor = new Vec4(1, 1, 1, 1), this._colorX = COLOR_RED.clone(), this._colorY = COLOR_GREEN.clone(), this._colorZ = COLOR_BLUE.clone(), this._colorNeg = new Color(0.3, 0.3, 0.3), this._radius = 10, this._textSize = 10, this._lineThickness = 2, this._lineLength = 40;
this.dom = document.createElement('div');
this.dom.id = 'view-cube-container';
this.dom.style.cssText = [
'position: absolute',
'margin: auto',
'pointer-events: none'
].join(';');
document.body.appendChild(this.dom);
this.anchor = anchor ?? this._anchor;
this._svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
this._svg.id = 'view-cube-svg';
this._group = document.createElementNS(this._svg.namespaceURI, 'g');
this._svg.appendChild(this._group);
this._resize();
const colX = this._colorX.toString(false);
const colY = this._colorY.toString(false);
const colZ = this._colorZ.toString(false);
this._shapes = {
nx: this._circle(colX),
ny: this._circle(colY),
nz: this._circle(colZ),
px: this._circle(colX, true, 'X'),
py: this._circle(colY, true, 'Y'),
pz: this._circle(colZ, true, 'Z'),
xaxis: this._line(colX),
yaxis: this._line(colY),
zaxis: this._line(colZ)
};
this._shapes.px.children[0].addEventListener('pointerdown', ()=>{
this.fire(ViewCube.EVENT_CAMERAALIGN, Vec3.RIGHT);
});
this._shapes.py.children[0].addEventListener('pointerdown', ()=>{
this.fire(ViewCube.EVENT_CAMERAALIGN, Vec3.UP);
});
this._shapes.pz.children[0].addEventListener('pointerdown', ()=>{
this.fire(ViewCube.EVENT_CAMERAALIGN, Vec3.BACK);
});
this._shapes.nx.children[0].addEventListener('pointerdown', ()=>{
this.fire(ViewCube.EVENT_CAMERAALIGN, Vec3.LEFT);
});
this._shapes.ny.children[0].addEventListener('pointerdown', ()=>{
this.fire(ViewCube.EVENT_CAMERAALIGN, Vec3.DOWN);
});
this._shapes.nz.children[0].addEventListener('pointerdown', ()=>{
this.fire(ViewCube.EVENT_CAMERAALIGN, Vec3.FORWARD);
});
this.dom.appendChild(this._svg);
}
set anchor(value) {
this._anchor.copy(value);
this.dom.style.top = this._anchor.x ? '0px' : 'auto';
this.dom.style.right = this._anchor.y ? '0px' : 'auto';
this.dom.style.bottom = this._anchor.z ? '0px' : 'auto';
this.dom.style.left = this._anchor.w ? '0px' : 'auto';
}
get anchor() {
return this._anchor;
}
set colorX(value) {
this._colorX.copy(value);
this._shapes.px.children[0].setAttribute('fill', this._colorX.toString(false));
this._shapes.px.children[0].setAttribute('stroke', this._colorX.toString(false));
this._shapes.nx.children[0].setAttribute('stroke', this._colorX.toString(false));
this._shapes.xaxis.setAttribute('stroke', this._colorX.toString(false));
}
get colorX() {
return this._colorX;
}
set colorY(value) {
this._colorY.copy(value);
this._shapes.py.children[0].setAttribute('fill', this._colorY.toString(false));
this._shapes.py.children[0].setAttribute('stroke', this._colorY.toString(false));
this._shapes.ny.children[0].setAttribute('stroke', this._colorY.toString(false));
this._shapes.yaxis.setAttribute('stroke', this._colorY.toString(false));
}
get colorY() {
return this._colorY;
}
set colorZ(value) {
this._colorZ.copy(value);
this._shapes.pz.children[0].setAttribute('fill', this._colorZ.toString(false));
this._shapes.pz.children[0].setAttribute('stroke', this._colorZ.toString(false));
this._shapes.nz.children[0].setAttribute('stroke', this._colorZ.toString(false));
this._shapes.zaxis.setAttribute('stroke', this._colorZ.toString(false));
}
get colorZ() {
return this._colorZ;
}
set colorNeg(value) {
this._colorNeg.copy(value);
this._shapes.px.children[0].setAttribute('fill', this._colorNeg.toString(false));
this._shapes.py.children[0].setAttribute('fill', this._colorNeg.toString(false));
this._shapes.pz.children[0].setAttribute('fill', this._colorNeg.toString(false));
}
get colorNeg() {
return this._colorNeg;
}
set radius(value) {
this._radius = value;
this._shapes.px.children[0].setAttribute('r', `${value}`);
this._shapes.py.children[0].setAttribute('r', `${value}`);
this._shapes.pz.children[0].setAttribute('r', `${value}`);
this._shapes.nx.children[0].setAttribute('r', `${value}`);
this._shapes.ny.children[0].setAttribute('r', `${value}`);
this._shapes.nz.children[0].setAttribute('r', `${value}`);
this._resize();
}
get radius() {
return this._radius;
}
set textSize(value) {
this._textSize = value;
this._shapes.px.children[1].setAttribute('font-size', `${value}`);
this._shapes.py.children[1].setAttribute('font-size', `${value}`);
this._shapes.pz.children[1].setAttribute('font-size', `${value}`);
}
get textSize() {
return this._textSize;
}
set lineThickness(value) {
this._lineThickness = value;
this._shapes.xaxis.setAttribute('stroke-width', `${value}`);
this._shapes.yaxis.setAttribute('stroke-width', `${value}`);
this._shapes.zaxis.setAttribute('stroke-width', `${value}`);
this._shapes.px.children[0].setAttribute('stroke-width', `${value}`);
this._shapes.py.children[0].setAttribute('stroke-width', `${value}`);
this._shapes.pz.children[0].setAttribute('stroke-width', `${value}`);
this._shapes.nx.children[0].setAttribute('stroke-width', `${value}`);
this._shapes.ny.children[0].setAttribute('stroke-width', `${value}`);
this._shapes.nz.children[0].setAttribute('stroke-width', `${value}`);
this._resize();
}
get lineThickness() {
return this._lineThickness;
}
set lineLength(value) {
this._lineLength = value;
this._resize();
}
get lineLength() {
return this._lineLength;
}
_resize() {
this._size = 2 * (this.lineLength + this.radius + this.lineThickness);
this.dom.style.width = `${this._size}px`;
this.dom.style.height = `${this._size}px`;
this._svg.setAttribute('width', `${this._size}`);
this._svg.setAttribute('height', `${this._size}`);
this._group.setAttribute('transform', `translate(${this._size * 0.5}, ${this._size * 0.5})`);
}
_transform(group, x, y) {
group.setAttribute('transform', `translate(${x * this._lineLength}, ${y * this._lineLength})`);
}
_x2y2(line, x, y) {
line.setAttribute('x2', `${x * this._lineLength}`);
line.setAttribute('y2', `${y * this._lineLength}`);
}
_line(color) {
const result = document.createElementNS(this._svg.namespaceURI, 'line');
result.setAttribute('stroke', color);
result.setAttribute('stroke-width', `${this._lineThickness}`);
this._group.appendChild(result);
return result;
}
_circle(color, fill = false, text) {
const group = document.createElementNS(this._svg.namespaceURI, 'g');
const circle = document.createElementNS(this._svg.namespaceURI, 'circle');
circle.setAttribute('fill', fill ? color : this._colorNeg.toString(false));
circle.setAttribute('stroke', color);
circle.setAttribute('stroke-width', `${this._lineThickness}`);
circle.setAttribute('r', `${this._radius}`);
circle.setAttribute('cx', '0');
circle.setAttribute('cy', '0');
circle.setAttribute('pointer-events', 'all');
group.appendChild(circle);
if (text) {
const t = document.createElementNS(this._svg.namespaceURI, 'text');
t.setAttribute('font-size', `${this._textSize}`);
t.setAttribute('font-family', 'Arial');
t.setAttribute('font-weight', 'bold');
t.setAttribute('text-anchor', 'middle');
t.setAttribute('alignment-baseline', 'central');
t.textContent = text;
group.appendChild(t);
}
group.setAttribute('cursor', 'pointer');
this._group.appendChild(group);
return group;
}
update(cameraMatrix) {
if (!this._size) {
return;
}
tmpM1.invert(cameraMatrix);
tmpM1.getX(tmpV1);
tmpM1.getY(tmpV2);
tmpM1.getZ(tmpV3);
this._transform(this._shapes.px, tmpV1.x, -tmpV1.y);
this._transform(this._shapes.nx, -tmpV1.x, tmpV1.y);
this._transform(this._shapes.py, tmpV2.x, -tmpV2.y);
this._transform(this._shapes.ny, -tmpV2.x, tmpV2.y);
this._transform(this._shapes.pz, tmpV3.x, -tmpV3.y);
this._transform(this._shapes.nz, -tmpV3.x, tmpV3.y);
this._x2y2(this._shapes.xaxis, tmpV1.x, -tmpV1.y);
this._x2y2(this._shapes.yaxis, tmpV2.x, -tmpV2.y);
this._x2y2(this._shapes.zaxis, tmpV3.x, -tmpV3.y);
const order = [
{
n: [
'xaxis',
'px'
],
value: tmpV1.z
},
{
n: [
'yaxis',
'py'
],
value: tmpV2.z
},
{
n: [
'zaxis',
'pz'
],
value: tmpV3.z
},
{
n: [
'nx'
],
value: -tmpV1.z
},
{
n: [
'ny'
],
value: -tmpV2.z
},
{
n: [
'nz'
],
value: -tmpV3.z
}
].sort((a, b)=>a.value - b.value);
const fragment = document.createDocumentFragment();
order.forEach((o)=>{
o.n.forEach((n)=>{
fragment.appendChild(this._shapes[n]);
});
});
this._group.appendChild(fragment);
}
destroy() {
this.dom.remove();
this.off();
}
}
export { ViewCube };