# Examples : Bender

## Description

Demonstrates Bending BufferGeometries in Threejs.

Bender GitHub Repository : https://github.com/Sean-Bradley/Bender

This example demonstrates,

• Bending a THREE.BoxGeometry
• Bending a TextGeometry
• Replacing a meshes geometry at runtime
• Replacing the text of a TextGeometry at runtime
• Using the FontLoader

## Code

### ./src/client/utils/bender.ts

``````//MIT License

export default class Bender {
public bend(geometry: THREE.BufferGeometry, axis: string, angle: number) {
let theta = 0
if (angle !== 0) {
const v = (geometry.attributes.position as THREE.BufferAttribute)
.array as number[]
for (let i = 0; i < v.length; i += 3) {
let x = v[i]
let y = v[i + 1]
let z = v[i + 2]

switch (axis) {
case 'x':
theta = z * angle
break
case 'y':
theta = x * angle
break
default:
//z
theta = x * angle
break
}

let sinTheta = Math.sin(theta)
let cosTheta = Math.cos(theta)

switch (axis) {
case 'x':
v[i] = x
v[i + 1] = (y - 1.0 / angle) * cosTheta + 1.0 / angle
v[i + 2] = -(y - 1.0 / angle) * sinTheta
break
case 'y':
v[i] = -(z - 1.0 / angle) * sinTheta
v[i + 1] = y
v[i + 2] = (z - 1.0 / angle) * cosTheta + 1.0 / angle
break
default:
//z
v[i] = -(y - 1.0 / angle) * sinTheta
v[i + 1] = (y - 1.0 / angle) * cosTheta + 1.0 / angle
v[i + 2] = z
break
}
}
geometry.attributes.position.needsUpdate = true
}
}
}
``````

### ./src/client/client.ts

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107``` ``````import * as THREE from 'three' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import Stats from 'three/examples/jsm/libs/stats.module' import { GUI } from 'dat.gui' import { Font, FontLoader } from 'three/examples/jsm/loaders/FontLoader' import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry' import Bender from './utils/bender' const bender = new Bender() const scene = new THREE.Scene() const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ) camera.position.set(4, 4, 6) const renderer = new THREE.WebGLRenderer() renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild(renderer.domElement) const controls = new OrbitControls(camera, renderer.domElement) controls.enableDamping = true const modelOptions = ['Cube', 'Text', 'Plane'] const axisOptions = ['x', 'y', 'z'] const data = { model: modelOptions[0], axis: axisOptions[1], angle: Math.PI / 16, text: 'seanwasere Threejs', } const material: THREE.MeshBasicMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true, }) const mesh: THREE.Mesh = new THREE.Mesh(new THREE.BufferGeometry(), material) scene.add(mesh) let font: Font const loader = new FontLoader() loader.load('fonts/helvetiker_regular.typeface.json', function (f) { font = f }) window.addEventListener('resize', onWindowResize, false) function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) render() } const gui = new GUI() gui.add(data, 'model', modelOptions).onChange(regenerateGeometry) gui.add(data, 'text').onFinishChange(regenerateGeometry) gui.add(data, 'axis', axisOptions).onChange(regenerateGeometry) gui.add(data, 'angle', -Math.PI / 2, Math.PI / 2, 0.01).onChange( regenerateGeometry ) gui.open() function regenerateGeometry() { let newGeometry if (data.model === 'Cube') { newGeometry = new THREE.BoxGeometry(5, 5, 5, 10, 10, 10) } else if (data.model === 'Plane') { newGeometry = new THREE.PlaneGeometry(10, 5, 20, 10) } else { newGeometry = new TextGeometry(data.text, { font: font, size: 1, height: 0.2, curveSegments: 2, }) } newGeometry.center() bender.bend(newGeometry, data.axis, data.angle) mesh.geometry.dispose() mesh.geometry = newGeometry } const stats = new Stats() document.body.appendChild(stats.dom) function animate() { requestAnimationFrame(animate) controls.update() render() stats.update() } function render() { renderer.render(scene, camera) } regenerateGeometry() animate() ``````