-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
291 additions
and
238 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
|
Oops, something went wrong.