UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

258 lines (257 loc) 9.3 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_BLUE, COLOR_GREEN, COLOR_RED } from "./color.js"; const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); const tmpV3 = new Vec3(); const tmpM1 = new Mat4(); class ViewCube extends EventHandler { static EVENT_CAMERAALIGN = "camera:align"; _size = 0; _svg; _group; _anchor = new Vec4(1, 1, 1, 1); _colorX = COLOR_RED.clone(); _colorY = COLOR_GREEN.clone(); _colorZ = COLOR_BLUE.clone(); _colorNeg = new Color(0.3, 0.3, 0.3); _radius = 10; _textSize = 10; _lineThickness = 2; _lineLength = 40; _shapes; constructor(anchor) { super(); 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 };