Skip to content

Commit

Permalink
refactoring commands
Browse files Browse the repository at this point in the history
  • Loading branch information
paolini committed Nov 16, 2024
1 parent 67d3684 commit 102bd63
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 238 deletions.
117 changes: 117 additions & 0 deletions js/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import World from './world'

import {Pringle} from './surfs/pringle'
import {Catenoid} from './surfs/catenoid'
import {Cube} from './surfs/cube'
import {Helicoid} from './surfs/helicoid'
import {Manu} from './surfs/manu'
import {Marco} from './surfs/marco'
import {Moebius} from './surfs/moebius'
import {MoebiusOriented} from './surfs/moebius'
import {Octa} from './surfs/octa'

class Command {
action: (World) => void
description: string

constructor(action: (World) => void, description='') {
this.action = action
this.description = description
}

exec(world: World) {
this.action(world)
}
}

export default class Commands {
commands: { [key: string]: Command }
octa_mode: number

constructor() {
this.octa_mode = -1
this.commands = {
'0': new Command(world =>
world.load(new Pringle()),
'pringle'),
'1': new Command(world =>
world.load(new Catenoid()),
'catenoid'),
'2': new Command(world =>
world.load(new Cube()),
'cube'),
'3': new Command(world =>
world.load(new Helicoid()),
'helicoid'),
'4': new Command(world =>
world.load(new Marco()),
'marco'),
'5': new Command(world =>
world.load(new Moebius()),
'moebius'),
'6': new Command(world =>
world.load(new MoebiusOriented()),
'moebius oriented'),
'8': new Command(world =>
this.loadNextOctaMode(world),
'next octa mode'),
'M': new Command(world =>
world.load(new Manu()),
'manu'),
't': new Command(world =>
world.triangulate(),
'triangulate'),
'e': new Command(world =>
world.evolve(0.05),
'evolve'),
'r': new Command(world =>
world.run(),
'run'),
'w': new Command(world => {
world.material.wireframe = !world.material.wireframe
}, 'wireframe'),
'S': new Command(world => {
const stl = world.surfMesh?.surf.exportToSTL()
if (stl) download(stl, 'surf.stl', 'text/plain')
}, 'export STL'),
'O': new Command(world => {
const pov = world.surfMesh?.surf.exportToPovRay(world.camera)
if (pov) download(pov, 'surf.pov', 'text/plain')
}, 'export POV-RAY')
}
}

exec(key: string, world: World) {
const command = this.commands[key];
if (command) {
command.exec(world);
} else {
console.log(`Unknown command. Key: ${key}`);
}
world.updateInfo()
}

loadNextOctaMode(world: World) {
const modes = ['brakke_1', 'brakke_2', 'brakke_3', 'brakke_4', 'brakke_5', 'brakke_a', 'brakke_b', 'brakke_c', 'brakke_d']
this.octa_mode = (this.octa_mode+1) % modes.length
const mode = modes[this.octa_mode]
console.log(`Loading Octa(${mode})`)
world.load(new Octa(mode))
}

htmlHelpString(): string {
return `command keys:<ul>`+Object.entries(this.commands).map(([key, val]) => {
return `<li><b>${key}</b>: ${val.description}</li>`
}).join('')+'</ul>'
}
}

function download(data: string, filename: string, type: string) {
const file = new Blob([data], {type: type})
const url = URL.createObjectURL(file)
const a = document.createElement("a")
a.href = url
a.download = filename
a.click()
URL.revokeObjectURL(url)
}
6 changes: 3 additions & 3 deletions js/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<title>surf evolver</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<h1>Surf</h1>
<canvas id="my-canvas" width="640" height="480"></canvas>
<div id="my-info">
</div>
<div id="my-commands">???</div>
<div id="my-info">???</div>
<script type="module" src="/main.ts"></script>
</body>
</html>
246 changes: 11 additions & 235 deletions js/main.ts
Original file line number Diff line number Diff line change
@@ -1,243 +1,19 @@
import * as THREE from 'three'
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'
import World from './world'
import Commands from './commands'

import Surf from './surf'
import {Pringle} from './surfs/pringle'
import {Catenoid} from './surfs/catenoid'
import {Cube} from './surfs/cube'
import {Helicoid} from './surfs/helicoid'
import {Manu} from './surfs/manu'
import {Marco} from './surfs/marco'
import {Moebius} from './surfs/moebius'
import {MoebiusOriented} from './surfs/moebius'
import {Octa} from './surfs/octa'

class SurfMesh extends THREE.Mesh {
surf: Surf
const canvas = document.getElementById('my-canvas') as HTMLCanvasElement
const info_div = document.getElementById('my-info') || undefined
const commands_div = document.getElementById('my-commands')

constructor(surf: Surf, material: THREE.Material) {
const geometry = new THREE.BufferGeometry()
geometry.setIndex( surf.indices )
geometry.setAttribute( 'position', new THREE.BufferAttribute( surf.vertices, 3 ) )
geometry.computeVertexNormals()
super( geometry, material )
this.surf = surf
}
}
const world = new World({canvas, info_div})
const commands = new Commands()

