From 9154e174325160ff9f6eb82178cfd9ef65740a58 Mon Sep 17 00:00:00 2001 From: Fabien Benetou Date: Wed, 8 May 2024 18:05:22 +0200 Subject: [PATCH] working mechanism --- index.html | 126 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 107 insertions(+), 19 deletions(-) diff --git a/index.html b/index.html index 95c327b..e835c53 100644 --- a/index.html +++ b/index.html @@ -18,34 +18,46 @@ AFRAME.registerComponent('startfunctions', { init: function(){ let newEl = document.createElement('a-entity') - newEl.setAttribute('instruction-coffee-machine', '') + newEl.setAttribute('instruction-machine', '') AFRAME.scenes[0].appendChild(newEl) } }) //___________________________________________________________________________________________________________________________________ -AFRAME.registerComponent('instruction-coffee-machine', { -// once done, equivalent for +AFRAME.registerComponent('instruction-machine', { +// other content next, ideally combinable // tournevie metal workshop // laser cutter at the EP - // todo - // describe the target task, e.g "Make extra long coffee (on PicoBarista)" - // 3D model of target object to align efficiently - // list the steps then iterate over them : - // highlight, e.g via arrow pointer, the next element to interact with - // confirm that the instruction was properly conducted - // optionally allow to - // rollback 1 step - // restart from scratch - // adjust bounding box - // port a much simplified version, i.e no 6DoF or hand tracking, to Monocle/Frame - // probably text only then, so only the load from wiki sequential instructions part +// todo + // visual timeline and where we are on it, e.g 1/4 steps + // a la Lego instruction, i.e horizontal bar with dot on current position + // could use a cylinder and a moving sphere +// could be helpful to persist once position is ok, after step 1 then + // https://aframe.io/docs/1.5.0/components/anchored.html + // timeout for increasingly explicit feedback + // jump to specific step via hash to better discussion +// port a text version, i.e no 6DoF or hand tracking, to Monocle/Frame + // adapt from wiki sequential instructions part, possibly with some images +// consider https://threejs.org/docs/?q=grid#api/en/helpers/GridHelper too + init: function(){ + + // shortcut to navigate through steps, 2D only + onwheel = (event) => { + event.deltaY < 0 ? + document.querySelector("["+generatorName+"]").emit('previousStep') + // does not yet exist + : document.querySelector("["+generatorName+"]").emit('nextStep') + } + let generatorName = this.attrName const idSuffix = "_framing_box" let el = this.el this.scale = 1/10 - this.steps = ["pick the grey box and align it with your coffee machine", "press button A", "press button B"] + this.steps = ["pick the grey box and align it with your 3D printer", "press change filament button", "remove the filament spool"] + // symplistic, could instead be a directed graph of named nodes, each with optionally multiple outgoing edges + this.buttonSuffix = "_step_button" + this.endOfInstructions = "You have finished the instructions. Feel free to reset and try again." const confirmationStep = "Done" // could also consider a "done" area, moving the text there let newEl = document.createElement('a-box') newEl.setAttribute("scale", ""+this.scale+" "+this.scale+" "+this.scale) @@ -62,9 +74,47 @@ AFRAME.registerComponent('instruction-coffee-machine', { newEl.id = generatorName+idSuffix el.appendChild(newEl) + let timelinegEl = document.createElement('a-cylinder') + timelinegEl.setAttribute("height", "1") + timelinegEl.setAttribute("radius", ".1") + timelinegEl.setAttribute("color", "white") + timelinegEl.setAttribute("rotation", "90 0 0") + timelinegEl.setAttribute("position", "0.1 0.1 0.5") + timelinegEl.setAttribute("visible","false") + timelinegEl.classList.add( generatorName ) + timelinegEl.id = generatorName+"_timeline" + //newEl.appendChild(timelinegEl) + + let buttonEl = document.createElement('a-box') + buttonEl.setAttribute("scale", ".2 .1 .1") + buttonEl.setAttribute("wireframe", "true") + buttonEl.setAttribute("color", "red") + buttonEl.setAttribute("position", "0.1 0.1 0.5") + buttonEl.setAttribute("target","true") + buttonEl.setAttribute("visible","false") + buttonEl.classList.add( generatorName ) + buttonEl.classList.add( generatorName+this.buttonSuffix) + buttonEl.id = generatorName+this.buttonSuffix+"_1" + newEl.appendChild(buttonEl) + this.addArrow([-4.5, 0, 0], buttonEl) + + buttonEl = document.createElement('a-cylinder') + buttonEl.setAttribute("scale", ".5 .5 .5") + buttonEl.setAttribute("wireframe", "true") + buttonEl.setAttribute("color", "blue") + buttonEl.setAttribute("rotation", "90 0 0") + buttonEl.setAttribute("position", "0 0 -0.5") + buttonEl.setAttribute("target", "true") + buttonEl.setAttribute("visible","false") + buttonEl.classList.add( generatorName ) + buttonEl.classList.add( generatorName+this.buttonSuffix) + buttonEl.id = generatorName+this.buttonSuffix+"_2" + newEl.appendChild(buttonEl) + this.addArrow([-4.5, 0, 0], buttonEl) let n = addNewNote("jxr qs #"+newEl.id+" sa rotation 0", "0.5 0.9 -.5") n.setAttribute("rotation", "90 0 0") n.setAttribute("annotation", "content: snap") + let actions = [ "translateX(.1)", "translateX(-.1)", "translateY(.1)", "translateY(-.1)", "translateZ(.1)", "translateZ(-.1)", "rotateX(-.1)", "rotateX(.1)", "rotateY(.1)", "rotateY(-.1)", "rotateZ(.1)", "rotateZ(-.1)", @@ -72,13 +122,21 @@ AFRAME.registerComponent('instruction-coffee-machine', { actions.reverse().map( (a,i) => { let n = addNewNote("jxr qs #"+newEl.id+" .object3D."+a, ".5 "+(1+i/20)+" -.5") n.setAttribute("rotation", "90 0 0") - n.setAttribute("annotation", "content: "+a) + n.setAttribute("annotation", "content: "+a.replace(/xxx/,'')) }) el.appendChild(newEl) n = addNewNote("jxr qs #"+newEl.id+" .emit('nextStep')" , "0 1.1 -.5") n.setAttribute("rotation", "90 0 0") - n.setAttribute("annotation", "content: confirm/reset") + n.setAttribute("annotation", "content: confirm") + + n = addNewNote("jxr qs #"+newEl.id+" .emit('previousStep')" , "0 1.0 -.5") + n.setAttribute("rotation", "90 0 0") + n.setAttribute("annotation", "content: go backward") + + n = addNewNote("jxr qs #"+newEl.id+" .emit('reset')" , "0 0.8 -.5") + n.setAttribute("rotation", "90 0 0") + n.setAttribute("annotation", "content: restart") this.currentInstructionStep = 0 this.currentInstruction = addNewNote(this.steps[this.currentInstructionStep], "-.3 1.2 -.5") @@ -88,11 +146,41 @@ AFRAME.registerComponent('instruction-coffee-machine', { reset: function (evt) { console.log(this.attrName, 'component was resetted!'); let generatorName = this.attrName + this.currentInstructionStep=-1 + document.querySelector("["+generatorName+"]").emit('nextStep') }, nextStep: function (evt) { - this.currentInstruction.setAttribute("value",this.steps[++this.currentInstructionStep]) + let generatorName = this.attrName + if (this.currentInstructionStep el.setAttribute("visible", "false" ) ) + document.getElementById( generatorName+this.buttonSuffix +"_"+this.currentInstructionStep )?.setAttribute("visible", "true" ) + } else { + this.currentInstruction.setAttribute("value", this.endOfInstructions) + } + }, + previousStep: function (evt) { + let generatorName = this.attrName + if (this.currentInstructionStep>0){ + this.currentInstruction.setAttribute("value",this.steps[--this.currentInstructionStep]) + Array.from( document.querySelectorAll( "."+generatorName+this.buttonSuffix) ).map( el => el.setAttribute("visible", "false" ) ) + document.getElementById( generatorName+this.buttonSuffix +"_"+this.currentInstructionStep )?.setAttribute("visible", "true" ) + } }, }, + addArrow: function(relativePosition, parentEl, color=0xffff00){ + const dir = new THREE.Vector3( 1, 0, 0 ) + + //normalize the direction vector (convert to vector of length 1) + dir.normalize() + + const origin = new THREE.Vector3( ...relativePosition ) + const length = 4 + const hex = color + + const arrowHelper = new THREE.ArrowHelper( dir, origin, length, hex ) + parentEl.object3D.add( arrowHelper ) + } }) function cloneTarget(target){