import {
PerspectiveCamera, Scene, WebGLRenderer
} from '../../node_modules/three/build/three.module.js';
import { Reporter } from '../reporter/reporter.js';
import { LoadableComponent } from '../package/component/loadable_component.js';
import { checkPropTypes, UUIDRegex } from '../lib.js';
import { LoadingBase } from '../package/loader/loading_base.js';
import { Export } from './export.js';
import { Composer } from '../scene/composer.js';
import { EffectComposer } from '../../node_modules/three/examples/jsm/postprocessing/EffectComposer.js';
import { View } from '../view/view.js';
import { DefaultView } from '../view/default_view.js';
import html2canvas from '../../node_modules/html2canvas/dist/html2canvas.esm.js';
/** @class */
class ImageExporter { //extends Export {
constructor(reporter, settings) {
this.revision = 1;
checkPropTypes(
settings,
{
view: View || DefaultView,
},
{}
);
this._view = settings.view
};
_dataURIToBlob( dataURI ) {
const binStr = window.atob( dataURI.split( ',' )[1] );
const len = binStr.length;
const arr = new Uint8Array( len );
for ( let i = 0; i < len; i++ ) {
arr[i] = binStr.charCodeAt( i );
}
return new window.Blob( [arr] );
};
_saveBlob(name, blob) {
const link = document.createElement("a");
link.download = name;
const url = URL.createObjectURL(blob);
link.href = url;
link.onclick = () => {
setTimeout(() => {
URL.revokeObjectURL(url);
link.removeAttribute("href");
}, 500);
};
link.click();
}
_defaultFileName(ext) {
const str = `${new Date().toLocaleDateString()} at ${new Date().toLocaleTimeString()}${ext}`;
return str.replace(/\//g, '-').replace(/:/g, '.');
};
async export(width, height, {
saveToFile,
transparent,
html,
extension,
fileName,
returnType = "dataUrl", // "dataUrl" or "blob"
quality // optional, for jpg/webp
} = {}) {
if (width == null) width = 800; // catches null + undefined
if (height == null) height = 600;
const originalAspect = this._view.perspectiveCamera.aspect;
this._view.perspectiveCamera.aspect = width / height;
this._view.perspectiveCamera.updateProjectionMatrix();
const ext = (extension ?? "png").toLowerCase();
const mime =
ext === "jpg" || ext === "jpeg" ? "image/jpeg" :
ext === "webp" ? "image/webp" :
"image/png";
if (transparent) this._view.renderer.setClearColor(0xffffff, 0);
await this._view.update();
this._view.renderer.setSize(width, height);
this._view.composer.setSize(width, height);
this._view.css2dRenderer.setSize(width, height);
this._view.composer.render(this._view.scene, this._view.perspectiveCamera, null, false);
let result = null;
if (html) {
const div = document.createElement("div");
div.style.width = `${width}px`;
div.style.height = `${height}px`;
div.id = "htmlCanvas";
div.style.zIndex = "-10";
if (!transparent) div.style.background = "white";
div.appendChild(this._view.renderer.domElement);
div.appendChild(this._view.css2dRenderer.domElement);
document.body.appendChild(div);
try {
this._view.composer.render(this._view.scene, this._view.perspectiveCamera, null, false);
this._view.css2dRenderer.render(this._view.scene, this._view.perspectiveCamera, null, false);
const mergedCanvas = await html2canvas(div, { backgroundColor: null });
const blob = await this._canvasToBlob(mergedCanvas, mime, quality);
if (saveToFile) {
const imageFileName = fileName ?? this._defaultFileName(`.${ext}`);
this._saveBlob(imageFileName, blob);
}
result = (returnType === "blob") ? blob : await this._blobToDataURL(blob);
} finally {
// restore the DOM even if html2canvas throws
this._view.domElement.appendChild(this._view.renderer.domElement);
this._view.domElement.appendChild(this._view.css2dRenderer.domElement);
div.remove();
}
} else {
const blob = await this._canvasToBlob(this._view.renderer.domElement, mime, quality);
if (saveToFile) {
const imageFileName = fileName ?? this._defaultFileName(`.${ext}`);
this._saveBlob(imageFileName, blob);
}
result = (returnType === "blob") ? blob : await this._blobToDataURL(blob);
}
if (transparent) this._view.renderer.setClearColor(0xffffff, 0);
this._view.perspectiveCamera.aspect = originalAspect;
this._view.perspectiveCamera.updateProjectionMatrix();
this._view.updateDOMElementDimensions();
await this._view.update();
return result;
}
// Helpers
_canvasToBlob(canvas, mime = "image/png", quality) {
return new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
if (blob) return resolve(blob);
// fallback for contexts where toBlob() yields null
try {
const dataUrl = canvas.toDataURL(mime, quality);
resolve(this._dataURIToBlob(dataUrl));
} catch (e) {
reject(new Error("canvas.toBlob() returned null"));
}
}, mime, quality);
});
}
_blobToDataURL(blob) {
return new Promise((resolve, reject) => {
const r = new FileReader();
r.onload = () => resolve(r.result);
r.onerror = reject;
r.readAsDataURL(blob);
});
}
}
export { ImageExporter }