class World {
AUTO_RUN: boolean
surfMesh: SurfMesh|undefined
scene: THREE.Scene
camera: THREE.Camera
renderer: THREE.Renderer
material: THREE.Material
controls: THREE.Controls

constructor() {
this.AUTO_RUN = true

this.scene = new THREE.Scene()
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000)
this.camera.position.x = 5;
this.camera.up.set(0, 0, 1);

// Get the existing canvas element
const canvas = document.getElementById('my-canvas') as HTMLCanvasElement;

// Create the renderer using the existing canvas
this.renderer = new THREE.WebGLRenderer({ canvas });
this.renderer.setSize(canvas.width, canvas.height);
this.material = new THREE.MeshPhongMaterial()
// this.material.wireframe = true
this.material.side = THREE.DoubleSide;
this.material.color.setRGB(0,0.5,1);
this.material.transparent = true;
this.material.opacity = 0.85;
if (false) {
this.material.flatShading = true;
this.material.emissive = new THREE.Color("blue");
this.material.emissiveIntensity = 4
}

if (true) {
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(0, 10, 0);
light.target.position.set(-5, 0, 0);
this.scene.add(light);
this.scene.add(light.target);
}

if (true) {
const sunlight = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
this.scene.add(sunlight)
}


this.controls = new OrbitControls(this.camera, this.renderer.domElement)
this.controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
this.controls.dampingFactor = 0.5;
this.controls.screenSpacePanning = false;
this.controls.minDistance = 1;
this.controls.maxDistance = 1000;
this.controls.maxPolarAngle = Math.PI / 2;
this.controls.addEventListener('end', () => this.updateInfo())

this.load(new Pringle())
this.updateInfo()
document.addEventListener("keydown", evt => this.onDocumentKeyDown(evt), false);

this.animate()
}

animate() {
requestAnimationFrame(() => this.animate())
this.controls.update()
this.renderer.render(this.scene, this.camera)
}

replaceSurf(surf: Surf) {
if (this.surfMesh) this.scene.remove(this.surfMesh)
this.surfMesh = new SurfMesh(surf, this.material)
this.scene.add(this.surfMesh)
}

load(surf: Surf) {
if (this.AUTO_RUN) surf.run()
this.replaceSurf(surf)
}

triangulate() {
if (!this.surfMesh) return
const surf = this.surfMesh.surf
surf.triangulate()
this.replaceSurf(surf)
}

evolve(count=1) {
if (!this.surfMesh) return
this.surfMesh.surf.evolveMeanCurvature(0.05,count)
this.surfMesh.geometry.attributes.position.needsUpdate = true
}

run() {
if (!this.surfMesh) return
const surf = this.surfMesh.surf
surf.run()
this.replaceSurf(surf)
}

updateInfo() {
const info: any = {}

const camera = this.camera as THREE.PerspectiveCamera
if (camera) {
info.camera = {
position: camera.position.toArray(),
rotation: camera.rotation.toArray(),
fov: camera.fov,
near: camera.near,
far: camera.far,
}
}

const surf = this.surfMesh?.surf
if (surf) {
info.surf = {
name: surf.name,
vertices: surf.vertices.length,
faces: surf.indices.length/3,
area: surf.computeArea(),
}
}

const info_div = document.getElementById('my-info')
if (info_div) {
info_div.textContent = JSON.stringify(info)
}
}

onDocumentKeyDown(event) {
var key= event.key
switch (key) {
case ' ':
// use to update info
break
case '0':
this.load(new Pringle())
break
case '1':
this.load(new Catenoid())
break
case '2':
this.load(new Cube())
break
case '3':
this.load(new Helicoid())
break
case '4':
this.load(new Marco())
break
case '5':
this.load(new Moebius())
break
case '6':
this.load(new MoebiusOriented())
break
case '8': {
const modes = ['brakke_1','brakke_2','brakke_3','brakke_4','brakke_5','brakke_a','brakke_b','brakke_c','brakke_d']
const mode = modes[((modes.indexOf(this.surfMesh?.surf?.mode) ?? -1)+1) % modes.length]
console.log(`loading Octa(${mode})`)
this.load(new Octa(mode))
}
break
case 'M':
this.load(new Manu())
break
case 't':
console.log(`triangulating`)
this.triangulate()
break
case 'e':
console.log(`evolving`)
this.evolve(0.05)
break
case 'r':
console.log('run')
this.run()
break
case 'w':
console.log(`wireframe`)
this.material.wireframe = !this.material.wireframe
break
case 'S':
console.log('export STL')
const stl = this.surfMesh?.surf.exportToSTL()
if (stl) download(stl, 'surf.stl', 'text/plain')
break
case 'O':
console.log('export POV-RAY')
const pov = this.surfMesh?.surf.exportToPovRay(this.camera)
if (pov) download(pov, 'surf.pov', 'text/plain')
break
default:
console.log(`unknown command. key: ${event.key} keyCode: ${event.which}`)
}
this.updateInfo()
}
if (commands_div) {
commands_div.innerHTML = commands.htmlHelpString()
}

function download(data: string, filename: string, type: string) {
const file = new Blob([data], {type: type})
const url = URL.createObjectURL(file)
const a = document.createElement("a")
a.href = url
a.download = filename
a.click()
URL.revokeObjectURL(url)
}
commands.exec('0', world) // load pringle
document.addEventListener("keydown", evt => commands.exec(evt.key, world), false)

const world = new World()

Loading

0 comments on commit 102bd63

Please sign in to comment.