threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
117 lines (98 loc) • 4.58 kB
text/typescript
import {Curve, Line, LineCurve3, Vector2, Vector3} from 'three'
import {Class} from 'ts-browser-helpers'
import {IUiConfigContainer, UiObjectConfig} from 'uiconfig.js'
import {IGeometry} from '../../../core/IGeometry'
import {IMaterial} from '../../../core/IMaterial'
import {IObject3D} from '../../../core/IObject'
import {BufferGeometry2} from '../../../core/geometry/BufferGeometry2'
import {LineGeometry2} from '../../../core/geometry/LineGeometry2'
import {LineMaterial2} from '../../../core/material/LineMaterial2'
import {UnlitLineMaterial} from '../../../core/material/UnlitLineMaterial'
import {MeshLine} from '../../../core/object/MeshLine'
import {AGeometryGenerator} from '../AGeometryGenerator'
import {createCurvePropertyUi, createCurveTypeDropdown, type SupportedCurveTypes} from '../helpers/CurveUiHelper'
export interface LineGeometryGeneratorParams {
curve?: SupportedCurveTypes | Curve<Vector2> | Curve<Vector3>
segments?: number
closePath?: boolean
}
export class LineGeometryGenerator extends AGeometryGenerator<LineGeometryGeneratorParams> {
constructor(type = 'line', defaultParams?: Partial<LineGeometryGeneratorParams>) {
super(type)
if (defaultParams) Object.assign(this.defaultParams, defaultParams)
}
defaultParams: LineGeometryGeneratorParams = {
curve: new LineCurve3(new Vector3(0, 0, 0), new Vector3(1, 1, 0)),
segments: 50,
closePath: false,
}
static UseMeshLines = true
defaultMeshClass: ()=>Class<IObject3D> = ()=> LineGeometryGenerator.UseMeshLines ? MeshLine : Line as any
defaultMaterialClass: ()=>Class<IMaterial> = ()=> LineGeometryGenerator.UseMeshLines ? LineMaterial2 : UnlitLineMaterial
defaultGeometryClass: ()=>Class<IGeometry> = ()=> LineGeometryGenerator.UseMeshLines ? LineGeometry2 : BufferGeometry2
protected _generateData(params: LineGeometryGeneratorParams) {
const positions: number[] = []
let curve = params.curve
if (!curve && this.defaultParams.curve) {
curve = this.defaultParams.curve
}
if (!curve) {
throw new Error('No curve specified for line generation')
}
const segments = Math.max(2, params.segments || 50)
const closePath = params.closePath || false
curve.updateArcLengths()
const points = curve.getSpacedPoints(segments - 1)
if (points.length !== segments) {
console.error(`LineGeometryGenerator: Curve points length (${points.length}) does not match segments (${segments}).`)
}
for (const point of points) {
if ('z' in point) {
positions.push(point.x, point.y, point.z)
} else {
positions.push(point.x, point.y, 0)
}
}
if (closePath && points.length > 0) {
const firstPoint = points[0]
if ('z' in firstPoint) {
positions.push(firstPoint.x, firstPoint.y, firstPoint.z)
} else {
positions.push(firstPoint.x, firstPoint.y, 0)
}
}
return {
vertices: positions,
normals: [],
uvs: [],
positions,
}
}
createUiConfig(geometry: IGeometry): UiObjectConfig[] {
const ui = super.createUiConfig(geometry)
const curveFolder = ui.find(u => (u.label as any) === 'curve' && u.type === 'folder')
const curve = (geometry.userData.generationParams as LineGeometryGeneratorParams)?.curve
if (!curve) return []
if ((curve as IUiConfigContainer)?.uiConfig) {
const ind = curveFolder ? ui.indexOf(curveFolder) : -1
if (ind >= 0) {
ui.splice(ind, 1, (curve as IUiConfigContainer).uiConfig!)
} else {
ui.push((curve as IUiConfigContainer).uiConfig!)
}
} else if (curveFolder) {
const propUi = createCurvePropertyUi(curve)
if (propUi.length > 0) {
const ind = ui.indexOf(curveFolder)
ui.splice(ind, 1, ...propUi)
}
}
const onTypeChange = (newCurve: SupportedCurveTypes) => {
(geometry.userData.generationParams as LineGeometryGeneratorParams).curve = newCurve
;(geometry.userData as any).__generationParamsUiType = '' // invalidate ui
this.generate(geometry)
}
const curveTypeDropdown = createCurveTypeDropdown(curve, onTypeChange)
return [curveTypeDropdown, ...ui]
}
}