// let sequentialFilters = [] // let currentFilter = null function applyNextFilter( filename ){ if ( currentFilter == null ) currentFilter = -1 currentFilter++ if ( sequentialFilters[currentFilter] ){ sequentialFilters[ currentFilter ]( filename ) } else { console.log( "done filtering for", filename ) currentFilter = null } } function showFile( filename, openingOptions = {}){ console.log('showFile', filename) fetch( '/mnt/root/scene-drumkeyboard/'+filename ).then( r => { let idFromFilename = filename.replaceAll('.','') // has to remove from proper CSS ID if (!filesWithMetadata[filename] ) filesWithMetadata[filename] = {} filesWithMetadata[filename].contentType = r.headers.get('Content-Type') filesWithMetadata[filename].idFromFilename = idFromFilename filesWithMetadata[filename].openingOptions = openingOptions console.log( 'ct metadata', filename, filesWithMetadata[filename].contentType ) applyNextFilter( filename ) // const showdefinitions = urlParams.get('showdefinitions'); // should be showFile() configuration parameter instead // can be used via e.g. showFile("https://fabien.benetou.fr/?action=source",{ mereology:"whole"}) // should emit an event when done, for now just console.log which isn't programmatic }) } function addDrumKeyboard(){ // get keymap showFile('fabien_corneish_zen.keymap') // showFile('fabien_corneish_zen.keymap') const swiping = urlParams.get('swiping'); const keyclass = "keys_from_drumsticks" // transform keymap to keys of keyboard let keyboardEl = document.createElement("a-entity") keyboardEl.id = 'keyboard' AFRAME.scenes[0].appendChild( keyboardEl ) AFRAME.scenes[0].addEventListener("keymaploaded", e => { applyToClass("keymap_layer", el => el.setAttribute("visible", "false") ) // alternatively other layers could be displayed but with lower opacity // might be very busy but easier to recall Array.from( document.querySelectorAll('.keymap_layer') ).map( (layerToAdd, layerNumber) => { layerToAdd.getAttribute("value").split('\n').map( (l,y) => { l.split("|").map( (k,x) => { if (k.trim()) { // zIndex could be once deep once shallow to potentially go faster between keys, kind of straggered vs ortho let xOffset = -.2 let yOffset = 1.3 let zOffset = -.4 let keysPerRow = l.split("|").length -1 let ratio = 1/20 zOffset -= Math.abs( keysPerRow/2 - x ) * ratio/5 // arguably more ergonomic if (swiping){ zOffset = zOffset + Math.abs( x - keysPerRow/2 ) * ratio } // opposite from swiping // somehow the center isn't... the center if (l.length < 70) xOffset += .15 // 80 was fine for layer 0 but not layer 1 and 2 let labelEl = document.createElement("a-troika-text") labelEl.setAttribute("value",k.trim()) labelEl.setAttribute("position", "0 .51 0") labelEl.setAttribute("font-size", "1") labelEl.setAttribute("color", "black") labelEl.setAttribute("rotation", "-90 0 0") let keyEl = document.createElement("a-cylinder") keyEl.setAttribute("segments-height", "2") keyEl.setAttribute("segments-radial", "24") keyEl.setAttribute("scale", ".01 .01 .01") keyEl.setAttribute("rotation", "60 0 0") keyEl.setAttribute("position", ""+(xOffset+x*ratio)+" " +(yOffset-y*ratio)+" "+(zOffset + y/50) ) keyEl.classList.add( keyclass ) keyEl.classList.add( "layer_"+ layerNumber ) keyEl.appendChild( labelEl ) keyboardEl.appendChild( keyEl ) // keyEl.id = keyclass+'_'+k.trim() // not correct anymore as multiple layers can have the same key keyEl.id = keyclass+'_'+layerNumber+'_'+k.trim() // setTimeout( _ => keyEl.object3D.lookAt( new THREE.Vector3(0, 1.5, 0)), 100 ) // not great as they have 90 deg rotation already, could find a better way // ... but arguably should be the opposite based on resting hand positions } }) }) }) keys_from_drumsticks_0_SHFT.setAttribute("wireframe", shiftFromVirtualKeyboard) // arguable, could do so for all layers // hide all layers but the current one Array.from( document.querySelectorAll(".keys_from_drumsticks") ).map( k => k.setAttribute("visible", "false") ) Array.from( document.querySelectorAll(".keys_from_drumsticks"+".layer_"+layerFromVirtualKeyboard) ).map( k => k.setAttribute("visible", "true") ) keyBoardCheck() }) const threshold = .02 // distance const refractionPeriod = 500 // ms until next keypress // add visible contact points let jointTestEl1 = document.createElement("a-sphere") jointTestEl1.setAttribute("radius", .01) jointTestEl1.id = "jointtest1" AFRAME.scenes[0].appendChild( jointTestEl1 ) let jointTestEl2 = document.createElement("a-sphere") jointTestEl2.setAttribute("radius", .01) jointTestEl2.id = "jointtest2" AFRAME.scenes[0].appendChild( jointTestEl2 ) AFRAME.scenes[0].setAttribute("bind-element-to-finger__test1", { hand: 'r_handMeshNode', finger: 'index-finger-tip', target: '#jointtest1' } ) AFRAME.scenes[0].setAttribute("bind-element-to-finger__test2", { hand: 'l_handMeshNode', finger: 'index-finger-tip', target: '#jointtest2' } ) // thumb tip test let jointTestEl3 = document.createElement("a-sphere") jointTestEl3.setAttribute("radius", .01) jointTestEl3.id = "jointtest3" AFRAME.scenes[0].appendChild( jointTestEl3 ) let jointTestEl4 = document.createElement("a-sphere") jointTestEl4.setAttribute("radius", .01) jointTestEl4.id = "jointtest4" AFRAME.scenes[0].appendChild( jointTestEl4 ) AFRAME.scenes[0].setAttribute("bind-element-to-finger__test3", { hand: 'r_handMeshNode', finger: 'thumb-tip', target: '#jointtest3' } ) AFRAME.scenes[0].setAttribute("bind-element-to-finger__test4", { hand: 'l_handMeshNode', finger: 'thumb-tip', target: '#jointtest4' } ) const forcecontrollers = urlParams.get('forcecontrollers'); if (forcecontrollers){ setTimeout( _ => { jointtest1.object3D.parent = document.querySelector("[meta-touch-controls]").object3D jointtest2.object3D.parent = document.querySelector("[oculus-touch-controls]").object3D }, 2000 ) // TODO does not work with pen // see that does not seem to show anything, even in AFrame 1.7.1 nor recent master build } // if done a la bind-element-to-finger consider // const joint = AFRAME.scenes[0].object3D.getObjectByName(this.data.hand)?.parent.getObjectByName(this.data.finger) // if ( joint && this.data.target.object3D.parent == AFRAME.scenes[0].object3D ) this.data.target.object3D.parent = joint let pos1 = new THREE.Vector3() let pos2 = new THREE.Vector3() let pos3 = new THREE.Vector3() let pos4 = new THREE.Vector3() let shiftFromVirtualKeyboard = true let layerFromVirtualKeyboard = 0 window.keyboardTarget = typinghud // check for potential contact let lastKeypress = Date.now() function keyBoardCheck() { return setInterval( _ => { jointTestEl1.object3D.getWorldPosition( pos1 ) jointTestEl2.object3D.getWorldPosition( pos2 ) jointTestEl3.object3D.getWorldPosition( pos3 ) jointTestEl4.object3D.getWorldPosition( pos4 ) // could also check only when jointTestEl1 / jointTestEl2 are visible, ignore otherwise // to do with all keys instead Array.from( document.querySelectorAll(".keys_from_drumsticks"+".layer_"+layerFromVirtualKeyboard) ) .concat( [keys_from_drumsticks_0_LWR, keys_from_drumsticks_0_RSE] ) .filter( k => k.getAttribute("visible") ) .map( k => { // should only look at visible keys, could limit via .filter( k => k.getAttribute("visible") == "true") // this way one wouldn't type on an invisible keyboard let d1 = k.object3D.position.distanceTo( pos1 ) let d2 = k.object3D.position.distanceTo( pos2 ) let d3 = k.object3D.position.distanceTo( pos3 ) let d4 = k.object3D.position.distanceTo( pos4 ) if ( d1 < threshold || d2 < threshold || d3 < threshold || d4 < threshold) { //if ( d1 < threshold || d2 < threshold) { k.setAttribute("color", "pink") if (keyboardTarget == typinghud) typinghud.setAttribute("material","opacity", .5) if ( Date.now() - lastKeypress < refractionPeriod ){ // console.warn('ignoring, executed during the last 500ms already') let x = 42 // added just to ignore } else { lastKeypress = Date.now() let value = k.firstChild.getAttribute("value") if ( keyboardTarget.getAttribute("value") == "[]" ) keyboardTarget.setAttribute("value", "" ) // for typinghud starting value if (value == "TAB") console.warn('does not complete yet') // TODO add completion if (value == "SPC") value = " " if (value == "ENT") { if (keyboardTarget == typinghud) { parseKeys("keydown", "Enter") keyboardTarget.setAttribute("value", "" ) } else { keyboardTarget.setAttribute("value", keyboardTarget.getAttribute("value") + '\n' ) } } else if (value == "SHFT") { shiftFromVirtualKeyboard = !shiftFromVirtualKeyboard // visual highlight, also note that is closer to CAPSLOCK behavior keys_from_drumsticks_0_SHFT.setAttribute("wireframe", shiftFromVirtualKeyboard) // arguable, could do so for all layers } else if (value == "RSE") { if (layerFromVirtualKeyboard<2) layerFromVirtualKeyboard++ // hardcoded max console.log('should raise layer', layerFromVirtualKeyboard) // a la CAPSLOCK too Array.from( document.querySelectorAll(".keys_from_drumsticks") ).map( k => k.setAttribute("visible", "false") ) Array.from( document.querySelectorAll(".keys_from_drumsticks"+".layer_"+layerFromVirtualKeyboard) ).map( k => k.setAttribute("visible", "true") ) // forcing visibility yet get ignored as on wrong layer keys_from_drumsticks_0_LWR.setAttribute("visible", "true") keys_from_drumsticks_0_RSE.setAttribute("visible", "true") } else if (value == "LWR") { if (layerFromVirtualKeyboard>0) layerFromVirtualKeyboard-- console.log('should lower layer', layerFromVirtualKeyboard) // a la CAPSLOCK too Array.from( document.querySelectorAll(".keys_from_drumsticks") ).map( k => k.setAttribute("visible", "false") ) Array.from( document.querySelectorAll(".keys_from_drumsticks"+".layer_"+layerFromVirtualKeyboard) ).map( k => k.setAttribute("visible", "true") ) keys_from_drumsticks_0_LWR.setAttribute("visible", "true") keys_from_drumsticks_0_RSE.setAttribute("visible", "true") } else if (value == "BKSP") { keyboardTarget.setAttribute("value", keyboardTarget.getAttribute("value").slice(0,-1) ) } else { if (!shiftFromVirtualKeyboard) value = value.toLowerCase() keyboardTarget.setAttribute("value", keyboardTarget.getAttribute("value") + value ) } keyboardEl.emit( "keypressed", {key: value} ) } } else if ( d1 < threshold*1.2 || d2 < threshold*1.2) { // arguably, not convinced it brings value more than confusion k.setAttribute("color", "#ffe4e1") } else { k.setAttribute("color", "white") } }) }, 20) } return keyboardEl }