UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

280 lines (277 loc) 10 kB
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 };