upgraded aframe + wip filters

This commit is contained in:
Leon van Kammen 2023-11-16 14:50:57 +01:00
parent 34f71d0238
commit bb35044df3
16 changed files with 249 additions and 11094 deletions

Binary file not shown.

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" href="./../../assets/css/axist.min.css" />
<link type="text/css" rel="stylesheet" href="./../../assets/css/style.css"/>
<script async src="./../../assets/js/alpine.min.js" defer></script>
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/aframe-blink-controls/dist/aframe-blink-controls.min.js"></script>
<script src="./../../../dist/xrfragment.aframe.js"></script>
<script src="./../../assets/js/qr.js"></script>
@ -42,7 +42,7 @@
<a-entity id="right-hand" laser-controls="hand: right" raycaster="objects:.ray" blink-controls="cameraRig:#player; teleportOrigin: #camera; collisionEntities: #floor"></a-entity>
</a-entity>
<a-entity id="home" xrf="index.gltf"></a-entity>
<a-entity id="home" xrf="index.glb"></a-entity>
<a-plane id="floor" position="0 0 0" rotation="-90 0 0" width="100" height="100" material="visible:false"></a-plane>
</a-scene>

Binary file not shown.

File diff suppressed because one or more lines are too long

3
make
View File

@ -55,8 +55,7 @@ build(){
parser(){
try rm dist/*
haxe build.hxml
ok=$?
haxe build.hxml || exit 1
sed -i 's|.*nonlocal .*||g' dist/xrfragment.py
ls -lah dist/*
echo -e "[OK] parser build\n"

View File

@ -1,43 +1,74 @@
/*
* TODO: refactor/fix this (queries are being refactored to filters)
*/
// spec: https://xrfragment.org/#queries
// spec: https://xrfragment.org/#filters
xrf.filter = function(){
xrf.filter = function(v, opts){
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
console.log(" └ running query ")
let qobjs = Object.keys(v.query)
}
// convience function for other fragments (which apply to the query)
frag.filter.getObjects = () => {
let objs = []
scene.traverse( (o) => {
for ( let name in v.query ) {
let qobj = v.query[name];
if( qobj.tag && o.userData.tag && xrf.hasTag(name,o.userData.tag) ) objs.push(o)
else if( qobj.id && o.name == name ) objs.push(o)
xrf.filter.scene = function(opts){
let {scene,frag} = opts
xrf.filter
.sort(frag) // get (sorted) filters from XR Fragments
.process(frag,scene) // show/hide things
return scene
}
xrf.filter.sort = function(frag){
// get all filters from XR Fragments
frag.filters = Object.values(frag)
.filter( (v) => v.filter ? v : null )
.sort( (a,b) => a.index > b.index )
return xrf.filter
}
xrf.filter.process = function(frag,scene,opts){
// include all when query starts with negative objectnames/tags are included
let firstFilter = frag.filters[0].filter.get()
let showAll = firstFilter.show === false
let showers = frag.filters.filter( (v) => v.filter.get().show === true )
if( !showAll ) scene.traverse( (n) => n.visible = false )
// reparent if first selector is positive and has no value
if( firstFilter.show === true ){
let obj = scene.getObjectByName( firstFilter.key )
while( scene.children.length > 0 ) scene.children[0].removeFromParent()
if(obj) scene.add(obj)
}
frag.filters.map( (v) => {
const filter = v.filter.get()
const name_or_tag = v.fragment.replace(/[-\*]/g,'')
let seen = {}
const setVisibleUnseen = (m,visible) => {
if( seen[m.uuid] ) return
m.visible = visible
seen[ m.uuid ] = true
}
scene.traverse( (m) => {
const isMatch = m.name == name_or_tag || m.userData[filter.key]
// filter on value(expression) #foo=>3 e.g.
if( filter.value && m.userData[filter.key] ){
const visible = v.filter.testProperty(filter.key, m.userData[filter.key], filter.show === false )
setVisibleUnseen(m,visible)
if( filter.deep ){
m.traverse( (n) => setVisibleUnseen(n,visible) )
}
return
}
// include/exclude object(s) when id/tag matches (#foo or #-foo e.g.)
if( isMatch ){
m.visible = filter.show
if( filter.deep ) m.traverse( (n) => n.visible = m.visible )
}
})
return objs.filter( (o) => o ) // return and filter out empty
.map( (o) => {
if( !o.positionOriginal ) o.positionOriginal = o.position.clone()
return o
})
}
xrf.filter.scene(scene,frag) // spec : https://xrfragment.org/#queries
})
return xrf.filter
}
xrf.filter.scene = function(scene,frag){
// spec: https://xrfragment.org/#queries
let q = frag.q.query
scene.traverse( (mesh) => {
for ( let i in q ) {
let isMeshId = q[i].id != undefined
let isMeshProperty = q[i].filter != undefined && !isMeshId
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)
if( isMeshId &&
(i == mesh.name || xrf.hasTag(i,mesh.userData.tag))) mesh.visible = q[i].id
//if( isMeshProperty && mesh.userData[i] ) mesh.visible = (new xrf.Query(frag.q.string)).testProperty(i,mesh.userData[i])
}
})
}

View File

@ -20,15 +20,14 @@ xrf.frag.src = function(v, opts){
const addModel = (model,url,frag) => {
let scene = model.scene
src = xrf.frag.src.filterScene(scene,{...opts,frag})
xrf.frag.src.scale( src, opts, url )
xrf.frag.src.eval( src, opts, url )
xrf.frag.src.filterScene(scene,{...opts,frag})
xrf.frag.src.scale( scene, opts, url )
xrf.frag.src.eval( scene, opts, url )
// allow 't'-fragment to setup separate animmixer
enableSourcePortation(src)
model.scene = src
//enableSourcePortation(scene)
mesh.add(model.scene)
mesh.traverse( (n) => n.isSRC = n.isXRF = true ) // mark everything SRC
xrf.emit('parseModel', {...opts, scene:src, model})
xrf.emit('parseModel', {...opts, scene, model})
if( mesh.material ) mesh.material.visible = false
}
@ -67,11 +66,13 @@ xrf.frag.src = function(v, opts){
}
if( url[0] == "#" ){
let modelClone = {...model, scene: model.scene.clone()}
modelClone.scenes = [modelClone.scene]
modelClone.animations = modelClone.animations.map( (a) => a.clone() )
addModel(modelClone,url,vfrag) // current file
}else externalSRC(url,vfrag) // external file
let _model = {
animations: model.animations,
scene: scene.clone()
}
_model.scenes = [_model.scene]
addModel(_model,url,vfrag) // current file
}else externalSRC(url,vfrag) // external file
}
xrf.frag.src.eval = function(scene, opts, url){
@ -126,8 +127,11 @@ xrf.frag.src.scale = function(scene, opts, url){
xrf.frag.src.filterScene = (scene,opts) => {
let { mesh, model, camera, renderer, THREE, hashbus, frag} = opts
let obj, src
// cherrypicking of object(s)
xrf.filter.scene({scene,frag})
if( scene.children.length == 1 ) scene.children[0].position.set(0,0,0)
/*
if( !frag.filter ){
src = new THREE.Group()
if( Object.keys(frag).length > 0 ){
@ -142,8 +146,7 @@ xrf.frag.src.filterScene = (scene,opts) => {
}
// filtering of objects using query
if( frag.filter ){
console.warn("TODO: filter scene");
if( Object.keys(frag).length > 1 ){
src = scene
xrf.filter.scene(src,frag)
}
@ -151,7 +154,8 @@ xrf.frag.src.filterScene = (scene,opts) => {
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
hashbus.pub.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
})
return src
*/
return scene
}
/*

View File

@ -1,18 +1,7 @@
xrf.portalNonEuclidian = function(opts){
let { frag, mesh, model, camera, scene, renderer} = opts
// turn plane into stencilplane
mesh.material = new xrf.THREE.MeshBasicMaterial({ color: 'white' });
mesh.material.depthWrite = true;
mesh.material.depthTest = false;
mesh.material.colorWrite = false;
mesh.material.stencilWrite = true;
mesh.material.stencilRef = xrf.portalNonEuclidian.stencilRef;
mesh.material.stencilFunc = THREE.AlwaysStencilFunc;
mesh.material.stencilZPass = THREE.ReplaceStencilOp;
mesh.material.stencilFail = THREE.ReplaceStencilOp;
mesh.material.stencilZFail = THREE.ReplaceStencilOp;
//mesh.material.side = THREE.DoubleSide // *TODO* this requires flipping normals based on camera orientation
mesh.portal = {
pos: mesh.position.clone(),
posWorld: new xrf.THREE.Vector3(),
@ -20,7 +9,11 @@ xrf.portalNonEuclidian = function(opts){
needUpdate: false
}
mesh.getWorldPosition(mesh.portal.posWorld)
// turn mesh into stencilplane
xrf
.portalNonEuclidian
.setMaterial(mesh)
.getWorldPosition(mesh.portal.posWorld)
// allow objects to flip between original and stencil position (which puts them behind stencilplane)
const addStencilFeature = (n) => {
@ -101,4 +94,19 @@ xrf.portalNonEuclidian.selectStencil = (n, stencilRef, nested) => {
if( n.children && !nested ) n.traverse( (m) => !m.portal && (xrf.portalNonEuclidian.selectStencil(m,stencilRef,true)) )
}
xrf.portalNonEuclidian.setMaterial = function(mesh){
mesh.material = new xrf.THREE.MeshBasicMaterial({ color: 'white' });
mesh.material.depthWrite = true;
mesh.material.depthTest = false;
mesh.material.colorWrite = false;
mesh.material.stencilWrite = true;
mesh.material.stencilRef = xrf.portalNonEuclidian.stencilRef;
mesh.material.stencilFunc = THREE.AlwaysStencilFunc;
mesh.material.stencilZPass = THREE.ReplaceStencilOp;
mesh.material.stencilFail = THREE.ReplaceStencilOp;
mesh.material.stencilZFail = THREE.ReplaceStencilOp;
return mesh
}
xrf.portalNonEuclidian.stencilRef = 1

View File

@ -5,6 +5,8 @@
{"fn":"url","data":"http://foo.com?foo=1#price=>2", "expect":{ "fn":"testParsed", "input":"price","out":true},"label":"query test"},
{"fn":"query","data":"tag=bar", "expect":{ "fn":"testProperty","input":["tag","bar"],"out":true}},
{"fn":"query","data":"-tag=foo", "expect":{ "fn":"testProperty","input":["tag","foo"],"out":false}},
{"fn":"query","data":"-tag*=foo", "expect":{ "fn":"testProperty","input":["tag","foo"],"out":false}},
{"fn":"query","data":"-tag=>2", "expect":{ "fn":"testProperty","input":["tag","3"],"out":false}},
{"fn":"query","data":"price=>2", "expect":{ "fn":"testProperty","input":["price","1"],"out":false}},
{"fn":"query","data":"price=<2", "expect":{ "fn":"testProperty","input":["price","5"],"out":false}},
{"fn":"query","data":"price=<2", "expect":{ "fn":"testProperty","input":["price","1"],"out":true}}

View File

@ -56,7 +56,7 @@ class Filter {
private var isExclude:EReg = ~/^-/; // 1. detect excluders like `-foo`,`-foo=1`,`-.foo`,`-/foo` (reference regex= `/^-/` )
private var isRoot:EReg = ~/^[-]?\//; // 1. detect root selectors like `/foo` (reference regex= `/^[-]?\//` )
private var isNumber:EReg = ~/^[0-9\.]+$/; // 1. detect number values like `foo=1` (reference regex= `/^[0-9\.]+$/` )
private var isDeepSelect:EReg = ~/\*$/; // 1. detect nested keys like 'foo*' (reference regex= `/\*$/` )
private var isDeepSelect:EReg = ~/(^-|\*$)/; // 1. detect nested keys like 'foo*' (reference regex= `/\*$/` )
private var isSelectorExclude:EReg = ~/^-/; // 1. detect exclude keys like `-foo` (reference regex= `/^-/` )
public function new(str:String){
@ -64,11 +64,11 @@ class Filter {
}
public function toObject() : Dynamic {
return this.q;
return Reflect.copy(this.q);
}
public function get() : Dynamic {
return this.q;
return Reflect.copy(this.q);
}
public function parse(str:String) : Dynamic {
@ -90,24 +90,21 @@ class Filter {
if( str.indexOf(">") != -1 ) oper = ">"; // 1. then scan for `>` operator
if( str.indexOf("<") != -1 ) oper = "<"; // 1. then scan for `<` operator
if( isExclude.match(k) ){
oper = "=!";
k = k.substr(1); // 1. then strip key-operator: convert "-foo" into "foo"
}else v = v.substr(oper.length); // 1. then strip value operator: change value ">=foo" into "foo"
}
v = v.substr(oper.length); // 1. then strip value operator: change value ">=foo" into "foo"
if( oper.length == 0 ) oper = "=";
var rule:haxe.DynamicAccess<Dynamic> = {};
if( isNumber.match(v) ) rule[ oper ] = Std.parseFloat(v);
else rule[oper] = v;
filter['filter'] = rule; // 1. add filter rule
q.set( k, filter );
return;
q.set('expr',rule);
}else{ // 1. <b>ELSE </b> we are dealing with an object
filter[ "id" ] = isExclude.match(str) ? false: true; // 1. therefore we we set `id` to `true` or `false` (false=excluder `-`)
filter[ "root" ] = isRoot.match(str) ? true: false; // 1. and we set `root` to `true` or `false` (true=`/` root selector is present)
filter[ "deep" ] = isDeepSelect.match(str) ? true: false; // 1. and we set `deep` to `true` or `false` (for objectnames with * suffix)
if( isExclude.match(str) ) str = str.substr(1); // convert '-foo' into 'foo'
if( isRoot.match(str) ) str = str.substr(1); // 1. we convert key '/foo' into 'foo'
q.set( str ,filter ); // 1. finally we add the key/value to the store (`store.foo = {id:false,root:true}` e.g.)
q.set("root", isRoot.match(str) ? true : false ); // 1. and we set `root` to `true` or `false` (true=`/` root selector is present)
}
q.set("show", isExclude.match(str) ? false : true ); // 1. therefore we we set `show` to `true` or `false` (false=excluder `-`)
q.set("deep", isDeepSelect.match(k) ? true : false ); // 1. set `deep` (for objectnames with * suffix or negative selectors)
q.set("key", isDeepSelect.replace(k,'') );
q.set("value",v);
}
for( i in 0...token.length ) process( token[i] );
return this.q = q;
@ -151,22 +148,18 @@ class Filter {
}
// conditional rules
for ( k in Reflect.fields(q) ){
var f:Dynamic = Reflect.field(q,k);
if( f.filter == null ) continue;
if( Reflect.field(q,'expr') ){
var f:Dynamic = Reflect.field(q,'expr');
//if( Std.isOfType(value, String) ) contiggnue;
if( exclude ){
if( Reflect.field(f.filter,'!=') != null && testprop( Std.string(value) == Std.string(Reflect.field(f.filter,'!='))) && exclude ) qualify += 1;
if( !Reflect.field(q,'show') ){
if( Reflect.field(f,'!=') != null && testprop( Std.string(value) == Std.string(Reflect.field(f,'!='))) && exclude ) qualify += 1;
}else{
if( Reflect.field(f.filter,'*') != null && testprop( Std.parseFloat(value) != null ) ) qualify += 1;
if( Reflect.field(f.filter,'>') != null && testprop( Std.parseFloat(value) > Std.parseFloat(Reflect.field(f.filter,'>' )) ) ) qualify += 1;
if( Reflect.field(f.filter,'<') != null && testprop( Std.parseFloat(value) < Std.parseFloat(Reflect.field(f.filter,'<' )) ) ) qualify += 1;
if( Reflect.field(f.filter,'>=') != null && testprop( Std.parseFloat(value) >= Std.parseFloat(Reflect.field(f.filter,'>=')) ) ) qualify += 1;
if( Reflect.field(f.filter,'<=') != null && testprop( Std.parseFloat(value) <= Std.parseFloat(Reflect.field(f.filter,'<=')) ) ) qualify += 1;
if( Reflect.field(f.filter,'=') != null && (
testprop( value == Reflect.field(f.filter,'=')) ||
testprop( Std.parseFloat(value) == Std.parseFloat(Reflect.field(f.filter,'=')))
if( Reflect.field(f,'*') != null && testprop( Std.parseFloat(value) != null ) ) qualify += 1;
if( Reflect.field(f,'>') != null && testprop( Std.parseFloat(value) > Std.parseFloat(Reflect.field(f,'>' )) ) ) qualify += 1;
if( Reflect.field(f,'<') != null && testprop( Std.parseFloat(value) < Std.parseFloat(Reflect.field(f,'<' )) ) ) qualify += 1;
if( Reflect.field(f,'=') != null && (
testprop( value == Reflect.field(f,'=')) ||
testprop( Std.parseFloat(value) == Std.parseFloat(Reflect.field(f,'=')))
)) qualify += 1;
}
}

View File

@ -12,7 +12,7 @@ class Parser {
public static var keyClean:EReg = ~/(\*$|^-)/g;
@:keep
public static function parse(key:String,value:String,store:haxe.DynamicAccess<Dynamic>):Bool {
public static function parse(key:String,value:String,store:haxe.DynamicAccess<Dynamic>,?index:Int):Bool {
// 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>();
@ -52,14 +52,14 @@ class Parser {
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)
var v:XRF = new XRF(key, XRF.PV_EXECUTE | XRF.NAVIGATOR );
var v:XRF = new XRF(key, XRF.PV_EXECUTE | XRF.NAVIGATOR, index );
v.validate(value); // will fail but will parse multiple args for us (separated by |)
store.set( keyClean.replace(key,''), v );
return true;
}
// regular fragments:
var v:XRF = new XRF(key, Frag.get(key));
var v:XRF = new XRF(key, Frag.get(key), index);
if( Frag.exists(key) ){ // 1. check if fragment is official XR Fragment
if( !v.validate(value) ){ // 1. guess the type of the value (string,int,float,x,y,z,color,args,query)
trace(" fragment '"+key+"' has incompatible value ("+value+")");// 1. don't add to store if value-type is incorrect

View File

@ -40,7 +40,7 @@ class URI {
if (splitByEqual.length > 1) {
value = StringTools.urlDecode(regexPlus.split(splitByEqual[1]).join(" "));
}
var ok:Bool = Parser.parse(key,value,store); // 1. for every recognized fragment key/value-pair call [Parser.parse](#%E2%86%AA%20Parser.parse%28k%2Cv%2Cstore%29)
var ok:Bool = Parser.parse(key,value,store,i); // 1. for every recognized fragment key/value-pair call [Parser.parse](#%E2%86%AA%20Parser.parse%28k%2Cv%2Cstore%29)
}
if( filter != null && filter != 0 ){
for (key in store.keys()) {

View File

@ -47,6 +47,7 @@ class XRF {
// value holder(s) // |------|------|--------|----------------------------------|
public var fragment:String;
public var flags:Int;
public var index:Int;
public var x:Float; // |vector| x,y,z| comma-separated | #pos=1,2,3 |
public var y:Float;
public var z:Float;
@ -58,9 +59,10 @@ class XRF {
public var filter:Filter;
public var noXRF:Bool;
//
public function new(_fragment:String,_flags:Int){
public function new(_fragment:String,_flags:Int,?_index:Int){
fragment = _fragment;
flags = _flags;
index = _index;
}
public function is(flag:Int):Bool {
@ -107,8 +109,8 @@ class XRF {
v.int = Std.parseInt(str);
v.x = cast(v.int);
}
filter = (new Filter(v.fragment+"="+v.string)).get();
}else filter = (new Filter(v.fragment)).get();
v.filter = new Filter(v.fragment+"="+v.string);
}else v.filter = new Filter(v.fragment);
}
}

104
test/aframe/filter.js Normal file
View File

@ -0,0 +1,104 @@
// test the XR Fragments parser-filters with THREEjs scenes
THREE = AFRAME.THREE
createScene = (noadd) => {
let obj = {a:{},b:{},c:{}}
for ( let i in obj ){
obj[i] = new THREE.Object3D()
obj[i].name = i
}
let {a,b,c} = obj
let scene = new THREE.Scene()
if( !noadd ){
a.add(b)
b.add(c)
scene.add(a)
}
b.userData.score = 2
return {a,b,c,scene}
}
filterScene = (URI) => {
frag = xrf.URI.parse(URI)
var {a,b,c,scene} = createScene()
xrf.filter.scene({scene,frag})
return scene
}
scene = filterScene("#b")
test = () => !scene.getObjectByName("a") &&
scene.getObjectByName("b").visible &&
!scene.getObjectByName("c")
console.assert( test(), {scene,reason:`objectname: #b => a = removed b = visible c = removed`})
scene = filterScene("#b*")
test = () => !scene.getObjectByName("a") &&
scene.getObjectByName("b").visible &&
scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #b* => a = removed b = visible c = visible`})
scene = filterScene("#-b")
test = () => scene.getObjectByName("a").visible &&
!scene.getObjectByName("b").visible &&
!scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #-b => a = visible b = invisible c = invisible`})
scene = filterScene("#a&-b")
test = () => scene.getObjectByName("a").visible &&
!scene.getObjectByName("b").visible &&
!scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #a&-b => a = visible b = invisible c = invisible`})
scene = filterScene("#-b&b")
test = () => !scene.getObjectByName("a") &&
scene.getObjectByName("b").visible &&
!scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #-b&b => a = removed b = invisible c = invisible (last duplicate wins)`})
scene = filterScene("#-c")
test = () => scene.getObjectByName("a").visible &&
scene.getObjectByName("b").visible &&
!scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #-b&b => a = visible b = visible c = invisible`})
scene = filterScene("#-b*")
test = () => scene.getObjectByName("a").visible &&
!scene.getObjectByName("b").visible &&
!scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #-b&b => a = visible b = visible c = invisible`})
scene = filterScene("#score")
test = () => !scene.getObjectByName("a") &&
scene.getObjectByName("b").visible &&
!scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #score => a = removed b = visible c = invisible`})
scene = filterScene("#score=>1")
test = () => !scene.getObjectByName("a") &&
scene.getObjectByName("b").visible &&
!scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #score=>1 => a = removed b = visible c = invisible`})
scene = filterScene("#score=>3")
test = () => !scene.getObjectByName("a").visible &&
!scene.getObjectByName("b").visible &&
!scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #score=>3 => a = invisible b = visible c = invisible`})
scene = filterScene("#score*=>1")
test = () => !scene.getObjectByName("a").visible &&
scene.getObjectByName("b").visible &&
scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #score*=>1 => a = invisible b = visible c = visible`})
scene = filterScene("#-score*=>1")
test = () => scene.getObjectByName("a").visible &&
!scene.getObjectByName("b").visible &&
!scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #-score*=>1 => a = visible b = invisible c = invisible`})
scene = filterScene("#-score*=>1&c")
test = () => scene.getObjectByName("a").visible &&
!scene.getObjectByName("b").visible &&
scene.getObjectByName("c").visible
console.assert( test(), {scene,reason:`objectname: #-score*=>1 => a = visible b = invisible c = visible`})

View File

@ -3,10 +3,11 @@
<title>AFRAME - xrfragment sandbox</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
<script src="./../../dist/xrfragment.aframe.js"></script>
<script src="./index.js"></script>
<script src="./pubsub.js"></script>
<script src="./filter.js"></script>
</head>
<body>
<h3>open the browserconsole to see the results</h3>

View File

@ -18,6 +18,6 @@ console.assert = ((assert) => (a,b) => {
let frags = xrf.URI.parse('://foo.com/1.gltf#pos=1.0,2.0,3.0&q=-.foo&t=1,100',true)
console.assert( frags.t, {frags, reason:'URI.parse(): t needs to be set'})
let q = new xrf.Query();
console.assert( new xrf.Query(), {reason: 'new Query() should be available'})
let frag = xrf.URI.parse("#foo=1")
console.assert( frag, {reason: 'xrf.URI.parse() should be available'})