Commit 7f2343b9 authored by loelkes's avatar loelkes
Browse files

Waves Version 2. Documentation.

parent bff57434
# Gerstner Wave Simulation
![Preview of the waves](media/preview.png)
## API
### Wave
```wave = new Wave( scaling, direction, speed, height, lambda )```
* scaling : THREE.Vector3. This defines the size of the field to emulate the wave in.
* direction : THREE.Vector3. Sets the direction of propgation of the wave.
* speed : float. Propagation speed.
* height : float. Height of the wave.
* lambda : float. Wavelenght relative to the scaling.
#### Properties
* ```.scaling : THREE.Vector3``` defines the size of the field.
* ```.direction : THREE.Vector3```. Propgation direction of the wave.
#### Methods
* ```.getParticle( particle )```
* ```.update()```
### MultipleWaves
```sea = new MultipleWaves( scaling )```
* scaling : THREE.Vector3. This defines the size of the field to emulate the wave in.
#### Properties
* ```.scaling : THREE.Vector3``` defines the size of the field.
#### Methods
* ```.example()```
* ```.addWave( direction, height, speed, lambda )```
* ```.getParticle( particle )```
* ```.update()```
## Related
* [Trochoidal wave](https://en.wikipedia.org/wiki/Trochoidal_wave) on Wikipedia.org
...@@ -19,96 +19,118 @@ ...@@ -19,96 +19,118 @@
// SOFTWARE. // SOFTWARE.
class Wave { class Wave {
constructor( scaling, direction, speed, height, lambda ) {
constructor( x, z, dir, speed=1, height=1, waveLength=1 ) { this.version = 2;
this.version = 1;
console.groupCollapsed( 'Gernster Wave. Version:', this.version ); console.groupCollapsed( 'Gernster Wave. Version:', this.version );
console.debug( 'Dimesions:', x, z, '(x, z)' ); console.debug( 'Parameters:', speed, height, lambda, '(Speed, Height, lambda)' );
console.debug( 'Direction:', dir + '°' )
console.debug( 'Parameters:', speed, height, waveLength, '(Speed, Height, Wavelength)' );
console.groupEnd(); console.groupEnd();
// x : dimension in X-axis
// z : dimension in Z-axis
// dir: direction in degrees, rotation around Y-axis
this.xSize = x;
this.zSize = z;
this.height = height; this.height = height;
this.speed = speed; this.speed = speed;
this.waveLength = waveLength; this.lambda = lambda;
this.direction = new THREE.Vector3( 1, 0, 0 ); this.direction = direction;
this.direction.applyAxisAngle( new THREE.Vector3( 0, 1, 0 ), THREE.Math.DEG2RAD * dir ); this.scaling = scaling;
this.t = 0; this.t = 0;
return; return;
}
set direction( vect ) {
if( vect instanceof THREE.Vector3 ){
this._direction = vect;
} else {
throw new Error( 'You must provide a THREE.Vector3 for direction.' );
}
} }
update( millis = 0 ) { get direction() {
return this._direction;
}
set scaling( vect ) {
if( vect instanceof THREE.Vector3 ){
this._scaling = vect;
} else {
throw new Error( 'You must provide a THREE.Vector3 for scaling.' );
}
}
get scaling() {
return this._scaling;
}
update( millis = 0 ) {
if( millis == 0 ) millis = Date.now(); if( millis == 0 ) millis = Date.now();
this.t = this.speed * 0.001 * millis % ( 2 * Math.PI ); this.t = this.speed * 0.001 * millis % ( 2 * Math.PI );
return; return;
} }
xOffset( particle ) { _xOffset( pos ) {
return 2 * Math.PI * ( pos.x / this.scaling.x ) * this.direction.x;
return 2 * Math.PI * ( particle.x / this.xSize ) * this.direction.x;
} }
zOffset( particle ) { _zOffset( pos ) {
return 2 * Math.PI * ( pos.z / this.scaling.z ) * this.direction.z;
return 2 * Math.PI * ( particle.z / this.zSize ) * this.direction.z;
} }
pOffset( particle ) { _pOffset( pos ) {
return this.lambda * ( this._xOffset( pos ) + this._zOffset( pos ) );
return this.waveLength * ( this.xOffset( particle ) + this.zOffset( particle ) );
} }
getParticle( particle ) { getParticle( particle ) {
if( particle instanceof THREE.Vector3 ) {
var x = this.direction.x * Math.cos( ( - this.t + this.pOffset( particle ) ) ); var x = this.direction.x * Math.cos( this._pOffset( particle ) - this.t );
var z = this.direction.z * Math.cos( ( - this.t + this.pOffset( particle ) ) ); var z = this.direction.z * Math.cos( this._pOffset( particle ) - this.t );
var y = Math.sin( - this.t + this.pOffset( particle ) ); var y = Math.sin( this._pOffset( particle ) - this.t );
return new THREE.Vector3( x, y, z ).multiplyScalar( this.height ); return new THREE.Vector3( x, y, z ).multiplyScalar( this.height );
} else {
throw new Error( 'You must provide a THREE.Vector3 as particle position.' );
}
} }
} }
class MultipleWaves { class MultipleWaves {
constructor( scaling ) {
constructor( x, y ) { this.version = 2;
this.scaling = scaling;
this.version = 1;
console.groupCollapsed( 'Multiple Waves. Version', this.version ); console.groupCollapsed( 'Multiple Waves. Version', this.version );
console.groupEnd(); console.groupEnd();
this.waves = [ this.waves = [];
new Wave( x, y, -90, 2, 0.2, 2 ), }
new Wave( x, y, 10, 2, 0.2, 1 ),
new Wave( x, y, 120, 1, 0.2, 4 ),
];
set scaling( vect ) {
if( vect instanceof THREE.Vector3 ){
this._scaling = vect;
} else {
throw new Error( 'You must provide a THREE.Vector3 for scaling.' );
}
} }
getParticle( particle ) { get scaling() {
return this._scaling;
}
example() {
this.addWave( -90, 0.2, 2, 2 );
this.addWave( 10, 0.2, 2, 1 );
this.addWave( 120, 0.3, 1, 4 );
}
addWave( direction=45, height=0.5, speed=2, lambda=2 ) {
var dir = direction;
if( !(direction instanceof THREE.Vector3 ) ) {
dir = new THREE.Vector3( 1, 0, 0 );
dir.applyAxisAngle( new THREE.Vector3( 0, 1, 0 ), direction * THREE.Math.DEG2RAD );
}
this.waves.push( new Wave( this.scaling, dir, speed, height, lambda ) );
}
getParticle( particle ) {
var position = new THREE.Vector3(); var position = new THREE.Vector3();
for( var wave of this.waves ) position.add( wave.getParticle( particle ) ); for( var wave of this.waves ) position.add( wave.getParticle( particle ) );
return position; return position;
} }
update() { // Call this within the animation loop. update() { // Call this within the animation loop.
var now = Date.now(); var now = Date.now();
for( var wave of this.waves ) wave.update( now ); for( var wave of this.waves ) wave.update( now );
} }
} }
...@@ -29,34 +29,6 @@ var stats = true; ...@@ -29,34 +29,6 @@ var stats = true;
const TwoPi = Math.PI * 2; const TwoPi = Math.PI * 2;
// class WireframeCube {
//
// constructor(size=25) {
// this.geometry = new THREE.BoxBufferGeometry( size, size, size );
// this.wireframe = new THREE.EdgesGeometry( this.geometry );
// this.material = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 1 } );
// this.mesh = new THREE.LineSegments( this.wireframe, this.material );
// }
//
// animate(offset) {
// this.rotationOffset = offset // new THREE.Vector3(0,0,0);
// this.rotation = new THREE.Vector3(0,0,0);
// this.rotation.add(this.rotationOffset);
// this.rotationTarget = new THREE.Vector3(0,TwoPi,0);
// this.rotationTarget.add(this.rotationOffset);
// this.mesh.userData.tween = new TWEEN.Tween(this.rotation).to(this.rotationTarget, 10000);
//
// function onUpdate() {
// this.mesh.rotation.setFromVector3(this.rotation);
// }
//
// this.mesh.userData.tween.onUpdate(onUpdate.bind(this));
// this.mesh.userData.tween.repeat(Infinity);
// this.mesh.userData.tween.start()
//
// }
// }
function polarArray( r, count ) { function polarArray( r, count ) {
let output = []; let output = [];
...@@ -112,12 +84,8 @@ class PointGrid { ...@@ -112,12 +84,8 @@ class PointGrid {
this.xres = 100; this.xres = 100;
this.zres = 100; this.zres = 100;
this.params = { this.params = {
xc: this.xres, xc: this.xres, yc: 1, zc: this.zres,
yc: 1, xs: x / this.xres, ys: 1, zs: z / this.zres
zc: this.zres,
xs: x / this.xres,
ys: 1,
zs: z / this.zres
}; };
this.grid = XYArray( this.params ); this.grid = XYArray( this.params );
this.fill(); this.fill();
...@@ -137,19 +105,15 @@ class PointGrid { ...@@ -137,19 +105,15 @@ class PointGrid {
this.group.add( cube ); this.group.add( cube );
} }
return; return;
} }
} }
function initWorld() { function initWorld() {
var i = 0; let scale = new THREE.Vector3(2500,0,2500)
let x = 2500; wave = new MultipleWaves( scale );
let z = 2500; wave.example();
wave = new MultipleWaves( x, z ); ptGrid = new PointGrid( scale.x, scale.z );
ptGrid = new PointGrid( x, z );
scene.add(ptGrid.group); scene.add(ptGrid.group);
onRenderFcts.push( function() { onRenderFcts.push( function() {
wave.update(); wave.update();
for(var particle of ptGrid.group.children) { for(var particle of ptGrid.group.children) {
...@@ -161,43 +125,31 @@ function initWorld() { ...@@ -161,43 +125,31 @@ function initWorld() {
return; return;
} }
///
class GUIControls { class GUIControls {
constructor() { constructor() {
this.gui = new dat.GUI(); this.gui = new dat.GUI();
} }
textFolder( target ) { textFolder( target ) {
this.textCtrl = this.gui.addFolder( 'Text' ); this.textCtrl = this.gui.addFolder( 'Text' );
this.textCtrl.add( target, 'size' ).name( 'Size' ); this.textCtrl.add( target, 'size' ).name( 'Size' );
this.textCtrl.add( target, 'height' ).name( 'Thickness' ); this.textCtrl.add( target, 'height' ).name( 'Thickness' );
this.textCtrl.addColor( target, 'color' ).name( 'Color' ); this.textCtrl.addColor( target, 'color' ).name( 'Color' );
} }
cameraFolder( camera ) { cameraFolder( target ) {
this.cameraCtrl = this.gui.addFolder( 'Camera' ); this.cameraCtrl = this.gui.addFolder( 'Camera' );
this.cameraCtrl.add( camera.position, 'y' ).name( 'Height' ); this.cameraCtrl.add( target.position, 'y' ).name( 'Height' );
this.cameraCtrl.add( camera.rotation, 'x', - Math.PI / 4, 0 ).name( 'Pitch' ); this.cameraCtrl.add( target.rotation, 'x', - Math.PI / 4, 0 ).name( 'Pitch' );
} }
waveFolder ( waves ) { waveFolder ( target ) {
this.waveCtrl = this.gui.addFolder ( 'Waves' ); this.waveCtrl = this.gui.addFolder ( 'Waves' );
this.waveCtrl.add( waves, 'speed', 0, 20 ).name( 'Speed' ); this.waveCtrl.add( target, 'speed', 0, 20 ).name( 'Speed' );
this.waveCtrl.add( waves, 'height', 0, 100 ).name( 'Height' ); this.waveCtrl.add( target, 'height', 0, 100 ).name( 'Height' );
this.waveCtrl.add( waves, 'waveLength', 10, 50).name( 'Width' ); this.waveCtrl.add( target, 'waveLength', 10, 50).name( 'Width' );
this.waveCtrl.open(); this.waveCtrl.open();
} }
} }
function initRenderer() { function initRenderer() {
...@@ -205,7 +157,7 @@ function initRenderer() { ...@@ -205,7 +157,7 @@ function initRenderer() {
renderer = new THREE.WebGLRenderer({ renderer = new THREE.WebGLRenderer({
antialias: true, // Performance on older mobile devices. antialias: true, // Performance on older mobile devices.
precision: 'mediump', // Performance on older mobile devices. precision: 'mediump', // Performance on older mobile devices.
alpha: true, // Display webcam image in background. // alpha: true, // Display webcam image in background.
// powerPreference: "low-power", // Mobile application. // powerPreference: "low-power", // Mobile application.
logarithmicDepthBuffer: true logarithmicDepthBuffer: true
}); });
...@@ -215,18 +167,15 @@ function initRenderer() { ...@@ -215,18 +167,15 @@ function initRenderer() {
renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement ); document.body.appendChild( renderer.domElement );
console.debug( 'OK: Initialized renderer.' ); console.debug( 'OK: Initialized renderer.' );
} }
initFcts.push( initRenderer ); initFcts.push( initRenderer );
function initStats() { function initStats() {
stats = new Stats(); stats = new Stats();
stats.showPanel( 0 ); // 0: fps, 1: ms, 2: mb, 3+: custom stats.showPanel( 0 ); // 0: fps, 1: ms, 2: mb, 3+: custom
document.body.appendChild( stats.dom ); document.body.appendChild( stats.dom );
console.debug( 'OK: Initialized statistics.' ); console.debug( 'OK: Initialized statistics.' );
} }
if( stats ) { if( stats ) {
...@@ -234,15 +183,6 @@ if( stats ) { ...@@ -234,15 +183,6 @@ if( stats ) {
} }
function initCamera() { function initCamera() {
// camera = new THREE.OrthographicCamera(
// - window.innerWidth / 2, // Left
// + window.innerWidth / 2, // Right
// + window.innerHeight / 2, // Top
// - window.innerHeight / 2, // Bottom
// 1, // Near
// 2000 // Far
// );
camera = new THREE.PerspectiveCamera( camera = new THREE.PerspectiveCamera(
45, 45,
window.innerWidth / window.innerHeight, window.innerWidth / window.innerHeight,
...@@ -251,17 +191,14 @@ function initCamera() { ...@@ -251,17 +191,14 @@ function initCamera() {
camera.position.set( 0, 2000, 3000 ); camera.position.set( 0, 2000, 3000 );
camera.lookAt( new THREE.Vector3() ); camera.lookAt( new THREE.Vector3() );
console.debug( 'OK: Initialized camera.' ); console.debug( 'OK: Initialized camera.' );
} }
initFcts.push( initCamera ); initFcts.push( initCamera );
function initScene() { function initScene() {
scene = new THREE.Scene(); scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0xffffff )); scene.add( new THREE.AmbientLight( 0xffffff ));
console.log( 'OK: Initialized scene.' ); console.log( 'OK: Initialized scene.' );
} }
initFcts.push( initScene ); initFcts.push( initScene );
...@@ -302,16 +239,13 @@ initFcts.push( initControls ); ...@@ -302,16 +239,13 @@ initFcts.push( initControls );
initFcts.push( initWorld ); initFcts.push( initWorld );
function render() { function render() {
renderer.render( scene, camera ); renderer.render( scene, camera );
TWEEN.update(); TWEEN.update();
} }
onRenderFcts.push( render ); onRenderFcts.push( render );
function onWindowResize() { function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight; camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix(); camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setSize( window.innerWidth, window.innerHeight );
...@@ -320,51 +254,19 @@ function onWindowResize() { ...@@ -320,51 +254,19 @@ function onWindowResize() {
window.addEventListener( 'resize', onWindowResize, false ); window.addEventListener( 'resize', onWindowResize, false );
// ----------------------------------------------------------------------
// Helper functions
function getRandomInt( min, max ) { // Maximum exclusive and minimum inclusive
min = Math.ceil( min );
max = Math.floor( max );
return Math.floor( Math.random() * ( max - min ) ) + min;
}
function getMaxOfArray( numArray ) { // Max value inside an array
return Math.max.apply( null, numArray );
}
function getMinOfArray( numArray ) { // Min value inside an array
return Math.min.apply( null, numArray );
}
function init() { function init() {
console.group( 'Intitialize all the things!' ); console.group( 'Intitialize all the things!' );
initFcts.forEach( function( initFct ) { initFcts.forEach( function( initFct ) {
initFct(); initFct();
}); });
console.groupEnd(); console.groupEnd();
} }
function animate() { function animate() {
if ( stats ) stats.begin();
if ( stats ) {
stats.begin();
}
onRenderFcts.forEach( function( onRenderFct ) { onRenderFcts.forEach( function( onRenderFct ) {
onRenderFct(); onRenderFct();
}); });
if ( stats ) { if ( stats ) stats.end();
stats.end();
}
requestAnimationFrame( animate ); // Animation frame requestAnimationFrame( animate ); // Animation frame
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment