major refactor
This commit is contained in:
parent
a01fb08e5d
commit
d757dab4e0
|
@ -0,0 +1,21 @@
|
||||||
|
name: Deploy to GitHub Pages
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
ref: main
|
||||||
|
- name: Build
|
||||||
|
run: cp test/test.html doc/.
|
||||||
|
working-directory: ./
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: ./doc
|
|
@ -1,11 +1,6 @@
|
||||||
package xrfragment;
|
package xrfragment;
|
||||||
|
|
||||||
/**
|
import xrfragment.XRF;
|
||||||
* # XR Fragments (key/value params)
|
|
||||||
*
|
|
||||||
* > ⛁ = define in 3D asset-file (as custom property or default projection)<br>
|
|
||||||
* > ☇ = mutable, using navigator URI (`document.location.href` e.g.)<br>
|
|
||||||
*/
|
|
||||||
|
|
||||||
@:expose // <- makes the class reachable from plain JavaScript
|
@:expose // <- makes the class reachable from plain JavaScript
|
||||||
@:keep // <- avoids accidental removal by dead code elimination
|
@:keep // <- avoids accidental removal by dead code elimination
|
||||||
|
@ -14,14 +9,51 @@ class Parser {
|
||||||
|
|
||||||
@:keep
|
@:keep
|
||||||
public static function parse(key:String,value:String,resultMap:haxe.DynamicAccess<Dynamic>):Bool {
|
public static function parse(key:String,value:String,resultMap:haxe.DynamicAccess<Dynamic>):Bool {
|
||||||
var Frag:Map<String, EReg> = new Map<String, EReg>(); // | param | type | scope(s) | category | notes |
|
|
||||||
// |---------|---------------|-------|--------------------|---------------------------------|
|
|
||||||
Frag.set("prio", Type.isInt); // | prio | int (-10..1) | ⛁ | Asset loading / linking | $(cat doc/notes/prio.md) |
|
|
||||||
|
|
||||||
Frag.set("pos", Type.isVector); // | pos | 3D vector | ⛁ ☇ |HREF navigation/portals | |
|
// here we define allowed characteristics & datatypes for each fragment (stored as bitmasked int for performance purposes)
|
||||||
Frag.set("q", Type.isString); // | q | string | ⛁ |Query Selector | |
|
var Frag:Map<String, Int> = new Map<String, Int>();
|
||||||
var vec:String = "1,2,3"; //
|
|
||||||
//if( Type.isVector(vec) ) trace("ja");
|
// category: asset loading linking
|
||||||
|
Frag.set("prio", XRF.ASSET_OBJ | XRF.T_INT );
|
||||||
|
Frag.set("#", XRF.ASSET | XRF.T_PREDEFINED_VIEW );
|
||||||
|
Frag.set("class", XRF.ASSET_OBJ | XRF.T_STRING );
|
||||||
|
Frag.set("src", XRF.ASSET_OBJ | XRF.T_URL );
|
||||||
|
Frag.set("src_audio", XRF.ASSET_OBJ | XRF.T_URL );
|
||||||
|
Frag.set("src_shader", XRF.ASSET_OBJ | XRF.T_URL );
|
||||||
|
Frag.set("src_env", XRF.ASSET | XRF.T_URL );
|
||||||
|
Frag.set("src_env_audio", XRF.ASSET | XRF.T_URL );
|
||||||
|
|
||||||
|
// category: href navigation / portals / teleporting
|
||||||
|
Frag.set("pos", XRF.PV_OVERRIDE | XRF.ROUNDROBIN | XRF.T_VECTOR3 | XRF.T_STRING_OBJ );
|
||||||
|
Frag.set("href", XRF.ASSET_OBJ | XRF.T_URL | XRF.T_PREDEFINED_VIEW );
|
||||||
|
|
||||||
|
// category: query selector | object manipulation
|
||||||
|
Frag.set("q", XRF.PV_OVERRIDE | XRF.T_STRING );
|
||||||
|
Frag.set("scale", XRF.QUERY_OPERATOR | XRF.PV_OVERRIDE | XRF.ROUNDROBIN | XRF.T_INT );
|
||||||
|
Frag.set("rot", XRF.QUERY_OPERATOR | XRF.PV_OVERRIDE | XRF.ROUNDROBIN | XRF.T_VECTOR3 );
|
||||||
|
Frag.set("translate", XRF.QUERY_OPERATOR | XRF.PV_OVERRIDE | XRF.ROUNDROBIN | XRF.T_VECTOR3 );
|
||||||
|
Frag.set("visible", XRF.QUERY_OPERATOR | XRF.PV_OVERRIDE | XRF.ROUNDROBIN | XRF.T_INT );
|
||||||
|
|
||||||
|
// category: animation
|
||||||
|
Frag.set("t", XRF.ASSET | XRF.PV_OVERRIDE | XRF.ROUNDROBIN | XRF.T_VECTOR2 | XRF.BROWSER_OVERRIDE );
|
||||||
|
Frag.set("gravity", XRF.ASSET | XRF.PV_OVERRIDE | XRF.T_VECTOR3 );
|
||||||
|
Frag.set("physics", XRF.ASSET | XRF.PV_OVERRIDE | XRF.T_VECTOR3 );
|
||||||
|
Frag.set("scroll", XRF.ASSET | XRF.PV_OVERRIDE | XRF.T_STRING );
|
||||||
|
|
||||||
|
// category: device / viewport settings
|
||||||
|
Frag.set("fov", XRF.ASSET | XRF.PV_OVERRIDE | XRF.T_INT | XRF.BROWSER_OVERRIDE );
|
||||||
|
Frag.set("clip", XRF.ASSET | XRF.PV_OVERRIDE | XRF.T_VECTOR2 | XRF.BROWSER_OVERRIDE );
|
||||||
|
Frag.set("fog", XRF.ASSET | XRF.PV_OVERRIDE | XRF.T_STRING | XRF.BROWSER_OVERRIDE );
|
||||||
|
|
||||||
|
// category: author / metadata
|
||||||
|
Frag.set("namespace", XRF.ASSET | XRF.T_STRING );
|
||||||
|
Frag.set("SPFX", 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("src_session", XRF.ASSET | XRF.T_URL | XRF.PV_OVERRIDE | XRF.BROWSER_OVERRIDE | XRF.PROMPT );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* # XR Fragments parser
|
* # XR Fragments parser
|
||||||
*
|
*
|
||||||
|
@ -29,65 +61,19 @@ class Parser {
|
||||||
* the gist of it:
|
* the gist of it:
|
||||||
*/
|
*/
|
||||||
if( Frag.exists(key) ){ // 1. check if param exist
|
if( Frag.exists(key) ){ // 1. check if param exist
|
||||||
if( Frag.get(key).match(value) ){ // 1. each key has a regex to validate its value-type (see regexes)
|
var v:XRF = new XRF(key, Frag.get(key));
|
||||||
var v:Value = new Value();
|
if( !v.validate(value) ){
|
||||||
guessType(v, value); // 1. extract the type
|
trace("[ i ] fragment '"+key+"' has incompatible value ("+value+")");
|
||||||
// process multiple values
|
return false;
|
||||||
if( value.split("|").length > 1 ){ // 1. use `|` on stringvalues, to split multiple values
|
}
|
||||||
v.args = new Array<Value>();
|
resultMap.set(key, v );
|
||||||
var args:Array<String> = value.split("|");
|
}else{ trace("[ i ] fragment '"+key+"' does not exist or has no type typed (yet)"); return false; }
|
||||||
for( i in 0...args.length){
|
|
||||||
var x:Value = new Value();
|
|
||||||
guessType(x, args[i]); // 1. for each multiple value, guess the type
|
|
||||||
v.args.push( x );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resultMap.set(key, v );
|
|
||||||
}else { trace("[ i ] fragment '"+key+"' has incompatible value ("+value+")"); return false; }
|
|
||||||
}else { trace("[ i ] fragment '"+key+"' does not exist or has no type defined (yet)"); return false; }
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@:keep
|
}
|
||||||
public static function guessType(v:Value, str:String):Void {
|
|
||||||
v.string = str;
|
|
||||||
if( str.split(",").length > 1){ // 1. `,` assumes 1D/2D/3D vector-values like x[,y[,z]]
|
|
||||||
var xyz:Array<String> = str.split(","); // 1. parseFloat(..) and parseInt(..) is applied to vector/float and int values
|
|
||||||
if( xyz.length > 0 ) v.x = Std.parseFloat(xyz[0]); // 1. anything else will be treated as string-value
|
|
||||||
if( xyz.length > 1 ) v.y = Std.parseFloat(xyz[1]); // 1. incompatible value-types will be dropped / not used
|
|
||||||
if( xyz.length > 2 ) v.y = Std.parseFloat(xyz[2]); //
|
|
||||||
} // > the xrfragment specification should stay simple enough
|
|
||||||
// > for anyone to write a parser using either regexes or grammar/lexers
|
|
||||||
if( Type.isColor.match(str) ) v.color = str; // > therefore expressions/comprehensions are not supported (max wildcard/comparison operators for queries e.g.)
|
|
||||||
if( Type.isFloat.match(str) ) v.float = Std.parseFloat(str);
|
|
||||||
if( Type.isInt.match(str) ) v.int = Std.parseInt(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// # Parser Value types
|
|
||||||
//
|
|
||||||
// | type | info | format | example |
|
|
||||||
class Value { // |------|------|--------|----------------------------------|
|
|
||||||
public var x:Float; // |vector| x,y,z| comma-separated | #pos=1,2,3 |
|
|
||||||
public var y:Float;
|
|
||||||
public var z: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 args:Array<Value>; // |array | mixed| \|-separated | #pos=0,0,0\|90,0,0 |
|
|
||||||
public function new(){} //
|
|
||||||
// > rule for thumb: type-limitations will piggyback JSON limitations (IEEE floatsize e.g.)
|
|
||||||
}
|
|
||||||
// Regexes:
|
|
||||||
class Type { //
|
|
||||||
static public 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})$/`
|
|
||||||
static public var isInt:EReg = ~/^[0-9]+$/; // 1. integers are detected using regex `/^[0-9]+$/`
|
|
||||||
static public var isFloat:EReg = ~/^[0-9]+\.[0-9]+$/; // 1. floats are detected using regex `/^[0-9]+\.[0-9]+$/`
|
|
||||||
static public var isVector:EReg = ~/([,]+|\w)/; // 1. vectors are detected using regex `/[,]/` (but can also be an string referring to an entity-ID in the asset)
|
|
||||||
static public var isString:EReg = ~/.*/; // 1. anything else is string `/.*/`
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Tests
|
/// # Tests
|
||||||
///
|
///
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
XF = {}
|
||||||
|
|
||||||
|
function split (inputstr, sep)
|
||||||
|
if sep == nil then
|
||||||
|
sep = "%s"
|
||||||
|
end
|
||||||
|
local t={}
|
||||||
|
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
|
||||||
|
table.insert(t, str)
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function XF.parse(key, value, resultMap)
|
||||||
|
local frag = {}
|
||||||
|
frag["prio"] = "^%d+$"
|
||||||
|
frag["pos"] = "(%d+),(%d+),(%d+)"
|
||||||
|
frag["q"] = ".+"
|
||||||
|
|
||||||
|
if frag[key] ~= nil then
|
||||||
|
local regex = frag[key]
|
||||||
|
if string.match(value, regex) then
|
||||||
|
local v = Value:new()
|
||||||
|
XF.guessType(v, value)
|
||||||
|
if string.find(value, "|") then
|
||||||
|
v.args = {}
|
||||||
|
local args = split(value, "|")
|
||||||
|
for k, arg in ipairs(args) do
|
||||||
|
local x = Value:new()
|
||||||
|
XF.guessType(x, arg)
|
||||||
|
table.insert(v.args, x)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resultMap[key] = v;
|
||||||
|
else
|
||||||
|
error("[ i ] fragment '"..key.."' has incompatible value ("..value..")")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
error("[ i ] fragment '"..key.."' does not exist or has no type defined (yet)")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function XF.guessType(v, str)
|
||||||
|
v.string = str
|
||||||
|
local parts = split(str, ",")
|
||||||
|
if #parts > 1 then
|
||||||
|
v.x = tonumber(parts[1])
|
||||||
|
v.y = tonumber(parts[2])
|
||||||
|
if #parts > 2 then
|
||||||
|
v.z = tonumber(parts[3])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if string.match(str, Type.isColor) then
|
||||||
|
v.color = str
|
||||||
|
end
|
||||||
|
if string.match(str, Type.isFloat) then
|
||||||
|
v.float = tonumber(str)
|
||||||
|
end
|
||||||
|
if string.match(str, Type.isInt) then
|
||||||
|
v.int_val = tonumber(str)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Value = {}
|
||||||
|
|
||||||
|
function Value:new()
|
||||||
|
local obj = {}
|
||||||
|
obj.x = nil
|
||||||
|
obj.y = nil
|
||||||
|
obj.z = nil
|
||||||
|
obj.color = nil
|
||||||
|
obj.string = nil
|
||||||
|
obj.int_val = nil
|
||||||
|
obj.float = nil
|
||||||
|
obj.args = nil
|
||||||
|
setmetatable(obj, self)
|
||||||
|
self.__index = self
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
Type = {}
|
||||||
|
Type.isColor = "^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"
|
||||||
|
Type.isInt = "^[0-9]+$"
|
||||||
|
Type.isFloat = "^[0-9]+%.[0-9]+$"
|
||||||
|
Type.isVector = "([,]+|%w)"
|
||||||
|
|
||||||
|
local map = {}
|
||||||
|
XF.parse("pos","1,2,3",map)
|
||||||
|
print(map.pos.z)
|
||||||
|
XF.parse("pos","1,2,3|3,4,5",map)
|
||||||
|
print(map.pos.args[2].z)
|
|
@ -59,6 +59,10 @@ class Query {
|
||||||
return classAlias.match(token) ? StringTools.replace(token,".","class:") : token;
|
return classAlias.match(token) ? StringTools.replace(token,".","class:") : token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function get() : Dynamic {
|
||||||
|
return this.q;
|
||||||
|
}
|
||||||
|
|
||||||
public function parse(str:String,recurse:Bool = false) : Dynamic {
|
public function parse(str:String,recurse:Bool = false) : Dynamic {
|
||||||
|
|
||||||
var token = str.split(" ");
|
var token = str.split(" ");
|
||||||
|
@ -103,8 +107,7 @@ class Query {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for( i in 0...token.length ) process( expandAliases(token[i]) );
|
for( i in 0...token.length ) process( expandAliases(token[i]) );
|
||||||
this.q = q;
|
return this.q = q;
|
||||||
return this.q;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@:keep
|
@:keep
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package xrfragment;
|
package xrfragment;
|
||||||
|
|
||||||
import xrfragment.Parser;
|
import xrfragment.Parser;
|
||||||
|
import xrfragment.XRF;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <link rel="stylesheet" href="style.css"/>
|
* <link rel="stylesheet" href="style.css"/>
|
||||||
|
@ -41,7 +42,7 @@ import xrfragment.Parser;
|
||||||
@:keep // <- avoids accidental removal by dead code elimination
|
@:keep // <- avoids accidental removal by dead code elimination
|
||||||
class URI {
|
class URI {
|
||||||
@:keep
|
@:keep
|
||||||
public static function parse(qs:String):haxe.DynamicAccess<Dynamic> {
|
public static function parse(qs:String,browser_override:Bool):haxe.DynamicAccess<Dynamic> {
|
||||||
var fragment:Array<String> = qs.split("#"); // 1. fragment URI starts with `#`
|
var fragment:Array<String> = qs.split("#"); // 1. fragment URI starts with `#`
|
||||||
var splitArray:Array<String> = fragment[1].split('&'); // 1. fragments are split by `&`
|
var splitArray:Array<String> = fragment[1].split('&'); // 1. fragments are split by `&`
|
||||||
var resultMap:haxe.DynamicAccess<Dynamic> = {}; // 1. store key/values into a associative array or dynamic object
|
var resultMap:haxe.DynamicAccess<Dynamic> = {}; // 1. store key/values into a associative array or dynamic object
|
||||||
|
@ -56,6 +57,12 @@ class URI {
|
||||||
var ok:Bool = Parser.parse(key,value,resultMap); // 1. every recognized fragment key/value-pair is added to a central map/associative array/object
|
var ok:Bool = Parser.parse(key,value,resultMap); // 1. every recognized fragment key/value-pair is added to a central map/associative array/object
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if( browser_override ){
|
||||||
|
for (key in resultMap.keys()) {
|
||||||
|
var xrf:XRF = resultMap.get(key);
|
||||||
|
if( !xrf.is( XRF.BROWSER_OVERRIDE ) ) resultMap.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
return resultMap;
|
return resultMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
package xrfragment;
|
||||||
|
|
||||||
|
class XRF {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this class represents a fragment (value)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// public static inline readonly ASSET
|
||||||
|
public static var ASSET:Int = 1; // fragment is immutable (typed in asset) globally
|
||||||
|
public static var ASSET_OBJ:Int = 2; // fragment is immutable (typed in object in asset)
|
||||||
|
public static var PV_OVERRIDE:Int = 4; // fragment can be overriden when specified in predefined view value
|
||||||
|
public static var QUERY_OPERATOR = 8; // fragment will be applied to result of queryselecto
|
||||||
|
public static var PROMPT:Int = 16; // ask user whether this fragment value can be changed
|
||||||
|
public static var ROUNDROBIN:Int = 32; // evaluation of this (multi) value can be roundrobined
|
||||||
|
public static var BROWSER_OVERRIDE:Int = 64; // fragment can be overriden by (manual) browser URI change
|
||||||
|
|
||||||
|
// highlevel types
|
||||||
|
public static var T_COLOR:Int = 128;
|
||||||
|
public static var T_INT:Int = 256;
|
||||||
|
public static var T_FLOAT:Int = 512;
|
||||||
|
public static var T_VECTOR2:Int = 1024;
|
||||||
|
public static var T_VECTOR3:Int = 2048;
|
||||||
|
public static var T_URL:Int = 4096;
|
||||||
|
public static var T_PREDEFINED_VIEW:Int = 8192;
|
||||||
|
public static var T_STRING:Int = 16384;
|
||||||
|
public static var T_STRING_OBJ:Int = 32768;
|
||||||
|
|
||||||
|
// 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})$/`
|
||||||
|
public static var isInt:EReg = ~/^[0-9]+$/; // 1. integers are detected using regex `/^[0-9]+$/`
|
||||||
|
public static var isFloat:EReg = ~/^[0-9]+\.[0-9]+$/; // 1. floats are detected using regex `/^[0-9]+\.[0-9]+$/`
|
||||||
|
public static var isVector:EReg = ~/([,]+|\w)/; // 1. vectors are detected using regex `/[,]/` (but can also be an string referring to an entity-ID in the asset)
|
||||||
|
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 `/.*/`
|
||||||
|
|
||||||
|
// value holder(s) // |------|------|--------|----------------------------------|
|
||||||
|
public var fragment:String;
|
||||||
|
public var flags:Int;
|
||||||
|
public var x:Float; // |vector| x,y,z| comma-separated | #pos=1,2,3 |
|
||||||
|
public var y:Float;
|
||||||
|
public var z: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 args:Array<XRF>; // |array | mixed| \|-separated | #pos=0,0,0\|90,0,0 |
|
||||||
|
public var query:Query;
|
||||||
|
//
|
||||||
|
public function new(_fragment:String,_flags:Int){
|
||||||
|
fragment = _fragment;
|
||||||
|
flags = _flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function is(flag:Int):Bool {
|
||||||
|
return (flags & flag) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function set(flag:Int, flags:Int):Int {
|
||||||
|
return flags | flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function unset(flag:Int, flags:Int):Int {
|
||||||
|
return flags & ~flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(value:String) : Bool{
|
||||||
|
guessType(this, value); // 1. extract the type
|
||||||
|
// process multiple values
|
||||||
|
if( value.split("|").length > 1 ){ // 1. use `|` on stringvalues, to split multiple values
|
||||||
|
this.args = new Array<XRF>();
|
||||||
|
var args:Array<String> = value.split("|");
|
||||||
|
for( i in 0...args.length){
|
||||||
|
var x:XRF = new XRF(fragment,flags);
|
||||||
|
guessType(x, args[i]); // 1. for each multiple value, guess the type
|
||||||
|
this.args.push( x );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// special case: query has its own DSL (*TODO* allow fragments to have custom validators)
|
||||||
|
if( fragment == "q" ) query = (new Query(value)).get();
|
||||||
|
// validate
|
||||||
|
var ok:Bool = true;
|
||||||
|
if( !Std.isOfType(args,Array) ){
|
||||||
|
if( is(T_VECTOR3) && !(Std.isOfType(x,Float) && Std.isOfType(y,Float) && Std.isOfType(z,Float)) ) ok = false;
|
||||||
|
if( is(T_VECTOR2) && !(Std.isOfType(x,Float) && Std.isOfType(y,Float)) ) ok = false;
|
||||||
|
if( is(T_INT) && !Std.isOfType(int,Int) ) ok = false;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:keep
|
||||||
|
public function guessType(v:XRF, str:String):Void {
|
||||||
|
v.string = str;
|
||||||
|
if( str.split(",").length > 1){ // 1. `,` assumes 1D/2D/3D vector-values like x[,y[,z]]
|
||||||
|
var xyz:Array<String> = str.split(","); // 1. parseFloat(..) and parseInt(..) is applied to vector/float and int values
|
||||||
|
if( xyz.length > 0 ) v.x = Std.parseFloat(xyz[0]); // 1. anything else will be treated as string-value
|
||||||
|
if( xyz.length > 1 ) v.y = Std.parseFloat(xyz[1]); // 1. incompatible value-types will be dropped / not used
|
||||||
|
if( xyz.length > 2 ) v.z = Std.parseFloat(xyz[2]); //
|
||||||
|
} // > the xrfragment specification should stay simple enough
|
||||||
|
// > for anyone to write a parser using either regexes or grammar/lexers
|
||||||
|
if( isColor.match(str) ) v.color = str; // > therefore expressions/comprehensions are not supported (max wildcard/comparison operators for queries e.g.)
|
||||||
|
if( isFloat.match(str) ) v.float = Std.parseFloat(str);
|
||||||
|
if( isInt.match(str) ) v.int = Std.parseInt(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
local XRF = {}
|
||||||
|
|
||||||
|
XRF.ASSET = 1
|
||||||
|
XRF.ASSET_OBJ = 2
|
||||||
|
XRF.PV_OVERRIDE = 4
|
||||||
|
XRF.QUERY_OPERATOR = 8
|
||||||
|
XRF.PROMPT = 16
|
||||||
|
XRF.ROUNDROBIN = 32
|
||||||
|
XRF.BROWSER_OVERRIDE = 64
|
||||||
|
XRF.T_COLOR = 128
|
||||||
|
XRF.T_INT = 256
|
||||||
|
XRF.T_FLOAT = 512
|
||||||
|
XRF.T_VECTOR2 = 1024
|
||||||
|
XRF.T_VECTOR3 = 2048
|
||||||
|
XRF.T_URL = 4096
|
||||||
|
XRF.T_PREDEFINED_VIEW = 8192
|
||||||
|
XRF.T_STRING = 16384
|
||||||
|
XRF.T_STRING_OBJ = 32768
|
||||||
|
|
||||||
|
XRF.isColor = string.match("#([A-fa-f0-9]{6}|[A-fa-f0-9]{3})", "")
|
||||||
|
XRF.isInt = string.match("^[0-9]+$", "")
|
||||||
|
XRF.isFloat = string.match("^[0-9]+%.[0-9]+$", "")
|
||||||
|
XRF.isVector = string.match("([,]+|%w)", "")
|
||||||
|
XRF.isUrl = string.match("(://)?..*", "")
|
||||||
|
XRF.isUrlOrPretypedView = string.match("(^#|://)?..*", "")
|
||||||
|
XRF.isString = string.match(".+", "")
|
||||||
|
|
||||||
|
function XRF.new(_fragment, _flags)
|
||||||
|
local self = {}
|
||||||
|
self.fragment = _fragment
|
||||||
|
self.flags = _flags
|
||||||
|
self.x = 0
|
||||||
|
self.y = 0
|
||||||
|
self.z = 0
|
||||||
|
self.color = ""
|
||||||
|
self.string = ""
|
||||||
|
self.int = 0
|
||||||
|
self.float = 0
|
||||||
|
self.args = {}
|
||||||
|
self.query = {}
|
||||||
|
function self.is(flag)
|
||||||
|
print(flag)
|
||||||
|
print(self.flags)
|
||||||
|
return self.flags & flag ~= 0
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function XRF.set(flag, flags)
|
||||||
|
return flags | flag
|
||||||
|
end
|
||||||
|
|
||||||
|
function XRF.unset(flag, flags)
|
||||||
|
return flags & ~flag
|
||||||
|
end
|
||||||
|
|
||||||
|
function XRF.validate(self, value)
|
||||||
|
XRF.guessType(self, value)
|
||||||
|
if string.len(value:split("|")) then
|
||||||
|
self.args = {}
|
||||||
|
for i, val in ipairs(value:split("|")) do
|
||||||
|
local xrf = XRF.new(self.fragment, self.flags)
|
||||||
|
XRF.guessType(xrf, val)
|
||||||
|
table.insert(self.args, xrf)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self.fragment == "q" then
|
||||||
|
--self.query = (new Query(value)):get()
|
||||||
|
end
|
||||||
|
if not isinstance(self.args, 'array') then
|
||||||
|
if self:is(XRF.T_VECTOR3) and (not isinstance(self.x, 'number') or not isinstance(self.y, 'number') or not isinstance(self.z, 'number')) then
|
||||||
|
ok = false
|
||||||
|
end
|
||||||
|
if self:is(XRF.T_VECTOR2) and (not isinstance(self.x, 'number') or not isinstance(self.y, 'number')) then
|
||||||
|
ok = false
|
||||||
|
end
|
||||||
|
if self:is(XRF.T_INT) and not isinstance(self.int, 'number') then
|
||||||
|
ok = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ok
|
||||||
|
end
|
||||||
|
|
||||||
|
function XRF.guessType(v, str)
|
||||||
|
v.string = str
|
||||||
|
if string.len(str:split(",")) > 1 then
|
||||||
|
local xyz = string.split(str, ",")
|
||||||
|
if #xyz > 0 then
|
||||||
|
v.x = tonumber(xyz[1])
|
||||||
|
end
|
||||||
|
if #xyz > 1 then
|
||||||
|
v.y = tonumber(xyz[2])
|
||||||
|
end
|
||||||
|
if #xyz > 2 then
|
||||||
|
v.z = tonumber(xyz[3])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if XRF.isColor:match(str) then
|
||||||
|
v.color = str
|
||||||
|
end
|
||||||
|
if XRF.isFloat:match(str) then
|
||||||
|
v.float = tonumber(str)
|
||||||
|
end
|
||||||
|
if XRF.isInt:match(str) then
|
||||||
|
v.int = tonumber(str)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
x = XRF.new("pos", XRF.ASSET | XRF.ROUNDROBIN )
|
||||||
|
print( "roundrobin: " .. ( x.is( XRF.ROUNDROBIN ) and "ja" or "nee" ) )
|
||||||
|
|
||||||
|
return XRF
|
|
@ -0,0 +1,118 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/axist@latest/dist/axist.min.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src="./../dist/xrfragment.js"></script>
|
||||||
|
<script>
|
||||||
|
var DOMReady = function(a,b,c){b=document,c='addEventListener';b[c]?b[c]('DOMContentLoaded',a):window.attachEvent('onload',a)}
|
||||||
|
|
||||||
|
DOMReady(function () {
|
||||||
|
|
||||||
|
let XRF = xrfragment;
|
||||||
|
let $ = (e) => document.querySelector(e)
|
||||||
|
|
||||||
|
log = (str) => {
|
||||||
|
$("#console").innerHTML = JSON.stringify(str, null, 2)
|
||||||
|
$("#console").style.border = "10px solid #"+ ((1 << 24) * Math.random() | 0).toString(16).padStart(6, "0")+"66";
|
||||||
|
}
|
||||||
|
|
||||||
|
let update = () => {
|
||||||
|
let result = {}
|
||||||
|
XRF.Parser.parse( $('#fragment').value, $('#value').value, result );
|
||||||
|
log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#fragment").addEventListener("change", (e) => {
|
||||||
|
let opt = e.target.options[ e.target.selectedIndex ]
|
||||||
|
$('#value').value = opt.getAttribute("x")
|
||||||
|
update()
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#value').addEventListener("change", update )
|
||||||
|
|
||||||
|
addEventListener("hashchange", (e) => log( XRF.URI.parse( document.location.href, true ) ) );
|
||||||
|
|
||||||
|
document.location.hash = "#pos=0,0,0"
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<section id="forms">
|
||||||
|
<a class="title-link" href="#forms"><h3 class="title">XR Fragments</h3></a>
|
||||||
|
<form>
|
||||||
|
<fieldset>
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td width="200">
|
||||||
|
<label for="fragment">Property in 3D file</label>
|
||||||
|
<br><br>
|
||||||
|
<select id="fragment" class="w-100">
|
||||||
|
<optgroup label="asset loading / linking">
|
||||||
|
<option x="1">prio</option>
|
||||||
|
<option x="pos=0,0,1&fov=2">#</option>
|
||||||
|
<option x="foo">class</option>
|
||||||
|
<option x="./2.gtlf">src</option>
|
||||||
|
<option x="radio.mp3">src_audio</option>
|
||||||
|
<option x="vert.glsl|frag.glsl">src_shader</option>
|
||||||
|
<option x="://env.jpg|1|equirect" >src_env</option>
|
||||||
|
<option x="podcast.mp3">src_env_audio</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="href navigation / portals / teleporting">
|
||||||
|
<option x="1,2,0">pos</option>
|
||||||
|
<option x="3.gltf#q=.kitchen">href</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="query selector / object manipulation">
|
||||||
|
<option x=".summer -.winter cube" selected="selected" default>q</option>
|
||||||
|
<option x="2" >scale</option>
|
||||||
|
<option x="1,2,3">rot</option>
|
||||||
|
<option x="1,1,1">translate</option>
|
||||||
|
<option x="1">visible</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="animation">
|
||||||
|
<option x="1,200">t</option>
|
||||||
|
<option x="0,-9.8,0">gravity</option>
|
||||||
|
<option x="0.2,1">physics</option>
|
||||||
|
<option x=#scroll=1,0|2s">scroll</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="device / viewport settings">
|
||||||
|
<option x="90">fov</option>
|
||||||
|
<option x="1,100">clip</option>
|
||||||
|
<option x="5m|FFAACC">fog</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="author / metadata">
|
||||||
|
<option x="XXX">namespace</option>
|
||||||
|
<option x="GPL-3.0-or-later">SPFX</option>
|
||||||
|
<option x="1m">unit</option>
|
||||||
|
<option x="this is an example scene">description</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="multiparty">
|
||||||
|
<option x="matrix://matrix.org/#myroom&room.key=123">src_session</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td style="text-align:left">
|
||||||
|
<label for="value">Value</label>
|
||||||
|
<br><br>
|
||||||
|
<input id="value" class="w-100" type="text" value=".summer -.winter cube" placeholder="" >
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="border=bottom:none;color:#666">
|
||||||
|
<br><br>
|
||||||
|
ps. you can test BROWSER_OVERRIDE-enabled fragments (<i>#pos=1,2,3</i> or <i>#t=1,100</i> e.g.) by updating the url
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="console">parser output:</label>
|
||||||
|
<br><br>
|
||||||
|
<textarea id="console" style="width:100%;height:50vh;" placeholder="" class="w-100"></textarea>
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue