2020-06-18 09:58:04 +02:00
var parade ;
var interview _sorter ;
2020-06-18 15:15:37 +02:00
var config = {
speedFactor : 0.01 , // speed of the shapes
distanceFactor : 20 , // distance between the shapes
startOffset : 0.001 , // starting position
shapeWidth : 100 , // 8 shape of animation: width
2020-06-19 16:45:30 +02:00
shapeHeight : 1300 , // 8 shape of animation: height;
playDuration : 16 , // seconds. how long should audio play after dragging/pressing next. Something like audio duration / nr of images. Note that if everything is sorted interview will just continue;
2020-06-19 17:45:46 +02:00
textColor : "orange" ,
font : "bold 70px Arial" ,
2020-06-19 21:16:35 +02:00
returnToSpectatorCamTimeout : 3000 , // ms, when dragging weight slider, after release, how long should it take before reversing to spectator camera position
2020-06-18 15:15:37 +02:00
}
2020-06-18 09:58:04 +02:00
// polyfill
window . performance = window . performance || { } ;
performance . now = ( function ( ) {
return performance . now ||
performance . mozNow ||
performance . msNow ||
performance . oNow ||
performance . webkitNow ||
Date . now /*none found - fallback to browser default */
} ) ( ) ;
const request = new Request ( 'dataset/images_prop.json' ) ;
fetch ( request )
. then ( response => {
if ( response . status === 200 ) {
return response . json ( ) ;
} else {
throw new Error ( 'Something went wrong on api server!' ) ;
}
} )
. then ( data => {
console . debug ( data ) ;
// loaded data from json, initialise interface
const canvas = document . getElementById ( "renderCanvas" ) ;
2020-06-18 15:15:37 +02:00
parade = new Parade ( canvas , data [ 'image' ] , config ) ;
2020-06-18 09:58:04 +02:00
// parade.scene.debugLayer.show();
} ) . catch ( error => {
console . error ( error ) ;
} ) ;
class Parade {
2020-06-18 15:15:37 +02:00
constructor ( canvasEl , images , config ) {
// animation parameters:
this . config = config ;
2020-06-18 09:58:04 +02:00
this . canvasEl = canvasEl ;
2020-06-18 15:15:37 +02:00
this . engine = new BABYLON . Engine ( canvasEl , true ) ; // create 3d engine
this . isPlaying = false ;
2020-06-19 16:45:30 +02:00
this . t = 0 ; // playback timer
2020-06-18 15:15:37 +02:00
// initialise image objects:
this . images = [ ] ;
this . imagePositions = [ ] ; // index: position, value: image-index
let storedPositions = localStorage . getItem ( 'imagePositions' ) ;
if ( storedPositions ) {
this . imagePositions = JSON . parse ( storedPositions ) ;
}
// else or invalid:
if ( ! storedPositions || this . imagePositions . length != images . length ) {
// fill with default: order of the JSON file
for ( let index = 0 ; index < images . length ; index ++ ) {
this . imagePositions [ index ] = index ;
}
}
for ( let i in images ) {
i = parseInt ( i ) ;
let pos = this . imagePositions . indexOf ( i ) ;
// console.log('pos', , pos);
let img = new ParadeImage ( images [ i ] , pos ) ;
this . images . push ( img ) ;
// for testing:
// if(this.images.length > 5)
// break;
}
this . setupSubtitles ( ) ;
this . setupSorter ( ) ;
2020-06-19 16:45:30 +02:00
this . restoreTime ( ) ;
2020-06-18 15:15:37 +02:00
// Watch for browser/canvas resize events
window . addEventListener ( "resize" , function ( ) {
this . engine . resize ( ) ;
} . bind ( this ) ) ;
//Register a render loop to repeatedly render the scene
this . engine . runRenderLoop ( this . renderLoop . bind ( this ) ) ;
2020-06-19 16:45:30 +02:00
this . createScene ( ) ;
// browsers block auto-play of audio
// if(this.imagesToSort.length < 1){
// this.play();
// }
2020-06-18 15:15:37 +02:00
}
2020-06-18 09:58:04 +02:00
2020-06-18 15:15:37 +02:00
setupSubtitles ( ) {
2020-06-18 09:58:04 +02:00
this . audioEl = document . getElementById ( 'interview' ) ; // audio player
2020-06-19 16:45:30 +02:00
this . subtitleEls = {
'en' : document . getElementById ( 'sub_en' ) ,
'nl' : document . getElementById ( 'sub_nl' ) ,
} ;
this . trackEls = this . audioEl . querySelectorAll ( 'track' ) ; // the subtitle track
2020-06-18 15:15:37 +02:00
this . timeEl = document . getElementById ( 'time' ) ; // the subtitle track
2020-06-18 09:58:04 +02:00
// set up subtitles:
2020-06-19 16:45:30 +02:00
for ( let trackEl of this . trackEls ) {
trackEl . addEventListener ( "cuechange" , ( event ) => {
let lang = event . srcElement . srclang ;
let cues = event . target . track . activeCues ;
let content = "" ;
for ( let cue of cues ) {
content += "<p>" + cue . text + "</p>" ;
}
this . subtitleEls [ lang ] . innerHTML = content ;
} ) ;
}
for ( let track of this . audioEl . textTracks ) {
// by default only one (or none) is set to showing.
// for some reason setting it on trackEl doesn't work, while this does the trick.
track . mode = 'showing' ;
}
2020-06-18 09:58:04 +02:00
2020-06-18 15:15:37 +02:00
this . audioEl . addEventListener ( 'timeupdate' , this . updateTimestring . bind ( this ) ) ;
2020-06-19 16:45:30 +02:00
this . audioEl . addEventListener ( 'timeupdate' , this . storeTime . bind ( this ) ) ;
2020-06-18 15:15:37 +02:00
this . updateTimestring ( ) ;
}
updateTimestring ( ) {
let now = this . audioEl . currentTime ;
let duration = this . audioEl . duration ;
2020-06-18 09:58:04 +02:00
2020-06-18 15:15:37 +02:00
let timestring = String ( Math . floor ( now / 60 ) ) . padStart ( 2 , '0' ) + ":"
+ String ( Math . floor ( now ) % 60 ) . padStart ( 2 , '0' )
+ " / " + String ( Math . floor ( duration / 60 ) ) . padStart ( 2 , '0' ) + ":"
+ String ( Math . floor ( duration ) % 60 ) . padStart ( 2 , '0' ) ;
2020-06-18 09:58:04 +02:00
2020-06-18 15:15:37 +02:00
this . timeEl . innerHTML = timestring ;
}
2020-06-19 16:45:30 +02:00
storeTime ( ) {
// do we use this.t or audioEl...?
localStorage . setItem ( 'playhead' , String ( this . audioEl . currentTime ) )
}
restoreTime ( ) {
let time = localStorage . getItem ( 'playhead' ) ;
this . audioEl . addEventListener ( 'playing' , ( e ) => {
if ( ! this . isPlaying ) this . play ( ) ; // make sure we're playing.
} ) ;
// causes odd glitch
// this.audioEl.addEventListener('pause', (e) => {
// if(this.isPlaying) this.pause(true); // make sure we're playing.
// });
this . audioEl . addEventListener ( 'seeking' , ( e ) => {
this . t = parseInt ( e . target . currentTime * 1000 ) ;
2020-06-19 21:16:35 +02:00
// console.log('restore timer',this.t,e);
2020-06-19 16:45:30 +02:00
} ) ;
if ( ! time ) return ;
time = parseFloat ( time ) ;
this . audioEl . currentTime = time ;
}
2020-06-18 15:15:37 +02:00
setupSorter ( ) {
this . sorterTemplate = document . getElementById ( 'annotationContentTemplate' ) ;
this . sorter = document . getElementById ( 'annotation' ) ;
this . pauseTimeout = null ;
2020-06-19 16:45:30 +02:00
let imagesToSort = localStorage . getItem ( 'imagesToSort' ) ;
if ( imagesToSort ) {
this . imagesToSort = JSON . parse ( imagesToSort ) ;
}
// else or invalid:
if ( ! imagesToSort || this . imagesToSort . length > this . images . length ) {
console . log ( 'createimages to sort' )
this . imagesToSort = [ ] ; // we will keep track of images that needs weighing
for ( let index = 0 ; index < this . images . length ; index ++ ) {
this . imagesToSort [ index ] = index ;
}
}
2020-06-18 15:15:37 +02:00
let nextButton = document . getElementById ( 'nextButton' ) ;
nextButton . addEventListener ( 'click' , this . nextSorterImage . bind ( this ) ) ;
2020-06-19 16:45:30 +02:00
let resetButton = document . getElementById ( 'resetButton' ) ;
resetButton . addEventListener ( 'click' , this . resetSortedImages . bind ( this ) ) ;
let resumeButton = document . getElementById ( 'resumeButton' ) ;
resumeButton . addEventListener ( 'click' , this . play . bind ( this ) ) ;
2020-06-18 15:15:37 +02:00
// set maximum to last image index
this . sorterTemplate . content . querySelector ( '.weight' ) . max = this . images . length - 1 ;
2020-06-19 21:16:35 +02:00
this . followCameraTimeout = null ;
document . body . addEventListener ( 'mouseup' , ( ) => {
// because the movement of the image can be a bit slower, we set a timeout instead
// of going straight back into spectator position
this . followCameraTimeout = setTimeout ( this . stopFollowSortingImage . bind ( this ) , this . config [ 'returnToSpectatorCamTimeout' ] ) ;
} ) ;
2020-06-18 15:15:37 +02:00
this . changeSorterImage ( ) ;
}
2020-06-19 16:45:30 +02:00
resetSortedImages ( ) {
this . imagesToSort = [ ] ; // we will keep track of images that needs weighing
for ( let index = 0 ; index < this . images . length ; index ++ ) {
this . imagesToSort [ index ] = index ;
}
this . changeSorterImage ( ) ; // display an image to sort
this . play ( ) ; // make sure playback timer gets set
}
2020-06-18 15:15:37 +02:00
changeSorterImage ( ) {
2020-06-19 16:45:30 +02:00
let sorterContent = this . sorter . querySelector ( '#annotationContent' ) ;
if ( ! this . imagesToSort . length ) {
document . body . classList . add ( 'finished' ) ;
sorterContent . innerHTML = "" ;
this . audioEl . controls = true ;
return ;
} else {
document . body . classList . remove ( 'finished' ) ;
this . audioEl . controls = false ;
}
2020-06-18 15:15:37 +02:00
// pick random image
2020-06-19 16:45:30 +02:00
this . imageIdToSort = this . imagesToSort [ Math . floor ( Math . random ( ) * this . imagesToSort . length ) ] ;
this . imageToSort = this . images [ this . imageIdToSort ] ;
2020-06-18 15:15:37 +02:00
let clone = this . sorterTemplate . content . cloneNode ( true ) ;
let imgEl = clone . querySelector ( ".img" ) ;
let descriptionEl = clone . querySelector ( ".description" ) ;
let nameEl = clone . querySelector ( ".name" ) ;
let weightEl = clone . querySelector ( ".weight" ) ;
imgEl . src = this . imageToSort . getImageUrl ( ) ;
nameEl . innerHTML = this . imageToSort . info [ 'image' ] ;
descriptionEl . innerHTML = this . imageToSort . info [ 'set' ] ;
// weightEl.value = parseInt(image.info['weight']);
weightEl . value = parseInt ( this . imageToSort . position ) ; // don't use weight, but the image's actual position
weightEl . addEventListener ( 'input' , this . changeSorterWeight . bind ( this ) ) ;
2020-06-19 21:16:35 +02:00
weightEl . addEventListener ( 'mousedown' , this . followSortingImage . bind ( this ) ) ;
2020-06-18 15:15:37 +02:00
sorterContent . innerHTML = "" ;
sorterContent . appendChild ( clone ) ;
}
2020-06-19 21:16:35 +02:00
followSortingImage ( ) {
clearTimeout ( this . followCameraTimeout ) ; // cancel any pending return to spectator cam
// this.imgCamera.lockedTarget = this.imageToSort.mesh;
this . imgCamera . parent = this . imageToSort . mesh ;
this . scene . activeCamera = this . imgCamera ;
}
stopFollowSortingImage ( ) {
this . scene . activeCamera = this . spectatorCamera ;
}
2020-06-18 15:15:37 +02:00
changeSorterWeight ( ev ) {
this . play ( ) ;
// make sure nr is never too big.
let newIndex = Math . min ( this . images . length - 1 , parseInt ( ev . target . value ) ) ;
this . setImagePosition ( this . imageToSort , newIndex ) ;
}
setImagePosition ( image , new _position ) {
let id = this . images . indexOf ( image ) ;
let old _position = this . imagePositions . indexOf ( id ) ;
// move from old to new position in the array, see https://stackoverflow.com/a/5306832
this . imagePositions . splice (
new _position , 0 ,
this . imagePositions . splice ( old _position , 1 ) [ 0 ]
) ;
// update positions of image objects
for ( let i in this . images ) {
i = parseInt ( i ) ;
this . images [ i ] . setPosition ( this . imagePositions . indexOf ( i ) , this . t + 2000 ) ;
}
// save:
localStorage . setItem ( 'imagePositions' , JSON . stringify ( this . imagePositions ) ) ;
}
nextSorterImage ( ) {
this . play ( ) ;
2020-06-19 16:45:30 +02:00
let idx = this . imagesToSort . indexOf ( this . imageIdToSort ) ;
this . imagesToSort . splice ( idx , 1 ) ; // remove from images to position
// store the array
localStorage . setItem ( 'imagesToSort' , JSON . stringify ( this . imagesToSort ) ) ;
2020-06-18 15:15:37 +02:00
this . changeSorterImage ( ) ;
}
2020-06-19 17:45:46 +02:00
reset ( ) {
// for use in terminal only. Quick way to test
localStorage . clear ( ) ;
location . reload ( ) ;
}
2020-06-18 15:15:37 +02:00
play ( ) {
// play audio for n seconds:
// extend timeout if it's already playing
2020-06-19 16:45:30 +02:00
const duration = this . config . playDuration ; // seconds
2020-06-18 15:15:37 +02:00
if ( this . pauseTimeout ) {
clearTimeout ( this . pauseTimeout ) ;
}
2020-06-19 16:45:30 +02:00
if ( this . imagesToSort . length > 0 ) {
this . pauseTimeout = setTimeout ( ( ) => {
this . pause ( ) ;
} , duration * 1000 ) ;
} else {
// don't pause anymore when everything is sorted.
this . pauseTimeout = null ;
}
2020-06-18 15:15:37 +02:00
this . audioEl . play ( ) ;
document . body . classList . remove ( 'paused' ) ;
this . isPlaying = true ;
}
2020-06-19 16:45:30 +02:00
pause ( dopause ) {
if ( this . imagesToSort . length < 1 && ( typeof dopause == 'undefined' || false ) ) {
console . log ( 'do not pause' ) ;
return ;
}
2020-06-18 15:15:37 +02:00
// pause audio and animation
document . body . classList . add ( 'paused' ) ;
this . isPlaying = false ;
2020-06-19 16:45:30 +02:00
this . audioEl . pause ( ) ;
2020-06-18 09:58:04 +02:00
}
renderLoop ( ) {
// limit camera movement:
// let camera = this.scene.cameras[0];
// if(camera.rotation['x'] < 0.1) {
// camera.rotation['x'] = 0.1
// }
// if(camera.rotation['x'] > .3) {
// camera.rotation['x'] = .3
// }
2020-06-18 15:15:37 +02:00
this . animate ( ) ; // update positions of the objects
2020-06-18 09:58:04 +02:00
this . scene . render ( ) ;
}
animate ( ) {
2020-06-18 15:15:37 +02:00
let n = window . performance . now ( ) ;
let dt = 0 ;
if ( this . isPlaying ) {
// todo: make it come to a slow halt, instead of sudden;
dt = n - this . lastRenderTime ;
2020-06-19 16:45:30 +02:00
if ( dt < 0 )
console . log ( 'change' , dt ) ;
2020-06-18 15:15:37 +02:00
this . t += dt ;
}
this . lastRenderTime = n ; // also increment when paused;
2020-06-18 09:58:04 +02:00
// console.log(maxStepSize);
for ( let i of this . images ) {
2020-06-18 15:15:37 +02:00
i . updateMeshPosition ( this . t , dt , this . config ) ;
2020-06-18 09:58:04 +02:00
}
}
createScene ( ) {
2020-06-18 15:15:37 +02:00
// This creates a basic Babylon Scene object
2020-06-18 09:58:04 +02:00
this . scene = new BABYLON . Scene ( this . engine ) ;
2020-06-18 15:15:37 +02:00
this . scene . clearColor = new BABYLON . Color3 ( . 22 , 0.27 , 0.38 ) ; // color RGB
2020-06-18 09:58:04 +02:00
// this.scene.clearColor = new BABYLON.Color4(0.6, 0.6, 0.,0); // transparent - RGBA
this . scene . fogMode = BABYLON . Scene . FOGMODE _EXP ;
this . scene . fogColor = this . scene . clearColor ;
this . scene . fogDensity = 0.002 ;
// let camera = new BABYLON.UniversalCamera("camera1", new BABYLON.Vector3( 0.0, 0.0, 120.86337575312979), this.scene);
// camera.rotation = new BABYLON.Vector3(0, Math.PI, 0);
// camera.fov = 1.1; // default: 0.8
2020-06-19 21:16:35 +02:00
// CAM 1: the specatator position.
this . spectatorCamera = new BABYLON . UniversalCamera ( "SpectatorCam" , new BABYLON . Vector3 ( - 11.79906036421707 , - 0.7028999316894499 , 32.43524104627659 ) , this . scene ) ;
this . spectatorCamera . rotation = new BABYLON . Vector3 ( - 0.16070762395712468 , 2.4005702973639886 , 0 ) ;
this . spectatorCamera . fov = 1.5 ;
// this.spectatorCamera.rotation = new BABYLON.Vector3(0.1, 0,0);
// this.spectatorCamera.lowerRadiusLimit = 6;
// this.spectatorCamera.upperRadiusLimit = 20;
2020-06-18 09:58:04 +02:00
// This attaches the camera to the canvas
2020-06-19 21:16:35 +02:00
this . spectatorCamera . attachControl ( this . canvasEl , true ) ;
2020-06-18 09:58:04 +02:00
2020-06-19 21:16:35 +02:00
// CAM 2: follow/track the image when we're editing its weight.
// Parameters: name, position, scene
this . imgCamera = new BABYLON . ArcRotateCamera ( "FollowCam" , 20 , 20 , 10 , new BABYLON . Vector3 ( 0 , 0 , 0 ) , this . scene ) ;
this . imgCamera . position = new BABYLON . Vector3 ( 20 , 10 , - 20 ) ;
// // this.imgCamera = new BABYLON.FollowCamera("FollowCam", new BABYLON.Vector3(0, 10, -10), this.scene);
// // The goal distance of camera from target
// this.imgCamera.radius = 50;
// // The goal height of camera above local origin (centre) of target
// this.imgCamera.heightOffset = 20;
// // The goal rotation of camera around local origin (centre) of target in x y plane
// this.imgCamera.rotationOffset = 120; //degree
// // Acceleration of camera in moving from current to goal position
// this.imgCamera.cameraAcceleration = 0.1;
// // The speed at which acceleration is halted
// this.imgCamera.maxCameraSpeed = 100
// This attaches the camera to the canvas (disabled for now)
// this.imgCamera.attachControl(this.canvasEl, true);
// this.imgCamera.lockedTarget = null; // So far there's nothing to track yet
this . scene . activeCamera = this . spectatorCamera ;
2020-06-18 09:58:04 +02:00
for ( let i in this . images ) {
2020-06-18 15:15:37 +02:00
// create the Meshes with the images in the scene
2020-06-19 17:45:46 +02:00
this . images [ i ] . makeMesh ( this . scene , this . config ) ;
2020-06-18 09:58:04 +02:00
}
2020-06-18 15:15:37 +02:00
this . lastRenderTime = window . performance . now ( ) ;
2020-06-19 21:16:35 +02:00
// this.scene.registerBeforeRender(this.animate.bind(this)); // we now do this in renderLoop().. for some reason works better/different?
2020-06-18 09:58:04 +02:00
return this . scene ;
}
}
class ParadeImage {
constructor ( info , position ) {
this . info = info ;
this . weight = info [ 'weight' ] ;
this . position = position ; // index in list
2020-06-18 15:15:37 +02:00
this . target _position = position ;
this . target _position _time = 0 ;
}
getImageUrl ( ) {
return 'dataset/small/' + this . info [ 'image' ] ;
}
/ * *
*
* @ param { int } new _position new position in parade
* @ param { float } time _complete time at which it should have transitioned to this position
* /
setPosition ( new _position , time _complete ) {
if ( this . target _position == new _position ) return ;
// console.log('changepos', this.target_position, new_position);
this . target _position = new _position ;
this . target _position _time = time _complete ;
2020-06-18 09:58:04 +02:00
}
2020-06-19 17:45:46 +02:00
makeMesh ( scene , config ) {
this . mesh = new BABYLON . TransformNode ( ) ;
2020-06-18 15:15:37 +02:00
2020-06-19 17:45:46 +02:00
2020-06-19 17:50:50 +02:00
var img = new Image ( ) ;
let self = this ; // use in callback
img . onload = function ( ) {
// we preload the image, so we can create the plane based on image dimensions
// see https://www.html5gamedevs.com/topic/8917-textures-source-image-aspect-ratio/?do=findComment&comment=52949
var planeOptions = {
// TODO: should size be dynamic?
height : this . height / 100 ,
width : this . width / 100 ,
sideOrientation : BABYLON . Mesh . DOUBLESIDE // texture should be visible from front and back
} ;
let imagePlane = BABYLON . MeshBuilder . CreatePlane ( "image" , planeOptions , scene ) ;
var imageMat = new BABYLON . StandardMaterial ( "m" , scene ) ;
imageMat . diffuseTexture = new BABYLON . Texture ( self . getImageUrl ( ) , scene ) ;
imageMat . roughness = 1 ;
imageMat . emissiveColor = new BABYLON . Color3 . White ( ) ;
imagePlane . material = imageMat ;
imagePlane . parent = self . mesh ;
}
img . src = this . getImageUrl ( ) ;
2020-06-19 17:45:46 +02:00
// text https://www.babylonjs-playground.com/#TMHF80
// TODO: cleanup this mess of the dynamic texture:
let text = this . info [ 'image' ] ;
var temp = new BABYLON . DynamicTexture ( "DynamicTexture" , 64 , scene ) ;
var tmpctx = temp . getContext ( ) ;
let maxWidth = 1700 ; // at some point texture starts doing messy things.. let's avoid that by squeezing text.
tmpctx . font = config [ 'font' ] ;
var DTWidth = Math . min ( tmpctx . measureText ( text ) . width , maxWidth ) ;
var planeHeight = 3 ;
var DTHeight = 256 ; //or set as wished
var ratio = planeHeight / DTHeight ;
var planeWidth = DTWidth * ratio ;
var dynamicTexture = new BABYLON . DynamicTexture ( "DynamicTexture" , { width : DTWidth , height : DTHeight } , scene , false ) ;
dynamicTexture . hasAlpha = true ;
var ctx = dynamicTexture . getContext ( ) ;
ctx . font = config [ 'font' ] ;
ctx . fillStyle = config [ 'textColor' ] ;
ctx . fillText ( text , 0 , 50 , maxWidth ) ;
dynamicTexture . update ( ) ;
var mat = new BABYLON . StandardMaterial ( "mat" , scene ) ;
mat . diffuseTexture = dynamicTexture ;
mat . opacityTexture = dynamicTexture ; // smoother transparency: https://www.html5gamedevs.com/topic/19647-quality-of-dynamictexture-text-with-transparent-background/?do=findComment&comment=111383
2020-06-18 15:15:37 +02:00
mat . roughness = 1 ;
mat . emissiveColor = new BABYLON . Color3 . White ( ) ;
2020-06-19 17:45:46 +02:00
var textPlane = BABYLON . MeshBuilder . CreatePlane ( "text" , { width : planeWidth , height : planeHeight , sideOrientation : BABYLON . Mesh . DOUBLESIDE } , scene ) ;
textPlane . material = mat ;
textPlane . parent = this . mesh ;
textPlane . position . y = - 4.3 ; // text sits below image
2020-06-18 09:58:04 +02:00
2020-06-18 15:15:37 +02:00
this . mesh . rotation . y = Math . PI ; // rotate 180 deg. to avoid looking at backside from the start.
2020-06-18 09:58:04 +02:00
}
2020-06-18 15:15:37 +02:00
updateMeshPosition ( t , dt , config ) {
// first see how we are with our moving positions:
2020-06-18 09:58:04 +02:00
2020-06-18 15:15:37 +02:00
if ( dt < 1 ) {
// don't change positions, because we're paused
}
else if ( Math . abs ( this . position - this . target _position ) > 0.00001 ) {
if ( t > this . target _position _time ) {
console . error ( 'were we too slow?' ) ;
this . position = this . target _position ;
}
2020-06-18 09:58:04 +02:00
2020-06-18 15:15:37 +02:00
let tdiff = this . target _position _time - t ;
if ( tdiff != 0 ) {
if ( tdiff < dt ) {
// never place it too far...
this . position = this . target _position ;
} else {
this . position += ( this . target _position - this . position ) / ( tdiff / dt ) ;
// console.log(this.position, this.target_position_time, t);
}
}
} else {
// it's practically the same, but set it to avoid any rounding uglyness.
this . position = this . target _position ;
}
let p = t ; //0; // if 0, disable movement for a bit.
p *= config . speedFactor / 1000
p -= config . startOffset / config . speedFactor ; // start offset ( milliseconds)
p -= config . distanceFactor * this . position / 1000 ;
2020-06-18 09:58:04 +02:00
2020-06-18 15:15:37 +02:00
// factors determine scale of animation
let target _x = Math . sin ( 2 * p ) * config . shapeWidth ;
let target _z = Math . sin ( p ) * config . shapeHeight ;
2020-06-18 09:58:04 +02:00
this . mesh . position . x = target _x ;
this . mesh . position . z = target _z ;
}
}