2023-06-07 17:42:21 +02:00
var $hx _exports = typeof exports != "undefined" ? exports : typeof window != "undefined" ? window : typeof self != "undefined" ? self : this ;
( function ( $global ) { "use strict" ;
$hx _exports [ "xrfragment" ] = $hx _exports [ "xrfragment" ] || { } ;
var EReg = function ( r , opt ) {
this . r = new RegExp ( r , opt . split ( "u" ) . join ( "" ) ) ;
} ;
EReg . _ _name _ _ = true ;
EReg . prototype = {
match : function ( s ) {
if ( this . r . global ) {
this . r . lastIndex = 0 ;
}
this . r . m = this . r . exec ( s ) ;
this . r . s = s ;
return this . r . m != null ;
}
, split : function ( s ) {
var d = "#__delim__#" ;
return s . replace ( this . r , d ) . split ( d ) ;
}
} ;
var HxOverrides = function ( ) { } ;
HxOverrides . _ _name _ _ = true ;
HxOverrides . cca = function ( s , index ) {
var x = s . charCodeAt ( index ) ;
if ( x != x ) {
return undefined ;
}
return x ;
} ;
HxOverrides . substr = function ( s , pos , len ) {
if ( len == null ) {
len = s . length ;
} else if ( len < 0 ) {
if ( pos == 0 ) {
len = s . length + len ;
} else {
return "" ;
}
}
return s . substr ( pos , len ) ;
} ;
HxOverrides . now = function ( ) {
return Date . now ( ) ;
} ;
Math . _ _name _ _ = true ;
var Reflect = function ( ) { } ;
Reflect . _ _name _ _ = true ;
Reflect . field = function ( o , field ) {
try {
return o [ field ] ;
} catch ( _g ) {
return null ;
}
} ;
Reflect . fields = function ( o ) {
var a = [ ] ;
if ( o != null ) {
var hasOwnProperty = Object . prototype . hasOwnProperty ;
for ( var f in o ) {
if ( f != "__id__" && f != "hx__closures__" && hasOwnProperty . call ( o , f ) ) {
a . push ( f ) ;
}
}
}
return a ;
} ;
Reflect . deleteField = function ( o , field ) {
if ( ! Object . prototype . hasOwnProperty . call ( o , field ) ) {
return false ;
}
delete ( o [ field ] ) ;
return true ;
} ;
var Std = function ( ) { } ;
Std . _ _name _ _ = true ;
Std . string = function ( s ) {
return js _Boot . _ _string _rec ( s , "" ) ;
} ;
Std . parseInt = function ( x ) {
if ( x != null ) {
var _g = 0 ;
var _g1 = x . length ;
while ( _g < _g1 ) {
var i = _g ++ ;
var c = x . charCodeAt ( i ) ;
if ( c <= 8 || c >= 14 && c != 32 && c != 45 ) {
var nc = x . charCodeAt ( i + 1 ) ;
var v = parseInt ( x , nc == 120 || nc == 88 ? 16 : 10 ) ;
if ( isNaN ( v ) ) {
return null ;
} else {
return v ;
}
}
}
}
return null ;
} ;
var StringTools = function ( ) { } ;
StringTools . _ _name _ _ = true ;
StringTools . isSpace = function ( s , pos ) {
var c = HxOverrides . cca ( s , pos ) ;
if ( ! ( c > 8 && c < 14 ) ) {
return c == 32 ;
} else {
return true ;
}
} ;
StringTools . ltrim = function ( s ) {
var l = s . length ;
var r = 0 ;
while ( r < l && StringTools . isSpace ( s , r ) ) ++ r ;
if ( r > 0 ) {
return HxOverrides . substr ( s , r , l - r ) ;
} else {
return s ;
}
} ;
StringTools . rtrim = function ( s ) {
var l = s . length ;
var r = 0 ;
while ( r < l && StringTools . isSpace ( s , l - r - 1 ) ) ++ r ;
if ( r > 0 ) {
return HxOverrides . substr ( s , 0 , l - r ) ;
} else {
return s ;
}
} ;
StringTools . trim = function ( s ) {
return StringTools . ltrim ( StringTools . rtrim ( s ) ) ;
} ;
StringTools . replace = function ( s , sub , by ) {
return s . split ( sub ) . join ( by ) ;
} ;
var haxe _iterators _ArrayIterator = function ( array ) {
this . current = 0 ;
this . array = array ;
} ;
haxe _iterators _ArrayIterator . _ _name _ _ = true ;
haxe _iterators _ArrayIterator . prototype = {
hasNext : function ( ) {
return this . current < this . array . length ;
}
, next : function ( ) {
return this . array [ this . current ++ ] ;
}
} ;
var js _Boot = function ( ) { } ;
js _Boot . _ _name _ _ = true ;
js _Boot . _ _string _rec = function ( o , s ) {
if ( o == null ) {
return "null" ;
}
if ( s . length >= 5 ) {
return "<...>" ;
}
var t = typeof ( o ) ;
if ( t == "function" && ( o . _ _name _ _ || o . _ _ename _ _ ) ) {
t = "object" ;
}
switch ( t ) {
case "function" :
return "<function>" ;
case "object" :
if ( ( ( o ) instanceof Array ) ) {
var str = "[" ;
s += "\t" ;
var _g = 0 ;
var _g1 = o . length ;
while ( _g < _g1 ) {
var i = _g ++ ;
str += ( i > 0 ? "," : "" ) + js _Boot . _ _string _rec ( o [ i ] , s ) ;
}
str += "]" ;
return str ;
}
var tostr ;
try {
tostr = o . toString ;
} catch ( _g ) {
return "???" ;
}
if ( tostr != null && tostr != Object . toString && typeof ( tostr ) == "function" ) {
var s2 = o . toString ( ) ;
if ( s2 != "[object Object]" ) {
return s2 ;
}
}
var str = "{\n" ;
s += "\t" ;
var hasp = o . hasOwnProperty != null ;
var k = null ;
for ( k in o ) {
if ( hasp && ! o . hasOwnProperty ( k ) ) {
continue ;
}
if ( k == "prototype" || k == "__class__" || k == "__super__" || k == "__interfaces__" || k == "__properties__" ) {
continue ;
}
if ( str . length != 2 ) {
str += ", \n" ;
}
str += s + k + " : " + js _Boot . _ _string _rec ( o [ k ] , s ) ;
}
s = s . substring ( 1 ) ;
str += "\n" + s + "}" ;
return str ;
case "string" :
return o ;
default :
return String ( o ) ;
}
} ;
var xrfragment _Parser = $hx _exports [ "xrfragment" ] [ "Parser" ] = function ( ) { } ;
xrfragment _Parser . _ _name _ _ = true ;
xrfragment _Parser . parse = function ( key , value , resultMap ) {
var Frag _h = Object . create ( null ) ;
Frag _h [ "prio" ] = xrfragment _XRF . ASSET | xrfragment _XRF . T _INT ;
Frag _h [ "#" ] = xrfragment _XRF . ASSET | xrfragment _XRF . T _PREDEFINED _VIEW ;
Frag _h [ "class" ] = xrfragment _XRF . ASSET | xrfragment _XRF . T _STRING ;
Frag _h [ "src" ] = xrfragment _XRF . ASSET | xrfragment _XRF . T _URL ;
Frag _h [ "pos" ] = xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . ROUNDROBIN | xrfragment _XRF . T _VECTOR3 | xrfragment _XRF . T _STRING _OBJ | xrfragment _XRF . EMBEDDED | xrfragment _XRF . NAVIGATOR ;
Frag _h [ "href" ] = xrfragment _XRF . ASSET | xrfragment _XRF . T _URL | xrfragment _XRF . T _PREDEFINED _VIEW ;
Frag _h [ "q" ] = xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . T _STRING | xrfragment _XRF . EMBEDDED ;
Frag _h [ "scale" ] = xrfragment _XRF . QUERY _OPERATOR | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . ROUNDROBIN | xrfragment _XRF . T _VECTOR3 | xrfragment _XRF . EMBEDDED ;
Frag _h [ "rot" ] = xrfragment _XRF . QUERY _OPERATOR | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . ROUNDROBIN | xrfragment _XRF . T _VECTOR3 | xrfragment _XRF . EMBEDDED | xrfragment _XRF . NAVIGATOR ;
Frag _h [ "translate" ] = xrfragment _XRF . QUERY _OPERATOR | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . ROUNDROBIN | xrfragment _XRF . T _VECTOR3 | xrfragment _XRF . EMBEDDED ;
Frag _h [ "visible" ] = xrfragment _XRF . QUERY _OPERATOR | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . ROUNDROBIN | xrfragment _XRF . T _INT | xrfragment _XRF . EMBEDDED ;
Frag _h [ "env" ] = xrfragment _XRF . ASSET | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . T _STRING | xrfragment _XRF . EMBEDDED ;
Frag _h [ "t" ] = xrfragment _XRF . ASSET | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . ROUNDROBIN | xrfragment _XRF . T _VECTOR2 | xrfragment _XRF . NAVIGATOR | xrfragment _XRF . EMBEDDED ;
Frag _h [ "gravity" ] = xrfragment _XRF . ASSET | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . T _VECTOR3 | xrfragment _XRF . EMBEDDED ;
Frag _h [ "physics" ] = xrfragment _XRF . ASSET | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . T _VECTOR3 | xrfragment _XRF . EMBEDDED ;
Frag _h [ "fov" ] = xrfragment _XRF . ASSET | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . T _INT | xrfragment _XRF . NAVIGATOR | xrfragment _XRF . EMBEDDED ;
Frag _h [ "clip" ] = xrfragment _XRF . ASSET | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . T _VECTOR2 | xrfragment _XRF . NAVIGATOR | xrfragment _XRF . EMBEDDED ;
Frag _h [ "fog" ] = xrfragment _XRF . ASSET | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . T _STRING | xrfragment _XRF . NAVIGATOR | xrfragment _XRF . EMBEDDED ;
Frag _h [ "namespace" ] = xrfragment _XRF . ASSET | xrfragment _XRF . T _STRING ;
Frag _h [ "SPDX" ] = xrfragment _XRF . ASSET | xrfragment _XRF . T _STRING ;
Frag _h [ "unit" ] = xrfragment _XRF . ASSET | xrfragment _XRF . T _STRING ;
Frag _h [ "description" ] = xrfragment _XRF . ASSET | xrfragment _XRF . T _STRING ;
Frag _h [ "session" ] = xrfragment _XRF . ASSET | xrfragment _XRF . T _URL | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . NAVIGATOR | xrfragment _XRF . EMBEDDED | xrfragment _XRF . PROMPT ;
if ( value . length == 0 && ! Object . prototype . hasOwnProperty . call ( Frag _h , key ) ) {
resultMap [ key ] = new xrfragment _XRF ( key , xrfragment _XRF . PV _EXECUTE | xrfragment _XRF . NAVIGATOR ) ;
return true ;
}
if ( key . split ( "." ) . length > 1 && value . split ( "." ) . length > 1 ) {
resultMap [ key ] = new xrfragment _XRF ( key , xrfragment _XRF . ASSET | xrfragment _XRF . PV _OVERRIDE | xrfragment _XRF . T _STRING | xrfragment _XRF . PROP _BIND ) ;
return true ;
}
if ( Object . prototype . hasOwnProperty . call ( Frag _h , key ) ) {
var v = new xrfragment _XRF ( key , Frag _h [ key ] ) ;
if ( ! v . validate ( value ) ) {
console . log ( "src/xrfragment/Parser.hx:77:" , "⚠ fragment '" + key + "' has incompatible value (" + value + ")" ) ;
return false ;
}
if ( xrfragment _Parser . debug ) {
console . log ( "src/xrfragment/Parser.hx:80:" , "✔ " + key + ": " + v . string ) ;
}
resultMap [ key ] = v ;
}
return true ;
} ;
var xrfragment _Query = $hx _exports [ "xrfragment" ] [ "Query" ] = function ( str ) {
this . isNumber = new EReg ( "^[0-9\\.]+$" , "" ) ;
this . isClass = new EReg ( "^[-]?class$" , "" ) ;
this . isAddition = new EReg ( "^\\+" , "" ) ;
this . isExclude = new EReg ( "^-" , "" ) ;
this . isProp = new EReg ( "^.*:[><=!]?" , "" ) ;
this . q = { } ;
this . str = "" ;
if ( str != null ) {
this . parse ( str ) ;
}
} ;
xrfragment _Query . _ _name _ _ = true ;
xrfragment _Query . prototype = {
toObject : function ( ) {
return this . q ;
}
, expandAliases : function ( token ) {
var classAlias = new EReg ( "^(-)?\\." , "" ) ;
if ( classAlias . match ( token ) ) {
return StringTools . replace ( token , "." , "class:" ) ;
} else {
return token ;
}
}
, get : function ( ) {
return this . q ;
}
, parse : function ( str , recurse ) {
if ( recurse == null ) {
recurse = false ;
}
var _gthis = this ;
var token = str . split ( " " ) ;
var q = { } ;
var process = function ( str , prefix ) {
if ( prefix == null ) {
prefix = "" ;
}
str = StringTools . trim ( str ) ;
var k = str . split ( ":" ) [ 0 ] ;
var v = str . split ( ":" ) [ 1 ] ;
var filter = { } ;
if ( q [ prefix + k ] ) {
filter = q [ prefix + k ] ;
}
filter [ "rules" ] = filter [ "rules" ] != null ? filter [ "rules" ] : [ ] ;
if ( _gthis . isProp . match ( str ) ) {
var oper = "" ;
if ( str . indexOf ( "*" ) != - 1 ) {
oper = "*" ;
}
if ( str . indexOf ( ">" ) != - 1 ) {
oper = ">" ;
}
if ( str . indexOf ( "<" ) != - 1 ) {
oper = "<" ;
}
if ( str . indexOf ( ">=" ) != - 1 ) {
oper = ">=" ;
}
if ( str . indexOf ( "<=" ) != - 1 ) {
oper = "<=" ;
}
if ( _gthis . isExclude . match ( k ) ) {
oper = "!=" ;
k = HxOverrides . substr ( k , 1 , null ) ;
} else if ( _gthis . isAddition . match ( k ) ) {
oper = "+=" ;
k = HxOverrides . substr ( k , 1 , null ) ;
} else {
v = HxOverrides . substr ( v , oper . length , null ) ;
}
if ( oper . length == 0 ) {
oper = "=" ;
}
if ( _gthis . isClass . match ( k ) ) {
filter [ prefix + k ] = oper != "!=" ;
q [ v ] = filter ;
} else {
var rule = { } ;
if ( _gthis . isNumber . match ( v ) ) {
rule [ oper ] = parseFloat ( v ) ;
} else {
rule [ oper ] = v ;
}
filter [ "rules" ] . push ( rule ) ;
q [ k ] = filter ;
}
return ;
} else {
filter [ "id" ] = _gthis . isExclude . match ( str ) ? false : true ;
var key = _gthis . isExclude . match ( str ) ? HxOverrides . substr ( str , 1 , null ) : str ;
q [ key ] = filter ;
}
} ;
var _g = 0 ;
var _g1 = token . length ;
while ( _g < _g1 ) {
var i = _g ++ ;
process ( this . expandAliases ( token [ i ] ) ) ;
}
return this . q = q ;
}
, test : function ( obj ) {
var qualify = false ;
var _g = 0 ;
var _g1 = Reflect . fields ( obj ) ;
while ( _g < _g1 . length ) {
var k = _g1 [ _g ] ;
++ _g ;
var v = Std . string ( Reflect . field ( obj , k ) ) ;
if ( this . testProperty ( k , v ) ) {
qualify = true ;
}
}
var _g = 0 ;
var _g1 = Reflect . fields ( obj ) ;
while ( _g < _g1 . length ) {
var k = _g1 [ _g ] ;
++ _g ;
var v = Std . string ( Reflect . field ( obj , k ) ) ;
if ( this . testProperty ( k , v , true ) ) {
qualify = false ;
}
}
return qualify ;
}
, testProperty : function ( property , value , exclude ) {
var conds = 0 ;
var fails = 0 ;
var qualify = 0 ;
var testprop = function ( expr ) {
conds += 1 ;
fails += expr ? 0 : 1 ;
return expr ;
} ;
if ( this . q [ value ] != null ) {
var v = this . q [ value ] ;
if ( v [ property ] != null ) {
return v [ property ] ;
}
}
var _g = 0 ;
var _g1 = Reflect . fields ( this . q ) ;
while ( _g < _g1 . length ) {
var k = _g1 [ _g ] ;
++ _g ;
var filter = Reflect . field ( this . q , k ) ;
if ( filter . rules == null ) {
continue ;
}
var rules = filter . rules ;
var _g2 = 0 ;
while ( _g2 < rules . length ) {
var rule = rules [ _g2 ] ;
++ _g2 ;
if ( exclude ) {
if ( Reflect . field ( rule , "!=" ) != null && testprop ( ( value == null ? "null" : "" + value ) == Std . string ( Reflect . field ( rule , "!=" ) ) ) && exclude ) {
++ qualify ;
}
} else {
if ( Reflect . field ( rule , "*" ) != null && testprop ( parseFloat ( value ) != null ) ) {
++ qualify ;
}
if ( Reflect . field ( rule , ">" ) != null && testprop ( parseFloat ( value ) > parseFloat ( Reflect . field ( rule , ">" ) ) ) ) {
++ qualify ;
}
if ( Reflect . field ( rule , "<" ) != null && testprop ( parseFloat ( value ) < parseFloat ( Reflect . field ( rule , "<" ) ) ) ) {
++ qualify ;
}
if ( Reflect . field ( rule , ">=" ) != null && testprop ( parseFloat ( value ) >= parseFloat ( Reflect . field ( rule , ">=" ) ) ) ) {
++ qualify ;
}
if ( Reflect . field ( rule , "<=" ) != null && testprop ( parseFloat ( value ) <= parseFloat ( Reflect . field ( rule , "<=" ) ) ) ) {
++ qualify ;
}
if ( Reflect . field ( rule , "=" ) != null && ( testprop ( value == Reflect . field ( rule , "=" ) ) || testprop ( parseFloat ( value ) == parseFloat ( Reflect . field ( rule , "=" ) ) ) ) ) {
++ qualify ;
}
}
}
}
return qualify > 0 ;
}
} ;
var xrfragment _URI = $hx _exports [ "xrfragment" ] [ "URI" ] = function ( ) { } ;
xrfragment _URI . _ _name _ _ = true ;
xrfragment _URI . parse = function ( url , filter ) {
var store = { } ;
if ( url . indexOf ( "#" ) == - 1 ) {
return store ;
}
var fragment = url . split ( "#" ) ;
var splitArray = fragment [ 1 ] . split ( "&" ) ;
var _g = 0 ;
var _g1 = splitArray . length ;
while ( _g < _g1 ) {
var i = _g ++ ;
var splitByEqual = splitArray [ i ] . split ( "=" ) ;
var regexPlus = new EReg ( "\\+" , "g" ) ;
var key = splitByEqual [ 0 ] ;
var value = "" ;
if ( splitByEqual . length > 1 ) {
var s = regexPlus . split ( splitByEqual [ 1 ] ) . join ( " " ) ;
value = decodeURIComponent ( s . split ( "+" ) . join ( " " ) ) ;
}
var ok = xrfragment _Parser . parse ( key , value , store ) ;
}
if ( filter != null && filter != 0 ) {
var _g = 0 ;
var _g1 = Reflect . fields ( store ) ;
while ( _g < _g1 . length ) {
var key = _g1 [ _g ] ;
++ _g ;
var xrf = store [ key ] ;
if ( ! xrf . is ( filter ) ) {
Reflect . deleteField ( store , key ) ;
}
}
}
return store ;
} ;
var xrfragment _XRF = $hx _exports [ "xrfragment" ] [ "XRF" ] = function ( _fragment , _flags ) {
this . fragment = _fragment ;
this . flags = _flags ;
} ;
xrfragment _XRF . _ _name _ _ = true ;
xrfragment _XRF . set = function ( flag , flags ) {
return flags | flag ;
} ;
xrfragment _XRF . unset = function ( flag , flags ) {
return flags & ~ flag ;
} ;
xrfragment _XRF . prototype = {
is : function ( flag ) {
return ( this . flags & flag ) != 0 ;
}
, validate : function ( value ) {
this . guessType ( this , value ) ;
if ( value . split ( "|" ) . length > 1 ) {
this . args = [ ] ;
var args = value . split ( "|" ) ;
var _g = 0 ;
var _g1 = args . length ;
while ( _g < _g1 ) {
var i = _g ++ ;
var x = new xrfragment _XRF ( this . fragment , this . flags ) ;
this . guessType ( x , args [ i ] ) ;
this . args . push ( x ) ;
}
}
if ( this . fragment == "q" ) {
this . query = new xrfragment _Query ( value ) . get ( ) ;
}
var ok = true ;
if ( ! ( ( this . args ) instanceof Array ) ) {
if ( this . is ( xrfragment _XRF . T _VECTOR3 ) && ! ( typeof ( this . x ) == "number" && typeof ( this . y ) == "number" && typeof ( this . z ) == "number" ) ) {
ok = false ;
}
if ( this . is ( xrfragment _XRF . T _VECTOR2 ) && ! ( typeof ( this . x ) == "number" && typeof ( this . y ) == "number" ) ) {
ok = false ;
}
var tmp ;
if ( this . is ( xrfragment _XRF . T _INT ) ) {
var v = this . int ;
tmp = ! ( typeof ( v ) == "number" && ( ( v | 0 ) === v ) ) ;
} else {
tmp = false ;
}
if ( tmp ) {
ok = false ;
}
}
return ok ;
}
, guessType : function ( v , str ) {
v . string = str ;
if ( str . split ( "," ) . length > 1 ) {
var xyz = str . split ( "," ) ;
if ( xyz . length > 0 ) {
v . x = parseFloat ( xyz [ 0 ] ) ;
}
if ( xyz . length > 1 ) {
v . y = parseFloat ( xyz [ 1 ] ) ;
}
if ( xyz . length > 2 ) {
v . z = parseFloat ( xyz [ 2 ] ) ;
}
}
if ( xrfragment _XRF . isColor . match ( str ) ) {
v . color = str ;
}
if ( xrfragment _XRF . isFloat . match ( str ) ) {
v . float = parseFloat ( str ) ;
}
if ( xrfragment _XRF . isInt . match ( str ) ) {
v . int = Std . parseInt ( str ) ;
}
}
} ;
if ( typeof ( performance ) != "undefined" ? typeof ( performance . now ) == "function" : false ) {
HxOverrides . now = performance . now . bind ( performance ) ;
}
String . _ _name _ _ = true ;
Array . _ _name _ _ = true ;
js _Boot . _ _toStr = ( { } ) . toString ;
xrfragment _Parser . error = "" ;
xrfragment _Parser . debug = false ;
xrfragment _XRF . ASSET = 1 ;
xrfragment _XRF . PROP _BIND = 2 ;
xrfragment _XRF . QUERY _OPERATOR = 4 ;
xrfragment _XRF . PROMPT = 8 ;
xrfragment _XRF . ROUNDROBIN = 16 ;
xrfragment _XRF . NAVIGATOR = 32 ;
xrfragment _XRF . EMBEDDED = 64 ;
xrfragment _XRF . PV _OVERRIDE = 128 ;
xrfragment _XRF . PV _EXECUTE = 256 ;
xrfragment _XRF . T _COLOR = 8192 ;
xrfragment _XRF . T _INT = 16384 ;
xrfragment _XRF . T _FLOAT = 32768 ;
xrfragment _XRF . T _VECTOR2 = 65536 ;
xrfragment _XRF . T _VECTOR3 = 131072 ;
xrfragment _XRF . T _URL = 262144 ;
xrfragment _XRF . T _PREDEFINED _VIEW = 524288 ;
xrfragment _XRF . T _STRING = 1048576 ;
xrfragment _XRF . T _STRING _OBJ = 2097152 ;
xrfragment _XRF . T _STRING _OBJ _PROP = 4194304 ;
xrfragment _XRF . isColor = new EReg ( "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$" , "" ) ;
xrfragment _XRF . isInt = new EReg ( "^[0-9]+$" , "" ) ;
xrfragment _XRF . isFloat = new EReg ( "^[0-9]+\\.[0-9]+$" , "" ) ;
xrfragment _XRF . isVector = new EReg ( "([,]+|\\w)" , "" ) ;
xrfragment _XRF . isUrl = new EReg ( "(://)?\\..*" , "" ) ;
xrfragment _XRF . isUrlOrPretypedView = new EReg ( "(^#|://)?\\..*" , "" ) ;
xrfragment _XRF . isString = new EReg ( ".*" , "" ) ;
} ) ( { } ) ;
var xrfragment = $hx _exports [ "xrfragment" ] ;
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 Leon van Kammen/NLNET
var xrf = { }
xrf . init = function ( opts ) {
opts = opts || { }
xrf . Parser . debug = xrf . debug
for ( let i in opts ) xrf [ i ] = opts [ i ]
xrf . emit ( 'init' , opts )
return xrf . query
}
xrf . query = function ( ) {
// framework implementations can override this function, see src/3rd/js/three/index.sj
alert ( "queries are not implemented (yet) for this particular framework" )
}
// map library functions to xrf
for ( let i in xrfragment ) xrf [ i ] = xrfragment [ i ]
/ *
* ( promise - able ) EVENTS
*
* example :
*
* xrf . addEventListener ( 'foo' , ( e ) => {
* // let promise = e.promise()
* console . log ( "navigating to: " + e . detail . destination . url )
* // promise.resolve()
* // promise.reject("not going to happen")
* } )
*
* xrf . emit ( 'foo' , 123 )
* xrf . emit ( 'foo' , 123 ) . then ( ... ) . catch ( ... ) . finally ( ... )
* /
xrf . addEventListener = function ( eventName , callback ) {
if ( ! this . _listeners ) this . _listeners = [ ]
if ( ! this . _listeners [ eventName ] ) {
// create a new array for this event name if it doesn't exist yet
this . _listeners [ eventName ] = [ ] ;
}
// add the callback to the listeners array for this event name
this . _listeners [ eventName ] . push ( callback ) ;
} ;
xrf . emit = function ( eventName , data ) {
return xrf . emit . promise ( eventName , data )
}
xrf . emit . normal = function ( eventName , data ) {
if ( ! xrf . _listeners ) xrf . _listeners = [ ]
var callbacks = xrf . _listeners [ eventName ]
if ( callbacks ) {
for ( var i = 0 ; i < callbacks . length ; i ++ ) {
callbacks [ i ] ( data ) ;
}
}
} ;
xrf . emit . promise = function ( e , opts ) {
opts . XRF = xrf // always pass root XRF obj
return new Promise ( ( resolve , reject ) => {
opts . promise = ( ) => {
opts . promise . halted = true
return { resolve , reject }
}
xrf . emit . normal ( e , opts )
if ( ! opts . promise . halted ) resolve ( )
} )
}
// wrapper to survive in/outside modules
xrf . InteractiveGroup = function ( THREE , renderer , camera ) {
let {
Group ,
Matrix4 ,
Raycaster ,
Vector2
} = THREE
const _pointer = new Vector2 ( ) ;
const _event = { type : '' , data : _pointer } ;
class InteractiveGroup extends Group {
constructor ( renderer , camera ) {
super ( ) ;
if ( ! renderer || ! camera ) return
// extract camera when camera-rig is passed
camera . traverse ( ( n ) => String ( n . type ) . match ( /Camera/ ) ? camera = n : null )
const scope = this ;
const raycaster = new Raycaster ( ) ;
const tempMatrix = new Matrix4 ( ) ;
function nocollide ( ) {
if ( nocollide . tid ) return // ratelimit
_event . type = "nocollide"
scope . children . map ( ( c ) => c . dispatchEvent ( _event ) )
nocollide . tid = setTimeout ( ( ) => nocollide . tid = null , 100 )
}
// Pointer Events
const element = renderer . domElement ;
function onPointerEvent ( event ) {
//event.stopPropagation();
const rect = renderer . domElement . getBoundingClientRect ( ) ;
_pointer . x = ( event . clientX - rect . left ) / rect . width * 2 - 1 ;
_pointer . y = - ( event . clientY - rect . top ) / rect . height * 2 + 1 ;
raycaster . setFromCamera ( _pointer , camera ) ;
const intersects = raycaster . intersectObjects ( scope . children , false ) ;
if ( intersects . length > 0 ) {
const intersection = intersects [ 0 ] ;
const object = intersection . object ;
const uv = intersection . uv ;
_event . type = event . type ;
_event . data . set ( uv . x , 1 - uv . y ) ;
object . dispatchEvent ( _event ) ;
} else nocollide ( )
}
element . addEventListener ( 'pointerdown' , onPointerEvent ) ;
element . addEventListener ( 'pointerup' , onPointerEvent ) ;
element . addEventListener ( 'pointermove' , onPointerEvent ) ;
element . addEventListener ( 'mousedown' , onPointerEvent ) ;
element . addEventListener ( 'mouseup' , onPointerEvent ) ;
element . addEventListener ( 'mousemove' , onPointerEvent ) ;
element . addEventListener ( 'click' , onPointerEvent ) ;
// WebXR Controller Events
// TODO: Dispatch pointerevents too
const events = {
'move' : 'mousemove' ,
'select' : 'click' ,
'selectstart' : 'mousedown' ,
'selectend' : 'mouseup'
} ;
function onXRControllerEvent ( event ) {
const controller = event . target ;
tempMatrix . identity ( ) . extractRotation ( controller . matrixWorld ) ;
raycaster . ray . origin . setFromMatrixPosition ( controller . matrixWorld ) ;
raycaster . ray . direction . set ( 0 , 0 , - 1 ) . applyMatrix4 ( tempMatrix ) ;
const intersections = raycaster . intersectObjects ( scope . children , false ) ;
if ( intersections . length > 0 ) {
const intersection = intersections [ 0 ] ;
const object = intersection . object ;
const uv = intersection . uv ;
_event . type = events [ event . type ] ;
_event . data . set ( uv . x , 1 - uv . y ) ;
if ( _event . type != "mousemove" ) {
console . log ( event . type + " => " + _event . type )
}
object . dispatchEvent ( _event ) ;
} else nocollide ( )
}
const controller1 = renderer . xr . getController ( 0 ) ;
controller1 . addEventListener ( 'move' , onXRControllerEvent ) ;
controller1 . addEventListener ( 'select' , onXRControllerEvent ) ;
controller1 . addEventListener ( 'selectstart' , onXRControllerEvent ) ;
controller1 . addEventListener ( 'selectend' , onXRControllerEvent ) ;
const controller2 = renderer . xr . getController ( 1 ) ;
controller2 . addEventListener ( 'move' , onXRControllerEvent ) ;
controller2 . addEventListener ( 'select' , onXRControllerEvent ) ;
controller2 . addEventListener ( 'selectstart' , onXRControllerEvent ) ;
controller2 . addEventListener ( 'selectend' , onXRControllerEvent ) ;
}
}
return new InteractiveGroup ( renderer , camera )
}
xrf . frag = { }
xrf . model = { }
xrf . init = ( ( init ) => function ( opts ) {
init ( opts )
if ( opts . loaders ) Object . values ( opts . loaders ) . map ( xrf . patchLoader )
xrf . patchRenderer ( opts . renderer )
xrf . navigator . init ( )
// return xrfragment lib as 'xrf' query functor (like jquery)
for ( let i in xrf ) xrf . query [ i ] = xrf [ i ]
return xrf . query
} ) ( xrf . init )
xrf . patchRenderer = function ( renderer ) {
renderer . xr . addEventListener ( 'sessionstart' , ( ) => xrf . baseReferenceSpace = renderer . xr . getReferenceSpace ( ) ) ;
renderer . xr . enabled = true ;
renderer . render = ( ( render ) => function ( scene , camera ) {
if ( xrf . model && xrf . model . render )
xrf . model . render ( scene , camera )
render ( scene , camera )
} ) ( renderer . render . bind ( renderer ) )
}
xrf . patchLoader = function ( loader ) {
loader . prototype . load = ( ( load ) => function ( url , onLoad , onProgress , onError ) {
load . call ( this ,
url ,
( model ) => {
onLoad ( model ) ;
xrf . parseModel ( model , url )
} ,
onProgress ,
onError )
} ) ( loader . prototype . load )
}
xrf . getFile = ( url ) => url . split ( "/" ) . pop ( ) . replace ( /#.*/ , '' )
xrf . parseModel = function ( model , url ) {
let file = xrf . getFile ( url )
model . file = file
// eval embedded XR fragments
model . scene . traverse ( ( mesh ) => xrf . eval . mesh ( mesh , model ) )
// add animations
model . clock = new THREE . Clock ( ) ;
model . mixer = new THREE . AnimationMixer ( model . scene )
model . animations . map ( ( anim ) => model . mixer . clipAction ( anim ) . play ( ) )
model . render = function ( ) {
model . mixer . update ( model . clock . getDelta ( ) )
2023-06-08 17:45:21 +02:00
xrf . navigator . material . selection . color . r = ( 1.0 + Math . sin ( model . clock . getElapsedTime ( ) * 10 ) ) / 2
2023-06-07 17:42:21 +02:00
}
}
xrf . getLastModel = ( ) => xrf . model . last
2023-06-08 17:45:21 +02:00
xrf . eval = function ( url , model , flags ) { // evaluate local toplevel url
2023-06-07 17:42:21 +02:00
let notice = false
model = model || xrf . model
let { THREE , camera } = xrf
2023-06-08 17:45:21 +02:00
let frag = xrf . URI . parse ( url , flags || xrf . XRF . NAVIGATOR )
for ( let k in frag ) {
let opts = { frag , mesh : xrf . camera , model , camera : xrf . camera , scene : xrf . scene , renderer : xrf . renderer , THREE : xrf . THREE }
xrf . emit ( 'eval' , opts )
. then ( ( ) => xrf . eval . fragment ( k , opts ) )
2023-06-07 17:42:21 +02:00
}
}
2023-06-08 17:45:21 +02:00
xrf . eval . mesh = ( mesh , model ) => { // evaluate embedded fragments (metadata) inside mesh of model
2023-06-07 17:42:21 +02:00
if ( mesh . userData ) {
let frag = { }
for ( let k in mesh . userData ) xrf . Parser . parse ( k , mesh . userData [ k ] , frag )
for ( let k in frag ) {
let opts = { frag , mesh , model , camera : xrf . camera , scene : xrf . scene , renderer : xrf . renderer , THREE : xrf . THREE }
mesh . userData . XRF = frag // allow fragment impl to access XRF obj already
2023-06-08 17:45:21 +02:00
xrf . emit ( 'eval' , opts )
. then ( ( ) => xrf . eval . fragment ( k , opts ) )
2023-06-07 17:42:21 +02:00
}
}
}
xrf . eval . fragment = ( k , opts ) => {
// call native function (xrf/env.js e.g.), or pass it to user decorator
let func = xrf . frag [ k ] || function ( ) { }
if ( xrf [ k ] ) xrf [ k ] ( func , opts . frag [ k ] , opts )
else func ( opts . frag [ k ] , opts )
}
xrf . reset = ( ) => {
const disposeObject = ( obj ) => {
if ( obj . children . length > 0 ) obj . children . forEach ( ( child ) => disposeObject ( child ) ) ;
if ( obj . geometry ) obj . geometry . dispose ( ) ;
if ( obj . material ) {
if ( obj . material . map ) obj . material . map . dispose ( ) ;
obj . material . dispose ( ) ;
}
obj . clear ( )
obj . removeFromParent ( )
return true
} ;
let nodes = [ ]
xrf . scene . traverse ( ( child ) => child . isXRF ? nodes . push ( child ) : false )
nodes . map ( disposeObject ) // leave non-XRF objects intact
xrf . interactive = xrf . InteractiveGroup ( xrf . THREE , xrf . renderer , xrf . camera )
xrf . add ( xrf . interactive )
}
xrf . parseUrl = ( url ) => {
const urlObj = new URL ( url . match ( /:\/\// ) ? url : String ( ` https://fake.com/ ${ url } ` ) . replace ( /\/\// , '/' ) )
let dir = url . substring ( 0 , url . lastIndexOf ( '/' ) + 1 )
const file = urlObj . pathname . substring ( urlObj . pathname . lastIndexOf ( '/' ) + 1 ) ;
const hash = url . match ( /#/ ) ? url . replace ( /.*#/ , '' ) : ''
const ext = file . split ( '.' ) . pop ( )
return { urlObj , dir , file , hash , ext }
}
xrf . add = ( object ) => {
object . isXRF = true // mark for easy deletion when replacing scene
xrf . scene . add ( object )
}
xrf . navigator = { }
xrf . navigator . to = ( url , event ) => {
if ( ! url ) throw 'xrf.navigator.to(..) no url given'
return new Promise ( ( resolve , reject ) => {
let { urlObj , dir , file , hash , ext } = xrf . parseUrl ( url )
console . log ( "xrfragment: navigating to " + url )
if ( ! file || xrf . model . file == file ) { // we're already loaded
document . location . hash = ` # ${ hash } ` // just update the hash
2023-06-08 17:45:21 +02:00
xrf . eval ( url , xrf . model ) // and eval local URI XR fragments
2023-06-07 17:42:21 +02:00
return resolve ( xrf . model )
}
if ( xrf . model && xrf . model . scene ) xrf . model . scene . visible = false
const Loader = xrf . loaders [ ext ]
if ( ! Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .' + ext
xrf . reset ( ) // clear xrf objects from scene
// force relative path
if ( dir ) dir = dir [ 0 ] == '.' ? dir : ` . ${ dir } `
const loader = new Loader ( ) . setPath ( dir )
loader . load ( file , function ( model ) {
model . file = file
xrf . add ( model . scene )
xrf . model = model
xrf . eval ( url , model ) // and eval URI XR fragments
xrf . navigator . pushState ( ` ${ dir } ${ file } ` , hash )
resolve ( model )
} )
} )
}
xrf . navigator . init = ( ) => {
if ( xrf . navigator . init . inited ) return
window . addEventListener ( 'popstate' , function ( event ) {
xrf . navigator . to ( document . location . search . substr ( 1 ) + document . location . hash , event )
} )
2023-06-08 17:45:21 +02:00
xrf . navigator . material = {
selection : new xrf . THREE . LineBasicMaterial ( { color : 0xFF00FF , linewidth : 2 } )
}
2023-06-07 17:42:21 +02:00
xrf . navigator . init . inited = true
}
xrf . navigator . pushState = ( file , hash ) => {
if ( file == document . location . search . substr ( 1 ) ) return // page is in its default state
window . history . pushState ( { } , ` ${ file } # ${ hash } ` , document . location . pathname + ` ? ${ file } # ${ hash } ` )
}
xrf . frag . env = function ( v , opts ) {
let { mesh , model , camera , scene , renderer , THREE } = opts
let env = mesh . getObjectByName ( v . string )
env . material . map . mapping = THREE . EquirectangularReflectionMapping ;
scene . environment = env . material . map
//scene.texture = env.material.map
renderer . toneMapping = THREE . ACESFilmicToneMapping ;
renderer . toneMappingExposure = 2 ;
// apply to meshes *DISABLED* renderer.environment does this
const maxAnisotropy = renderer . capabilities . getMaxAnisotropy ( ) ;
setTimeout ( ( ) => {
scene . traverse ( ( mesh ) => {
//if (mesh.material && mesh.material.map && mesh.material.metalness == 1.0) {
// mesh.material = new THREE.MeshBasicMaterial({ map: mesh.material.map });
// mesh.material.dithering = true
// mesh.material.map.anisotropy = maxAnisotropy;
// mesh.material.needsUpdate = true;
//}
//if (mesh.material && mesh.material.metalness == 1.0 ){
// mesh.material = new THREE.MeshBasicMaterial({
// color:0xffffff,
// emissive: mesh.material.map,
// envMap: env.material.map,
// side: THREE.DoubleSide,
// flatShading: true
// })
// mesh.material.needsUpdate = true
// //mesh.material.envMap = env.material.map;
// //mesh.material.envMap.intensity = 5;
// //mesh.material.needsUpdate = true;
//}
} ) ;
} , 500 )
console . log ( ` └ applied image ' ${ v . string } ' as environment map ` )
}
/ * *
*
* navigation , portals & mutations
*
* | fragment | type | scope | example value |
* | ` href ` | string ( uri or predefined view ) | 🔒 | ` #pos=1,1,0 ` < br > ` #pos=1,1,0&rot=90,0,0 ` < br > ` #pos=pyramid ` < br > ` #pos=lastvisit|pyramid ` < br > ` ://somefile.gltf#pos=1,1,0 ` < br > |
*
* [ [ » example implementation | https : //github.com/coderofsalvation/xrfragment/blob/main/src/3rd/three/xrf/href.js]]<br>
* [ [ » example 3 D asset | https : //github.com/coderofsalvation/xrfragment/blob/main/example/assets/href.gltf#L192]]<br>
* [ [ » discussion | https : //github.com/coderofsalvation/xrfragment/issues/1]]<br>
*
* [ img [ xrfragment . jpg ] ]
*
*
* ! ! ! spec 1.0
*
* 1. an '' external '' - or '' file URI '' fully replaces the current scene and assumes ` pos=0,0,0&rot=0,0,0 ` by default ( unless specified )
*
* 2. navigation should not happen when queries ( ` q= ` ) are present in local url : queries will apply ( ` pos= ` , ` rot= ` e . g . ) to the targeted object ( s ) instead .
*
* 3. navigation should not happen '' immediately '' when user is more than 2 meter away from the portal / object containing the href ( to prevent accidental navigation e . g . )
*
* 4. URL navigation should always be reflected in the client ( in case of javascript : see [ [ here | https : //github.com/coderofsalvation/xrfragment/blob/dev/src/3rd/three/navigator.js]] for an example navigator).
*
* 5. In XR mode , the navigator back / forward - buttons should be always visible ( using a wearable e . g . , see [ [ here | https : //github.com/coderofsalvation/xrfragment/blob/dev/example/aframe/sandbox/index.html#L26-L29]] for an example wearable)
*
* [ img [ navigation . png ] ]
*
* /
xrf . frag . href = function ( v , opts ) {
2023-06-08 17:45:21 +02:00
opts . embedded = v // indicate embedded XR fragment
2023-06-07 17:42:21 +02:00
let { mesh , model , camera , scene , renderer , THREE } = opts
const world = {
pos : new THREE . Vector3 ( ) ,
scale : new THREE . Vector3 ( ) ,
quat : new THREE . Quaternion ( )
}
// detect equirectangular image
let texture = mesh . material . map
if ( texture && texture . source . data . height == texture . source . data . width / 2 ) {
texture . mapping = THREE . ClampToEdgeWrapping
texture . needsUpdate = true
// poor man's equi-portal
mesh . material = new THREE . ShaderMaterial ( {
side : THREE . DoubleSide ,
uniforms : {
pano : { value : texture } ,
selected : { value : false } ,
} ,
vertexShader : `
vec3 portalPosition ;
varying vec3 vWorldPosition ;
varying float vDistanceToCenter ;
varying float vDistance ;
void main ( ) {
vDistanceToCenter = clamp ( length ( position - vec3 ( 0.0 , 0.0 , 0.0 ) ) , 0.0 , 1.0 ) ;
portalPosition = ( modelMatrix * vec4 ( 0.0 , 0.0 , 0.0 , 1.0 ) ) . xyz ;
vDistance = length ( portalPosition - cameraPosition ) ;
vWorldPosition = ( modelMatrix * vec4 ( position , 1.0 ) ) . xyz ;
gl _Position = projectionMatrix * modelViewMatrix * vec4 ( position , 1.0 ) ;
}
` ,
fragmentShader : `
# define RECIPROCAL _PI2 0.15915494
uniform sampler2D pano ;
uniform bool selected ;
varying float vDistanceToCenter ;
varying float vDistance ;
varying vec3 vWorldPosition ;
void main ( ) {
vec3 direction = normalize ( vWorldPosition - cameraPosition ) ;
vec2 sampleUV ;
sampleUV . y = - clamp ( direction . y * 0.5 + 0.5 , 0.0 , 1.0 ) ;
sampleUV . x = atan ( direction . z , - direction . x ) * - RECIPROCAL _PI2 ;
sampleUV . x += 0.33 ; // adjust focus to AFRAME's a-scene.components.screenshot.capture()
vec4 color = texture2D ( pano , sampleUV ) ;
// Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js)
float luminance = 0.2126 * color . r + 0.7152 * color . g + 0.0722 * color . b ;
vec4 grayscale _color = selected ? color : vec4 ( vec3 ( luminance ) + vec3 ( 0.33 ) , color . a ) ;
gl _FragColor = grayscale _color ;
}
` ,
} ) ;
mesh . material . needsUpdate = true
} else mesh . material = mesh . material . clone ( )
let click = mesh . userData . XRF . href . exec = ( e ) => {
2023-06-08 17:45:21 +02:00
xrf
. emit ( 'href' , { click : true , mesh , xrf : v } ) // let all listeners agree
. then ( ( ) => {
if ( v . string [ 0 ] == '#' ) { // apply modifications to scene
xrf . eval ( v . string , xrf . model , xrf . XRF . PV _OVERRIDE )
} else xrf . navigator . to ( v . string ) // or let's surf to HREF!
} )
2023-06-07 17:42:21 +02:00
}
let selected = ( state ) => ( ) => {
if ( mesh . selected == state ) return // nothing changed
if ( mesh . material . uniforms ) mesh . material . uniforms . selected . value = state
else mesh . material . color . r = mesh . material . color . g = mesh . material . color . b = state ? 2.0 : 1.0
// update mouse cursor
if ( ! renderer . domElement . lastCursor )
renderer . domElement . lastCursor = renderer . domElement . style . cursor
renderer . domElement . style . cursor = state ? 'pointer' : renderer . domElement . lastCursor
xrf
. emit ( 'href' , { selected : state , mesh , xrf : v } ) // let all listeners agree
. then ( ( ) => mesh . selected = state )
}
mesh . addEventListener ( 'click' , click )
mesh . addEventListener ( 'mousemove' , selected ( true ) )
mesh . addEventListener ( 'nocollide' , selected ( false ) )
// lazy add mesh (because we're inside a recursive traverse)
setTimeout ( ( mesh ) => {
mesh . getWorldPosition ( world . pos )
mesh . getWorldScale ( world . scale )
mesh . getWorldQuaternion ( world . quat ) ;
mesh . position . copy ( world . pos )
mesh . scale . copy ( world . scale )
mesh . setRotationFromQuaternion ( world . quat ) ;
xrf . interactive . add ( mesh )
} , 20 , mesh )
}
/ * *
* > above solutions were abducted from [ [ this | https : //i.imgur.com/E3En0gJ.png]] and [[this|https://i.imgur.com/lpnTz3A.png]] survey result
*
* ! ! ! Demo
*
* < $videojs controls = "controls" aspectratio = "16:9" preload = "auto" poster = "" fluid = "fluid" class = "vjs-big-play-centered" >
* < source src = "https://coderofsalvation.github.io/xrfragment.media/href.mp4" type = "video/mp4" / >
* < / $ v i d e o j s >
*
* > capture of < a href = "./example/aframe/sandbox" target = "_blank" > aframe / sandbox < / a >
* /
xrf . frag . pos = function ( v , opts ) {
let { frag , mesh , model , camera , scene , renderer , THREE } = opts
console . log ( " └ setting camera position to " + v . string )
if ( ! frag . q ) {
2023-06-08 17:45:21 +02:00
camera . position . x = v . x
camera . position . y = v . y
camera . position . z = v . z
}
}
const doPredefinedView = ( opts ) => {
let { frag , scene } = opts
console . dir ( opts )
const selectionOfInterest = ( id , scene , mesh ) => {
// Selection of Interest if predefined_view matches object name
if ( mesh . selection ) {
scene . remove ( mesh . selection )
delete mesh . selection
2023-06-07 17:42:21 +02:00
}
2023-06-08 17:45:21 +02:00
if ( id == mesh . name || id . substr ( 1 ) == mesh . userData . class ) {
xrf . emit ( 'selection' , opts )
. then ( ( ) => {
const margin = 1.2
console . dir ( mesh . scale . x )
mesh . scale . multiplyScalar ( margin )
mesh . selection = new xrf . THREE . BoxHelper ( mesh , 0xff00ff )
mesh . scale . divideScalar ( margin )
mesh . selection . material . dispose ( )
mesh . selection . material = xrf . navigator . material . selection
mesh . selection . isXRF = true
scene . add ( mesh . selection )
} )
}
}
2023-06-07 17:42:21 +02:00
2023-06-08 17:45:21 +02:00
const predefinedView = ( id , scene , mesh ) => {
if ( mesh . userData [ id ] ) {
let frag = xrf . URI . parse ( mesh . userData [ id ] , xrf . XRF . NAVIGATOR | xrf . XRF . PV _OVERRIDE | xrf . XRF . EMBEDDED )
for ( let k in frag ) {
let opts = { frag , model , camera : xrf . camera , scene : xrf . scene , renderer : xrf . renderer , THREE : xrf . THREE }
console . log ( k )
xrf . emit ( 'predefinedView' , opts )
. then ( ( ) => xrf . eval . fragment ( k , opts ) )
}
2023-06-07 17:42:21 +02:00
}
2023-06-08 17:45:21 +02:00
}
2023-06-07 17:42:21 +02:00
2023-06-08 17:45:21 +02:00
for ( let i in frag ) {
let v = frag [ i ]
if ( v . is ( xrf . XRF . PV _EXECUTE ) ) {
// wait for nested instances to arrive at the scene
setTimeout ( ( ) => {
if ( ! scene ) return
scene . traverse ( ( mesh ) => {
selectionOfInterest ( v . fragment , scene , mesh )
predefinedView ( v . fragment , scene , mesh )
} )
} , 100 )
}
2023-06-07 17:42:21 +02:00
}
}
2023-06-08 17:45:21 +02:00
// when predefined view occurs in url changes
xrf . addEventListener ( 'eval' , doPredefinedView )
// clicking href url with predefined view
xrf . addEventListener ( 'href' , ( opts ) => {
if ( ! opts . click || opts . xrf . string [ 0 ] != '#' ) return
let frag = xrf . URI . parse ( opts . xrf . string , xrf . XRF . NAVIGATOR | xrf . XRF . PV _OVERRIDE | xrf . XRF . EMBEDDED )
doPredefinedView ( { frag , scene : xrf . scene } )
} )
2023-06-07 17:42:21 +02:00
xrf . frag . q = function ( v , opts ) {
let { frag , mesh , model , camera , scene , renderer , THREE } = opts
console . log ( " └ running query " )
let qobjs = Object . keys ( v . query )
2023-06-08 17:45:21 +02:00
const instanceScene = ( ) => {
v . scene = new THREE . Group ( )
for ( let i in v . query ) {
let target = v . query [ i ]
if ( ! scene . getObjectByName ( i ) && i != '*' ) return console . log ( ` └ mesh not found: ${ i } ` )
if ( i == '*' ) {
let cloneScene = scene . clone ( )
cloneScene . children . forEach ( ( child ) => v . scene . getObjectByName ( child . name ) ? null : v . scene . add ( child ) )
target . mesh = v . scene
} else {
console . log ( ` └ query-ing mesh: ${ i } ` )
if ( ! v . scene . getObjectByName ( i ) && target . id === true ) {
v . scene . add ( target . mesh = scene . getObjectByName ( i ) . clone ( ) )
}
}
if ( target . id != undefined && target . mesh ) {
target . mesh . position . set ( 0 , 0 , 0 )
target . mesh . rotation . set ( 0 , 0 , 0 )
2023-06-07 17:42:21 +02:00
}
}
2023-06-08 17:45:21 +02:00
// remove negative selectors
let remove = [ ]
v . scene . traverse ( ( mesh ) => {
for ( let i in v . query ) {
if ( mesh . name == i && v . query [ i ] . id === false ) remove . push ( mesh )
}
} )
remove . map ( ( mesh ) => mesh . parent . remove ( mesh ) )
2023-06-07 17:42:21 +02:00
}
2023-06-08 17:45:21 +02:00
const showHide = ( ) => {
let q = frag . q . query
scene . traverse ( ( mesh ) => {
for ( let i in q ) {
if ( i == mesh . name && q [ i ] . id != undefined ) mesh . visible = q [ i ] . id
if ( i == mesh . userData . class && q [ i ] . class != undefined ) mesh . visible = q [ i ] . class
}
} )
}
if ( opts . embedded && opts . embedded . fragment == "src" ) instanceScene ( )
else showHide ( ) // href
2023-06-07 17:42:21 +02:00
}
xrf . frag . rot = function ( v , opts ) {
let { mesh , model , camera , scene , renderer , THREE } = opts
console . log ( " └ setting camera rotation to " + v . string )
camera . rotation . set (
v . x * Math . PI / 180 ,
v . y * Math . PI / 180 ,
v . z * Math . PI / 180
)
camera . updateMatrixWorld ( )
}
// *TODO* use webgl instancing
xrf . frag . src = function ( v , opts ) {
2023-06-08 17:45:21 +02:00
opts . embedded = v // indicate embedded XR fragment
2023-06-07 17:42:21 +02:00
let { mesh , model , camera , scene , renderer , THREE } = opts
let src = new THREE . Group ( )
if ( v . string [ 0 ] == "#" ) { // local
console . log ( " └ instancing src" )
let frag = xrfragment . URI . parse ( v . string )
// apply embedded XR fragments
setTimeout ( ( ) => {
// apply URI XR Fragments inside src-value
for ( var i in frag ) {
xrf . eval . fragment ( i , Object . assign ( opts , { frag , model , scene } ) )
}
if ( frag . q . query ) {
let srcScene = frag . q . scene
if ( ! srcScene || ! srcScene . visible ) return
console . log ( " └ inserting " + i + " (srcScene)" )
srcScene . position . set ( 0 , 0 , 0 )
srcScene . rotation . set ( 0 , 0 , 0 )
srcScene . traverse ( ( m ) => {
if ( m . userData && ( m . userData . src || m . userData . href ) ) return ; //delete m.userData.src // prevent infinite recursion
xrf . eval . mesh ( m , { scene , recursive : true } )
} )
if ( srcScene . visible ) src . add ( srcScene )
}
src . position . copy ( mesh . position )
src . rotation . copy ( mesh . rotation )
src . scale . copy ( mesh . scale )
mesh . add ( src )
console . dir ( opts )
if ( ! opts . recursive ) mesh . material . visible = false // lets hide the preview object because deleting disables animations
} , 10 )
}
}
2023-06-08 17:45:21 +02:00
export default xrf ;