@threepipe/plugin-svg-renderer
Version:
Plugins for SVG Rendering of 3d objects for Threepipe
114 lines (99 loc) • 3.82 kB
text/typescript
import {
AViewerPluginSync,
type IViewerEvent,
ThreeViewer,
uiButton,
uiConfig,
uiFolderContainer,
uiToggle,
} from 'threepipe'
import {BasicSVGRenderer} from './basic/BasicSVGRenderer'
/**
* SVG rendering of 3d objects using SVGRenderer from three/addons
*/
export class BasicSVGRendererPlugin extends AViewerPluginSync {
static readonly PluginType = 'BasicSVGRendererPlugin'
enabled = true
readonly renderer = new BasicSVGRenderer()
/**
* @param enabled
* @param autoAddToContainer - automatically add the svg to the viewer container and style it same as the viewer is position is absolute
*/
constructor(enabled = true, readonly autoAddToContainer = true) {
super()
this._onResize = this._onResize.bind(this)
this.enabled = enabled
this.renderer.domElement.style.position = 'absolute'
this.renderer.domElement.style.display = 'none'
}
protected _lastStyles?: string = undefined
onAdded(viewer: ThreeViewer) {
super.onAdded(viewer)
this.renderer.setSize(viewer.canvas.clientWidth, viewer.canvas.clientHeight)
this._refreshParams()
if (this.autoAddToContainer) {
viewer.container.prepend(this.renderer.domElement) // behind the canvas so that we get pointer events
const element = this.renderer.domElement
element.style.pointerEvents = 'none'
const canvasStyles = getComputedStyle(viewer.canvas)
if (canvasStyles.position === 'absolute') {
this._lastStyles = element.style.cssText
// copy styles from canvas to svg so it looks the same.
element.style.top = canvasStyles.top
element.style.left = canvasStyles.left
element.style.width = canvasStyles.width
element.style.height = canvasStyles.height
// element.style.zIndex = '999999' // svg should be behind the canvas
} else {
this._viewer?.console.warn('BasicSVGRendererPlugin: canvas position should be absolute for proper rendering')
}
viewer.renderManager.addEventListener('resize', this._onResize)
}
this.renderer.domElement.style.display = this.enabled ? '' : 'none'
}
onRemove(viewer: ThreeViewer) {
super.onRemove(viewer)
if (this.autoAddToContainer) {
viewer.container.removeChild(this.renderer.domElement)
}
if (this._lastStyles !== undefined) {
this.renderer.domElement.style.cssText = this._lastStyles
this._lastStyles = undefined
}
viewer.renderManager.removeEventListener('resize', this._onResize)
this.renderer.domElement.style.display = 'none'
}
autoRender = true
render() {
if (!this._viewer) return
if (this.isDisabled()) return
this.renderer.render(this._viewer.scene, this._viewer.scene.mainCamera)
}
download() {
const svg = this.renderer.domElement.outerHTML
const blob = new Blob([svg], {type: 'image/svg+xml'})
this._viewer?.exportBlob(blob, 'scene.svg')
}
protected _viewerListeners = {
postRender: (_: IViewerEvent)=>{
if (this.autoRender) this.render()
},
}
get svgNode() {
return this.renderer.domElement
}
protected _refreshParams() {
if (this.isDisabled()) return
this.renderer.setQuality('medium')
}
protected _onResize() {
if (!this._viewer) return
this.renderer.setSize(this._viewer.canvas.clientWidth, this._viewer.canvas.clientHeight)
}
}