src/three/Background.js
import RoundRectCurve from './RoundRectCurve.js'
/**
Background extends THREE.Mesh (an Object3D) and is used to render a background panel for a Three node.
Unlike the DOM's background, this Background is designed to float a few centimeters behind the node's BorderLine.
*/
function Background(width = 0, height = 0, radius = [0, 0, 0, 0]) {
THREE.Mesh.call(
this,
new BackgroundGeometry(width, height, radius),
new THREE.MeshStandardMaterial({
side: THREE.DoubleSide
}),
undefined
)
this.name = 'Background'
// Shadow SOM nodes are ignored during some layout calculations
this.shadowSOM = true
}
Background.prototype = Object.create(THREE.Mesh.prototype)
Background.prototype.constructor = Background
function BackgroundGeometry(width = 0, height = 0, radius = [0, 0, 0, 0]) {
THREE.BufferGeometry.call(this)
this.setParams(width, height, radius)
}
BackgroundGeometry.prototype = Object.create(THREE.BufferGeometry.prototype)
BackgroundGeometry.prototype.constructor = BackgroundGeometry
BackgroundGeometry.CurveDivisions = 15
Object.defineProperty(BackgroundGeometry.prototype, 'width', {
get: function() {
return this._width
},
set: function(val) {
if (val < 0) return
this.setParams(val, this._height, this._radius)
}
})
Object.defineProperty(BackgroundGeometry.prototype, 'height', {
get: function() {
return this._height
},
set: function(val) {
if (val < 0) return
this.setParams(this._width, val, this._radius)
}
})
Object.defineProperty(BackgroundGeometry.prototype, 'radius', {
get: function() {
return this._radius
},
set: function(val) {
if (val < 0) return
this.setParams(this._width, this._height, val)
}
})
BackgroundGeometry.prototype.setContentSize = function(width, height) {
this.setParams(width, height, this._radius)
}
BackgroundGeometry.prototype.setParams = function(width, height, radius) {
if (this._width === width && this._height === height && this._radius === radius) return
this._width = Math.max(0, width)
this._height = Math.max(0, height)
this._radius = Math.max(0, radius[0]) // TODO handle per-corner radius values
this._updatePoints()
}
BackgroundGeometry.prototype._updatePoints = function() {
this.clearGroups()
this.removeAttribute('position')
if (this._width <= 0 || this._height <= 0) return
const points = RoundRectCurve.generateCurve(this._width, this._height, this._radius).getPoints(
BackgroundGeometry.CurveDivisions
)
const pointsCount = points.length
const positions = new Float32Array(pointsCount * 9) // 9 floats per triangle
const push = (pointsIndex, start) => {
positions.set([points[pointsIndex].x, points[pointsIndex].y, 0], start)
}
// Make one triangle per point
for (let i = 0; i < pointsCount; i++) {
const positionsIndex = i * 9 // 9 floats per triangle
push((i + 1) % pointsCount, positionsIndex)
positions.set([0, 0, 0], positionsIndex + 3)
push(i, positionsIndex + 6)
}
this.addAttribute('position', new THREE.BufferAttribute(positions, 3))
}
export default Background
export { Background, BackgroundGeometry }