import { SplatData } from "./SplatData"; import { Object3D } from "../core/Object3D"; import { Vector3 } from "../math/Vector3"; import { Quaternion } from "../math/Quaternion"; import { Converter } from "../utils/Converter"; import { Matrix4 } from "../math/Matrix4"; import { Box3 } from "../math/Box3"; class Splat extends Object3D { public selectedChanged: boolean = false; public colorTransformChanged: boolean = false; private _data: SplatData; private _selected: boolean = false; private _colorTransforms: Array = []; private _colorTransformsMap: Map = new Map(); private _bounds: Box3; recalculateBounds: () => void; constructor(splat: SplatData | undefined = undefined) { super(); this._data = splat || new SplatData(); this._bounds = new Box3( new Vector3(Infinity, Infinity, Infinity), new Vector3(-Infinity, -Infinity, -Infinity), ); this.recalculateBounds = () => { this._bounds = new Box3( new Vector3(Infinity, Infinity, Infinity), new Vector3(-Infinity, -Infinity, -Infinity), ); for (let i = 0; i < this._data.vertexCount; i++) { this._bounds.expand( new Vector3( this._data.positions[3 * i], this._data.positions[3 * i + 1], this._data.positions[3 * i + 2], ), ); } }; this.applyPosition = () => { this.data.translate(this.position); this.position = new Vector3(); }; this.applyRotation = () => { this.data.rotate(this.rotation); this.rotation = new Quaternion(); }; this.applyScale = () => { this.data.scale(this.scale); this.scale = new Vector3(1, 1, 1); }; this.recalculateBounds(); } saveToFile(name: string | null = null, format: "splat" | "ply" = "splat") { if (!document) return; if (!name) { const now = new Date(); name = `splat-${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}.${format}`; } const splatClone = this.clone(); splatClone.applyRotation(); splatClone.applyScale(); splatClone.applyPosition(); const data = splatClone.data.serialize(); let blob; if (format === "ply") { const plyData = Converter.SplatToPLY(data.buffer, splatClone.data.vertexCount); blob = new Blob([plyData], { type: "application/octet-stream" }); } else { blob = new Blob([data.buffer], { type: "application/octet-stream" }); } const link = document.createElement("a"); link.download = name; link.href = URL.createObjectURL(blob); link.click(); } get data() { return this._data; } get selected() { return this._selected; } set selected(selected: boolean) { if (this._selected !== selected) { this._selected = selected; this.selectedChanged = true; this.dispatchEvent(this._changeEvent); } } get colorTransforms() { return this._colorTransforms; } get colorTransformsMap() { return this._colorTransformsMap; } get bounds() { let center = this._bounds.center(); center = center.add(this.position); let size = this._bounds.size(); size = size.multiply(this.scale); return new Box3(center.subtract(size.divide(2)), center.add(size.divide(2))); } clone() { const splat = new Splat(this.data.clone()); splat.position = this.position.clone(); splat.rotation = this.rotation.clone(); splat.scale = this.scale.clone(); return splat; } } export { Splat };