230 lines
11 KiB
JavaScript
230 lines
11 KiB
JavaScript
|
|
// let sequentialFilters = []
|
||
|
|
// <script src="filters/keymap.js"></script>
|
||
|
|
|
||
|
|
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 <a-entity logiteck-mx-ink-controls="hand: right"></a-entity> 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
|
||
|
|
}
|