refactor media fragments
This commit is contained in:
parent
1851ee1d4f
commit
e8308d738b
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.5 MiB |
|
@ -6,4 +6,5 @@ for topic in Fragments Macros; do
|
|||
mmark --html RFC_XR_$topic.md | grep -vE '(<!--{|}-->)' > RFC_XR_$topic.html
|
||||
xml2rfc --v3 RFC_XR_$topic.xml # RFC_XR_$topic.txt
|
||||
sed -i 's/Expires: .*//g' RFC_XR_$topic.txt
|
||||
convert -size 700x2400 xc:white -font "FreeMono" +antialias -pointsize 12 -fill black -annotate +15+15 "@RFC_XR_Fragments.txt" -colorspace gray +dither -posterize 6 RF6_XR_Fragments.png
|
||||
done
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
mmark
|
||||
xml2rfc
|
||||
wkhtmltopdf-bin
|
||||
imagemagick
|
||||
|
||||
];
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ XRWG.match = (str,types,level) => {
|
|||
return n
|
||||
})
|
||||
str = str.toLowerCase()
|
||||
.replace(/[-\*]/,'') // remove excludes and wildcards
|
||||
if( level <10 ) res = res.filter( (n) => n.key == str )
|
||||
if( level >=10 ) res = res.filter( (n) => n.word == str || n.key == str )
|
||||
if( level >30 ) res = res.filter( (n) => n.word.match(str) || n.key == str )
|
||||
|
|
|
@ -4,6 +4,13 @@ AFRAME.registerComponent('xrf-gaze',{
|
|||
schema:{
|
||||
spawn:{type:'boolean',default:false},
|
||||
},
|
||||
events:{
|
||||
"fusing": function(e){
|
||||
if( e.detail.mouseEvent ) return // ignore click event
|
||||
console.dir(e)
|
||||
|
||||
}
|
||||
},
|
||||
setGazer: function(state){
|
||||
let cam = document.querySelector("[camera]")
|
||||
if( state ){
|
||||
|
@ -15,7 +22,6 @@ AFRAME.registerComponent('xrf-gaze',{
|
|||
raycaster="objects: .ray"
|
||||
visible="true"
|
||||
position="0 0 -1"
|
||||
geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
|
||||
material="color: #BBBBBB; shader: flat">
|
||||
</a-entity>`
|
||||
}else{
|
||||
|
@ -25,9 +31,12 @@ AFRAME.registerComponent('xrf-gaze',{
|
|||
}
|
||||
},
|
||||
init:function(data){
|
||||
let enabled = () => AFRAME.utils.device.isMobile()
|
||||
let setVisible = (state) => {
|
||||
if( enabled() ) this.setGazer(state)
|
||||
if( AFRAME.utils.device.isMobile() ){
|
||||
this.setGazer(state)
|
||||
if( state || xrf.debug ) this.el.setAttribute("geometry","primitive: ring; radiusInner: 0.02; radiusOuter: 0.03")
|
||||
else this.el.removeAttribute("geometry")
|
||||
}
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
|
|
|
@ -3,17 +3,29 @@
|
|||
// it allows metadata-keys ('foo' e.g.) of 3D scene-nodes (.userData.foo e.g.) to
|
||||
// react by executing code
|
||||
|
||||
let pub = function( url, model, flags ){ // evaluate fragments in url
|
||||
let pub = function( url, node_or_model, flags ){ // evaluate fragments in url
|
||||
if( !url ) return
|
||||
if( !url.match(/#/) ) url = `#${url}`
|
||||
model = model || xrf.model
|
||||
let { THREE, camera } = xrf
|
||||
let frag = xrf.URI.parse( url, flags )
|
||||
let opts = {frag, mesh:xrf.camera, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE, hashbus: xrf.hashbus }
|
||||
let fromNode = node_or_model != xrf.model
|
||||
|
||||
let opts = {
|
||||
frag,
|
||||
mesh: fromNode ? node_or_model : xrf.camera,
|
||||
model: xrf.model,
|
||||
camera: xrf.camera,
|
||||
scene: xrf.scene,
|
||||
renderer: xrf.renderer,
|
||||
THREE: xrf.THREE,
|
||||
hashbus: xrf.hashbus
|
||||
}
|
||||
xrf.emit('hashbus',opts)
|
||||
.then( () => {
|
||||
for ( let k in frag ){
|
||||
pub.fragment(k,opts)
|
||||
let nodeAlias = fromNode && opts.mesh && opts.mesh.userData && opts.mesh.userData[k] && opts.mesh.userData[k][0] == '#'
|
||||
if( nodeAlias ) pub(opts.mesh.userData[k], opts.mesh) // evaluate node alias
|
||||
else pub.fragment(k,opts)
|
||||
}
|
||||
})
|
||||
return frag
|
||||
|
@ -37,7 +49,7 @@ pub.fragment = (k, opts ) => { // evaluate one fragment
|
|||
let frag = opts.frag[k];
|
||||
|
||||
let isPVorMediaFrag = frag.is( xrf.XRF.PV_EXECUTE ) || frag.is( xrf.XRF.T_MEDIAFRAG)
|
||||
if( !opts.skipXRWG && isPVorMediaFrag ) pub.XRWG(opts)
|
||||
if( !opts.skipXRWG && isPVorMediaFrag ) pub.XRWG(k,opts)
|
||||
|
||||
// call native function (xrf/env.js e.g.), or pass it to user decorator
|
||||
xrf.emit(k,opts)
|
||||
|
@ -48,20 +60,23 @@ pub.fragment = (k, opts ) => { // evaluate one fragment
|
|||
})
|
||||
}
|
||||
|
||||
pub.XRWG = (opts) => {
|
||||
pub.XRWG = (word,opts) => {
|
||||
let {frag,scene,model,renderer} = opts
|
||||
|
||||
// if this query was triggered by an src-value, lets filter it
|
||||
const isSRC = opts.embedded && opts.embedded.fragment == 'src'
|
||||
if( !isSRC ){ // spec : https://xrfragment.org/#src
|
||||
for ( let i in frag ) {
|
||||
let v = frag[i]
|
||||
let id = v.is( xrf.XRF.T_DYNAMIC ) ? v.fragment : v.string || v.fragment
|
||||
|
||||
let triggeredByMesh = opts.model != opts.mesh
|
||||
|
||||
let v = frag[word]
|
||||
let id = v.is( xrf.XRF.T_DYNAMICKEY ) ? word : v.string || word
|
||||
|
||||
if( id == '#' || !id ) return
|
||||
let match = xrf.XRWG.match(id)
|
||||
|
||||
if( v.is( xrf.XRF.PV_EXECUTE ) && !v.is( xrf.XRF.T_DYNAMIC ) ){
|
||||
// evaluate aliases
|
||||
if( !triggeredByMesh && (v.is( xrf.XRF.PV_EXECUTE ) || v.is( xrf.XRF.T_DYNAMIC)) && !v.is( xrf.XRF.T_DYNAMICKEYVALUE ) ){
|
||||
// evaluate global aliases or tag/objectnames
|
||||
match.map( (w) => {
|
||||
if( w.key == `#${id}` ){
|
||||
if( w.value && w.value[0] == '#' ){
|
||||
|
@ -71,9 +86,11 @@ pub.XRWG = (opts) => {
|
|||
}
|
||||
})
|
||||
xrf.emit('dynamicKey',{ ...opts,v,frag,id,match,scene })
|
||||
}else{
|
||||
}else if( v.string ){
|
||||
// evaluate global aliases
|
||||
xrf.emit('dynamicKeyValue',{ ...opts,v,frag,id,match,scene })
|
||||
}
|
||||
}else{
|
||||
xrf.emit('dynamicKey',{ ...opts,v,frag,id,match,scene })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
xrf.frag = {}
|
||||
xrf.frag = {dynamic:{}}
|
||||
xrf.model = {}
|
||||
xrf.mixers = []
|
||||
|
||||
|
@ -24,7 +24,7 @@ xrf.patchRenderer = function(opts){
|
|||
xrf.clock = new xrf.THREE.Clock()
|
||||
renderer.render = ((render) => function(scene,camera){
|
||||
// update clock
|
||||
let time = xrf.clock.getDelta()
|
||||
let time = xrf.clock.delta = xrf.clock.getDelta()
|
||||
xrf.emit('render',{scene,camera,time,render}) // allow fragments to do something at renderframe
|
||||
render(scene,camera)
|
||||
xrf.emit('renderPost',{scene,camera,time,render,renderer}) // allow fragments to do something after renderframe
|
||||
|
|
|
@ -30,6 +30,12 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
|
|||
const raycaster = new Raycaster();
|
||||
const tempMatrix = new Matrix4();
|
||||
|
||||
let dispatchEvent = (object,_event) => {
|
||||
object.dispatchEvent(_event)
|
||||
// bubble up
|
||||
object.traverseAncestors( (n) => n.userData && n.userData.href && n.dispatchEvent(_event) )
|
||||
}
|
||||
|
||||
// Pointer Events
|
||||
|
||||
const element = renderer.domElement;
|
||||
|
@ -56,12 +62,12 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
|
|||
|
||||
_event.type = event.type;
|
||||
_event.data.set( uv.x, 1 - uv.y );
|
||||
object.dispatchEvent( _event );
|
||||
dispatchEvent( object, _event );
|
||||
|
||||
}else{
|
||||
if( object.selected ) {
|
||||
_event.type = 'mouseleave'
|
||||
object.dispatchEvent(_event)
|
||||
dispatchEvent( object, _event)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,12 +112,12 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
|
|||
_event.type = events[ event.type ];
|
||||
_event.data.set( uv.x, 1 - uv.y );
|
||||
|
||||
object.dispatchEvent( _event );
|
||||
dispatchEvent( object, _event );
|
||||
|
||||
}else{
|
||||
if( object.selected ) {
|
||||
_event.type = 'mouseleave'
|
||||
object.dispatchEvent(_event)
|
||||
dispatchEvent( object, _event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// this is called by navigator.js rather than by a URL e.g.
|
||||
|
||||
xrf.frag.defaultPredefinedViews = (opts) => {
|
||||
let {scene,model} = opts;
|
||||
scene.traverse( (n) => {
|
||||
if( n.userData && n.userData['#'] ){
|
||||
let frag = xrf.URI.parse( n.userData['#'] )
|
||||
if( !n.parent && document.location.hash.length < 2){
|
||||
xrf.navigator.to( n.userData['#'] ) // evaluate static XR fragments
|
||||
xrf.navigator.to( n.userData['#'] ) // evaluate default XR fragments (global-level)
|
||||
}else{
|
||||
xrf.hashbus.pub( n.userData['#'] ) // evaluate static XR fragments
|
||||
xrf.hashbus.pub( n.userData['#'], n ) // evaluate default XR fragments (node-level)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
// this holds all the URI Template variables (https://www.rfc-editor.org/rfc/rfc6570)
|
||||
|
||||
xrf.addEventListener('parseModel', (opts) => {
|
||||
let {model,url,file} = opts
|
||||
if( model.isSRC || opts.isSRC ) return // ignore SRC models
|
||||
xrf.URI.vars = new Proxy({},{
|
||||
set(me,k,v){ me[k] = v },
|
||||
get(me,k ){
|
||||
if( k == '__object' ){
|
||||
let obj = {}
|
||||
for( let i in xrf.URI.vars ) obj[i] = xrf.URI.vars[i]()
|
||||
return obj
|
||||
}
|
||||
return me[k]
|
||||
},
|
||||
})
|
||||
|
||||
model.scene.traverse( (n) => {
|
||||
if( n.userData ){
|
||||
for( let i in n.userData ){
|
||||
if( i[0] == '#' || i.match(/^(href|src|tag)$/) ) continue // ignore XR Fragment aliases
|
||||
xrf.URI.vars[i] = () => n.userData[i]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
xrf.addEventListener('dynamicKeyValue', (opts) => {
|
||||
// select active camera if any
|
||||
let {id,match,v} = opts
|
||||
|
||||
if( !v.is( xrf.XRF.CUSTOMFRAG) ) return // only process custom frags from here
|
||||
|
||||
// check if fragment is an objectname
|
||||
if( match.length > 0 ){
|
||||
xrf.frag.dynamic.material(v,opts)
|
||||
}else{
|
||||
if( !xrf.URI.vars[ v.string ] ) return // only assign to known values
|
||||
xrf.URI.vars[ id ] = xrf.URI.vars[ v.string ] // update var
|
||||
if( xrf.debug ) console.log(`URI.vars[${id}]='${v.string}'`)
|
||||
|
||||
xrf.scene.traverse( (n) => { // reflect new changes
|
||||
if( n.userData && n.userData.src && n.userData.srcTemplate ){
|
||||
let srcOldFragments = n.userData.src.replace(/.*#/,'')
|
||||
let srcNewFragments = xrf.frag.src.expandURI( n ).replace(/.*#/,'')
|
||||
if( srcOldFragments != srcNewFragments ){
|
||||
console.log(`URI.vars[${id}] => updating ${n.name}`)
|
||||
let frag = xrf.hashbus.pub( srcNewFragments, n )
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
|
@ -0,0 +1,14 @@
|
|||
// switch camera when multiple cameras for url #mycameraname
|
||||
|
||||
xrf.addEventListener('dynamicKey', (opts) => {
|
||||
// select active camera if any
|
||||
let {id,match,v} = opts
|
||||
match.map( (w) => {
|
||||
w.nodes.map( (node) => {
|
||||
if( node.isCamera ){
|
||||
console.log("switching camera to cam: "+node.name)
|
||||
xrf.model.camera = node
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,7 +1,3 @@
|
|||
/*
|
||||
* TODO: refactor/fix this (queries are being refactored to filters)
|
||||
*/
|
||||
|
||||
|
||||
xrf.addEventListener('dynamicKey', (opts) => {
|
||||
let {scene,id,match,v} = opts
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
xrf.frag.dynamic.material = function(v,opts){
|
||||
let {match} = opts
|
||||
|
||||
setMaterial = (mesh,material,reset) => {
|
||||
if( !mesh.materialOriginal ) mesh.materialOriginal = mesh.material
|
||||
if( reset ) mesh.material = mesh.materialOriginal
|
||||
else mesh.material = material
|
||||
}
|
||||
|
||||
// update material in case of <tag_or_object>[*]=<materialname>
|
||||
let material
|
||||
xrf.scene.traverse( (n) => n.material && (n.material.name == v.string) && (material = n.material) )
|
||||
if( !material && !v.reset ) return // nothing to do
|
||||
|
||||
if( material ) xrf.frag.dynamic.material.setMatch(match,material,v)
|
||||
}
|
||||
|
||||
xrf.frag.dynamic.material.setMatch = function(match,material,v){
|
||||
match.map( (m) => {
|
||||
m.nodes.map( (n) => {
|
||||
n.material = setMaterial( n, material, v.reset )
|
||||
if( v.filter.q.deep ) n.traverse( (c) => c.material && setMaterial( c, material, v.reset ) )
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
xrf.addEventListener('dynamicKey', (opts) => {
|
||||
|
||||
let {v,match} = opts
|
||||
|
||||
if( v.reset ){
|
||||
xrf.frag.dynamic.material.setMatch(match,null,v)
|
||||
}
|
||||
|
||||
})
|
|
@ -62,7 +62,7 @@ xrf.frag.href = function(v, opts){
|
|||
xrf.interactive.objects.map( (o) => {
|
||||
let newState = o.name == mesh.name ? state : false
|
||||
if( o.material ){
|
||||
if( o.material.uniforms ) o.material.uniforms.selected.value = newState
|
||||
if( o.material.uniforms && o.material.uniforms.selected ) o.material.uniforms.selected.value = newState
|
||||
//if( o.material.emissive ) o.material.emissive.r = o.material.emissive.g = o.material.emissive.b = newState ? 2.0 : 1.0
|
||||
if( o.material.emissive ){
|
||||
if( !o.material.emissive.original ) o.material.emissive.original = o.material.emissive.clone()
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
xrf.frag.suv = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( !mesh.geometry ) return // nothing to do here
|
||||
|
||||
xrf.frag.uv.init(mesh)
|
||||
mesh.suv.x = v.x
|
||||
mesh.suv.y = v.y !== undefined ? v.y : v.x
|
||||
mesh.suv.loop = v.loop === true ? true : false
|
||||
xrf.frag.uv.scroll(mesh)
|
||||
}
|
|
@ -4,7 +4,7 @@ xrf.frag.src = function(v, opts){
|
|||
opts.embedded = v // indicate embedded XR fragment
|
||||
let { mesh, model, camera, scene, renderer, THREE, hashbus, frag} = opts
|
||||
|
||||
let url = v.string
|
||||
let url = xrf.frag.src.expandURI( mesh, v.string )
|
||||
let srcFrag = opts.srcFrag = xrfragment.URI.parse(url)
|
||||
opts.isLocal = v.string[0] == '#'
|
||||
opts.isPortal = xrf.frag.src.renderAsPortal(mesh)
|
||||
|
@ -15,6 +15,14 @@ xrf.frag.src = function(v, opts){
|
|||
if( opts.isLocal ){
|
||||
xrf.frag.src.localSRC(url,srcFrag,opts) // local
|
||||
}else xrf.frag.src.externalSRC(url,srcFrag,opts) // external file
|
||||
|
||||
xrf.hashbus.pub( url.replace(/.*#/,''), mesh) // eval src-url fragments
|
||||
}
|
||||
|
||||
xrf.frag.src.expandURI = function(mesh,uri){
|
||||
if( uri ) mesh.userData.srcTemplate = uri
|
||||
mesh.userData.src = xrf.URI.template( mesh.userData.srcTemplate, xrf.URI.vars.__object )
|
||||
return mesh.userData.src
|
||||
}
|
||||
|
||||
xrf.frag.src.addModel = (model,url,frag,opts) => {
|
||||
|
@ -32,7 +40,7 @@ xrf.frag.src.addModel = (model,url,frag,opts) => {
|
|||
}else{
|
||||
xrf.frag.src.scale( scene, opts, url ) // scale scene
|
||||
mesh.add(scene)
|
||||
xrf.emit('parseModel', {...opts, scene, model})
|
||||
xrf.emit('parseModel', {...opts, isSRC:true, scene, model})
|
||||
}
|
||||
// flag everything isSRC & isXRF
|
||||
mesh.traverse( (n) => { n.isSRC = n.isXRF = n[ opts.isLocal ? 'isSRCLocal' : 'isSRCExternal' ] = true })
|
||||
|
@ -67,6 +75,8 @@ xrf.frag.src.externalSRC = (url,frag,opts) => {
|
|||
.then( (res) => {
|
||||
let mimetype = res.headers.get('Content-type')
|
||||
if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf'
|
||||
if( url.replace(/#.*/,'').match(/\.(frag|fs|glsl)$/) ) mimetype = 'x-shader/x-fragment'
|
||||
if( url.replace(/#.*/,'').match(/\.(vert|vs)$/) ) mimetype = 'x-shader/x-fragment'
|
||||
//if( url.match(/\.(fbx|stl|obj)$/) ) mimetype =
|
||||
opts = { ...opts, frag, mimetype }
|
||||
return xrf.frag.src.type[ mimetype ] ? xrf.frag.src.type[ mimetype ](url,opts) : xrf.frag.src.type.unknown(url,opts)
|
||||
|
@ -98,7 +108,6 @@ xrf.frag.src.scale = function(scene, opts, url){
|
|||
|
||||
// remove invisible objects (hidden by selectors) which might corrupt boundingbox size-detection
|
||||
let cleanScene = scene.clone()
|
||||
if( !cleanScene ) debugger
|
||||
let remove = []
|
||||
const notVisible = (n) => !n.visible || (n.material && !n.material.visible)
|
||||
cleanScene.traverse( (n) => notVisible(n) && n.children.length == 0 && (remove.push(n)) )
|
||||
|
|
|
@ -23,7 +23,8 @@ let loadAudio = (mimetype) => function(url,opts){
|
|||
let sound = isPositionalAudio ? new THREE.PositionalAudio( camera.listener)
|
||||
: new THREE.Audio( camera.listener )
|
||||
|
||||
mesh.audio = {}
|
||||
mesh.media = mesh.media || {}
|
||||
mesh.media.audio = { play: () => mesh.media.audio.autoplay = true }
|
||||
|
||||
audioLoader.load( url.replace(/#.*/,''), function( buffer ) {
|
||||
|
||||
|
@ -36,9 +37,11 @@ let loadAudio = (mimetype) => function(url,opts){
|
|||
//sound.setDirectionalCone( 360, 360, 0.01 );
|
||||
}
|
||||
|
||||
sound.playXRF = (t) => {
|
||||
mesh.add(sound)
|
||||
|
||||
sound.pub = (t) => {
|
||||
try{
|
||||
sound.t = t
|
||||
if( sound.isPlaying && t.y != undefined ) sound.stop()
|
||||
if( sound.isPlaying && t.y == undefined ) sound.pause()
|
||||
|
||||
|
@ -65,11 +68,11 @@ let loadAudio = (mimetype) => function(url,opts){
|
|||
}catch(e){ console.warn(e) }
|
||||
}
|
||||
|
||||
// autoplay if user already requested play
|
||||
let autoplay = mesh.audio && mesh.audio.autoplay
|
||||
mesh.audio = sound
|
||||
// autoplay if user already requested play (before the sound was loaded)
|
||||
let autoplay = mesh.media.audio && mesh.media.audio.autoplay
|
||||
mesh.media.audio = sound
|
||||
if( autoplay ){
|
||||
xrf.hashbus.pub(mesh.audio.autoplay)
|
||||
xrf.hashbus.pub(mesh.media.audio.autoplay)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* extensions: .frag/.fs/.vs/.vert
|
||||
*/
|
||||
|
||||
xrf.frag.src.type['x-shader/x-fragment'] = function(url,opts){
|
||||
let {mesh,THREE} = opts
|
||||
|
||||
let isFragmentShader = /\.(fs|frag|glsl)$/
|
||||
let isVertexShader = /\.(vs|vert)$/
|
||||
|
||||
let shaderReqs = []
|
||||
let shaderCode = {}
|
||||
let shader = {
|
||||
fragment: { code: '', url: url.match( isFragmentShader ) ? url : '' },
|
||||
vertex: { code: '', url: url.match( isVertexShader ) ? url : '' }
|
||||
}
|
||||
|
||||
var onShaderLoaded = ((args) => (type, status, code) => {
|
||||
shader[type].status = status
|
||||
shader[type].code = code
|
||||
if( shader.fragment.code && shader.vertex.code ){
|
||||
|
||||
let oldMaterial = mesh.material
|
||||
mesh.material = new THREE.RawShaderMaterial({
|
||||
uniforms: {
|
||||
time: { value: 1.0 },
|
||||
resolution: { value: new THREE.Vector2(1.0,1.0) }
|
||||
},
|
||||
// basic shaders include following common vars/funcs: https://github.com/mrdoob/three.js/blob/master/src/renderers/shaders/ShaderChunk/common.glsl.js
|
||||
fragmentShader: shader.fragment.status == 200 ? shader.fragment.code : THREE.ShaderChunk.meshbasic_frag,
|
||||
vertexShader: shader.vertex.status == 200 ? shader.vertex.code : THREE.ShaderChunk.meshbasic_vert,
|
||||
|
||||
});
|
||||
|
||||
mesh.material.needsUpdate = true
|
||||
mesh.needsUpdate = true
|
||||
|
||||
mesh.onBeforeRender = () => {
|
||||
if( !mesh.material || !mesh.material.uniforms ) return mesh.onBeforeRender = function(){}
|
||||
mesh.material.uniforms.time.value = xrf.clock.elapsedTime
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})({})
|
||||
|
||||
// sidecar-load vertex shader file
|
||||
if( shader.fragment.url && !shader.vertex.url ){
|
||||
shader.vertex.url = shader.fragment.url.replace(/\.fs$/, '.vs')
|
||||
.replace(/\.frag$/, '.vert')
|
||||
}
|
||||
|
||||
if( shader.fragment.url ){
|
||||
fetch(shader.fragment.url)
|
||||
.then( (res) => res.text().then( (code) => onShaderLoaded('fragment',res.status,code) ) )
|
||||
}
|
||||
|
||||
if( shader.vertex.url ){
|
||||
fetch(shader.vertex.url)
|
||||
.then( (res) => res.text().then( (code) => onShaderLoaded('vertex',res.status,code) ) )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
xrf.frag.src.type['x-shader/x-vertex'] = xrf.frag.src.type['x-shader/x-fragment']
|
||||
|
|
@ -74,6 +74,9 @@ xrf.portalNonEuclidian = function(opts){
|
|||
|
||||
this.setupListeners = () => {
|
||||
|
||||
// below is a somewhat weird tapdance to render the portals **after** the scene
|
||||
// is rendered (otherwise it messes up occlusion)
|
||||
|
||||
mesh.onAfterRender = function(renderer, scene, camera, geometry, material, group ){
|
||||
mesh.portal.needUpdate = true
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ let loadVideo = (mimetype) => function(url,opts){
|
|||
const THREE = xrf.THREE
|
||||
let frag = xrf.URI.parse( url )
|
||||
|
||||
let video = mesh.video = document.createElement('video')
|
||||
mesh.media = mesh.media || {}
|
||||
|
||||
let video = mesh.media.video = document.createElement('video')
|
||||
video.setAttribute("crossOrigin","anonymous")
|
||||
video.setAttribute("playsinline",'')
|
||||
video.addEventListener('loadedmetadata', function(){
|
||||
|
@ -17,21 +19,23 @@ let loadVideo = (mimetype) => function(url,opts){
|
|||
// set range
|
||||
video.addEventListener('timeupdate', function timeupdate() {
|
||||
if (video.t && video.t.y !== undefined && video.t.y > video.t.x && Math.abs(video.currentTime) >= video.t.y ){
|
||||
if( video.t.speed.length ) video.currentTime = video.t.x // speed means loop
|
||||
if( video.looping ) video.currentTime = video.t.x // speed means loop
|
||||
else video.pause()
|
||||
}
|
||||
},false)
|
||||
})
|
||||
|
||||
video.src = url
|
||||
video.playXRF = (t) => {
|
||||
video.speed = 1.0
|
||||
video.looping = false
|
||||
video.pub = (t) => {
|
||||
video.t = t
|
||||
video.pause()
|
||||
if( t.x !== undefined && t.x == t.y ) return // stop paused
|
||||
else{
|
||||
video.currentTime = t.x
|
||||
video.time = t.x
|
||||
video.playbackRate = Math.abs( t.speed.length ? t.speed[0] : 1.0 ) // html5 video does not support reverseplay :/
|
||||
video.playbackRate = Math.abs( video.speed ) // html5 video does not support reverseplay :/
|
||||
video.play()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
xrf.frag.suv = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( !mesh.geometry ) return // nothing to do here
|
||||
|
||||
xrf.frag.uv.init(mesh)
|
||||
mesh.suv.x = v.x
|
||||
mesh.suv.y = v.y !== undefined ? v.y : v.x
|
||||
mesh.suv.loop = v.loop === true ? true : false
|
||||
mesh.onBeforeRender = xrf.frag.uv.scroll
|
||||
}
|
|
@ -1,5 +1,15 @@
|
|||
// this is the global #t mediafragment handler (which affects the 3D animation)
|
||||
|
||||
xrf.frag.t = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
// handle object media players
|
||||
if( mesh && mesh.media ){
|
||||
for( let i in mesh.media ) mesh.media[i].pub(v)
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise handle global 3D animations
|
||||
if( !model.mixer ) return
|
||||
if( !model.animations || model.animations[0] == undefined ){
|
||||
console.warn('no animations found in model')
|
||||
|
@ -11,10 +21,10 @@ xrf.frag.t = function(v, opts){
|
|||
mixer.t = v
|
||||
|
||||
// update speed
|
||||
mixer.timeScale = mixer.loop.speed = v.x
|
||||
mixer.loop.speedAbs = Math.abs(v.x)
|
||||
mixer.timeScale = mixer.loop.speed || 1.0
|
||||
mixer.loop.speedAbs = Math.abs( mixer.timeScale )
|
||||
|
||||
if( v.y != undefined || v.z != undefined ) mixer.updateLoop( v )
|
||||
mixer.updateLoop( v )
|
||||
|
||||
// play animations
|
||||
mixer.play( v )
|
||||
|
@ -31,7 +41,7 @@ xrf.addEventListener('parseModel', (opts) => {
|
|||
let {model} = opts
|
||||
let mixer = model.mixer = new xrf.THREE.AnimationMixer(model.scene)
|
||||
mixer.model = model
|
||||
mixer.loop = {timeStart:0,timeStop:0}
|
||||
mixer.loop = {timeStart:0,timeStop:0,speed:1.0}
|
||||
mixer.i = xrf.mixers.length
|
||||
mixer.actions = []
|
||||
|
||||
|
@ -41,7 +51,6 @@ xrf.addEventListener('parseModel', (opts) => {
|
|||
mixer.actions.push( mixer.clipAction( anim, model.scene ) )
|
||||
})
|
||||
|
||||
|
||||
mixer.checkZombies = (animations) => {
|
||||
if( mixer.zombieCheck ) return // fire only once
|
||||
animations.map( (anim) => {
|
||||
|
@ -62,7 +71,7 @@ xrf.addEventListener('parseModel', (opts) => {
|
|||
}
|
||||
|
||||
mixer.play = (t) => {
|
||||
mixer.isPlaying = t.x != 0
|
||||
mixer.isPlaying = t.x !== undefined && t.x != t.y
|
||||
mixer.updateLoop(t)
|
||||
xrf.emit( mixer.isPlaying === false ? 'stop' : 'play',{isPlaying: mixer.isPlaying})
|
||||
}
|
||||
|
@ -72,17 +81,15 @@ xrf.addEventListener('parseModel', (opts) => {
|
|||
}
|
||||
|
||||
mixer.updateLoop = (t) => {
|
||||
mixer.loop.timeStart = t.y != undefined ? t.y : mixer.loop.timeStart
|
||||
mixer.loop.timeStop = t.z != undefined ? t.z : mixer.loop.timeStop
|
||||
mixer.loop.timeStart = t.x != undefined ? t.x : mixer.loop.timeStart
|
||||
mixer.loop.timeStop = t.y != undefined ? t.y : mixer.loop.timeStop
|
||||
mixer.actions.map( (action) => {
|
||||
if( mixer.loop.timeStart != undefined ){
|
||||
action.time = mixer.loop.timeStart
|
||||
action.setLoop( xrf.THREE.LoopOnce, )
|
||||
action.timeScale = mixer.timeScale
|
||||
action.enabled = true
|
||||
if( t.x != 0 ){
|
||||
action.play()
|
||||
}
|
||||
if( t.x === 0 ) action.play()
|
||||
}
|
||||
})
|
||||
mixer.setTime(mixer.loop.timeStart)
|
||||
|
@ -152,19 +159,6 @@ xrf.addEventListener('render', (opts) => {
|
|||
}
|
||||
})
|
||||
|
||||
xrf.addEventListener('dynamicKey', (opts) => {
|
||||
// select active camera if any
|
||||
let {id,match,v} = opts
|
||||
match.map( (w) => {
|
||||
w.nodes.map( (node) => {
|
||||
if( node.isCamera ){
|
||||
console.log("switching camera to cam: "+node.name)
|
||||
xrf.model.camera = node
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// remove mixers and stop mixers when loading another scene
|
||||
xrf.addEventListener('reset', (opts) => {
|
||||
xrf.mixers.map( (m) => m.stop())
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
xrf.frag.uv = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( !mesh.geometry ) return // nothing to do here
|
||||
|
||||
xrf.frag.uv.init(mesh)
|
||||
mesh.uv.x = v.x
|
||||
mesh.uv.y = v.y !== undefined ? v.y : v.x
|
||||
mesh.onBeforeRender = xrf.frag.uv.scroll
|
||||
}
|
||||
|
||||
xrf.frag.uv.init = function(mesh){
|
||||
if( !mesh.uv ) mesh.uv = {x:0, y:0, w:1, h:1, uv:false}
|
||||
if( !mesh.suv ) mesh.suv = {x:1, y:1, loop:false }
|
||||
let uv = mesh.geometry.getAttribute("uv")
|
||||
if( !uv.old ) uv.old = uv.clone()
|
||||
}
|
||||
|
||||
xrf.frag.uv.scroll = function(){
|
||||
if( this.suv.x > 0.0 || this.suv.y > 0.0 ){
|
||||
|
||||
let diff = 0.0 // distance to end-state (non-looping mode)
|
||||
let uv = this.geometry.getAttribute("uv")
|
||||
|
||||
// translate!
|
||||
for( let i = 0; i < uv.count; i++ ){
|
||||
let u = uv.getX(i)
|
||||
let v = uv.getY(i)
|
||||
let uTarget = uv.old.getX(i) + this.uv.x
|
||||
let vTarget = uv.old.getY(i) + this.uv.y
|
||||
|
||||
if( this.suv.loop ){
|
||||
u += this.suv.x * xrf.clock.delta
|
||||
v += this.suv.y * xrf.clock.delta
|
||||
}else{
|
||||
|
||||
// recover from super-high uv-values due to looped scrolling
|
||||
if( Math.abs(u-uTarget) > 1.0 ) u = uv.old.getX(i)
|
||||
if( Math.abs(v-vTarget) > 1.0 ) v = uv.old.getY(i)
|
||||
|
||||
u = u > uTarget ? u + (this.suv.x * -xrf.clock.delta)
|
||||
: u + (this.suv.x * xrf.clock.delta)
|
||||
v = v > vTarget ? v + (this.suv.y * -xrf.clock.delta)
|
||||
: v + (this.suv.y * xrf.clock.delta)
|
||||
diff += Math.abs( u - uTarget ) // are we done yet? (non-looping mode)
|
||||
diff += Math.abs( v - vTarget )
|
||||
|
||||
}
|
||||
uv.setXY(i,u,v)
|
||||
}
|
||||
uv.needsUpdate = true
|
||||
|
||||
if( !this.suv.loop && diff < 0.05 ){ // stop animating if done
|
||||
this.onBeforeRender = function(){}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
xrf.frag.xywh = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
xrf.mediafragment.init(mesh)
|
||||
mesh.xywh.x = v.floats[0]
|
||||
mesh.xywh.y = v.floats[1] !== undefined ? v.floats[1] : mesh.xywh.x
|
||||
mesh.xywh.w = v.floats[2] !== undefined ? v.floats[2] : mesh.xywh.y
|
||||
mesh.xywh.h = v.floats[3] !== undefined ? v.floats[3] : mesh.xywh.w
|
||||
// TODO: nondestructive cropping of texture (not superimportant now)
|
||||
}
|
|
@ -20,14 +20,15 @@ class Test {
|
|||
test( "t.json", Spec.load("src/spec/t.json") );
|
||||
test( "xywh.json", Spec.load("src/spec/xywh.json") );
|
||||
test( "s.json", Spec.load("src/spec/s.json") );
|
||||
test( "sxy.json", Spec.load("src/spec/sxy.json") );
|
||||
test( "suv.json", Spec.load("src/spec/suv.json") );
|
||||
test( "suv.json", Spec.load("src/spec/uv.json") );
|
||||
test( "filter.selectors.json", Spec.load("src/spec/filter.selectors.json") );
|
||||
//test( Spec.load("src/spec/tmp.json") );
|
||||
if( errors > 1 ) trace("\n-----\n[ ❌] "+errors+" errors :/");
|
||||
}
|
||||
|
||||
static public function test( topic:String, spec:Array<Dynamic>):Void {
|
||||
trace("\n[.] running "+topic);
|
||||
trace("\n[ . ] running "+topic);
|
||||
var Filter = xrfragment.Filter;
|
||||
for( i in 0...spec.length ){
|
||||
var f:Filter = null;
|
||||
|
@ -52,7 +53,7 @@ class Test {
|
|||
if( item.expect.fn == "equal.mediafragmentT" ) valid = equalMediaFragment(res,item,"t");
|
||||
if( item.expect.fn == "equal.mediafragmentXYWH") valid = equalMediaFragment(res,item,"xywh");
|
||||
if( item.expect.fn == "equal.mediafragmentS") valid = equalMediaFragment(res,item,"s");
|
||||
if( item.expect.fn == "equal.mediafragmentSXY") valid = equalMediaFragment(res,item,"sxy");
|
||||
if( item.expect.fn == "equal.mediafragmentSUV") valid = equalMediaFragment(res,item,"suv");
|
||||
if( item.expect.fn == "testFilterRoot" ) valid = res.exists(item.expect.input[0]) && res.get(item.expect.input[0]).filter.get().root == item.expect.out;
|
||||
if( item.expect.fn == "testFilterDeep" ) valid = res.exists(item.expect.input[0]) && res.get(item.expect.input[0]).filter.get().deep == item.expect.out;
|
||||
var ok:String = valid ? "[ ✔ ] " : "[ ❌] ";
|
||||
|
|
|
@ -10,6 +10,5 @@
|
|||
{"fn":"filter","data":"price=>2", "expect":{ "fn":"testProperty","input":["price","1"],"out":false}},
|
||||
{"fn":"filter","data":"price=<2", "expect":{ "fn":"testProperty","input":["price","5"],"out":false}},
|
||||
{"fn":"filter","data":"price=<2", "expect":{ "fn":"testProperty","input":["price","1"],"out":true}},
|
||||
{"fn":"url","data":"#foo*", "expect":{ "fn":"testFilterDeep","input":["foo"],"out":1},"label":"foo should be deep"},
|
||||
{"fn":"url","data":"#foo**", "expect":{ "fn":"testFilterDeep","input":["foo"],"out":2},"label":"foo should be deep incl. embeds"}
|
||||
{"fn":"url","data":"#foo*", "expect":{ "fn":"testFilterDeep","input":["foo"],"out":1},"label":"foo should be deep"}
|
||||
]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[
|
||||
{"fn":"url","data":"http://foo.com?foo=1#s=1", "expect":{ "fn":"equal.mediafragmentSpeed", "input":"0","out":"1"},"label":"playback speed"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#s=0.5", "expect":{ "fn":"equal.mediafragmentSpeed", "input":"0","out":"0.5"},"label":"playback speed"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#s=-0.5", "expect":{ "fn":"equal.mediafragmentSpeed", "input":"0","out":"-0.5"},"label":"playback speed"}
|
||||
{"fn":"url","data":"http://foo.com?foo=1#s=1", "expect":{ "fn":"equal.x", "input":"s","out":"1"},"label":"playback speed"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#s=0.5", "expect":{ "fn":"equal.x", "input":"s","out":"0.5"},"label":"playback speed"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#s=-0.5", "expect":{ "fn":"equal.x", "input":"s","out":"-0.5"},"label":"playback speed"}
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[
|
||||
{"fn":"url","data":"http://foo.com?foo=1#sxy=l:0,0.1", "expect":{ "fn":"equal.mediafragmentSXY", "input":"1","out":"0.2"},"label":"sxy"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#sxy=0,0.1", "expect":{ "fn":"equal.mediafragmentSXY", "input":"1","out":"0.2"},"label":"sxy looped"}
|
||||
{"fn":"url","data":"http://foo.com?foo=1#suv=l:0,0.1", "expect":{ "fn":"equal.mediafragmentSUV", "input":"1","out":"0.1"},"label":"suv"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#suv=0.2,0.1", "expect":{ "fn":"equal.mediafragmentSUV", "input":"0","out":"0.2"},"label":"suv looped"}
|
||||
]
|
||||
|
|
|
@ -8,8 +8,5 @@
|
|||
{"fn":"url","data":"http://foo.com?foo=1#t=1,100", "expect":{ "fn":"equal.xy", "input":"t","out":"1,100"},"label":"a equal.xy"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#t=2,500", "expect":{ "fn":"testBrowserOverride", "input":"t","out":true},"label":"browser URI can override t (defined in asset)"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#t=1,100,400,500", "expect":{ "fn":"equal.mediafragmentT", "input":"3","out":"500"},"label":"a equal.mediafragment"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#t=l:1,100,400,500", "expect":{ "fn":"equal.mediafragmentT", "input":"3","out":"500"},"label":"a equal.mediafragment loop"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#t=v:l:1,100,400,500", "expect":{ "fn":"equal.mediafragmentT", "input":"3","out":"500"},"label":"a equal.mediafragment uv loop "},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#t=v:1,100,400,500", "expect":{ "fn":"equal.mediafragmentT", "input":"3","out":"500"},"label":"a equal.mediafragment uv"},
|
||||
{"fn":"url","data":"http://foo.com?foo=1#t=v:1,2", "expect":{ "fn":"testParsed", "input":"mycustom","out":true},"label":"test uv is set"}
|
||||
{"fn":"url","data":"http://foo.com?foo=1#t=l:1,100,400,500", "expect":{ "fn":"equal.mediafragmentT", "input":"3","out":"500"},"label":"a equal.mediafragment loop"}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[
|
||||
{"fn":"url","data":"http://foo.com?foo=1#uv=1.2,2.2", "expect":{ "fn":"equal.xy", "input":"uv","out":"1.2,2.2"},"label":"equal.string uv"}
|
||||
]
|
|
@ -15,27 +15,27 @@ class Parser {
|
|||
// here we define allowed characteristics & datatypes for each fragment (stored as bitmasked int for performance purposes)
|
||||
var Frag:Map<String, Int> = new Map<String, Int>();
|
||||
|
||||
Frag.set("#", XRF.ASSET | XRF.T_PREDEFINED_VIEW | XRF.PV_EXECUTE );
|
||||
Frag.set("src", XRF.ASSET | XRF.T_URL );
|
||||
Frag.set("href", XRF.ASSET | XRF.T_URL | XRF.T_PREDEFINED_VIEW );
|
||||
Frag.set("tag", XRF.ASSET | XRF.T_STRING );
|
||||
Frag.set("#", XRF.IMMUTABLE | XRF.T_PREDEFINED_VIEW | XRF.PV_EXECUTE );
|
||||
Frag.set("src", XRF.T_URL );
|
||||
Frag.set("href", XRF.T_URL | XRF.T_PREDEFINED_VIEW );
|
||||
Frag.set("tag", XRF.IMMUTABLE | XRF.T_STRING );
|
||||
|
||||
// spatial category: query selector / object manipulation
|
||||
Frag.set("pos", XRF.PV_OVERRIDE | XRF.T_VECTOR3 | XRF.T_STRING | XRF.METADATA | XRF.NAVIGATOR );
|
||||
Frag.set("rot", XRF.QUERY_OPERATOR | XRF.PV_OVERRIDE | XRF.T_VECTOR3 | XRF.METADATA | XRF.NAVIGATOR );
|
||||
|
||||
// category: media fragments
|
||||
Frag.set("t", XRF.ASSET | XRF.PV_OVERRIDE | XRF.T_FLOAT | XRF.T_VECTOR2 | XRF.T_MEDIAFRAG | XRF.NAVIGATOR | XRF.METADATA);
|
||||
Frag.set("xywh", XRF.ASSET | XRF.PV_OVERRIDE | XRF.T_FLOAT | XRF.T_VECTOR2 | XRF.T_MEDIAFRAG | XRF.NAVIGATOR | XRF.METADATA);
|
||||
Frag.set("t", XRF.PV_OVERRIDE | XRF.T_FLOAT | XRF.T_VECTOR2 | XRF.T_MEDIAFRAG | XRF.NAVIGATOR | XRF.METADATA);
|
||||
Frag.set("xywh", XRF.T_FLOAT | XRF.T_VECTOR2 | XRF.T_MEDIAFRAG | XRF.NAVIGATOR | XRF.METADATA);
|
||||
Frag.set("s", XRF.PV_OVERRIDE | XRF.T_MEDIAFRAG | XRF.T_FLOAT );
|
||||
Frag.set("uv", XRF.T_VECTOR2 | XRF.T_MEDIAFRAG );
|
||||
Frag.set("suv", XRF.T_VECTOR2 | XRF.T_MEDIAFRAG );
|
||||
|
||||
// category: author / metadata
|
||||
Frag.set("namespace", XRF.ASSET | XRF.T_STRING );
|
||||
Frag.set("SPDX", XRF.ASSET | XRF.T_STRING );
|
||||
Frag.set("unit", XRF.ASSET | XRF.T_STRING );
|
||||
Frag.set("description", XRF.ASSET | XRF.T_STRING );
|
||||
|
||||
// category: multiparty
|
||||
Frag.set("session", XRF.ASSET | XRF.T_URL | XRF.PV_OVERRIDE | XRF.NAVIGATOR | XRF.METADATA | XRF.PROMPT );
|
||||
Frag.set("namespace", XRF.IMMUTABLE | XRF.T_STRING );
|
||||
Frag.set("SPDX", XRF.IMMUTABLE | XRF.T_STRING );
|
||||
Frag.set("unit", XRF.IMMUTABLE | XRF.T_STRING );
|
||||
Frag.set("description", XRF.IMMUTABLE | XRF.T_STRING );
|
||||
|
||||
/**
|
||||
* # Spec
|
||||
|
@ -51,11 +51,12 @@ class Parser {
|
|||
|
||||
// dynamic fragments cases: predefined views & assign/binds
|
||||
var isPVDynamic:Bool = key.length > 0 && !Frag.exists(key);
|
||||
var isPVDefault:Bool = value.length == 0 && key.length > 0 && key == "#";
|
||||
if( isPVDynamic ){ //|| isPVDefault ){ // 1. add keys without values to store as [predefined view](predefined_view)
|
||||
if( isPVDynamic ){ // 1. add key(values) to store as [predefined view](predefined_view) or dynamic assignment
|
||||
var v:XRF = new XRF(key, XRF.PV_EXECUTE | XRF.NAVIGATOR, index );
|
||||
v.validate(value); // ignore failures (empty values are allowed)
|
||||
v.flags = XRF.set( XRF.T_DYNAMIC, v.flags );
|
||||
v.flags = XRF.set( XRF.T_DYNAMICKEY, v.flags );
|
||||
if( !Frag.exists(key) ) v.flags = XRF.set( XRF.CUSTOMFRAG, v.flags );
|
||||
if( value.length == 0 ) v.flags = XRF.set( XRF.T_DYNAMICKEYVALUE, v.flags );
|
||||
store.set( keyStripped, v );
|
||||
return true;
|
||||
}
|
||||
|
@ -71,7 +72,7 @@ class Parser {
|
|||
if( debug ) trace("✔ "+key+": "+v.string);
|
||||
}else{ // 1. expose (but mark) non-offical fragments too
|
||||
if( Std.isOfType(value, String) ) v.guessType(v,value);
|
||||
v.noXRF = true;
|
||||
v.flags = XRF.set( XRF.CUSTOMFRAG, v.flags );
|
||||
store.set( keyStripped ,v);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -52,6 +52,19 @@ class URI {
|
|||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
@keep
|
||||
public static function template(uri:String, vars:Dynamic):String {
|
||||
var parts = uri.split("#");
|
||||
if( parts.length == 1 ) return uri; // we only do level1 fragment expansion
|
||||
var frag = parts[1];
|
||||
frag = StringTools.replace(frag,"{","::");
|
||||
frag = StringTools.replace(frag,"}","::");
|
||||
frag = new haxe.Template(frag).execute(vars);
|
||||
frag = StringTools.replace(frag,"null",""); // *TODO* needs regex to check for [#&]null[&]
|
||||
parts[1] = frag;
|
||||
return parts.join("#");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,14 +10,14 @@ class XRF {
|
|||
* this class represents a fragment (value)
|
||||
*/
|
||||
|
||||
// public static inline readonly ASSET
|
||||
// public static inline readonly IMMUTABLE
|
||||
|
||||
// scope types (powers of 2)
|
||||
public static var ASSET:Int = 1; // fragment is immutable
|
||||
public static var IMMUTABLE:Int = 1; // fragment is immutable
|
||||
public static var PROP_BIND:Int = 2; // fragment binds/controls one property with another
|
||||
public static var QUERY_OPERATOR:Int = 4; // fragment will be applied to result of filterselecto
|
||||
public static var PROMPT:Int = 8; // ask user whether this fragment value can be changed
|
||||
public static var ROUNDROBIN:Int = 16; // evaluation of this (multi) value can be roundrobined
|
||||
public static var CUSTOMFRAG:Int = 16; // evaluation of this (multi) value can be roundrobined
|
||||
public static var NAVIGATOR:Int = 32; // fragment can be overridden by (manual) browser URI change
|
||||
public static var METADATA:Int = 64; // fragment can be overridden by an embedded URL
|
||||
public static var PV_OVERRIDE:Int = 128; // embedded fragment can be overridden when specified in predefined view value
|
||||
|
@ -33,7 +33,8 @@ class XRF {
|
|||
public static var T_PREDEFINED_VIEW:Int = 524288;
|
||||
public static var T_STRING:Int = 1048576;
|
||||
public static var T_MEDIAFRAG:Int = 2097152;
|
||||
public static var T_DYNAMIC:Int = 4194304;
|
||||
public static var T_DYNAMICKEY:Int = 4194304;
|
||||
public static var T_DYNAMICKEYVALUE:Int = 8388608;
|
||||
|
||||
// regexes
|
||||
public static var isColor:EReg = ~/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; // 1. hex colors are detected using regex `/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/`
|
||||
|
@ -43,12 +44,13 @@ class XRF {
|
|||
public static var isUrl:EReg = ~/(:\/\/)?\..*/; // 1. url/file */`
|
||||
public static var isUrlOrPretypedView:EReg = ~/(^#|:\/\/)?\..*/; // 1. url/file */`
|
||||
public static var isString:EReg = ~/.*/; // 1. anything else is string `/.*/`
|
||||
public static var operators:EReg = ~/(^-|[\*]+)/; // 1. detect operators so you can easily strip keys (reference regex= `~/(^-)?(\/)?(\*)?/` )
|
||||
public static var operators:EReg = ~/(^[-]|^[!]|[\*]$)/g; // 1. detect operators so you can easily strip keys (reference regex= `~/(^-)?(\*)?/` )
|
||||
public static var isProp:EReg = ~/^.*=[><=]?/; // 1. detect object id's & properties `foo=1` and `foo` (reference regex= `~/^.*=[><=]?/` )
|
||||
public static var isExclude:EReg = ~/^-/; // 1. detect excluders like `-foo`,`-foo=1`,`-.foo`,`-/foo` (reference regex= `/^-/` )
|
||||
public static var isDeep:EReg = ~/\*/; // 1. detect deep selectors like `foo*` (reference regex= `/\*$/` )
|
||||
public static var isNumber:EReg = ~/^[0-9\.]+$/; // 1. detect number values like `foo=1` (reference regex= `/^[0-9\.]+$/` )
|
||||
public static var isMediaFrag:EReg = ~/^(uv:)?(l:)?([0-9\.,\*]+)$/; // 1. detect (extended) media fragment
|
||||
public static var isMediaFrag:EReg = ~/^(l:)?([0-9\.,\*]+)$/; // 1. detect (extended) media fragment
|
||||
public static var isReset:EReg = ~/^!/; // 1. detect reset operation
|
||||
|
||||
// value holder(s) // |------|------|--------|----------------------------------|
|
||||
public var fragment:String;
|
||||
|
@ -58,15 +60,13 @@ class XRF {
|
|||
public var y:Float;
|
||||
public var z:Float;
|
||||
public var floats:Array<Float> = new Array<Float>();
|
||||
public var speed:Array<Float> = new Array<Float>();
|
||||
public var color:String; // |string| color| FFFFFF (hex) | #fog=5m,FFAACC |
|
||||
public var string:String; // |string| | | #q=-sun |
|
||||
public var int:Int; // |int | | [-]x[xxxxx] | #price:>=100 |
|
||||
public var float:Float; // |float | | [-]x[.xxxx] (ieee)| #prio=-20 |
|
||||
public var filter:Filter;
|
||||
public var noXRF:Bool;
|
||||
public var reset:Bool;
|
||||
public var loop:Bool;
|
||||
public var uv:Bool;
|
||||
//
|
||||
public function new(_fragment:String,_flags:Int,?_index:Int){
|
||||
fragment = _fragment;
|
||||
|
@ -99,16 +99,13 @@ class XRF {
|
|||
@:keep
|
||||
public function guessType(v:XRF, str:String):Void {
|
||||
v.string = str;
|
||||
if( isReset.match(v.fragment) ) v.reset = true;
|
||||
if( !Std.isOfType(str,String) ) return;
|
||||
if( str.length > 0 ){
|
||||
if( str.split("l:").length > 1 ){
|
||||
str = str.split("l:")[1];
|
||||
v.loop = true;
|
||||
}
|
||||
if( str.split("uv:").length > 1 ){
|
||||
str = str.split("uv:")[1];
|
||||
v.uv = true;
|
||||
}
|
||||
if( str.split(",").length > 1){ // 1. `,` assumes 1D/2D/3D vector-values like x[,y[,z]]
|
||||
var xyzn:Array<String> = str.split(","); // 1. parseFloat(..) and parseInt(..) is applied to vector/float and int values
|
||||
if( xyzn.length > 0 ) v.x = Std.parseFloat(xyzn[0]); // 1. anything else will be treated as string-value
|
||||
|
@ -127,6 +124,7 @@ class XRF {
|
|||
if( isInt.match(str) ){
|
||||
v.int = Std.parseInt(str);
|
||||
v.x = cast(v.int);
|
||||
v.floats.push( cast(v.x) );
|
||||
}
|
||||
v.filter = new Filter(v.fragment+"="+v.string);
|
||||
}else v.filter = new Filter(v.fragment);
|
||||
|
|
Loading…
Reference in New Issue