diff --git a/app_web/game.js b/app_web/game.js new file mode 100644 index 0000000..627ec3a --- /dev/null +++ b/app_web/game.js @@ -0,0 +1,2 @@ +(()=>{"use strict";const t=new class{constructor(){this.states=new Map}get(t){return this.states.get(t)}set(t,i){return this.states.set(t,i)}};class i{constructor(){this.inventory=[],this.currentRoom="start"}}class e{speak(t){}stop(){}setOptions(t){}}class s extends e{constructor(t={}){super(),this.timeout=100,this.timeout=t.timeout||100,this.init()}init(){this.container=document.createElement("div"),this.container.setAttribute("aria-live","polite"),this.speechDisplay=document.createElement("div"),this.speechDisplay.setAttribute("aria-live","polite"),this.container.append(this.speechDisplay),document.body.appendChild(this.container),document.body.insertBefore(this.container,document.body.firstChild)}speak(t){this.clearDisplay();const i=document.createTextNode(t),e=document.createElement("p");e.appendChild(i),this.speechDisplay.appendChild(e),setTimeout(this.clearDisplay.bind(this),this.timeout)}stop(){this.clearDisplay()}clearDisplay(){this.speechDisplay.innerHTML=""}}class o extends e{}class n{constructor(t=function(t="aria"){return"webtts"===t?o:s}()){this.output=t}speak(t){this.output.speak(t)}stop(){this.output.stop()}}class a{constructor(){this.tts=new n(new s),this.history=document.getElementById("output-area")}say(t){const i=document.createElement("p");i.appendChild(document.createTextNode(t)),this.history.appendChild(i)}}class r{constructor(t){this.handler=t,this.inputField=document.getElementById("input-area"),this.init()}init(){this.inputField.addEventListener("keydown",(t=>{if(13==t.which){const t=this.inputField.value;this.inputField.value="",this.handler.doCommand(t)}}))}}const h=[[["look","l"],function(t,i){i.examineRoom()}]];class c{constructor(t,i){this.context=t,this.commands=i||new Map,this.addDefaultCommands()}doCommand(t){const i=this.context.getRoom(this.context.player.currentRoom),e=t.split(" ");this.commands.get(e[0])&&this.commands.get(e[0])(e,this.context),i.getExit(e[0])&&this.context.move(i.getExit(e[0]))}addCommand(t,i){Array.isArray(t)?t.forEach((t=>this.commands.set(t,i))):this.commands.set(t,i)}addCommands(t){t.forEach((t=>{this.addCommand(t[0],t[1])}))}addDefaultCommands(){this.addCommands(h)}}class l{constructor(){this.id="room",this.title="A room",this.description="You see nothing special",this.firstDescription="As you walk into the room, you notice nothing special",this.objects=[],this.exits=new Map,this.enterCallback=null,this.exitCallback=null,this.canEnterLogic=null,this.canExitLogic=null,this.tickCallback=null,this.context=null}async onEnter(){if(this.enterCallback)return this.enterCallback(this.context)}async onExit(){if(this.exitCallback)return this.exitCallback(this.context)}canEnter(){return!this.canEnterLogic||this.canEnterLogic(this.context)}canExit(){return!this.canExitLogic||this.canExitLogic(this.context)}addExit(t,i){return this.exits.set(t,i),this}getExit(t){return this.exits.get(t)}addItem(t){this.objects.push(t)}addEnterCallback(t){this.enterCallback=t}addExitCallback(t){this.exitCallback=t}addEnterLogic(t){this.canEnterLogic=t}addExitLogic(t){this.canExitLogic=t}addTickCallback(t){this.tickCallback=t}}class d{constructor(){this.room=new l}withID(t){return this.room.id=t,this}withTitle(t){return this.room.title=t,this}withFirstDescription(t){return this.room.firstDescription=t,this}withDescription(t){return this.room.description=t,this}withExit(t,i){return this.room.addExit(t,i),this}withItem(t){return this.room.addItem(t),this}withEnterCallback(t){return this.room.addEnterCallback(t),this}withExitCallback(t){return this.room.addExitCallback(t),this}withEnterLogic(t){return this.room.addEnterLogic(t),this}withExitLogic(t){return this.room.addExitLogic(t),this}withTick(t){return this.room.addTickCallback(t),this}create(){return this.room}}const m=[(new d).withID("start").withTitle("The starting room").withFirstDescription("You set foot in your very first room").withDescription("The first room. Nothing special about it.").withExit("north","tunnel_1").withEnterCallback((async function(t){const{output:i,wait:e}=t;i.say("You slowly wake up"),await e(5e3),i.say("It's strange. You never used to be able to be conscious about the fact that you were waking up."),await e(5e3),i.say("Yet here we are.")})).create(),(new d).withID("tunnel_1").withTitle("A long dark tunnel").withFirstDescription("You first step foot in this dark loomy tunnel.").withDescription("The walls are wet. Everything is wet. Ugh. Why do you even.").withExit("south","start").create()];(new class{constructor(){this.player=new i,this.state=t,this.rooms=[],this.items=[],this.output=new a,this.commandHandler=new c(this),this.input=new r(this.commandHandler),this.visitedRooms=new Map}print(t){this.output.say(t)}init(t){this.rooms=t.rooms.map((t=>(t.context=this,t))),this.items=t.items.map((t=>(t.context=this,t))),this.state=t.state,this.commandHandler.addCommands(t.commands),this.player=new i,this.move(this.player.currentRoom)}examineRoom(){const t=this.getRoom(this.player.currentRoom);this.output.say(t.title),this.visitedRooms.get(this.player.currentRoom)||""==t.firstDescription?this.output.say(t.description):this.output.say(t.firstDescription)}getRoom(t){return this.rooms.find((i=>i.id==t))}getItem(t){return this.items.find((i=>i.id==t))}wait(t){return new Promise(((i,e)=>{setTimeout(i,t)}))}async move(t){const i=this.getRoom(this.player.currentRoom),e=this.getRoom(t);i.canExit()&&e.canEnter()&&(await i.onExit(),await e.onEnter(),this.player.currentRoom=t,this.examineRoom(),this.visitedRooms.set(t,!0))}}).init({rooms:m,commands:[[["meow","mew"],async function(t,i){i.print("You meow.")}]],items:[]})})(); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,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QUNsQlAsS0FBS3FFLEtBQUtyRSxLQUFLa0UsT0FBT3pELGFBRzFCa0QsY0FDSSxNQUFNSyxFQUFPaEUsS0FBS2lFLFFBQVFqRSxLQUFLa0UsT0FBT3pELGFBQ3RDVCxLQUFLdUMsT0FBT00sSUFBSW1CLEVBQUtnQixPQUNoQmhGLEtBQUs2SCxhQUFhMUgsSUFBSUgsS0FBS2tFLE9BQU96RCxjQUF5QyxJQUF6QnVELEVBQUtrQixpQkFHeERsRixLQUFLdUMsT0FBT00sSUFBSW1CLEVBQUtpQixhQUZyQmpGLEtBQUt1QyxPQUFPTSxJQUFJbUIsRUFBS2tCLGtCQU03QmpCLFFBQVFjLEdBQ0osT0FBTy9FLEtBQUswSCxNQUFNTyxNQUFNakUsR0FBU0EsRUFBS2UsSUFBTUEsSUFHaERtRCxRQUFRbkQsR0FDSixPQUFPL0UsS0FBSzJILE1BQU1NLE1BQU1oQyxHQUFTQSxFQUFLbEIsSUFBTUEsSUFHaER5QyxLQUFLVyxHQUNELE9BQU8sSUFBSUMsU0FBUSxDQUFDQyxFQUFTQyxLQUN6QnBHLFdBQVdtRyxFQUFTRixNQUk1QnpDLFdBQVdLLEdBQ1AsTUFBTXRGLEVBQWNULEtBQUtpRSxRQUFRakUsS0FBS2tFLE9BQU96RCxhQUN2QzhILEVBQVV2SSxLQUFLaUUsUUFBUThCLEdBQ3pCdEYsRUFBWW1GLFdBQWEyQyxFQUFRNUMsbUJBQzNCbEYsRUFBWStILGVBQ1pELEVBQVFFLFVBQ2R6SSxLQUFLa0UsT0FBT3pELFlBQWNzRixFQUMxQi9GLEtBQUsyRCxjQUNMM0QsS0FBSzZILGFBQWF4SCxJQUFJMEYsR0FBUSxPRC9EckM1RSxLQUFLLENBQ051RyxNQUFPLEVBQ1A3RCxTQUFVLENBQ04sQ0FBQyxDQUFDLE9BQVEsT0VWSDZCLGVBQTJCakMsRUFBTUMsR0FDNUNBLEVBQVFvRSxNQUFNLGdCRldkSCxNQUFPLE0iLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9hc3Nhc3Npbi1idWcvLi9zcmMvZW5naW5lL3N0YXRlLmpzIiwid2VicGFjazovL2Fzc2Fzc2luLWJ1Zy8uL3NyYy9lbmdpbmUvcGxheWVyLmpzIiwid2VicGFjazovL2Fzc2Fzc2luLWJ1Zy8uL3NyYy9mcmFtZXdvcmsvdHRzL291dHB1dHMvYmFzZS1vdXRwdXQuanMiLCJ3ZWJwYWNrOi8vYXNzYXNzaW4tYnVnLy4vc3JjL2ZyYW1ld29yay90dHMvb3V0cHV0cy9hcmlhLmpzIiwid2VicGFjazovL2Fzc2Fzc2luLWJ1Zy8uL3NyYy9mcmFtZXdvcmsvdHRzL291dHB1dHMvd2VidHRzLmpzIiwid2VicGFjazovL2Fzc2Fzc2luLWJ1Zy8uL3NyYy9mcmFtZXdvcmsvdHRzL2luZGV4LmpzIiwid2VicGFjazovL2Fzc2Fzc2luLWJ1Zy8uL3NyYy9mcmFtZXdvcmsvdHRzL291dHB1dC1mYWN0b3J5LmpzIiwid2VicGFjazovL2Fzc2Fzc2luLWJ1Zy8uL3NyYy9lbmdpbmUvb3V0cHV0LmpzIiwid2VicGFjazovL2Fzc2Fzc2luLWJ1Zy8uL3NyYy9lbmdpbmUvaW5wdXQuanMiLCJ3ZWJwYWNrOi8vYXNzYXNzaW4tYnVnLy4vc3JjL2VuZ2luZS9jb21tYW5kcy5qcyIsIndlYnBhY2s6Ly9hc3Nhc3Npbi1idWcvLi9zcmMvZW5naW5lL2NvbW1hbmRzL2xvb2suanMiLCJ3ZWJwYWNrOi8vYXNzYXNzaW4tYnVnLy4vc3JjL2VuZ2luZS9yb29tLmpzIiwid2VicGFjazovL2Fzc2Fzc2luLWJ1Zy8uL3NyYy9lbmdpbmUvYnVpbGRlcnMvcm9vbS5qcyIsIndlYnBhY2s6Ly9hc3Nhc3Npbi1idWcvLi9zcmMvZ2FtZS9yb29tcy9zdGFydC5qcyIsIndlYnBhY2s6Ly9hc3Nhc3Npbi1idWcvLi9zcmMvZ2FtZS9yb29tcy9pbmRleC5qcyIsIndlYnBhY2s6Ly9hc3Nhc3Npbi1idWcvLi9zcmMvZ2FtZS9yb29tcy90dW5uZWwxLmpzIiwid2VicGFjazovL2Fzc2Fzc2luLWJ1Zy8uL3NyYy9nYW1lL2luZGV4LmpzIiwid2VicGFjazovL2Fzc2Fzc2luLWJ1Zy8uL3NyYy9lbmdpbmUvaW5kZXguanMiLCJ3ZWJwYWNrOi8vYXNzYXNzaW4tYnVnLy4vc3JjL2dhbWUvY29tbWFuZHMvbWVvdy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJjbGFzcyBTdGF0ZSB7XHJcbiAgICBjb25zdHJ1Y3RvcigpIHtcclxuICAgICAgICB0aGlzLnN0YXRlcyA9IG5ldyBNYXAoKTtcclxuICAgIH1cclxuXHJcbiAgICBnZXQoa2V5KSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuc3RhdGVzLmdldChrZXkpO1xyXG4gICAgfVxyXG5cclxuICAgIHNldChrZXksIHZhbHVlKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuc3RhdGVzLnNldChrZXksIHZhbHVlKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgbmV3IFN0YXRlKCk7IiwiZXhwb3J0IGRlZmF1bHQgY2xhc3MgUGxheWVyIHtcclxuICAgIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgICAgIHRoaXMuaW52ZW50b3J5ID0gW107XHJcbiAgICAgICAgdGhpcy5jdXJyZW50Um9vbSA9IFwic3RhcnRcIjtcclxuICAgIH1cclxufSIsImV4cG9ydCBjbGFzcyBCYXNlT3V0cHV0IHtcclxuICAgIHNwZWFrKHRleHQpIHtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgICBzdG9wKCkge1xyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIHNldE9wdGlvbnMob3B0aW9ucykge1xyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxufVxyXG4iLCJpbXBvcnQgeyBCYXNlT3V0cHV0IH0gZnJvbSAnLi9iYXNlLW91dHB1dCc7XHJcbmV4cG9ydCBjbGFzcyBBcmlhT3V0cHV0IGV4dGVuZHMgQmFzZU91dHB1dCB7XHJcbiAgICBjb25zdHJ1Y3RvcihvcHRpb25zID0ge30pIHtcclxuICAgICAgICBzdXBlcigpO1xyXG4gICAgICAgIHRoaXMudGltZW91dCA9IDEwMDtcclxuICAgICAgICB0aGlzLnRpbWVvdXQgPSBvcHRpb25zLnRpbWVvdXQgfHwgMTAwO1xyXG4gICAgICAgIHRoaXMuaW5pdCgpO1xyXG4gICAgfVxyXG4gICAgaW5pdCgpIHtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xyXG4gICAgICAgIHRoaXMuY29udGFpbmVyLnNldEF0dHJpYnV0ZSgnYXJpYS1saXZlJywgJ3BvbGl0ZScpO1xyXG4gICAgICAgIHRoaXMuc3BlZWNoRGlzcGxheSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xyXG4gICAgICAgIHRoaXMuc3BlZWNoRGlzcGxheS5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGl2ZScsICdwb2xpdGUnKTtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lci5hcHBlbmQodGhpcy5zcGVlY2hEaXNwbGF5KTtcclxuICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHRoaXMuY29udGFpbmVyKTtcclxuICAgICAgICBkb2N1bWVudC5ib2R5Lmluc2VydEJlZm9yZSh0aGlzLmNvbnRhaW5lciwgZG9jdW1lbnQuYm9keS5maXJzdENoaWxkKTtcclxuICAgIH1cclxuICAgIHNwZWFrKHRleHQpIHtcclxuICAgICAgICB0aGlzLmNsZWFyRGlzcGxheSgpO1xyXG4gICAgICAgIGNvbnN0IG5vZGUgPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZSh0ZXh0KTtcclxuICAgICAgICBjb25zdCBwYXJhID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgncCcpO1xyXG4gICAgICAgIHBhcmEuYXBwZW5kQ2hpbGQobm9kZSk7XHJcbiAgICAgICAgdGhpcy5zcGVlY2hEaXNwbGF5LmFwcGVuZENoaWxkKHBhcmEpO1xyXG4gICAgICAgIHNldFRpbWVvdXQodGhpcy5jbGVhckRpc3BsYXkuYmluZCh0aGlzKSwgdGhpcy50aW1lb3V0KTtcclxuICAgIH1cclxuICAgIHN0b3AoKSB7XHJcbiAgICAgICAgdGhpcy5jbGVhckRpc3BsYXkoKTtcclxuICAgIH1cclxuICAgIGNsZWFyRGlzcGxheSgpIHtcclxuICAgICAgICB0aGlzLnNwZWVjaERpc3BsYXkuaW5uZXJIVE1MID0gJyc7XHJcbiAgICB9XHJcbn1cclxuIiwiaW1wb3J0IHsgQmFzZU91dHB1dCB9IGZyb20gJy4vYmFzZS1vdXRwdXQnO1xyXG5leHBvcnQgY2xhc3MgV2ViVFRTT3V0cHV0IGV4dGVuZHMgQmFzZU91dHB1dCB7XHJcbn1cclxuIiwiaW1wb3J0IHsgY3JlYXRlT3V0cHV0IH0gZnJvbSAnLi9vdXRwdXQtZmFjdG9yeSc7XHJcbmV4cG9ydCBjbGFzcyBUVFMge1xyXG4gICAgY29uc3RydWN0b3Iob3V0cHV0ID0gY3JlYXRlT3V0cHV0KCkpIHtcclxuICAgICAgICB0aGlzLm91dHB1dCA9IG91dHB1dDtcclxuICAgIH1cclxuICAgIHNwZWFrKHRleHQpIHtcclxuICAgICAgICB0aGlzLm91dHB1dC5zcGVhayh0ZXh0KTtcclxuICAgIH1cclxuICAgIHN0b3AoKSB7XHJcbiAgICAgICAgdGhpcy5vdXRwdXQuc3RvcCgpO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IEJhc2VPdXRwdXQgfSBmcm9tICcuL291dHB1dHMvYmFzZS1vdXRwdXQnO1xyXG5pbXBvcnQgeyBBcmlhT3V0cHV0IH0gZnJvbSAnLi9vdXRwdXRzL2FyaWEnO1xyXG5pbXBvcnQgeyBXZWJUVFNPdXRwdXQgfSBmcm9tICcuL291dHB1dHMvd2VidHRzJztcclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU91dHB1dChrZXkgPSAnYXJpYScpIHtcclxuICAgIHN3aXRjaCAoa2V5KSB7XHJcbiAgICAgICAgY2FzZSAnYXJpYSc6XHJcbiAgICAgICAgICAgIHJldHVybiBBcmlhT3V0cHV0O1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICd3ZWJ0dHMnOlxyXG4gICAgICAgICAgICByZXR1cm4gV2ViVFRTT3V0cHV0O1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgICAgICBkZWZhdWx0OlxyXG4gICAgICAgICAgICByZXR1cm4gQXJpYU91dHB1dDtcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICB9XHJcbn1cclxuZXhwb3J0IHsgV2ViVFRTT3V0cHV0LCBBcmlhT3V0cHV0LCBCYXNlT3V0cHV0IH07XHJcbiIsImltcG9ydCB7IFRUUyB9IGZyb20gJy4uL2ZyYW1ld29yay90dHMnO1xyXG5pbXBvcnQgeyBBcmlhT3V0cHV0IH0gZnJvbSAnLi4vZnJhbWV3b3JrL3R0cy9vdXRwdXRzL2FyaWEnO1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgT3V0cHV0IHtcclxuICAgIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgICAgIHRoaXMudHRzID0gbmV3IFRUUyhuZXcgQXJpYU91dHB1dCgpKTtcclxuICAgICAgICB0aGlzLmhpc3RvcnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcIm91dHB1dC1hcmVhXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIHNheShzdHJpbmcpIHtcclxuICAgICAgICBjb25zdCBub2RlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInBcIik7XHJcbiAgICAgICAgbm9kZS5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShzdHJpbmcpKTtcclxuICAgICAgICB0aGlzLmhpc3RvcnkuYXBwZW5kQ2hpbGQobm9kZSk7XHJcbiAgICAgICAgLy8gdGhpcy50dHMuc3BlYWsoc3RyaW5nKTtcclxuICAgIH1cclxufSIsImV4cG9ydCBkZWZhdWx0IGNsYXNzIElucHV0IHtcclxuICAgIGNvbnN0cnVjdG9yKGNvbW1hbmRIYW5kbGVyKSB7XHJcbiAgICAgICAgdGhpcy5oYW5kbGVyID0gY29tbWFuZEhhbmRsZXI7XHJcbiAgICAgICAgdGhpcy5pbnB1dEZpZWxkID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJpbnB1dC1hcmVhXCIpO1xyXG4gICAgICAgIHRoaXMuaW5pdCgpO1xyXG4gICAgfVxyXG5cclxuICAgIGluaXQoKSB7XHJcbiAgICAgICAgdGhpcy5pbnB1dEZpZWxkLmFkZEV2ZW50TGlzdGVuZXIoXCJrZXlkb3duXCIsIChlKSA9PiB7XHJcbiAgICAgICAgICAgIGlmIChlLndoaWNoID09IDEzKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zdCB2YWwgPSB0aGlzLmlucHV0RmllbGQudmFsdWU7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmlucHV0RmllbGQudmFsdWUgPSBcIlwiO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVyLmRvQ29tbWFuZCh2YWwpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfSlcclxuICAgIH1cclxufSIsImltcG9ydCBMb29rQ29tbWFuZCBmcm9tIFwiLi9jb21tYW5kcy9sb29rXCI7XHJcblxyXG5jb25zdCBkZWZhdWx0Q29tbWFuZHMgPSBbXHJcbiAgICBbW1wibG9va1wiLCBcImxcIl0sIExvb2tDb21tYW5kXVxyXG5dO1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQ29tbWFuZHMge1xyXG4gICAgY29uc3RydWN0b3IoY29udGV4dCwgY29tbWFuZHMpIHtcclxuICAgICAgICB0aGlzLmNvbnRleHQgPSBjb250ZXh0O1xyXG4gICAgICAgIHRoaXMuY29tbWFuZHMgPSBjb21tYW5kcyB8fCBuZXcgTWFwKCk7XHJcbiAgICAgICAgdGhpcy5hZGREZWZhdWx0Q29tbWFuZHMoKTtcclxuICAgIH1cclxuXHJcbiAgICBkb0NvbW1hbmQoc3RyKSB7XHJcbiAgICAgICAgY29uc3Qgcm9vbSA9IHRoaXMuY29udGV4dC5nZXRSb29tKHRoaXMuY29udGV4dC5wbGF5ZXIuY3VycmVudFJvb20pO1xyXG4gICAgICAgIGNvbnN0IHNwbGl0ID0gc3RyLnNwbGl0KFwiIFwiKTtcclxuICAgICAgICBpZiAodGhpcy5jb21tYW5kcy5nZXQoc3BsaXRbMF0pKSB7XHJcbiAgICAgICAgICAgIHRoaXMuY29tbWFuZHMuZ2V0KHNwbGl0WzBdKShzcGxpdCwgdGhpcy5jb250ZXh0KTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKHJvb20uZ2V0RXhpdChzcGxpdFswXSkpIHtcclxuICAgICAgICAgICAgdGhpcy5jb250ZXh0Lm1vdmUocm9vbS5nZXRFeGl0KHNwbGl0WzBdKSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGFkZENvbW1hbmQobmFtZSwgZnVuYykge1xyXG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KG5hbWUpKSB7XHJcbiAgICAgICAgICAgIG5hbWUuZm9yRWFjaCgoY29tbWFuZCkgPT4gdGhpcy5jb21tYW5kcy5zZXQoY29tbWFuZCwgZnVuYykpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHRoaXMuY29tbWFuZHMuc2V0KG5hbWUsIGZ1bmMpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBhZGRDb21tYW5kcyhjb21tYW5kcykge1xyXG4gICAgICAgIGNvbW1hbmRzLmZvckVhY2goKGNvbW1hbmQpID0+IHtcclxuICAgICAgICAgICAgdGhpcy5hZGRDb21tYW5kKGNvbW1hbmRbMF0sIGNvbW1hbmRbMV0pO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIGFkZERlZmF1bHRDb21tYW5kcygpIHtcclxuICAgICAgICB0aGlzLmFkZENvbW1hbmRzKGRlZmF1bHRDb21tYW5kcyk7XHJcbiAgICB9XHJcbn0iLCJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBMb29rQ29tbWFuZChhcmdzLCBjb250ZXh0KSB7XHJcbiAgICBjb250ZXh0LmV4YW1pbmVSb29tKCk7XHJcbn0iLCJleHBvcnQgZGVmYXVsdCBjbGFzcyBSb29tIHtcclxuICAgIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgICAgIHRoaXMuaWQgPSBcInJvb21cIjtcclxuICAgICAgICB0aGlzLnRpdGxlID0gXCJBIHJvb21cIjtcclxuICAgICAgICB0aGlzLmRlc2NyaXB0aW9uID0gXCJZb3Ugc2VlIG5vdGhpbmcgc3BlY2lhbFwiO1xyXG4gICAgICAgIHRoaXMuZmlyc3REZXNjcmlwdGlvbiA9IFwiQXMgeW91IHdhbGsgaW50byB0aGUgcm9vbSwgeW91IG5vdGljZSBub3RoaW5nIHNwZWNpYWxcIjtcclxuICAgICAgICB0aGlzLm9iamVjdHMgPSBbXTtcclxuICAgICAgICB0aGlzLmV4aXRzID0gbmV3IE1hcCgpO1xyXG4gICAgICAgIHRoaXMuZW50ZXJDYWxsYmFjayA9IG51bGw7XHJcbiAgICAgICAgdGhpcy5leGl0Q2FsbGJhY2sgPSBudWxsO1xyXG4gICAgICAgIHRoaXMuY2FuRW50ZXJMb2dpYyA9IG51bGw7XHJcbiAgICAgICAgdGhpcy5jYW5FeGl0TG9naWMgPSBudWxsO1xyXG4gICAgICAgIHRoaXMudGlja0NhbGxiYWNrID0gbnVsbDtcclxuICAgICAgICB0aGlzLmNvbnRleHQgPSBudWxsO1xyXG4gICAgfVxyXG5cclxuICAgIGFzeW5jIG9uRW50ZXIoKSB7XHJcbiAgICAgICAgaWYgKHRoaXMuZW50ZXJDYWxsYmFjaykgcmV0dXJuIHRoaXMuZW50ZXJDYWxsYmFjayh0aGlzLmNvbnRleHQpO1xyXG4gICAgfVxyXG5cclxuICAgIGFzeW5jIG9uRXhpdCgpIHtcclxuICAgICAgICBpZiAodGhpcy5leGl0Q2FsbGJhY2spIHJldHVybiB0aGlzLmV4aXRDYWxsYmFjayh0aGlzLmNvbnRleHQpO1xyXG4gICAgfVxyXG5cclxuICAgIGNhbkVudGVyKCkge1xyXG4gICAgICAgIGlmICh0aGlzLmNhbkVudGVyTG9naWMpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuY2FuRW50ZXJMb2dpYyh0aGlzLmNvbnRleHQpO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuXHJcbiAgICBjYW5FeGl0KCkge1xyXG4gICAgICAgIGlmICh0aGlzLmNhbkV4aXRMb2dpYykge1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5jYW5FeGl0TG9naWModGhpcy5jb250ZXh0KTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcblxyXG4gICAgYWRkRXhpdChkaXJlY3Rpb24sIHJvb21JRCkge1xyXG4gICAgICAgIHRoaXMuZXhpdHMuc2V0KGRpcmVjdGlvbiwgcm9vbUlEKTtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuXHJcbiAgICBnZXRFeGl0KGRpcmVjdGlvbikge1xyXG4gICAgICAgIHJldHVybiB0aGlzLmV4aXRzLmdldChkaXJlY3Rpb24pO1xyXG4gICAgfVxyXG5cclxuICAgIGFkZEl0ZW0oaXRlbSkge1xyXG4gICAgICAgIHRoaXMub2JqZWN0cy5wdXNoKGl0ZW0pO1xyXG4gICAgfVxyXG5cclxuICAgIGFkZEVudGVyQ2FsbGJhY2soY2FsbGJhY2spIHtcclxuICAgICAgICB0aGlzLmVudGVyQ2FsbGJhY2sgPSBjYWxsYmFjaztcclxuICAgIH1cclxuXHJcbiAgICBhZGRFeGl0Q2FsbGJhY2soY2FsbGJhY2spIHtcclxuICAgICAgICB0aGlzLmV4aXRDYWxsYmFjayA9IGNhbGxiYWNrO1xyXG4gICAgfVxyXG5cclxuICAgIGFkZEVudGVyTG9naWMoZnVuYykge1xyXG4gICAgICAgIHRoaXMuY2FuRW50ZXJMb2dpYyA9IGZ1bmM7XHJcbiAgICB9XHJcblxyXG4gICAgYWRkRXhpdExvZ2ljKGZ1bmMpIHtcclxuICAgICAgICB0aGlzLmNhbkV4aXRMb2dpYyA9IGZ1bmM7XHJcbiAgICB9XHJcblxyXG4gICAgYWRkVGlja0NhbGxiYWNrKGNhbGxiYWNrKSB7XHJcbiAgICAgICAgdGhpcy50aWNrQ2FsbGJhY2sgPSBjYWxsYmFjaztcclxuICAgIH1cclxufSIsImltcG9ydCBSb29tIGZyb20gJy4uL3Jvb20nO1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgUm9vbUJ1aWxkZXIge1xyXG4gICAgY29uc3RydWN0b3IoKSB7XHJcbiAgICAgICAgdGhpcy5yb29tID0gbmV3IFJvb20oKTtcclxuICAgIH1cclxuXHJcbiAgICB3aXRoSUQoSUQpIHtcclxuICAgICAgICB0aGlzLnJvb20uaWQgPSBJRDtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIFxyXG4gICAgd2l0aFRpdGxlKHRpdGxlKSB7XHJcbiAgICAgICAgdGhpcy5yb29tLnRpdGxlID0gdGl0bGU7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcblxyXG4gICAgd2l0aEZpcnN0RGVzY3JpcHRpb24oZGVzY3JpcHRpb24pIHtcclxuICAgICAgICB0aGlzLnJvb20uZmlyc3REZXNjcmlwdGlvbiA9IGRlc2NyaXB0aW9uO1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG5cclxuICAgIHdpdGhEZXNjcmlwdGlvbihkZXNjcmlwdGlvbikge1xyXG4gICAgICAgIHRoaXMucm9vbS5kZXNjcmlwdGlvbiA9IGRlc2NyaXB0aW9uO1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG5cclxuICAgIHdpdGhFeGl0KGRpcmVjdGlvbiwgcm9vbUlEKSB7XHJcbiAgICAgICAgdGhpcy5yb29tLmFkZEV4aXQoZGlyZWN0aW9uLCByb29tSUQpO1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG5cclxuICAgIHdpdGhJdGVtKGl0ZW1JRCkge1xyXG4gICAgICAgIHRoaXMucm9vbS5hZGRJdGVtKGl0ZW1JRCk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcblxyXG4gICAgd2l0aEVudGVyQ2FsbGJhY2soY2FsbGJhY2spIHtcclxuICAgICAgICB0aGlzLnJvb20uYWRkRW50ZXJDYWxsYmFjayhjYWxsYmFjayk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcblxyXG4gICAgd2l0aEV4aXRDYWxsYmFjayhjYWxsYmFjaykge1xyXG4gICAgICAgIHRoaXMucm9vbS5hZGRFeGl0Q2FsbGJhY2soY2FsbGJhY2spO1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG5cclxuICAgIHdpdGhFbnRlckxvZ2ljKGZ1bmMpIHtcclxuICAgICAgICB0aGlzLnJvb20uYWRkRW50ZXJMb2dpYyhmdW5jKTtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuXHJcbiAgICB3aXRoRXhpdExvZ2ljKGZ1bmMpIHtcclxuICAgICAgICB0aGlzLnJvb20uYWRkRXhpdExvZ2ljKGZ1bmMpO1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG5cclxuICAgIHdpdGhUaWNrKGZ1bmMpIHtcclxuICAgICAgICB0aGlzLnJvb20uYWRkVGlja0NhbGxiYWNrKGZ1bmMpO1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG5cclxuICAgIGNyZWF0ZSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5yb29tO1xyXG4gICAgfVxyXG59IiwiaW1wb3J0IFJvb21CdWlsZGVyIGZyb20gJy4uLy4uL2VuZ2luZS9idWlsZGVycy9yb29tJztcclxuXHJcbmV4cG9ydCBkZWZhdWx0IG5ldyBSb29tQnVpbGRlcigpXHJcbi53aXRoSUQoXCJzdGFydFwiKVxyXG4ud2l0aFRpdGxlKFwiVGhlIHN0YXJ0aW5nIHJvb21cIilcclxuLndpdGhGaXJzdERlc2NyaXB0aW9uKFwiWW91IHNldCBmb290IGluIHlvdXIgdmVyeSBmaXJzdCByb29tXCIpXHJcbi53aXRoRGVzY3JpcHRpb24oXCJUaGUgZmlyc3Qgcm9vbS4gTm90aGluZyBzcGVjaWFsIGFib3V0IGl0LlwiKVxyXG4ud2l0aEV4aXQoXCJub3J0aFwiLCBcInR1bm5lbF8xXCIpXHJcbi53aXRoRW50ZXJDYWxsYmFjayhhc3luYyBmdW5jdGlvbihjb250ZXh0KSB7XHJcbiAgICBjb25zdCB7IG91dHB1dCwgd2FpdCB9ID0gY29udGV4dDtcclxuICAgIG91dHB1dC5zYXkoXCJZb3Ugc2xvd2x5IHdha2UgdXBcIik7XHJcbiAgICBhd2FpdCB3YWl0KDUwMDApO1xyXG4gICAgb3V0cHV0LnNheShcIkl0J3Mgc3RyYW5nZS4gWW91IG5ldmVyIHVzZWQgdG8gYmUgYWJsZSB0byBiZSBjb25zY2lvdXMgYWJvdXQgdGhlIGZhY3QgdGhhdCB5b3Ugd2VyZSB3YWtpbmcgdXAuXCIpO1xyXG4gICAgYXdhaXQgd2FpdCg1MDAwKTtcclxuICAgIG91dHB1dC5zYXkoXCJZZXQgaGVyZSB3ZSBhcmUuXCIpO1xyXG59KVxyXG4uY3JlYXRlKCk7IiwiaW1wb3J0IFN0YXJ0IGZyb20gJy4vc3RhcnQnO1xyXG5pbXBvcnQgVHVubmVsMSBmcm9tICcuL3R1bm5lbDEnO1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgW1xyXG4gICAgU3RhcnQsXHJcbiAgICBUdW5uZWwxXHJcbl07IiwiaW1wb3J0IFJvb21CdWlsZGVyIGZyb20gJy4uLy4uL2VuZ2luZS9idWlsZGVycy9yb29tJztcclxuXHJcbmV4cG9ydCBkZWZhdWx0IG5ldyBSb29tQnVpbGRlcigpXHJcbi53aXRoSUQoXCJ0dW5uZWxfMVwiKVxyXG4ud2l0aFRpdGxlKFwiQSBsb25nIGRhcmsgdHVubmVsXCIpXHJcbi53aXRoRmlyc3REZXNjcmlwdGlvbihcIllvdSBmaXJzdCBzdGVwIGZvb3QgaW4gdGhpcyBkYXJrIGxvb215IHR1bm5lbC5cIilcclxuLndpdGhEZXNjcmlwdGlvbihcIlRoZSB3YWxscyBhcmUgd2V0LiBFdmVyeXRoaW5nIGlzIHdldC4gVWdoLiBXaHkgZG8geW91IGV2ZW4uXCIpXHJcbi53aXRoRXhpdChcInNvdXRoXCIsIFwic3RhcnRcIilcclxuLmNyZWF0ZSgpOyIsImltcG9ydCBHYW1lIGZyb20gJy4uL2VuZ2luZSc7XHJcbmltcG9ydCBSb29tcyBmcm9tICcuL3Jvb21zJztcclxuaW1wb3J0IE1lb3dDb21tYW5kIGZyb20gJy4vY29tbWFuZHMvbWVvdyc7XHJcblxyXG5jb25zdCBnYW1lID0gbmV3IEdhbWUoKTtcclxuXHJcblxyXG5nYW1lLmluaXQoe1xyXG4gICAgcm9vbXM6IFJvb21zLFxyXG4gICAgY29tbWFuZHM6IFtcclxuICAgICAgICBbW1wibWVvd1wiLCBcIm1ld1wiXSwgTWVvd0NvbW1hbmRdXHJcbiAgICBdLFxyXG4gICAgaXRlbXM6IFtdXHJcbn0pOyIsImltcG9ydCBTdGF0ZSBmcm9tICcuL3N0YXRlJztcclxuaW1wb3J0IFJvb20gZnJvbSAnLi9yb29tJztcclxuaW1wb3J0IFBsYXllciBmcm9tICcuL3BsYXllcic7XHJcbmltcG9ydCBPdXRwdXQgZnJvbSAnLi9vdXRwdXQnO1xyXG5pbXBvcnQgSW5wdXQgZnJvbSAnLi9pbnB1dCc7XHJcbmltcG9ydCBDb21tYW5kcyBmcm9tICcuL2NvbW1hbmRzJztcclxuXHJcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEdhbWUge1xyXG4gICAgY29uc3RydWN0b3IoKSB7XHJcbiAgICAgICAgdGhpcy5wbGF5ZXIgPSBuZXcgUGxheWVyKCk7XHJcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlO1xyXG4gICAgICAgIHRoaXMucm9vbXMgPSBbXTtcclxuICAgICAgICB0aGlzLml0ZW1zID0gW107XHJcbiAgICAgICAgdGhpcy5vdXRwdXQgPSBuZXcgT3V0cHV0KCk7XHJcbiAgICAgICAgdGhpcy5jb21tYW5kSGFuZGxlciA9IG5ldyBDb21tYW5kcyh0aGlzKTtcclxuICAgICAgICB0aGlzLmlucHV0ID0gbmV3IElucHV0KHRoaXMuY29tbWFuZEhhbmRsZXIpO1xyXG4gICAgICAgIHRoaXMudmlzaXRlZFJvb21zID0gbmV3IE1hcCgpO1xyXG4gICAgfVxyXG5cclxuICAgIHByaW50KHN0cmluZykge1xyXG4gICAgICAgIHRoaXMub3V0cHV0LnNheShzdHJpbmcpO1xyXG4gICAgfVxyXG5cclxuICAgIGluaXQoZGF0YSkge1xyXG4gICAgICAgIHRoaXMucm9vbXMgPSBkYXRhLnJvb21zLm1hcCgocm9vbSkgPT4ge1xyXG4gICAgICAgICAgICByb29tLmNvbnRleHQgPSB0aGlzO1xyXG4gICAgICAgICAgICByZXR1cm4gcm9vbTtcclxuICAgICAgICB9KTtcclxuICAgICAgICB0aGlzLml0ZW1zID0gZGF0YS5pdGVtcy5tYXAoKGl0ZW0pID0+IHtcclxuICAgICAgICAgICAgaXRlbS5jb250ZXh0ID0gdGhpcztcclxuICAgICAgICAgICAgcmV0dXJuIGl0ZW07XHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgdGhpcy5zdGF0ZSA9IGRhdGEuc3RhdGU7XHJcbiAgICAgICAgdGhpcy5jb21tYW5kSGFuZGxlci5hZGRDb21tYW5kcyhkYXRhLmNvbW1hbmRzKTtcclxuICAgICAgICB0aGlzLnBsYXllciA9IG5ldyBQbGF5ZXIoKTtcclxuICAgICAgICB0aGlzLm1vdmUodGhpcy5wbGF5ZXIuY3VycmVudFJvb20pO1xyXG4gICAgfVxyXG5cclxuICAgIGV4YW1pbmVSb29tKCkge1xyXG4gICAgICAgIGNvbnN0IHJvb20gPSB0aGlzLmdldFJvb20odGhpcy5wbGF5ZXIuY3VycmVudFJvb20pO1xyXG4gICAgICAgIHRoaXMub3V0cHV0LnNheShyb29tLnRpdGxlKTtcclxuICAgICAgICBpZiAoIXRoaXMudmlzaXRlZFJvb21zLmdldCh0aGlzLnBsYXllci5jdXJyZW50Um9vbSkgJiYgcm9vbS5maXJzdERlc2NyaXB0aW9uICE9IFwiXCIpIHtcclxuICAgICAgICAgICAgdGhpcy5vdXRwdXQuc2F5KHJvb20uZmlyc3REZXNjcmlwdGlvbik7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgdGhpcy5vdXRwdXQuc2F5KHJvb20uZGVzY3JpcHRpb24pO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBnZXRSb29tKGlkKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMucm9vbXMuZmluZCgocm9vbSkgPT4gcm9vbS5pZCA9PSBpZCk7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0SXRlbShpZCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLml0ZW1zLmZpbmQoKGl0ZW0pID0+IGl0ZW0uaWQgPT0gaWQpO1xyXG4gICAgfVxyXG5cclxuICAgIHdhaXQobXMpIHtcclxuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICBzZXRUaW1lb3V0KHJlc29sdmUsIG1zKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBhc3luYyBtb3ZlKHJvb21JRCkge1xyXG4gICAgICAgIGNvbnN0IGN1cnJlbnRSb29tID0gdGhpcy5nZXRSb29tKHRoaXMucGxheWVyLmN1cnJlbnRSb29tKTtcclxuICAgICAgICBjb25zdCBuZXdSb29tID0gdGhpcy5nZXRSb29tKHJvb21JRCk7XHJcbiAgICAgICAgaWYgKGN1cnJlbnRSb29tLmNhbkV4aXQoKSAmJiBuZXdSb29tLmNhbkVudGVyKCkpIHtcclxuICAgICAgICAgICAgYXdhaXQgY3VycmVudFJvb20ub25FeGl0KCk7XHJcbiAgICAgICAgICAgIGF3YWl0IG5ld1Jvb20ub25FbnRlcigpO1xyXG4gICAgICAgICAgICB0aGlzLnBsYXllci5jdXJyZW50Um9vbSA9IHJvb21JRDtcclxuICAgICAgICAgICAgdGhpcy5leGFtaW5lUm9vbSgpO1xyXG4gICAgICAgICAgICB0aGlzLnZpc2l0ZWRSb29tcy5zZXQocm9vbUlELCB0cnVlKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbn0iLCJleHBvcnQgZGVmYXVsdCBhc3luYyBmdW5jdGlvbiBNZW93Q29tbWFuZChhcmdzLCBjb250ZXh0KSB7XHJcbiAgICBjb250ZXh0LnByaW50KGBZb3UgbWVvdy5gKTtcclxufSJdLCJuYW1lcyI6WyJjb25zdHJ1Y3RvciIsInRoaXMiLCJzdGF0ZXMiLCJNYXAiLCJnZXQiLCJrZXkiLCJzZXQiLCJ2YWx1ZSIsIlBsYXllciIsImludmVudG9yeSIsImN1cnJlbnRSb29tIiwiQmFzZU91dHB1dCIsInNwZWFrIiwidGV4dCIsInN0b3AiLCJzZXRPcHRpb25zIiwib3B0aW9ucyIsIkFyaWFPdXRwdXQiLCJzdXBlciIsInRpbWVvdXQiLCJpbml0IiwiY29udGFpbmVyIiwiZG9jdW1lbnQiLCJjcmVhdGVFbGVtZW50Iiwic2V0QXR0cmlidXRlIiwic3BlZWNoRGlzcGxheSIsImFwcGVuZCIsImJvZHkiLCJhcHBlbmRDaGlsZCIsImluc2VydEJlZm9yZSIsImZpcnN0Q2hpbGQiLCJjbGVhckRpc3BsYXkiLCJub2RlIiwiY3JlYXRlVGV4dE5vZGUiLCJwYXJhIiwic2V0VGltZW91dCIsImJpbmQiLCJpbm5lckhUTUwiLCJXZWJUVFNPdXRwdXQiLCJUVFMiLCJvdXRwdXQiLCJjcmVhdGVPdXRwdXQiLCJPdXRwdXQiLCJ0dHMiLCJoaXN0b3J5IiwiZ2V0RWxlbWVudEJ5SWQiLCJzYXkiLCJzdHJpbmciLCJJbnB1dCIsImNvbW1hbmRIYW5kbGVyIiwiaGFuZGxlciIsImlucHV0RmllbGQiLCJhZGRFdmVudExpc3RlbmVyIiwiZSIsIndoaWNoIiwidmFsIiwiZG9Db21tYW5kIiwiZGVmYXVsdENvbW1hbmRzIiwiYXJncyIsImNvbnRleHQiLCJleGFtaW5lUm9vbSIsIkNvbW1hbmRzIiwiY29tbWFuZHMiLCJhZGREZWZhdWx0Q29tbWFuZHMiLCJzdHIiLCJyb29tIiwiZ2V0Um9vbSIsInBsYXllciIsInNwbGl0IiwiZ2V0RXhpdCIsIm1vdmUiLCJhZGRDb21tYW5kIiwibmFtZSIsImZ1bmMiLCJBcnJheSIsImlzQXJyYXkiLCJmb3JFYWNoIiwiY29tbWFuZCIsImFkZENvbW1hbmRzIiwiUm9vbSIsImlkIiwidGl0bGUiLCJkZXNjcmlwdGlvbiIsImZpcnN0RGVzY3JpcHRpb24iLCJvYmplY3RzIiwiZXhpdHMiLCJlbnRlckNhbGxiYWNrIiwiZXhpdENhbGxiYWNrIiwiY2FuRW50ZXJMb2dpYyIsImNhbkV4aXRMb2dpYyIsInRpY2tDYWxsYmFjayIsImFzeW5jIiwiY2FuRW50ZXIiLCJjYW5FeGl0IiwiYWRkRXhpdCIsImRpcmVjdGlvbiIsInJvb21JRCIsImFkZEl0ZW0iLCJpdGVtIiwicHVzaCIsImFkZEVudGVyQ2FsbGJhY2siLCJjYWxsYmFjayIsImFkZEV4aXRDYWxsYmFjayIsImFkZEVudGVyTG9naWMiLCJhZGRFeGl0TG9naWMiLCJhZGRUaWNrQ2FsbGJhY2siLCJSb29tQnVpbGRlciIsIndpdGhJRCIsIklEIiwid2l0aFRpdGxlIiwid2l0aEZpcnN0RGVzY3JpcHRpb24iLCJ3aXRoRGVzY3JpcHRpb24iLCJ3aXRoRXhpdCIsIndpdGhJdGVtIiwiaXRlbUlEIiwid2l0aEVudGVyQ2FsbGJhY2siLCJ3aXRoRXhpdENhbGxiYWNrIiwid2l0aEVudGVyTG9naWMiLCJ3aXRoRXhpdExvZ2ljIiwid2l0aFRpY2siLCJjcmVhdGUiLCJ3YWl0Iiwic3RhdGUiLCJyb29tcyIsIml0ZW1zIiwiaW5wdXQiLCJ2aXNpdGVkUm9vbXMiLCJwcmludCIsImRhdGEiLCJtYXAiLCJmaW5kIiwiZ2V0SXRlbSIsIm1zIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJuZXdSb29tIiwib25FeGl0Iiwib25FbnRlciJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/app_web/index.html b/app_web/index.html new file mode 100644 index 0000000..88fc593 --- /dev/null +++ b/app_web/index.html @@ -0,0 +1 @@ +Assassin bug

Assassin bug

\ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5e2c506..8ac2208 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,12 @@ "license": "ISC", "dependencies": { "copy-webpack-plugin": "^9.0.1", + "eventemitter3": "^4.0.7", "html-webpack-plugin": "^5.5.0", + "terser-webpack-plugin": "^5.2.4", "webpack": "^5.61.0", - "webpack-dev-server": "^4.4.0" + "webpack-dev-server": "^4.4.0", + "yaml": "^1.10.2" }, "devDependencies": { "webpack-cli": "^4.9.1" @@ -3655,6 +3658,14 @@ } } }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -6374,6 +6385,11 @@ "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", "requires": {} }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 3e69299..2ec8713 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,12 @@ "license": "ISC", "dependencies": { "copy-webpack-plugin": "^9.0.1", + "eventemitter3": "^4.0.7", "html-webpack-plugin": "^5.5.0", + "terser-webpack-plugin": "^5.2.4", "webpack": "^5.61.0", - "webpack-dev-server": "^4.4.0" + "webpack-dev-server": "^4.4.0", + "yaml": "^1.10.2" }, "devDependencies": { "webpack-cli": "^4.9.1" diff --git a/src/engine/builders/item.js b/src/engine/builders/item.js new file mode 100644 index 0000000..7221606 --- /dev/null +++ b/src/engine/builders/item.js @@ -0,0 +1,46 @@ +import Item from '../item'; + +export default class ItemBuilder { + constructor() { + this.item = new Item(); + } + + withID(ID) { + this.item.id = ID; + return this; + } + + withName(name) { + this.item.name = name; + return this; + } + + withDescription(description) { + this.item.description = description; + return this; + } + + isUsable(value) { + this.item.usable = value; + return this; + } + + isTakeable(value) { + this.item.takeable = value; + return this; + } + + withUseCallback(callback) { + this.item.useCallback = callback; + return this; + } + + withTakeCallback(callback) { + this.item.takeCallback = callback; + return this; + } + + create() { + return this.item; + } +} \ No newline at end of file diff --git a/src/engine/builders/room.js b/src/engine/builders/room.js new file mode 100644 index 0000000..e7e4698 --- /dev/null +++ b/src/engine/builders/room.js @@ -0,0 +1,66 @@ +import Room from '../room'; + +export default class RoomBuilder { + constructor() { + this.room = new Room(); + } + + withID(ID) { + this.room.id = ID; + return this; + } + + withTitle(title) { + this.room.title = title; + return this; + } + + withFirstDescription(description) { + this.room.firstDescription = description; + return this; + } + + withDescription(description) { + this.room.description = description; + return this; + } + + withExit(direction, roomID) { + this.room.addExit(direction, roomID); + return this; + } + + withItem(itemID) { + this.room.addItem(itemID); + return this; + } + + withEnterCallback(callback) { + this.room.addEnterCallback(callback); + return this; + } + + withExitCallback(callback) { + this.room.addExitCallback(callback); + return this; + } + + withEnterLogic(func) { + this.room.addEnterLogic(func); + return this; + } + + withExitLogic(func) { + this.room.addExitLogic(func); + return this; + } + + withTick(func) { + this.room.addTickCallback(func); + return this; + } + + create() { + return this.room; + } +} \ No newline at end of file diff --git a/src/engine/commands.js b/src/engine/commands.js new file mode 100644 index 0000000..0f23e32 --- /dev/null +++ b/src/engine/commands.js @@ -0,0 +1,43 @@ +import LookCommand from "./commands/look"; +import UseCommand from "./commands/use"; +const defaultCommands = [ + [["look", "l"], LookCommand], + [["use", "interact"], UseCommand] +]; + +export default class Commands { + constructor(context, commands) { + this.context = context; + this.commands = commands || new Map(); + this.addDefaultCommands(); + } + + doCommand(str) { + const room = this.context.getRoom(this.context.player.currentRoom); + const split = str.split(" "); + if (this.commands.get(split[0])) { + this.commands.get(split[0])(split, this.context); + } + if (room.getExit(split[0])) { + this.context.move(room.getExit(split[0])); + } + } + + addCommand(name, func) { + if (Array.isArray(name)) { + name.forEach((command) => this.commands.set(command, func)); + } else { + this.commands.set(name, func); + } + } + + addCommands(commands) { + commands.forEach((command) => { + this.addCommand(command[0], command[1]); + }); + } + + addDefaultCommands() { + this.addCommands(defaultCommands); + } +} \ No newline at end of file diff --git a/src/engine/commands/look.js b/src/engine/commands/look.js new file mode 100644 index 0000000..9c0a0bc --- /dev/null +++ b/src/engine/commands/look.js @@ -0,0 +1,21 @@ +export default function LookCommand(args, context) { + if (args.length == 0) { + context.examineRoom(); + } else { + const room = context.getRoom(context.player.currentRoom); + const items = room.getItems(); + let item = null; + for (let i of items) { + if (i.name.includes(args[1])) { + item = i; + break; + } + } + if (!item) { + context.output.say(`I could not find a ${args[1]}`); + } else { + context.output.say(item.name); + context.output.say(item.description); + } + } +} \ No newline at end of file diff --git a/src/engine/commands/take.js b/src/engine/commands/take.js new file mode 100644 index 0000000..e69de29 diff --git a/src/engine/commands/use.js b/src/engine/commands/use.js new file mode 100644 index 0000000..ff6d0b1 --- /dev/null +++ b/src/engine/commands/use.js @@ -0,0 +1,16 @@ +export default async function UseCommand(args, context) { + const room = context.getRoom(context.player.currentRoom); + const items = room.getItems(); + let item = null; + for (let i of items) { + if (i.name.includes(args[1])) { + item = i; + break; + } + } + if (!item) { + context.output.say(`I could not find a ${args[1]}`); + } else { + await item.onUse(); + } +} \ No newline at end of file diff --git a/src/engine/index.js b/src/engine/index.js new file mode 100644 index 0000000..b8d28f5 --- /dev/null +++ b/src/engine/index.js @@ -0,0 +1,92 @@ +import State from './state'; +import Room from './room'; +import Player from './player'; +import Output from './output'; +import Input from './input'; +import Commands from './commands'; + +export default class Game { + constructor() { + this.player = new Player(); + this.state = State; + this.rooms = []; + this.items = []; + this.output = new Output(); + this.commandHandler = new Commands(this); + this.input = new Input(this.commandHandler); + this.visitedRooms = new Map(); + } + + print(string) { + this.output.say(string); + } + + init(data) { + console.log(data); + this.rooms = data.rooms.map((room) => { + room.context = this; + return room; + }); + this.items = data.items.map((item) => { + item.context = this; + return item; + }); + this.state = data.state; + this.commandHandler.addCommands(data.commands); + this.player = new Player(); + this.move(this.player.currentRoom); + } + + examineRoom() { + const room = this.getRoom(this.player.currentRoom); + this.output.say(room.title); + if (!this.visitedRooms.get(this.player.currentRoom) && room.firstDescription != "") { + this.output.say(room.firstDescription); + } else { + this.output.say(room.description); + } + this.examineItems(); + this.examineExits(); + } + + examineItems() { + const room = this.getRoom(this.player.currentRoom); + const items = room.getItems(); + items.forEach((item) => this.output.say(item.name)); + } + + examineExits() { + const room = this.getRoom(this.player.currentRoom); + let exitDescription = "You can go "; + for (let exit of room.exits.keys()) { + exitDescription += " " + exit; + } + this.output.say(exitDescription); + } + + getRoom(id) { + return this.rooms.find((room) => room.id == id); + } + + getItem(id) { + return this.items.find((item) => item.id == id); + } + + wait(ms) { + return new Promise((resolve, reject) => { + setTimeout(resolve, ms); + }); + } + + async move(roomID) { + const currentRoom = this.getRoom(this.player.currentRoom); + const newRoom = this.getRoom(roomID); + if (currentRoom.canExit() && newRoom.canEnter()) { + await currentRoom.onExit(); + await newRoom.onEnter(); + this.player.currentRoom = roomID; + this.examineRoom(); + this.visitedRooms.set(roomID, true); + } + } +} \ No newline at end of file diff --git a/src/engine/input.js b/src/engine/input.js new file mode 100644 index 0000000..b17f7b0 --- /dev/null +++ b/src/engine/input.js @@ -0,0 +1,17 @@ +export default class Input { + constructor(commandHandler) { + this.handler = commandHandler; + this.inputField = document.getElementById("input-area"); + this.init(); + } + + init() { + this.inputField.addEventListener("keydown", (e) => { + if (e.which == 13) { + const val = this.inputField.value; + this.inputField.value = ""; + this.handler.doCommand(val); + } + }) + } +} \ No newline at end of file diff --git a/src/engine/item.js b/src/engine/item.js new file mode 100644 index 0000000..59ccc98 --- /dev/null +++ b/src/engine/item.js @@ -0,0 +1,20 @@ +export default class Item { + constructor() { + this.id = "item"; + this.name = "An item"; + this.description = "You see nothing special about this item"; + this.usable = true; + this.takeable = true; + this.useCallback = null; + this.takeCallback = null; + this.context = null; + } + + async onUse() { + if (this.useCallback) return this.useCallback(this.context); + } + + async onTake() { + if (this.takeCallback) return this.takeCallback(); + } +} \ No newline at end of file diff --git a/src/engine/output.js b/src/engine/output.js new file mode 100644 index 0000000..e9c1ee1 --- /dev/null +++ b/src/engine/output.js @@ -0,0 +1,16 @@ +import { TTS } from '../framework/tts'; +import { AriaOutput } from '../framework/tts/outputs/aria'; + +export default class Output { + constructor() { + this.tts = new TTS(new AriaOutput()); + this.history = document.getElementById("output-area"); + } + + say(string) { + const node = document.createElement("p"); + node.appendChild(document.createTextNode(string)); + this.history.appendChild(node); + // this.tts.speak(string); + } +} \ No newline at end of file diff --git a/src/engine/player.js b/src/engine/player.js new file mode 100644 index 0000000..c4d3485 --- /dev/null +++ b/src/engine/player.js @@ -0,0 +1,6 @@ +export default class Player { + constructor() { + this.inventory = []; + this.currentRoom = "start"; + } +} \ No newline at end of file diff --git a/src/engine/room.js b/src/engine/room.js new file mode 100644 index 0000000..100e0d2 --- /dev/null +++ b/src/engine/room.js @@ -0,0 +1,75 @@ +export default class Room { + constructor() { + this.id = "room"; + this.title = "A room"; + this.description = "You see nothing special"; + this.firstDescription = "As you walk into the room, you notice nothing special"; + this.objects = []; + this.exits = new Map(); + this.enterCallback = null; + this.exitCallback = null; + this.canEnterLogic = null; + this.canExitLogic = null; + this.tickCallback = null; + this.context = null; + } + + async onEnter() { + if (this.enterCallback) return this.enterCallback(this.context); + } + + async onExit() { + if (this.exitCallback) return this.exitCallback(this.context); + } + + canEnter() { + if (this.canEnterLogic) { + return this.canEnterLogic(this.context); + } + return true; + } + + canExit() { + if (this.canExitLogic) { + return this.canExitLogic(this.context); + } + return true; + } + + addExit(direction, roomID) { + this.exits.set(direction, roomID); + return this; + } + + getExit(direction) { + return this.exits.get(direction); + } + + addItem(item) { + this.objects.push(item); + } + + addEnterCallback(callback) { + this.enterCallback = callback; + } + + addExitCallback(callback) { + this.exitCallback = callback; + } + + addEnterLogic(func) { + this.canEnterLogic = func; + } + + addExitLogic(func) { + this.canExitLogic = func; + } + + addTickCallback(callback) { + this.tickCallback = callback; + } + + getItems() { + return this.objects.map((item) => this.context.getItem(item)); + } +} \ No newline at end of file diff --git a/src/engine/state.js b/src/engine/state.js new file mode 100644 index 0000000..ad89a66 --- /dev/null +++ b/src/engine/state.js @@ -0,0 +1,15 @@ +class State { + constructor() { + this.states = new Map(); + } + + get(key) { + return this.states.get(key); + } + + set(key, value) { + return this.states.set(key, value); + } +} + +export default new State(); \ No newline at end of file diff --git a/src/framework/asset-manager/downloader.d.ts b/src/framework/asset-manager/downloader.d.ts new file mode 100644 index 0000000..e29dacb --- /dev/null +++ b/src/framework/asset-manager/downloader.d.ts @@ -0,0 +1,12 @@ +import EventEmitter from 'eventemitter3'; +import { Queue } from './queue'; +import { AssetStorage } from './storage'; +export declare class Downloader extends EventEmitter { + private storage; + private queue; + private basePath; + constructor(storage: AssetStorage, queue: Queue, basePath?: string); + setBasePath(path: string): void; + download(): Promise; + downloadItem(path: string): Promise; +} diff --git a/src/framework/asset-manager/downloader.js b/src/framework/asset-manager/downloader.js new file mode 100644 index 0000000..735fc35 --- /dev/null +++ b/src/framework/asset-manager/downloader.js @@ -0,0 +1,50 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import EventEmitter from 'eventemitter3'; +import { buildPath } from './utils'; +export class Downloader extends EventEmitter { + constructor(storage, queue, basePath = '') { + super(); + this.storage = storage; + this.queue = queue; + this.basePath = basePath; + } + setBasePath(path) { + this.basePath = this.basePath; + } + download() { + return __awaiter(this, void 0, void 0, function* () { + const downloaded = new Map(); + let numDownloaded = 0; + while (this.queue.length() > 0) { + const path = this.queue.pop(); + const item = yield this.downloadItem(buildPath(this.basePath, path)); + downloaded.set(path, item); + numDownloaded++; + this.emit('download.progress', { + downloaded: numDownloaded, + remaining: this.queue.length() + }); + } + return downloaded; + }); + } + downloadItem(path) { + return __awaiter(this, void 0, void 0, function* () { + const inCache = yield this.storage.get(path); + if (inCache) { + return inCache; + } + const response = yield fetch(path); + this.storage.add(path, response); + return response; + }); + } +} diff --git a/src/framework/asset-manager/index.d.ts b/src/framework/asset-manager/index.d.ts new file mode 100644 index 0000000..a5207df --- /dev/null +++ b/src/framework/asset-manager/index.d.ts @@ -0,0 +1,18 @@ +import EventEmitter from 'eventemitter3'; +export declare class AssetManager extends EventEmitter { + private name; + private basePath; + private downloader; + private queue; + private storage; + private manifest; + constructor(name: string, basePath: string); + init(): Promise; + setManifest(path: string): Promise; + enqueue(path: string): void; + download(): Promise; + downloadFromManifest(key: string): Promise; + downloadFile(path: string): Promise; + setBasePath(path: string): void; + clearCache(): void; +} diff --git a/src/framework/asset-manager/index.js b/src/framework/asset-manager/index.js new file mode 100644 index 0000000..2428bea --- /dev/null +++ b/src/framework/asset-manager/index.js @@ -0,0 +1,71 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import { Downloader } from './downloader'; +import { Queue } from './queue'; +import { Manifest } from './manifest'; +import { AssetStorage } from './storage'; +import EventEmitter from 'eventemitter3'; +import { buildPath } from './utils'; +export class AssetManager extends EventEmitter { + constructor(name, basePath) { + super(); + this.name = name; + this.basePath = basePath; + this.queue = new Queue(); + this.storage = new AssetStorage(name); + this.downloader = new Downloader(this.storage, this.queue, this.basePath); + console.log(`Asset manager initialized`); + } + init() { + return __awaiter(this, void 0, void 0, function* () { + yield this.storage.init(); + return true; + }); + } + setManifest(path) { + return __awaiter(this, void 0, void 0, function* () { + this.manifest = yield Manifest(`${this.basePath}/${path}`); + this.storage.setManifest(this.manifest); + return this.manifest; + }); + } + enqueue(path) { + this.queue.add(path); + } + download() { + return __awaiter(this, void 0, void 0, function* () { + const result = yield this.downloader.download(); + return result; + }); + } + downloadFromManifest(key) { + return __awaiter(this, void 0, void 0, function* () { + const paths = this.manifest[key]; + paths.forEach((path) => this.enqueue(path)); + this.downloader.on('download.progress', (info) => this.emit('progress', info)); + const files = yield this.downloader.download(); + this.downloader.off('download.progress'); + return files; + }); + } + downloadFile(path) { + return __awaiter(this, void 0, void 0, function* () { + const result = yield this.downloader.downloadItem(buildPath(this.basePath, path)); + return result; + }); + } + setBasePath(path) { + this.basePath = this.basePath; + this.downloader.setBasePath(this.basePath); + } + clearCache() { + this.storage.clear(); + } +} diff --git a/src/framework/asset-manager/manifest.d.ts b/src/framework/asset-manager/manifest.d.ts new file mode 100644 index 0000000..f4a36bf --- /dev/null +++ b/src/framework/asset-manager/manifest.d.ts @@ -0,0 +1,2 @@ +export declare function Manifest(manifestPath: string): Promise; +export declare function CheckManifest(manifest: any): boolean; diff --git a/src/framework/asset-manager/manifest.js b/src/framework/asset-manager/manifest.js new file mode 100644 index 0000000..d8c3f61 --- /dev/null +++ b/src/framework/asset-manager/manifest.js @@ -0,0 +1,40 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import * as yaml from 'yaml'; +export function Manifest(manifestPath) { + return __awaiter(this, void 0, void 0, function* () { + try { + const response = yield fetch(manifestPath); + console.log(response); + const data = yield response.text(); + console.log(`Parsing: `, data); + const manifest = yaml.parse(data); + return manifest; + } + catch (error) { + alert(`Error occured: ${error.toString()}`); + } + }); +} +export function CheckManifest(manifest) { + const prevManifestStr = localStorage.getItem('manifest'); + if (!prevManifestStr) { + localStorage.setItem('manifest', JSON.stringify(manifest)); + return false; + } + const prevManifest = JSON.parse(prevManifestStr); + if (prevManifest.version === manifest.version) { + return true; + } + else { + localStorage.setItem('manifest', manifest); + return false; + } +} diff --git a/src/framework/asset-manager/queue.d.ts b/src/framework/asset-manager/queue.d.ts new file mode 100644 index 0000000..c3c0026 --- /dev/null +++ b/src/framework/asset-manager/queue.d.ts @@ -0,0 +1,8 @@ +export declare class Queue { + private items; + constructor(); + add(file: string): string[]; + remove(file: string): string[]; + pop(): string; + length(): number; +} diff --git a/src/framework/asset-manager/queue.js b/src/framework/asset-manager/queue.js new file mode 100644 index 0000000..05380a3 --- /dev/null +++ b/src/framework/asset-manager/queue.js @@ -0,0 +1,19 @@ +export class Queue { + constructor() { + this.items = []; + } + add(file) { + this.items.push(file); + return this.items; + } + remove(file) { + this.items = this.items.filter((item) => item !== file); + return this.items; + } + pop() { + return this.items.pop(); + } + length() { + return this.items.length; + } +} diff --git a/src/framework/asset-manager/storage.d.ts b/src/framework/asset-manager/storage.d.ts new file mode 100644 index 0000000..36a491f --- /dev/null +++ b/src/framework/asset-manager/storage.d.ts @@ -0,0 +1,11 @@ +export declare class AssetStorage { + private id; + private cache; + private manifest; + constructor(id: string); + init(): Promise; + add(request: RequestInfo, response: Response): Promise; + get(request: RequestInfo): Promise; + setManifest(manifest: any): Promise; + clear(): Promise; +} diff --git a/src/framework/asset-manager/storage.js b/src/framework/asset-manager/storage.js new file mode 100644 index 0000000..48b2ebd --- /dev/null +++ b/src/framework/asset-manager/storage.js @@ -0,0 +1,48 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import { CheckManifest } from './manifest'; +export class AssetStorage { + constructor(id) { + this.id = id; + } + init() { + return __awaiter(this, void 0, void 0, function* () { + this.cache = yield caches.open(this.id); + }); + } + add(request, response) { + return __awaiter(this, void 0, void 0, function* () { + const result = yield this.cache.put(request, response); + return true; + }); + } + get(request) { + return __awaiter(this, void 0, void 0, function* () { + const result = yield this.cache.match(request); + return result; + }); + } + setManifest(manifest) { + return __awaiter(this, void 0, void 0, function* () { + this.manifest = manifest; + if (!CheckManifest(this.manifest)) { + yield this.clear(); + } + }); + } + clear() { + return __awaiter(this, void 0, void 0, function* () { + const keys = yield this.cache.keys(); + keys.forEach((key) => __awaiter(this, void 0, void 0, function* () { + const result = yield this.cache.delete(key); + })); + }); + } +} diff --git a/src/framework/asset-manager/test/index.d.ts b/src/framework/asset-manager/test/index.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/asset-manager/test/index.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/asset-manager/test/index.js b/src/framework/asset-manager/test/index.js new file mode 100644 index 0000000..92d3bd5 --- /dev/null +++ b/src/framework/asset-manager/test/index.js @@ -0,0 +1,5 @@ +const yaml = require('yaml'); +const fs = require('fs'); +const data = fs.readFileSync('manifest.yaml'); +const parsed = yaml.parse(data.toString()); +console.log(parsed); diff --git a/src/framework/asset-manager/utils.d.ts b/src/framework/asset-manager/utils.d.ts new file mode 100644 index 0000000..c8f1ef4 --- /dev/null +++ b/src/framework/asset-manager/utils.d.ts @@ -0,0 +1 @@ +export declare function buildPath(basePath: string, path: string): string; diff --git a/src/framework/asset-manager/utils.js b/src/framework/asset-manager/utils.js new file mode 100644 index 0000000..12e628a --- /dev/null +++ b/src/framework/asset-manager/utils.js @@ -0,0 +1,8 @@ +export function buildPath(basePath, path) { + if (!basePath) { + return path; + } + else { + return `${basePath}/${path}`; + } +} diff --git a/src/framework/ecs/component.d.ts b/src/framework/ecs/component.d.ts new file mode 100644 index 0000000..6617706 --- /dev/null +++ b/src/framework/ecs/component.d.ts @@ -0,0 +1,12 @@ +export declare class BaseComponent { + id: number; + properties: any; + constructor(); + clone(): BaseComponent; +} +export interface Component { + id: number; + properties: any; + clone(): BaseComponent; + new (): BaseComponent; +} diff --git a/src/framework/ecs/component.js b/src/framework/ecs/component.js new file mode 100644 index 0000000..a9ea06b --- /dev/null +++ b/src/framework/ecs/component.js @@ -0,0 +1,11 @@ +export class BaseComponent { + constructor() { + this.id = 0; + this.properties = {}; + } + clone() { + const comp = new BaseComponent(); + comp.properties = this.properties; + return comp; + } +} diff --git a/src/framework/ecs/entity.d.ts b/src/framework/ecs/entity.d.ts new file mode 100644 index 0000000..800738c --- /dev/null +++ b/src/framework/ecs/entity.d.ts @@ -0,0 +1,19 @@ +import { BaseComponent, Component } from './component'; +export declare class BaseEntity { + id: number; + components: Map; + constructor(); + addComponent(component: Component): void; + removeComponent(component: BaseComponent): void; + getComponentIDs(): number[]; + getComponent(component: BaseComponent): BaseComponent; + getComponentByID(id: number): BaseComponent; +} +export interface Entity { + new (): BaseComponent; + addComponent(component: Component): any; + removeComponent(component: BaseComponent): any; + getComponentIDs(): number[]; + getComponent(component: BaseComponent): any; + getComponentByID(id: number): BaseComponent; +} diff --git a/src/framework/ecs/entity.js b/src/framework/ecs/entity.js new file mode 100644 index 0000000..2732635 --- /dev/null +++ b/src/framework/ecs/entity.js @@ -0,0 +1,23 @@ +export class BaseEntity { + constructor() { + this.components = new Map(); + this.id = 0; + } + addComponent(component) { + let comp = new component(); + comp.id = component.id; + this.components.set(component.id, comp); + } + removeComponent(component) { + this.components.delete(component.id); + } + getComponentIDs() { + return [...this.components.keys()]; + } + getComponent(component) { + return this.components.get(component.id); + } + getComponentByID(id) { + return this.components.get(id); + } +} diff --git a/src/framework/ecs/index.d.ts b/src/framework/ecs/index.d.ts new file mode 100644 index 0000000..7a8314e --- /dev/null +++ b/src/framework/ecs/index.d.ts @@ -0,0 +1,28 @@ +import { Component } from './component'; +import { BaseEntity, Entity } from './entity'; +import { EventBus } from '../event-bus'; +import { Query } from './query'; +import { System } from './system'; +export declare class World { + entities: Array; + components: Map; + componentNamesToIDs: Map; + systems: Set; + nextEntityID: number; + nextComponentID: number; + nextQueryID: number; + queryCache: Array; + eventBus: EventBus; + constructor(); + run(): void; + createSystem(systemExecutor: Function): void; + addSystem(system: System): void; + addEntity(entity: BaseEntity): void; + removeEntity(entityToRemove: BaseEntity): void; + createEntity(components: Array): BaseEntity; + extendEntity(entity: Entity, components: Array): BaseEntity; + createComponent(component: Component): Component; + query(include: Array, exclude: Array): Array; + createQuery(include: Array, exclude: Array): Query; + markQueriesDirty(): void; +} diff --git a/src/framework/ecs/index.js b/src/framework/ecs/index.js new file mode 100644 index 0000000..7a7ef26 --- /dev/null +++ b/src/framework/ecs/index.js @@ -0,0 +1,105 @@ +import { BaseEntity } from './entity'; +import { EventBus } from '../event-bus'; +import { Query } from './query'; +import { System } from './system'; +export class World { + constructor() { + this.nextEntityID = 0; + this.nextComponentID = 0; + this.nextQueryID = 0; + this.entities = new Array(); + this.systems = new Set(); + this.components = new Map(); + this.componentNamesToIDs = new Map(); + this.queryCache = new Array(); + this.eventBus = new EventBus(); + } + run() { + this.systems.forEach((system) => { + system.execute(this); + }); + } + createSystem(systemExecutor) { + const newSystem = new System(systemExecutor); + this.systems.add(newSystem); + } + addSystem(system) { + this.systems.add(system); + } + addEntity(entity) { + this.entities.push(entity); + this.markQueriesDirty(); + } + removeEntity(entityToRemove) { + this.entities = this.entities.filter((entity) => entity !== entityToRemove); + this.markQueriesDirty(); + } + createEntity(components) { + const newEntity = new BaseEntity(); + newEntity.id = this.nextEntityID; + this.nextEntityID++; + components.forEach((component) => { + if (this.componentNamesToIDs.has(component.name)) { + component.id = this.componentNamesToIDs.get(component.name); + } + else { + this.componentNamesToIDs.set(component.name, this.nextComponentID); + component.id = this.nextComponentID; + this.nextComponentID++; + } + newEntity.addComponent(component); + }); + this.entities.push(newEntity); + this.markQueriesDirty(); + return newEntity; + } + extendEntity(entity, components) { + const toClone = this.entities.find((found) => entity.name === found.constructor.name); + const cloned = new BaseEntity(); + cloned.id = this.nextEntityID; + this.nextEntityID++; + toClone.components.forEach((component) => { + cloned.addComponent(this.components.get(component.id)); + }); + components.forEach((component) => { + if (this.componentNamesToIDs.has(component.name)) { + component.id = this.componentNamesToIDs.get(component.name); + } + else { + this.componentNamesToIDs.set(component.name, this.nextComponentID); + component.id = this.nextComponentID; + this.nextComponentID++; + } + cloned.addComponent(component); + }); + this.entities.push(cloned); + return cloned; + } + createComponent(component) { + const newComponent = component; + newComponent.id = this.nextComponentID; + this.nextComponentID++; + this.components.set(newComponent.id, newComponent); + this.componentNamesToIDs.set(component.name, component.id); + return newComponent; + } + query(include, exclude) { + const query = new Query(include, exclude, this); + const cache = this.queryCache.find((item) => item.include == include && item.exclude == exclude); + if (cache) { + return cache.execute(); + } + this.queryCache.push(query); + return query.execute(); + } + createQuery(include, exclude) { + const newQuery = new Query(include, exclude, this); + newQuery.id = this.nextQueryID; + this.nextQueryID++; + this.queryCache.push(newQuery); + return newQuery; + } + markQueriesDirty() { + this.queryCache.forEach((query) => (query.isDirty = true)); + } +} diff --git a/src/framework/ecs/query.d.ts b/src/framework/ecs/query.d.ts new file mode 100644 index 0000000..37c4901 --- /dev/null +++ b/src/framework/ecs/query.d.ts @@ -0,0 +1,15 @@ +import { World } from '.'; +import { Component } from './component'; +import { BaseEntity } from './entity'; +export declare class Query { + include: Array; + exclude: Array; + world: World; + id: number; + private results; + isDirty: boolean; + includeComponentIds: number[]; + excludeComponentIds: number[]; + constructor(include: Array, exclude: Array, world: World); + execute(): Array; +} diff --git a/src/framework/ecs/query.js b/src/framework/ecs/query.js new file mode 100644 index 0000000..1ae868f --- /dev/null +++ b/src/framework/ecs/query.js @@ -0,0 +1,34 @@ +export class Query { + constructor(include, exclude, world) { + this.include = include; + this.exclude = exclude; + this.world = world; + this.isDirty = true; + this.results = new Array(); + this.includeComponentIds = include.map((component) => component.id); + this.excludeComponentIds = exclude.map((component) => component.id); + this.id = 0; + } + execute() { + if (!this.isDirty && this.results) { + return this.results; + } + let filtered; + this.includeComponentIds = this.include.map((component) => this.world.componentNamesToIDs.get(component.name)); + this.excludeComponentIds = this.exclude.map((component) => this.world.componentNamesToIDs.get(component.name)); + const entities = this.world.entities.filter((entity) => { + let ids = entity.getComponentIDs(); + // let includes = ids.map(id => this.includeComponentIds.includes(id)).includes(true); + let excludes = ids + .map((id) => this.excludeComponentIds.includes(id)) + .includes(true); + let includes = ids.filter((id) => this.includeComponentIds.includes(id)); + return includes.length === this.includeComponentIds.length && !excludes; + }); + if (entities.length > 0) { + this.isDirty = false; + this.results = entities; + } + return entities; + } +} diff --git a/src/framework/ecs/system.d.ts b/src/framework/ecs/system.d.ts new file mode 100644 index 0000000..65af5de --- /dev/null +++ b/src/framework/ecs/system.d.ts @@ -0,0 +1,6 @@ +import { World } from '.'; +export declare class System { + executor: Function; + constructor(executor: Function); + execute(world: World): void; +} diff --git a/src/framework/ecs/system.js b/src/framework/ecs/system.js new file mode 100644 index 0000000..dbccaa4 --- /dev/null +++ b/src/framework/ecs/system.js @@ -0,0 +1,10 @@ +export class System { + constructor(executor) { + this.executor = executor; + } + execute(world) { + if (this.executor) { + this.executor(world); + } + } +} diff --git a/src/framework/engine.js b/src/framework/engine.js new file mode 100644 index 0000000..5a7acad --- /dev/null +++ b/src/framework/engine.js @@ -0,0 +1,2 @@ +var Engine;(()=>{"use strict";var t={729:t=>{var e=Object.prototype.hasOwnProperty,s="~";function i(){}function n(t,e,s){this.fn=t,this.context=e,this.once=s||!1}function h(t,e,i,h,a){if("function"!=typeof i)throw new TypeError("The listener must be a function");var u=new n(i,h||t,a),r=s?s+e:e;return t._events[r]?t._events[r].fn?t._events[r]=[t._events[r],u]:t._events[r].push(u):(t._events[r]=u,t._eventsCount++),t}function a(t,e){0==--t._eventsCount?t._events=new i:delete t._events[e]}function u(){this._events=new i,this._eventsCount=0}Object.create&&(i.prototype=Object.create(null),(new i).__proto__||(s=!1)),u.prototype.eventNames=function(){var t,i,n=[];if(0===this._eventsCount)return n;for(i in t=this._events)e.call(t,i)&&n.push(s?i.slice(1):i);return Object.getOwnPropertySymbols?n.concat(Object.getOwnPropertySymbols(t)):n},u.prototype.listeners=function(t){var e=s?s+t:t,i=this._events[e];if(!i)return[];if(i.fn)return[i.fn];for(var n=0,h=i.length,a=new Array(h);n{var e=t&&t.__esModule?()=>t.default:()=>t;return s.d(e,{a:e}),e},s.d=(t,e)=>{for(var i in e)s.o(e,i)&&!s.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},s.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),s.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var i={};(()=>{s.r(i),s.d(i,{AssetManager:()=>v,BaseItem:()=>T,CheckboxItem:()=>R,EditItem:()=>B,EventBus:()=>m,EventItem:()=>p,Input:()=>I,Menu:()=>Q,MenuItem:()=>$,SelectorItem:()=>V,SliderItem:()=>U,TTS:()=>F,World:()=>f});var t=s(729),e=s.n(t);function n(t,e){return t?`${t}/${e}`:e}var h=function(t,e,s,i){return new(s||(s=Promise))((function(n,h){function a(t){try{r(i.next(t))}catch(t){h(t)}}function u(t){try{r(i.throw(t))}catch(t){h(t)}}function r(t){var e;t.done?n(t.value):(e=t.value,e instanceof s?e:new s((function(t){t(e)}))).then(a,u)}r((i=i.apply(t,e||[])).next())}))};class a extends(e()){constructor(t,e,s=""){super(),this.storage=t,this.queue=e,this.basePath=s}setBasePath(t){this.basePath=this.basePath}download(){return h(this,void 0,void 0,(function*(){const t=new Map;let e=0;for(;this.queue.length()>0;){const s=this.queue.pop(),i=yield this.downloadItem(n(this.basePath,s));t.set(s,i),e++,this.emit("download.progress",{downloaded:e,remaining:this.queue.length()})}return t}))}downloadItem(t){return h(this,void 0,void 0,(function*(){const e=yield this.storage.get(t);if(e)return e;const s=yield fetch(t);return this.storage.add(t,s),s}))}}class u{constructor(){this.items=[]}add(t){return this.items.push(t),this.items}remove(t){return this.items=this.items.filter((e=>e!==t)),this.items}pop(){return this.items.pop()}length(){return this.items.length}}var r=function(t,e,s,i){return new(s||(s=Promise))((function(n,h){function a(t){try{r(i.next(t))}catch(t){h(t)}}function u(t){try{r(i.throw(t))}catch(t){h(t)}}function r(t){var e;t.done?n(t.value):(e=t.value,e instanceof s?e:new s((function(t){t(e)}))).then(a,u)}r((i=i.apply(t,e||[])).next())}))};class l{constructor(t){this.id=t}init(){return r(this,void 0,void 0,(function*(){this.cache=yield caches.open(this.id)}))}add(t,e){return r(this,void 0,void 0,(function*(){return yield this.cache.put(t,e),!0}))}get(t){return r(this,void 0,void 0,(function*(){return yield this.cache.match(t)}))}setManifest(t){return r(this,void 0,void 0,(function*(){this.manifest=t,function(t){const e=localStorage.getItem("manifest");return e?JSON.parse(e).version===t.version||(localStorage.setItem("manifest",t),!1):(localStorage.setItem("manifest",JSON.stringify(t)),!1)}(this.manifest)||(yield this.clear())}))}clear(){return r(this,void 0,void 0,(function*(){(yield this.cache.keys()).forEach((t=>r(this,void 0,void 0,(function*(){yield this.cache.delete(t)}))))}))}}var o,c,d=function(t,e,s,i){return new(s||(s=Promise))((function(n,h){function a(t){try{r(i.next(t))}catch(t){h(t)}}function u(t){try{r(i.throw(t))}catch(t){h(t)}}function r(t){var e;t.done?n(t.value):(e=t.value,e instanceof s?e:new s((function(t){t(e)}))).then(a,u)}r((i=i.apply(t,e||[])).next())}))};class v extends(e()){constructor(t,e){super(),this.name=t,this.basePath=e,this.queue=new u,this.storage=new l(t),this.downloader=new a(this.storage,this.queue,this.basePath),console.log("Asset manager initialized")}init(){return d(this,void 0,void 0,(function*(){return yield this.storage.init(),!0}))}setManifest(t){return d(this,void 0,void 0,(function*(){return this.manifest=yield function(t){return e=this,s=void 0,n=function*(){try{const e=yield fetch(t);console.log(e);const s=yield e.text();return console.log("Parsing: ",s),(void 0).parse(s)}catch(t){alert(`Error occured: ${t.toString()}`)}},new((i=void 0)||(i=Promise))((function(t,h){function a(t){try{r(n.next(t))}catch(t){h(t)}}function u(t){try{r(n.throw(t))}catch(t){h(t)}}function r(e){var s;e.done?t(e.value):(s=e.value,s instanceof i?s:new i((function(t){t(s)}))).then(a,u)}r((n=n.apply(e,s||[])).next())}));var e,s,i,n}(`${this.basePath}/${t}`),this.storage.setManifest(this.manifest),this.manifest}))}enqueue(t){this.queue.add(t)}download(){return d(this,void 0,void 0,(function*(){return yield this.downloader.download()}))}downloadFromManifest(t){return d(this,void 0,void 0,(function*(){this.manifest[t].forEach((t=>this.enqueue(t))),this.downloader.on("download.progress",(t=>this.emit("progress",t)));const e=yield this.downloader.download();return this.downloader.off("download.progress"),e}))}downloadFile(t){return d(this,void 0,void 0,(function*(){return yield this.downloader.downloadItem(n(this.basePath,t))}))}setBasePath(t){this.basePath=this.basePath,this.downloader.setBasePath(this.basePath)}clearCache(){this.storage.clear()}}class y{constructor(){this.components=new Map,this.id=0}addComponent(t){let e=new t;e.id=t.id,this.components.set(t.id,e)}removeComponent(t){this.components.delete(t.id)}getComponentIDs(){return[...this.components.keys()]}getComponent(t){return this.components.get(t.id)}getComponentByID(t){return this.components.get(t)}}class m{constructor(){this.events=new Map}emit(t,e={}){let s=this.events.get(t);if(s)s.subscribers.forEach((t=>{t(e)}));else{let e=new p(t);this.events.set(t,e)}}subscribe(t,e){let s=this.events.get(t);s||(s=new p(t),this.events.set(t,s)),s.subscribers.push(e)}unsubscribe(t,e){if(this.events.has(t)){let s=this.events.get(t);s.subscribers=s.subscribers.filter((t=>t!==e)),s.subscribers.length<1&&this.events.delete(t)}}unsubscribeAll(t){this.events.has(t)&&this.events.delete(t)}}class p{constructor(t){this.id=t,this.subscribers=[]}}class x{constructor(t,e,s){this.include=t,this.exclude=e,this.world=s,this.isDirty=!0,this.results=new Array,this.includeComponentIds=t.map((t=>t.id)),this.excludeComponentIds=e.map((t=>t.id)),this.id=0}execute(){if(!this.isDirty&&this.results)return this.results;this.includeComponentIds=this.include.map((t=>this.world.componentNamesToIDs.get(t.name))),this.excludeComponentIds=this.exclude.map((t=>this.world.componentNamesToIDs.get(t.name)));const t=this.world.entities.filter((t=>{let e=t.getComponentIDs(),s=e.map((t=>this.excludeComponentIds.includes(t))).includes(!0);return e.filter((t=>this.includeComponentIds.includes(t))).length===this.includeComponentIds.length&&!s}));return t.length>0&&(this.isDirty=!1,this.results=t),t}}class w{constructor(t){this.executor=t}execute(t){this.executor&&this.executor(t)}}class f{constructor(){this.nextEntityID=0,this.nextComponentID=0,this.nextQueryID=0,this.entities=new Array,this.systems=new Set,this.components=new Map,this.componentNamesToIDs=new Map,this.queryCache=new Array,this.eventBus=new m}run(){this.systems.forEach((t=>{t.execute(this)}))}createSystem(t){const e=new w(t);this.systems.add(e)}addSystem(t){this.systems.add(t)}addEntity(t){this.entities.push(t),this.markQueriesDirty()}removeEntity(t){this.entities=this.entities.filter((e=>e!==t)),this.markQueriesDirty()}createEntity(t){const e=new y;return e.id=this.nextEntityID,this.nextEntityID++,t.forEach((t=>{this.componentNamesToIDs.has(t.name)?t.id=this.componentNamesToIDs.get(t.name):(this.componentNamesToIDs.set(t.name,this.nextComponentID),t.id=this.nextComponentID,this.nextComponentID++),e.addComponent(t)})),this.entities.push(e),this.markQueriesDirty(),e}extendEntity(t,e){const s=this.entities.find((e=>t.name===e.constructor.name)),i=new y;return i.id=this.nextEntityID,this.nextEntityID++,s.components.forEach((t=>{i.addComponent(this.components.get(t.id))})),e.forEach((t=>{this.componentNamesToIDs.has(t.name)?t.id=this.componentNamesToIDs.get(t.name):(this.componentNamesToIDs.set(t.name,this.nextComponentID),t.id=this.nextComponentID,this.nextComponentID++),i.addComponent(t)})),this.entities.push(i),i}createComponent(t){const e=t;return e.id=this.nextComponentID,this.nextComponentID++,this.components.set(e.id,e),this.componentNamesToIDs.set(t.name,t.id),e}query(t,e){const s=new x(t,e,this),i=this.queryCache.find((s=>s.include==t&&s.exclude==e));return i?i.execute():(this.queryCache.push(s),s.execute())}createQuery(t,e){const s=new x(t,e,this);return s.id=this.nextQueryID,this.nextQueryID++,this.queryCache.push(s),s}markQueriesDirty(){this.queryCache.forEach((t=>t.isDirty=!0))}}class g{constructor(t){this.element=t}getState(){}capture(t){}release(){}}class z extends g{constructor(t){super(t),this.keysDown=new Map,this.keysJustPressed=new Map,this.keysJustReleased=new Map,this.handleKeyDown=this.handleKeyDown.bind(this),this.handleKeyUp=this.handleKeyUp.bind(this)}capture(t){this.active=!0,this.preventDefault=t,this.element.addEventListener("keydown",this.handleKeyDown),this.element.addEventListener("keyup",this.handleKeyUp)}release(){this.active=!1,this.element.removeEventListener("keydown",this.handleKeyDown),this.element.removeEventListener("keyup",this.handleKeyUp)}getState(){const t={keysDown:new Map(this.keysDown),keysJustPressed:new Map(this.keysJustPressed),keysJustReleased:new Map(this.keysJustReleased)};return this.keysJustPressed.clear(),this.keysJustReleased.clear(),t}handleKeyDown(t){this.active&&this.preventDefault&&t.preventDefault(),this.keysDown.get(t.keyCode)||(this.keysDown.set(t.keyCode,!0),this.keysJustPressed.set(t.keyCode,!0),this.keysJustReleased.set(t.keyCode,!1))}handleKeyUp(t){this.active&&this.preventDefault&&t.preventDefault(),this.keysDown.get(t.keyCode)&&(this.keysDown.set(t.keyCode,!1),this.keysJustPressed.set(t.keyCode,!1),this.keysJustReleased.set(t.keyCode,!0))}}class b extends g{constructor(t){super(t),this.mousePosition=new M,this.mouseDelta=new C,this.mouseWheel=new C,this.mouseButtons=new k}capture(){this.handleMouseDown=this.handleMouseDown.bind(this),this.handleMouseMove=this.handleMouseMove.bind(this),this.handleMouseUp=this.handleMouseUp.bind(this),this.handlePointerChange=this.handlePointerChange.bind(this),this.active=!0,this.element.addEventListener("mousedown",this.handleMouseDown),this.element.addEventListener("mousemove",this.handleMouseMove),this.element.addEventListener("mouseup",this.handleMouseUp),document.addEventListener("pointerlockchange",this.handlePointerChange)}release(){this.active=!1,this.element.removeEventListener("mousedown",this.handleMouseDown),this.element.removeEventListener("mousemove",this.handleMouseMove),this.element.removeEventListener("mouseup",this.handleMouseUp)}getState(){const{mouseButtons:t,mouseDelta:e,mousePosition:s,mouseWheel:i}=this,n={mouseButtons:{keysDown:new Map(this.mouseButtons.keysDown),keysJustPressed:new Map(this.mouseButtons.keysJustPressed),keysJustReleased:new Map(this.mouseButtons.keysJustReleased)},mouseDelta:e,mousePosition:s,mouseWheel:i};return this.mouseButtons.keysJustPressed.clear(),this.mouseButtons.keysJustReleased.clear(),this.mouseDelta.x=0,this.mouseDelta.y=0,n}handleMouseDown(t){this.active&&t.preventDefault(),this.mouseButtons.keysDown.set(t.button,!0),this.mouseButtons.keysJustPressed.set(t.button,!0),this.mouseButtons.keysJustReleased.set(t.button,!1)}handleMouseMove(t){this.active&&t.preventDefault(),this.mousePosition.x=t.clientX,this.mousePosition.y=t.clientY,this.mouseDelta.x=t.movementX,this.mouseDelta.y=t.movementY}handleMouseUp(t){this.active&&t.preventDefault(),this.mouseButtons.keysJustReleased.set(t.button,!0),this.mouseButtons.keysDown.set(t.button,!1),this.mouseButtons.keysJustPressed.set(t.button,!1)}handlePointerChange(){document.pointerLockElement!==this.element&&this.element.addEventListener("click",(()=>{this.element.requestPointerLock()}),{once:!0})}}class M{}class k{constructor(){this.keysDown=new Map,this.keysJustPressed=new Map,this.keysJustReleased=new Map}}class C{}class I{constructor(t,e){this.InputIDs=t,this.element=e,this.inputs=new Map,this.init()}init(){this.InputIDs.forEach((t=>{const e=new(function(t){switch(t){case"keyboard":return z;case"mouse":return b}}(t))(this.element);this.inputs.set(t,e)}))}addInput(t,e){return this.inputs.set(t,e),this}capture(t=!0){this.inputs.forEach((e=>e.capture(t)))}release(){this.inputs.forEach((t=>t.release()))}getState(){const t={};return this.inputs.forEach(((e,s)=>{e&&(t[s]=e.getState())})),t}}class D{constructor(t){this.values=new Float32Array(4),void 0!==t&&(this.xyzw=t)}get x(){return this.values[0]}get y(){return this.values[1]}get z(){return this.values[2]}get w(){return this.values[3]}get xy(){return[this.values[0],this.values[1]]}get xyz(){return[this.values[0],this.values[1],this.values[2]]}get xyzw(){return[this.values[0],this.values[1],this.values[2],this.values[3]]}set x(t){this.values[0]=t}set y(t){this.values[1]=t}set z(t){this.values[2]=t}set w(t){this.values[3]=t}set xy(t){this.values[0]=t[0],this.values[1]=t[1]}set xyz(t){this.values[0]=t[0],this.values[1]=t[1],this.values[2]=t[2]}set xyzw(t){this.values[0]=t[0],this.values[1]=t[1],this.values[2]=t[2],this.values[3]=t[3]}get r(){return this.values[0]}get g(){return this.values[1]}get b(){return this.values[2]}get a(){return this.values[3]}get rg(){return[this.values[0],this.values[1]]}get rgb(){return[this.values[0],this.values[1],this.values[2]]}get rgba(){return[this.values[0],this.values[1],this.values[2],this.values[3]]}set r(t){this.values[0]=t}set g(t){this.values[1]=t}set b(t){this.values[2]=t}set a(t){this.values[3]=t}set rg(t){this.values[0]=t[0],this.values[1]=t[1]}set rgb(t){this.values[0]=t[0],this.values[1]=t[1],this.values[2]=t[2]}set rgba(t){this.values[0]=t[0],this.values[1]=t[1],this.values[2]=t[2],this.values[3]=t[3]}at(t){return this.values[t]}reset(){this.x=0,this.y=0,this.z=0,this.w=0}copy(t){return t||(t=new D),t.x=this.x,t.y=this.y,t.z=this.z,t.w=this.w,t}negate(t){return t||(t=this),t.x=-this.x,t.y=-this.y,t.z=-this.z,t.w=-this.w,t}equals(t,e=1e-5){return!(Math.abs(this.x-t.x)>e||Math.abs(this.y-t.y)>e||Math.abs(this.z-t.z)>e||Math.abs(this.w-t.w)>e)}length(){return Math.sqrt(this.squaredLength())}squaredLength(){const t=this.x,e=this.y,s=this.z,i=this.w;return t*t+e*e+s*s+i*i}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this.w+=t.w,this}subtract(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this.w-=t.w,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this.w*=t.w,this}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this.w/=t.w,this}scale(t,e){return e||(e=this),e.x*=t,e.y*=t,e.z*=t,e.w*=t,e}normalize(t){t||(t=this);let e=this.length();return 1===e?this:0===e?(t.x*=0,t.y*=0,t.z*=0,t.w*=0,t):(e=1/e,t.x*=e,t.y*=e,t.z*=e,t.w*=e,t)}multiplyMat4(t,e){return e||(e=this),t.multiplyVec4(this,e)}static mix(t,e,s,i){return i||(i=new D),i.x=t.x+s*(e.x-t.x),i.y=t.y+s*(e.y-t.y),i.z=t.z+s*(e.z-t.z),i.w=t.w+s*(e.w-t.w),i}static sum(t,e,s){return s||(s=new D),s.x=t.x+e.x,s.y=t.y+e.y,s.z=t.z+e.z,s.w=t.w+e.w,s}static difference(t,e,s){return s||(s=new D),s.x=t.x-e.x,s.y=t.y-e.y,s.z=t.z-e.z,s.w=t.w-e.w,s}static product(t,e,s){return s||(s=new D),s.x=t.x*e.x,s.y=t.y*e.y,s.z=t.z*e.z,s.w=t.w*e.w,s}static quotient(t,e,s){return s||(s=new D),s.x=t.x/e.x,s.y=t.y/e.y,s.z=t.z/e.z,s.w=t.w/e.w,s}}D.zero=new D([0,0,0,1]),D.one=new D([1,1,1,1]);class S{constructor(t){this.values=new Float32Array(16),void 0!==t&&this.init(t)}at(t){return this.values[t]}init(t){for(let e=0;e<16;e++)this.values[e]=t[e];return this}reset(){for(let t=0;t<16;t++)this.values[t]=0}copy(t){t||(t=new S);for(let e=0;e<16;e++)t.values[e]=this.values[e];return t}all(){const t=[];for(let e=0;e<16;e++)t[e]=this.values[e];return t}row(t){return[this.values[4*t+0],this.values[4*t+1],this.values[4*t+2],this.values[4*t+3]]}col(t){return[this.values[t],this.values[t+4],this.values[t+8],this.values[t+12]]}equals(t,e=1e-5){for(let s=0;s<16;s++)if(Math.abs(this.values[s]-t.at(s))>e)return!1;return!0}determinant(){const t=this.values[0],e=this.values[1],s=this.values[2],i=this.values[3],n=this.values[4],h=this.values[5],a=this.values[6],u=this.values[7],r=this.values[8],l=this.values[9],o=this.values[10],c=this.values[11],d=this.values[12],v=this.values[13],y=this.values[14],m=this.values[15];return(t*h-e*n)*(o*m-c*y)-(t*a-s*n)*(l*m-c*v)+(t*u-i*n)*(l*y-o*v)+(e*a-s*h)*(r*m-c*d)-(e*u-i*h)*(r*y-o*d)+(s*u-i*a)*(r*v-l*d)}setIdentity(){return this.values[0]=1,this.values[1]=0,this.values[2]=0,this.values[3]=0,this.values[4]=0,this.values[5]=1,this.values[6]=0,this.values[7]=0,this.values[8]=0,this.values[9]=0,this.values[10]=1,this.values[11]=0,this.values[12]=0,this.values[13]=0,this.values[14]=0,this.values[15]=1,this}transpose(){const t=this.values[1],e=this.values[2],s=this.values[3],i=this.values[6],n=this.values[7],h=this.values[11];return this.values[1]=this.values[4],this.values[2]=this.values[8],this.values[3]=this.values[12],this.values[4]=t,this.values[6]=this.values[9],this.values[7]=this.values[13],this.values[8]=e,this.values[9]=i,this.values[11]=this.values[14],this.values[12]=s,this.values[13]=n,this.values[14]=h,this}inverse(){const t=this.values[0],e=this.values[1],s=this.values[2],i=this.values[3],n=this.values[4],h=this.values[5],a=this.values[6],u=this.values[7],r=this.values[8],l=this.values[9],o=this.values[10],c=this.values[11],d=this.values[12],v=this.values[13],y=this.values[14],m=this.values[15],p=t*h-e*n,x=t*a-s*n,w=t*u-i*n,f=e*a-s*h,g=e*u-i*h,z=s*u-i*a,b=r*v-l*d,M=r*y-o*d,k=r*m-c*d,C=l*y-o*v,I=l*m-c*v,D=o*m-c*y;let S=p*D-x*I+w*C+f*k-g*M+z*b;return S?(S=1/S,this.values[0]=(h*D-a*I+u*C)*S,this.values[1]=(-e*D+s*I-i*C)*S,this.values[2]=(v*z-y*g+m*f)*S,this.values[3]=(-l*z+o*g-c*f)*S,this.values[4]=(-n*D+a*k-u*M)*S,this.values[5]=(t*D-s*k+i*M)*S,this.values[6]=(-d*z+y*w-m*x)*S,this.values[7]=(r*z-o*w+c*x)*S,this.values[8]=(n*I-h*k+u*b)*S,this.values[9]=(-t*I+e*k-i*b)*S,this.values[10]=(d*g-v*w+m*p)*S,this.values[11]=(-r*g+l*w-c*p)*S,this.values[12]=(-n*C+h*M-a*b)*S,this.values[13]=(t*C-e*M+s*b)*S,this.values[14]=(-d*f+v*x-y*p)*S,this.values[15]=(r*f-l*x+o*p)*S,this):null}multiply(t){const e=this.values[0],s=this.values[1],i=this.values[2],n=this.values[3],h=this.values[4],a=this.values[5],u=this.values[6],r=this.values[7],l=this.values[8],o=this.values[9],c=this.values[10],d=this.values[11],v=this.values[12],y=this.values[13],m=this.values[14],p=this.values[15];let x=t.at(0),w=t.at(1),f=t.at(2),g=t.at(3);return this.values[0]=x*e+w*h+f*l+g*v,this.values[1]=x*s+w*a+f*o+g*y,this.values[2]=x*i+w*u+f*c+g*m,this.values[3]=x*n+w*r+f*d+g*p,x=t.at(4),w=t.at(5),f=t.at(6),g=t.at(7),this.values[4]=x*e+w*h+f*l+g*v,this.values[5]=x*s+w*a+f*o+g*y,this.values[6]=x*i+w*u+f*c+g*m,this.values[7]=x*n+w*r+f*d+g*p,x=t.at(8),w=t.at(9),f=t.at(10),g=t.at(11),this.values[8]=x*e+w*h+f*l+g*v,this.values[9]=x*s+w*a+f*o+g*y,this.values[10]=x*i+w*u+f*c+g*m,this.values[11]=x*n+w*r+f*d+g*p,x=t.at(12),w=t.at(13),f=t.at(14),g=t.at(15),this.values[12]=x*e+w*h+f*l+g*v,this.values[13]=x*s+w*a+f*o+g*y,this.values[14]=x*i+w*u+f*c+g*m,this.values[15]=x*n+w*r+f*d+g*p,this}multiplyVec3(t){const e=t.x,s=t.y,i=t.z;return new A([this.values[0]*e+this.values[4]*s+this.values[8]*i+this.values[12],this.values[1]*e+this.values[5]*s+this.values[9]*i+this.values[13],this.values[2]*e+this.values[6]*s+this.values[10]*i+this.values[14]])}multiplyVec4(t,e){e||(e=new D);const s=t.x,i=t.y,n=t.z,h=t.w;return e.x=this.values[0]*s+this.values[4]*i+this.values[8]*n+this.values[12]*h,e.y=this.values[1]*s+this.values[5]*i+this.values[9]*n+this.values[13]*h,e.z=this.values[2]*s+this.values[6]*i+this.values[10]*n+this.values[14]*h,e.w=this.values[3]*s+this.values[7]*i+this.values[11]*n+this.values[15]*h,e}toMat3(){return new P([this.values[0],this.values[1],this.values[2],this.values[4],this.values[5],this.values[6],this.values[8],this.values[9],this.values[10]])}toInverseMat3(){const t=this.values[0],e=this.values[1],s=this.values[2],i=this.values[4],n=this.values[5],h=this.values[6],a=this.values[8],u=this.values[9],r=this.values[10],l=r*n-h*u,o=-r*i+h*a,c=u*i-n*a;let d=t*l+e*o+s*c;return d?(d=1/d,new P([l*d,(-r*e+s*u)*d,(h*e-s*n)*d,o*d,(r*t-s*a)*d,(-h*t+s*i)*d,c*d,(-u*t+e*a)*d,(n*t-e*i)*d])):null}translate(t){const e=t.x,s=t.y,i=t.z;return this.values[12]+=this.values[0]*e+this.values[4]*s+this.values[8]*i,this.values[13]+=this.values[1]*e+this.values[5]*s+this.values[9]*i,this.values[14]+=this.values[2]*e+this.values[6]*s+this.values[10]*i,this.values[15]+=this.values[3]*e+this.values[7]*s+this.values[11]*i,this}scale(t){const e=t.x,s=t.y,i=t.z;return this.values[0]*=e,this.values[1]*=e,this.values[2]*=e,this.values[3]*=e,this.values[4]*=s,this.values[5]*=s,this.values[6]*=s,this.values[7]*=s,this.values[8]*=i,this.values[9]*=i,this.values[10]*=i,this.values[11]*=i,this}rotate(t,e){let s=e.x,i=e.y,n=e.z,h=Math.sqrt(s*s+i*i+n*n);if(!h)return null;1!==h&&(h=1/h,s*=h,i*=h,n*=h);const a=Math.sin(t),u=Math.cos(t),r=1-u,l=this.values[0],o=this.values[1],c=this.values[2],d=this.values[3],v=this.values[4],y=this.values[5],m=this.values[6],p=this.values[7],x=this.values[8],w=this.values[9],f=this.values[10],g=this.values[11],z=s*s*r+u,b=i*s*r+n*a,M=n*s*r-i*a,k=s*i*r-n*a,C=i*i*r+u,I=n*i*r+s*a,D=s*n*r+i*a,S=i*n*r-s*a,E=n*n*r+u;return this.values[0]=l*z+v*b+x*M,this.values[1]=o*z+y*b+w*M,this.values[2]=c*z+m*b+f*M,this.values[3]=d*z+p*b+g*M,this.values[4]=l*k+v*C+x*I,this.values[5]=o*k+y*C+w*I,this.values[6]=c*k+m*C+f*I,this.values[7]=d*k+p*C+g*I,this.values[8]=l*D+v*S+x*E,this.values[9]=o*D+y*S+w*E,this.values[10]=c*D+m*S+f*E,this.values[11]=d*D+p*S+g*E,this}static frustum(t,e,s,i,n,h){const a=e-t,u=i-s,r=h-n;return new S([2*n/a,0,0,0,0,2*n/u,0,0,(e+t)/a,(i+s)/u,-(h+n)/r,-1,0,0,-h*n*2/r,0])}static perspective(t,e,s,i){const n=s*Math.tan(t*Math.PI/360),h=n*e;return S.frustum(-h,h,-n,n,s,i)}static orthographic(t,e,s,i,n,h){const a=e-t,u=i-s,r=h-n;return new S([2/a,0,0,0,0,2/u,0,0,0,0,-2/r,0,-(t+e)/a,-(i+s)/u,-(h+n)/r,1])}static lookAt(t,e,s=A.up){if(t.equals(e))return this.identity;const i=A.difference(t,e).normalize(),n=A.cross(s,i).normalize(),h=A.cross(i,n).normalize();return new S([n.x,h.x,i.x,0,n.y,h.y,i.y,0,n.z,h.z,i.z,0,-A.dot(n,t),-A.dot(h,t),-A.dot(i,t),1])}static product(t,e,s){const i=t.at(0),n=t.at(1),h=t.at(2),a=t.at(3),u=t.at(4),r=t.at(5),l=t.at(6),o=t.at(7),c=t.at(8),d=t.at(9),v=t.at(10),y=t.at(11),m=t.at(12),p=t.at(13),x=t.at(14),w=t.at(15),f=e.at(0),g=e.at(1),z=e.at(2),b=e.at(3),M=e.at(4),k=e.at(5),C=e.at(6),I=e.at(7),D=e.at(8),E=e.at(9),P=e.at(10),q=e.at(11),A=e.at(12),L=e.at(13),_=e.at(14),O=e.at(15);return s?(s.init([f*i+g*u+z*c+b*m,f*n+g*r+z*d+b*p,f*h+g*l+z*v+b*x,f*a+g*o+z*y+b*w,M*i+k*u+C*c+I*m,M*n+k*r+C*d+I*p,M*h+k*l+C*v+I*x,M*a+k*o+C*y+I*w,D*i+E*u+P*c+q*m,D*n+E*r+P*d+q*p,D*h+E*l+P*v+q*x,D*a+E*o+P*y+q*w,A*i+L*u+_*c+O*m,A*n+L*r+_*d+O*p,A*h+L*l+_*v+O*x,A*a+L*o+_*y+O*w]),s):new S([f*i+g*u+z*c+b*m,f*n+g*r+z*d+b*p,f*h+g*l+z*v+b*x,f*a+g*o+z*y+b*w,M*i+k*u+C*c+I*m,M*n+k*r+C*d+I*p,M*h+k*l+C*v+I*x,M*a+k*o+C*y+I*w,D*i+E*u+P*c+q*m,D*n+E*r+P*d+q*p,D*h+E*l+P*v+q*x,D*a+E*o+P*y+q*w,A*i+L*u+_*c+O*m,A*n+L*r+_*d+O*p,A*h+L*l+_*v+O*x,A*a+L*o+_*y+O*w])}}S.identity=(new S).setIdentity();class E{constructor(t){this.values=new Float32Array(2),void 0!==t&&(this.xy=t)}get x(){return this.values[0]}get y(){return this.values[1]}get xy(){return[this.values[0],this.values[1]]}set x(t){this.values[0]=t}set y(t){this.values[1]=t}set xy(t){this.values[0]=t[0],this.values[1]=t[1]}at(t){return this.values[t]}reset(){this.x=0,this.y=0}copy(t){return t||(t=new E),t.x=this.x,t.y=this.y,t}negate(t){return t||(t=this),t.x=-this.x,t.y=-this.y,t}equals(t,e=1e-5){return!(Math.abs(this.x-t.x)>e||Math.abs(this.y-t.y)>e)}length(){return Math.sqrt(this.squaredLength())}squaredLength(){const t=this.x,e=this.y;return t*t+e*e}add(t){return this.x+=t.x,this.y+=t.y,this}subtract(t){return this.x-=t.x,this.y-=t.y,this}multiply(t){return this.x*=t.x,this.y*=t.y,this}divide(t){return this.x/=t.x,this.y/=t.y,this}scale(t,e){return e||(e=this),e.x*=t,e.y*=t,e}normalize(t){t||(t=this);let e=this.length();return 1===e?this:0===e?(t.x=0,t.y=0,t):(e=1/e,t.x*=e,t.y*=e,t)}multiplyMat2(t,e){return e||(e=this),t.multiplyVec2(this,e)}multiplyMat3(t,e){return e||(e=this),t.multiplyVec2(this,e)}static cross(t,e,s){s||(s=new A);const i=t.x,n=t.y,h=e.x,a=i*e.y-n*h;return s.x=0,s.y=0,s.z=a,s}static dot(t,e){return t.x*e.x+t.y*e.y}static distance(t,e){return Math.sqrt(this.squaredDistance(t,e))}static squaredDistance(t,e){const s=e.x-t.x,i=e.y-t.y;return s*s+i*i}static direction(t,e,s){s||(s=new E);const i=t.x-e.x,n=t.y-e.y;let h=Math.sqrt(i*i+n*n);return 0===h?(s.x=0,s.y=0,s):(h=1/h,s.x=i*h,s.y=n*h,s)}static mix(t,e,s,i){i||(i=new E);const n=t.x,h=t.y,a=e.x,u=e.y;return i.x=n+s*(a-n),i.y=h+s*(u-h),i}static sum(t,e,s){return s||(s=new E),s.x=t.x+e.x,s.y=t.y+e.y,s}static difference(t,e,s){return s||(s=new E),s.x=t.x-e.x,s.y=t.y-e.y,s}static product(t,e,s){return s||(s=new E),s.x=t.x*e.x,s.y=t.y*e.y,s}static quotient(t,e,s){return s||(s=new E),s.x=t.x/e.x,s.y=t.y/e.y,s}}E.zero=new E([0,0]),E.one=new E([1,1]);class P{constructor(t){this.values=new Float32Array(9),void 0!==t&&this.init(t)}at(t){return this.values[t]}init(t){for(let e=0;e<9;e++)this.values[e]=t[e];return this}reset(){for(let t=0;t<9;t++)this.values[t]=0}copy(t){t||(t=new P);for(let e=0;e<9;e++)t.values[e]=this.values[e];return t}all(){const t=[];for(let e=0;e<9;e++)t[e]=this.values[e];return t}row(t){return[this.values[3*t+0],this.values[3*t+1],this.values[3*t+2]]}col(t){return[this.values[t],this.values[t+3],this.values[t+6]]}equals(t,e=1e-5){for(let s=0;s<9;s++)if(Math.abs(this.values[s]-t.at(s))>e)return!1;return!0}determinant(){const t=this.values[0],e=this.values[1],s=this.values[2],i=this.values[3],n=this.values[4],h=this.values[5],a=this.values[6],u=this.values[7],r=this.values[8];return t*(r*n-h*u)+e*(-r*i+h*a)+s*(u*i-n*a)}setIdentity(){return this.values[0]=1,this.values[1]=0,this.values[2]=0,this.values[3]=0,this.values[4]=1,this.values[5]=0,this.values[6]=0,this.values[7]=0,this.values[8]=1,this}transpose(){const t=this.values[1],e=this.values[2],s=this.values[5];return this.values[1]=this.values[3],this.values[2]=this.values[6],this.values[3]=t,this.values[5]=this.values[7],this.values[6]=e,this.values[7]=s,this}inverse(){const t=this.values[0],e=this.values[1],s=this.values[2],i=this.values[3],n=this.values[4],h=this.values[5],a=this.values[6],u=this.values[7],r=this.values[8],l=r*n-h*u,o=-r*i+h*a,c=u*i-n*a;let d=t*l+e*o+s*c;return d?(d=1/d,this.values[0]=l*d,this.values[1]=(-r*e+s*u)*d,this.values[2]=(h*e-s*n)*d,this.values[3]=o*d,this.values[4]=(r*t-s*a)*d,this.values[5]=(-h*t+s*i)*d,this.values[6]=c*d,this.values[7]=(-u*t+e*a)*d,this.values[8]=(n*t-e*i)*d,this):null}multiply(t){const e=this.values[0],s=this.values[1],i=this.values[2],n=this.values[3],h=this.values[4],a=this.values[5],u=this.values[6],r=this.values[7],l=this.values[8],o=t.at(0),c=t.at(1),d=t.at(2),v=t.at(3),y=t.at(4),m=t.at(5),p=t.at(6),x=t.at(7),w=t.at(8);return this.values[0]=o*e+c*n+d*u,this.values[1]=o*s+c*h+d*r,this.values[2]=o*i+c*a+d*l,this.values[3]=v*e+y*n+m*u,this.values[4]=v*s+y*h+m*r,this.values[5]=v*i+y*a+m*l,this.values[6]=p*e+x*n+w*u,this.values[7]=p*s+x*h+w*r,this.values[8]=p*i+x*a+w*l,this}multiplyVec2(t,e){const s=t.x,i=t.y;return e?(e.xy=[s*this.values[0]+i*this.values[3]+this.values[6],s*this.values[1]+i*this.values[4]+this.values[7]],e):new E([s*this.values[0]+i*this.values[3]+this.values[6],s*this.values[1]+i*this.values[4]+this.values[7]])}multiplyVec3(t,e){const s=t.x,i=t.y,n=t.z;return e?(e.xyz=[s*this.values[0]+i*this.values[3]+n*this.values[6],s*this.values[1]+i*this.values[4]+n*this.values[7],s*this.values[2]+i*this.values[5]+n*this.values[8]],e):new A([s*this.values[0]+i*this.values[3]+n*this.values[6],s*this.values[1]+i*this.values[4]+n*this.values[7],s*this.values[2]+i*this.values[5]+n*this.values[8]])}toMat4(t){return t?(t.init([this.values[0],this.values[1],this.values[2],0,this.values[3],this.values[4],this.values[5],0,this.values[6],this.values[7],this.values[8],0,0,0,0,1]),t):new S([this.values[0],this.values[1],this.values[2],0,this.values[3],this.values[4],this.values[5],0,this.values[6],this.values[7],this.values[8],0,0,0,0,1])}toQuat(){const t=this.values[0],e=this.values[1],s=this.values[2],i=this.values[3],n=this.values[4],h=this.values[5],a=this.values[6],u=this.values[7],r=this.values[8],l=t-n-r,o=n-t-r,c=r-t-n;let d=0,v=t+n+r;l>v&&(v=l,d=1),o>v&&(v=o,d=2),c>v&&(v=c,d=3);const y=.5*Math.sqrt(v+1),m=.25/y,p=new q;switch(d){case 0:p.w=y,p.x=(h-u)*m,p.y=(a-s)*m,p.z=(e-i)*m;break;case 1:p.w=(h-u)*m,p.x=y,p.y=(e+i)*m,p.z=(a+s)*m;break;case 2:p.w=(a-s)*m,p.x=(e+i)*m,p.y=y,p.z=(h+u)*m;break;case 3:p.w=(e-i)*m,p.x=(a+s)*m,p.y=(h+u)*m,p.z=y}return p}rotate(t,e){let s=e.x,i=e.y,n=e.z,h=Math.sqrt(s*s+i*i+n*n);if(!h)return null;1!==h&&(h=1/h,s*=h,i*=h,n*=h);const a=Math.sin(t),u=Math.cos(t),r=1-u,l=this.values[0],o=this.values[1],c=this.values[2],d=this.values[4],v=this.values[5],y=this.values[6],m=this.values[8],p=this.values[9],x=this.values[10],w=s*s*r+u,f=i*s*r+n*a,g=n*s*r-i*a,z=s*i*r-n*a,b=i*i*r+u,M=n*i*r+s*a,k=s*n*r+i*a,C=i*n*r-s*a,I=n*n*r+u;return this.values[0]=l*w+d*f+m*g,this.values[1]=o*w+v*f+p*g,this.values[2]=c*w+y*f+x*g,this.values[3]=l*z+d*b+m*M,this.values[4]=o*z+v*b+p*M,this.values[5]=c*z+y*b+x*M,this.values[6]=l*k+d*C+m*I,this.values[7]=o*k+v*C+p*I,this.values[8]=c*k+y*C+x*I,this}static product(t,e,s){const i=t.at(0),n=t.at(1),h=t.at(2),a=t.at(3),u=t.at(4),r=t.at(5),l=t.at(6),o=t.at(7),c=t.at(8),d=e.at(0),v=e.at(1),y=e.at(2),m=e.at(3),p=e.at(4),x=e.at(5),w=e.at(6),f=e.at(7),g=e.at(8);return s?(s.init([d*i+v*a+y*l,d*n+v*u+y*o,d*h+v*r+y*c,m*i+p*a+x*l,m*n+p*u+x*o,m*h+p*r+x*c,w*i+f*a+g*l,w*n+f*u+g*o,w*h+f*r+g*c]),s):new P([d*i+v*a+y*l,d*n+v*u+y*o,d*h+v*r+y*c,m*i+p*a+x*l,m*n+p*u+x*o,m*h+p*r+x*c,w*i+f*a+g*l,w*n+f*u+g*o,w*h+f*r+g*c])}}P.identity=(new P).setIdentity();class q{constructor(t){this.values=new Float32Array(4),void 0!==t&&(this.xyzw=t)}get x(){return this.values[0]}get y(){return this.values[1]}get z(){return this.values[2]}get w(){return this.values[3]}get xy(){return[this.values[0],this.values[1]]}get xyz(){return[this.values[0],this.values[1],this.values[2]]}get xyzw(){return[this.values[0],this.values[1],this.values[2],this.values[3]]}set x(t){this.values[0]=t}set y(t){this.values[1]=t}set z(t){this.values[2]=t}set w(t){this.values[3]=t}set xy(t){this.values[0]=t[0],this.values[1]=t[1]}set xyz(t){this.values[0]=t[0],this.values[1]=t[1],this.values[2]=t[2]}set xyzw(t){this.values[0]=t[0],this.values[1]=t[1],this.values[2]=t[2],this.values[3]=t[3]}at(t){return this.values[t]}reset(){for(let t=0;t<4;t++)this.values[t]=0}copy(t){t||(t=new q);for(let e=0;e<4;e++)t.values[e]=this.values[e];return t}roll(){const t=this.x,e=this.y,s=this.z,i=this.w;return Math.atan2(2*(t*e+i*s),i*i+t*t-e*e-s*s)}pitch(){const t=this.x,e=this.y,s=this.z,i=this.w;return Math.atan2(2*(e*s+i*t),i*i-t*t-e*e+s*s)}yaw(){return Math.asin(2*(this.x*this.z-this.w*this.y))}equals(t,e=1e-5){for(let s=0;s<4;s++)if(Math.abs(this.values[s]-t.at(s))>e)return!1;return!0}setIdentity(){return this.x=0,this.y=0,this.z=0,this.w=1,this}calculateW(){const t=this.x,e=this.y,s=this.z;return this.w=-Math.sqrt(Math.abs(1-t*t-e*e-s*s)),this}inverse(){const t=q.dot(this,this);if(!t)return this.xyzw=[0,0,0,0],this;const e=t?1/t:0;return this.x*=-e,this.y*=-e,this.z*=-e,this.w*=e,this}conjugate(){return this.values[0]*=-1,this.values[1]*=-1,this.values[2]*=-1,this}length(){const t=this.x,e=this.y,s=this.z,i=this.w;return Math.sqrt(t*t+e*e+s*s+i*i)}normalize(t){t||(t=this);const e=this.x,s=this.y,i=this.z,n=this.w;let h=Math.sqrt(e*e+s*s+i*i+n*n);return h?(h=1/h,t.x=e*h,t.y=s*h,t.z=i*h,t.w=n*h,t):(t.x=0,t.y=0,t.z=0,t.w=0,t)}add(t){for(let e=0;e<4;e++)this.values[e]+=t.at(e);return this}multiply(t){const e=this.values[0],s=this.values[1],i=this.values[2],n=this.values[3],h=t.x,a=t.y,u=t.z,r=t.w;return this.x=e*r+n*h+s*u-i*a,this.y=s*r+n*a+i*h-e*u,this.z=i*r+n*u+e*a-s*h,this.w=n*r-e*h-s*a-i*u,this}multiplyVec3(t,e){e||(e=new A);const s=t.x,i=t.y,n=t.z,h=this.x,a=this.y,u=this.z,r=this.w,l=r*s+a*n-u*i,o=r*i+u*s-h*n,c=r*n+h*i-a*s,d=-h*s-a*i-u*n;return e.x=l*r+d*-h+o*-u-c*-a,e.y=o*r+d*-a+c*-h-l*-u,e.z=c*r+d*-u+l*-a-o*-h,e}toMat3(t){t||(t=new P);const e=this.x,s=this.y,i=this.z,n=this.w,h=e+e,a=s+s,u=i+i,r=e*h,l=e*a,o=e*u,c=s*a,d=s*u,v=i*u,y=n*h,m=n*a,p=n*u;return t.init([1-(c+v),l+p,o-m,l-p,1-(r+v),d+y,o+m,d-y,1-(r+c)]),t}toMat4(t){t||(t=new S);const e=this.x,s=this.y,i=this.z,n=this.w,h=e+e,a=s+s,u=i+i,r=e*h,l=e*a,o=e*u,c=s*a,d=s*u,v=i*u,y=n*h,m=n*a,p=n*u;return t.init([1-(c+v),l+p,o-m,0,l-p,1-(r+v),d+y,0,o+m,d-y,1-(r+c),0,0,0,0,1]),t}static dot(t,e){return t.x*e.x+t.y*e.y+t.z*e.z+t.w*e.w}static sum(t,e,s){return s||(s=new q),s.x=t.x+e.x,s.y=t.y+e.y,s.z=t.z+e.z,s.w=t.w+e.w,s}static product(t,e,s){s||(s=new q);const i=t.x,n=t.y,h=t.z,a=t.w,u=e.x,r=e.y,l=e.z,o=e.w;return s.x=i*o+a*u+n*l-h*r,s.y=n*o+a*r+h*u-i*l,s.z=h*o+a*l+i*r-n*u,s.w=a*o-i*u-n*r-h*l,s}static cross(t,e,s){s||(s=new q);const i=t.x,n=t.y,h=t.z,a=t.w,u=e.x,r=e.y,l=e.z,o=e.w;return s.x=a*l+h*o+i*r-n*u,s.y=a*o-i*u-n*r-h*l,s.z=a*u+i*o+n*l-h*r,s.w=a*r+n*o+h*u-i*l,s}static shortMix(t,e,s,i){if(i||(i=new q),s<=0)return i.xyzw=t.xyzw,i;if(s>=1)return i.xyzw=e.xyzw,i;let n=q.dot(t,e);const h=e.copy();let a,u;if(n<0&&(h.inverse(),n=-n),n>.9999)a=1-s,u=0+s;else{const t=Math.sqrt(1-n*n),e=Math.atan2(t,n),i=1/t;a=Math.sin((1-s)*e)*i,u=Math.sin((0+s)*e)*i}return i.x=a*t.x+u*h.x,i.y=a*t.y+u*h.y,i.z=a*t.z+u*h.z,i.w=a*t.w+u*h.w,i}static mix(t,e,s,i){i||(i=new q);const n=t.x*e.x+t.y*e.y+t.z*e.z+t.w*e.w;if(Math.abs(n)>=1)return i.xyzw=t.xyzw,i;const h=Math.acos(n),a=Math.sqrt(1-n*n);if(Math.abs(a)<.001)return i.x=.5*t.x+.5*e.x,i.y=.5*t.y+.5*e.y,i.z=.5*t.z+.5*e.z,i.w=.5*t.w+.5*e.w,i;const u=Math.sin((1-s)*h)/a,r=Math.sin(s*h)/a;return i.x=t.x*u+e.x*r,i.y=t.y*u+e.y*r,i.z=t.z*u+e.z*r,i.w=t.w*u+e.w*r,i}static fromAxisAngle(t,e,s){s||(s=new q),e*=.5;const i=Math.sin(e);return s.x=t.x*i,s.y=t.y*i,s.z=t.z*i,s.w=Math.cos(e),s}}q.identity=(new q).setIdentity();class A{constructor(t){this.values=new Float32Array(3),void 0!==t&&(this.xyz=t)}get x(){return this.values[0]}get y(){return this.values[1]}get z(){return this.values[2]}get xy(){return[this.values[0],this.values[1]]}get xyz(){return[this.values[0],this.values[1],this.values[2]]}set x(t){this.values[0]=t}set y(t){this.values[1]=t}set z(t){this.values[2]=t}set xy(t){this.values[0]=t[0],this.values[1]=t[1]}set xyz(t){this.values[0]=t[0],this.values[1]=t[1],this.values[2]=t[2]}at(t){return this.values[t]}reset(){this.x=0,this.y=0,this.z=0}copy(t){return t||(t=new A),t.x=this.x,t.y=this.y,t.z=this.z,t}negate(t){return t||(t=this),t.x=-this.x,t.y=-this.y,t.z=-this.z,t}equals(t,e=1e-5){return!(Math.abs(this.x-t.x)>e||Math.abs(this.y-t.y)>e||Math.abs(this.z-t.z)>e)}length(){return Math.sqrt(this.squaredLength())}squaredLength(){const t=this.x,e=this.y,s=this.z;return t*t+e*e+s*s}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this}subtract(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this}scale(t,e){return e||(e=this),e.x*=t,e.y*=t,e.z*=t,e}normalize(t){t||(t=this);let e=this.length();return 1===e?this:0===e?(t.x=0,t.y=0,t.z=0,t):(e=1/e,t.x*=e,t.y*=e,t.z*=e,t)}multiplyByMat3(t,e){return e||(e=this),t.multiplyVec3(this,e)}multiplyByQuat(t,e){return e||(e=this),t.multiplyVec3(this,e)}toQuat(t){t||(t=new q);const e=new A,s=new A;return e.x=Math.cos(.5*this.x),s.x=Math.sin(.5*this.x),e.y=Math.cos(.5*this.y),s.y=Math.sin(.5*this.y),e.z=Math.cos(.5*this.z),s.z=Math.sin(.5*this.z),t.x=s.x*e.y*e.z-e.x*s.y*s.z,t.y=e.x*s.y*e.z+s.x*e.y*s.z,t.z=e.x*e.y*s.z-s.x*s.y*e.z,t.w=e.x*e.y*e.z+s.x*s.y*s.z,t}static cross(t,e,s){s||(s=new A);const i=t.x,n=t.y,h=t.z,a=e.x,u=e.y,r=e.z;return s.x=n*r-h*u,s.y=h*a-i*r,s.z=i*u-n*a,s}static dot(t,e){const s=t.x,i=t.y,n=t.z;return s*e.x+i*e.y+n*e.z}static distance(t,e){return e.x,t.x,e.y,t.y,e.z,t.z,Math.sqrt(this.squaredDistance(t,e))}static squaredDistance(t,e){const s=e.x-t.x,i=e.y-t.y,n=e.z-t.z;return s*s+i*i+n*n}static direction(t,e,s){s||(s=new A);const i=t.x-e.x,n=t.y-e.y,h=t.z-e.z;let a=Math.sqrt(i*i+n*n+h*h);return 0===a?(s.x=0,s.y=0,s.z=0,s):(a=1/a,s.x=i*a,s.y=n*a,s.z=h*a,s)}static mix(t,e,s,i){return i||(i=new A),i.x=t.x+s*(e.x-t.x),i.y=t.y+s*(e.y-t.y),i.z=t.z+s*(e.z-t.z),i}static sum(t,e,s){return s||(s=new A),s.x=t.x+e.x,s.y=t.y+e.y,s.z=t.z+e.z,s}static difference(t,e,s){return s||(s=new A),s.x=t.x-e.x,s.y=t.y-e.y,s.z=t.z-e.z,s}static product(t,e,s){return s||(s=new A),s.x=t.x*e.x,s.y=t.y*e.y,s.z=t.z*e.z,s}static quotient(t,e,s){return s||(s=new A),s.x=t.x/e.x,s.y=t.y/e.y,s.z=t.z/e.z,s}}A.zero=new A([0,0,0]),A.one=new A([1,1,1]),A.up=new A([0,1,0]),A.right=new A([1,0,0]),A.forward=new A([0,0,1]),(c=o||(o={}))[c.WorldSource=0]="WorldSource",c[c.UISource=1]="UISource",c[c.MasterSource=2]="MasterSource";class L{speak(t){}stop(){}setOptions(t){}}class _ extends L{constructor(t={}){super(),this.timeout=100,this.timeout=t.timeout||100,this.init()}init(){this.container=document.createElement("div"),this.container.setAttribute("aria-live","polite"),this.speechDisplay=document.createElement("div"),this.speechDisplay.setAttribute("aria-live","polite"),this.container.append(this.speechDisplay),document.body.appendChild(this.container),document.body.insertBefore(this.container,document.body.firstChild)}speak(t){this.clearDisplay();const e=document.createTextNode(t),s=document.createElement("p");s.appendChild(e),this.speechDisplay.appendChild(s),setTimeout(this.clearDisplay.bind(this),this.timeout)}stop(){this.clearDisplay()}clearDisplay(){this.speechDisplay.innerHTML=""}}class O extends L{}class F{constructor(t=function(t="aria"){return"webtts"===t?O:_}()){this.output=t}speak(t){this.output.speak(t)}stop(){this.output.stop()}}class N{constructor(t=null){this.soundSet=null,this.data=new Map,this.soundSet=t}setSoundSet(t){this.soundSet=t}handleSound(t,e=null){switch(t){case"edit":this.handleEditSound(e);break;case"slider":this.handleSliderSound(e);break;case"selector":this.handleSelectorSound(e);break;case"checkbox":this.handleCheckboxSound(e);break;case"focus":this.handleFocusSound();break;case"choose":this.handleChooseSound();break;case"open":this.handleOpenSound();break;case"close":this.handleCloseSound();break;default:return}}handleEditSound(t){const e=this.data.get("edit")||"";t.length<=e.length?this.soundSet.delete&&this.soundSet.delete.play():this.soundSet.char&&this.soundSet.char.play(),this.data.set("edit",t)}handleSelectorSound(t){this.soundSet.scroller&&this.soundSet.scroller.play()}handleSliderSound(t){tthis.menu.clickCancelAction()}handler(t){switch(t.key){case"ArrowDown":t.preventDefault(),this.menu.focusNext();break;case"ArrowUp":t.preventDefault(),this.menu.focusPrevious();break;case"Enter":t.preventDefault(),this.menu.clickDefaultAction();break;case"Escape":t.preventDefault(),this.menu.clickCancelAction()}}release(){this.menu.getContainer().removeEventListener("keydown",this.handler.bind(this)),window.onpopstate=null}}class T extends t{constructor(t,e){super(),this.id=t,this.title=e}getDOMNode(){let t=document.createTextNode(this.title),e=document.createElement("div");return e.appendChild(t),e}getContents(){}onFocus(t){this.emit("focus",this.id)}focus(){this.container&&this.container.focus()}click(){}getID(){return this.id}}class B extends T{constructor(t,e,s,i=!1){super(t,e),this.initialText=s,this.isPassword=i,this.contents=s}getDOMNode(){const t=document.createElement("div"),e=document.createElement("label");e.setAttribute("for",`edit_${this.id}`),e.textContent=this.title;const s=document.createElement("input");return s.id=`edit_${this.id}`,s.value=this.contents,s.addEventListener("keydown",this.onChange.bind(this)),s.addEventListener("focus",this.onFocus.bind(this)),this.isPassword&&(s.type="password"),t.appendChild(e),t.appendChild(s),t.addEventListener("focus",this.onFocus.bind(this)),this.editField=s,this.label=e,this.container=t,t}getContents(){return this.editField.value}onChange(t){this.emit("update",{type:"edit",value:this.editField.value})}focus(){this.editField&&this.editField.focus()}}class $ extends T{constructor(t,e){super(t,e)}getDOMNode(){const t=document.createElement("div"),e=document.createElement("button");return e.textContent=this.title,e.addEventListener("click",this.handleClick.bind(this)),e.addEventListener("focus",this.onFocus.bind(this)),t.appendChild(e),this.container=t,this.button=e,t}getContents(){return this.id}handleClick(t){this.emit("choose",this.id)}focus(){this.button&&this.button.focus()}click(){this.button.click()}}class V extends T{constructor(t,e,s){super(t,e),this.items=s,this.entries=[]}getDOMNode(){this.container=document.createElement("div"),this.listContainer=document.createElement("ul"),this.label=document.createElement("legend"),this.fieldSet=document.createElement("fieldset"),this.fieldSet.setAttribute("class","radiogroup"),this.fieldSet.id=`fs_selector_${this.id}`;const t=document.createTextNode(this.title);return this.label.appendChild(t),this.fieldSet.appendChild(this.label),this.buildEntries(),this.container.appendChild(this.fieldSet),this.container.addEventListener("focus",this.onFocus.bind(this)),this.container}buildEntries(){this.items.forEach(((t,e)=>{const s=document.createElement("input");s.type="radio",s.id=`${this.id}_${t.id}`,s.name=this.id,s.value=t.id||`${e}`,s.addEventListener("focus",this.onItemFocus.bind(this)),s.addEventListener("select",this.onSelectItem.bind(this)),s.addEventListener("change",this.onChangeItem.bind(this)),this.entries.push(s);const i=document.createElement("label");i.setAttribute("for",`${this.id}_${t.id}`),i.textContent=t.title,this.fieldSet.append(s),this.fieldSet.append(i)}))}onItemFocus(t){console.log("Item focused: ",t),this.emit("focus",this.id)}getContents(){return this.currentValue}onSelectItem(t){}onChangeItem(t){const e=document.querySelector(`input[name = "${this.id}"]:checked`);this.currentValue=this.items.find((t=>`${this.id}_${t.id}`===e.id)),this.emit("update",{type:"selector",value:this.currentValue})}focus(){(document.querySelector(`input[name = "${this.id}"]:checked`)||this.entries[0]).focus()}}class U extends T{constructor(t,e,s,i,n,h=null){super(t,e),this.min=s,this.max=i,this.step=n,this.defaultValue=h}getDOMNode(){return this.container=document.createElement("div"),this.label=document.createElement("label"),this.label.textContent=this.title,this.label.setAttribute("for",`slider_${this.id}`),this.slider=document.createElement("input"),this.slider.id=`slider_${this.id}`,this.slider.type="range",this.slider.setAttribute("min",this.min.toString()),this.slider.setAttribute("max",this.max.toString()),this.slider.setAttribute("step",this.step.toString()),this.defaultValue&&(this.slider.value=this.defaultValue.toString()),this.slider.addEventListener("change",this.onChange.bind(this)),this.slider.addEventListener("focus",this.onFocus.bind(this)),this.container.appendChild(this.label),this.container.appendChild(this.slider),this.container.addEventListener("focus",this.onFocus.bind(this)),this.container}getContents(){return this.slider.value}onChange(t){this.emit("update",{type:"slider",value:this.slider.value})}focus(){this.slider&&this.slider.focus()}}class R extends T{constructor(t,e){super(t,e)}getDOMNode(){return this.container=document.createElement("div"),this.label=document.createElement("label"),this.label.setAttribute("for",`chkbx_${this.id}`),this.label.textContent=this.title,this.checkboxElement=document.createElement("input"),this.checkboxElement.setAttribute("type","checkbox"),this.checkboxElement.setAttribute("id",`chkbx_${this.id}`),this.checkboxElement.addEventListener("focus",this.onFocus.bind(this)),this.checkboxElement.addEventListener("change",this.onChange.bind(this)),this.container.appendChild(this.label),this.container.appendChild(this.checkboxElement),this.container}getContents(){return this.checkboxElement.checked}onChange(t){this.emit("update",{type:"checkbox",value:this.checkboxElement.checked})}focus(){this.checkboxElement.focus()}}class Q extends t{constructor(t="Menu",e=[],s=null,i=null,n=null){super(),this.title=t,this.menuItems=e,this.soundSet=s,this.defaultAction=i,this.cancelAction=n,this.currentIndex=0,this.DOMNodes=[],this.currentIndex=0,this.currentItem=null,this.soundManager=new N(s),this.keyboardManager=new J(this),this.init()}init(){this.menuItems[this.currentIndex]&&this.menuItems[this.currentIndex].focus(),this.emit("init")}addItem(t){return this.menuItems.push(t),this.emit("item.add",t),this}setTitle(t){return this.title=t,this}setSoundSet(t){return this.soundSet=t,this.soundManager.setSoundSet(this.soundSet),this}setDefaultAction(t){return this.defaultAction=t,this}setCancelAction(t){return this.cancelAction=t,this}run(t){return e=this,s=void 0,n=function*(){return new Promise(((e,s)=>{this.element=t,this.container=document.createElement("div"),this.titleContainer=document.createElement("h1"),this.titleContainer.textContent=this.title,this.container.appendChild(this.titleContainer),this.menuItems.forEach((t=>{this.appendToContainer(t.getDOMNode()),t.on("update",this.handleItemUpdate.bind(this)),t.on("focus",this.onItemFocus.bind(this)),t.on("choose",(t=>{const s=this.compile();this.soundManager.handleSound("choose"),this.emit("choose",s),e(s)}))})),t.appendChild(this.container),this.soundManager.handleSound("open"),this.keyboardManager.init(),history.pushState({menu:!0},null,null)}))},new((i=void 0)||(i=Promise))((function(t,h){function a(t){try{r(n.next(t))}catch(t){h(t)}}function u(t){try{r(n.throw(t))}catch(t){h(t)}}function r(e){var s;e.done?t(e.value):(s=e.value,s instanceof i?s:new i((function(t){t(s)}))).then(a,u)}r((n=n.apply(e,s||[])).next())}));var e,s,i,n}close(){this.container.remove(),this.soundManager.handleSound("close"),this.keyboardManager.release(),this.DOMNodes.forEach((t=>{this.container.removeChild(t)})),this.emit("close")}appendToContainer(t){this.container.appendChild(t),this.DOMNodes.push(t)}handleItemUpdate(t){this.soundManager.handleSound(t.type,t.value),this.emit("update",this.compile())}onItemFocus(t){this.soundManager.handleSound("focus"),this.currentIndex=this.menuItems.indexOf(this.menuItems.find((e=>e.getID()==t))),this.emit("focus",this.menuItems[this.currentIndex])}focusNext(){this.currentIndex0&&this.currentIndex--,this.focusCurrentIndex()}focusCurrentIndex(){this.menuItems[this.currentIndex].focus()}getCurrentFocus(){return this.menuItems[this.currentIndex]}getContainer(){return this.container}clickDefaultAction(){this.defaultAction&&this.menuItems.find((t=>t.getID()===this.defaultAction)).click()}clickCancelAction(){this.cancelAction&&this.menuItems.find((t=>t.getID()===this.cancelAction)).click()}compile(){const t=new Map;return this.menuItems.forEach((e=>t.set(e.getID(),e.getContents()))),t.set("selected",this.menuItems[this.currentIndex].getID()),t}}})(),Engine=i})(); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,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aEMsU0FDbkQ0QyxLIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vRW5naW5lLy4vbm9kZV9tb2R1bGVzL2V2ZW50ZW1pdHRlcjMvaW5kZXguanMiLCJ3ZWJwYWNrOi8vRW5naW5lL3dlYnBhY2svYm9vdHN0cmFwIiwid2VicGFjazovL0VuZ2luZS93ZWJwYWNrL3J1bnRpbWUvY29tcGF0IGdldCBkZWZhdWx0IGV4cG9ydCIsIndlYnBhY2s6Ly9FbmdpbmUvd2VicGFjay9ydW50aW1lL2RlZmluZSBwcm9wZXJ0eSBnZXR0ZXJzIiwid2VicGFjazovL0VuZ2luZS93ZWJwYWNrL3J1bnRpbWUvaGFzT3duUHJvcGVydHkgc2hvcnRoYW5kIiwid2VicGFjazovL0VuZ2luZS93ZWJwYWNrL3J1bnRpbWUvbWFrZSBuYW1lc3BhY2Ugb2JqZWN0Iiwid2VicGFjazovL0VuZ2luZS8uL2Rpc3QvYXNzZXQtbWFuYWdlci91dGlscy5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L2Fzc2V0LW1hbmFnZXIvZG93bmxvYWRlci5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L2Fzc2V0LW1hbmFnZXIvcXVldWUuanMiLCJ3ZWJwYWNrOi8vRW5naW5lLy4vZGlzdC9hc3NldC1tYW5hZ2VyL21hbmlmZXN0LmpzIiwid2VicGFjazovL0VuZ2luZS8uL2Rpc3QvYXNzZXQtbWFuYWdlci9zdG9yYWdlLmpzIiwid2VicGFjazovL0VuZ2luZS8uL2Rpc3QvYXNzZXQtbWFuYWdlci9pbmRleC5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3Jlc29uYXRvci9zb3VyY2VzL3NvdXJjZS10eXBlLmpzIiwid2VicGFjazovL0VuZ2luZS9leHRlcm5hbCB2YXIge1wiY29tbW9uanNcIjpcInlhbWxcIixcImNvbW1vbmpzMlwiOlwieWFtbFwiLFwiYW1kXCI6XCJ5YW1sXCIsXCJyb290XCI6XCJfXCJ9Iiwid2VicGFjazovL0VuZ2luZS8uL2Rpc3QvZWNzL2VudGl0eS5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L2V2ZW50LWJ1cy9pbmRleC5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L2Vjcy9xdWVyeS5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L2Vjcy9zeXN0ZW0uanMiLCJ3ZWJwYWNrOi8vRW5naW5lLy4vZGlzdC9lY3MvaW5kZXguanMiLCJ3ZWJwYWNrOi8vRW5naW5lLy4vZGlzdC9pbnB1dC9pbnB1dHMvYmFzZS1pbnB1dC5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L2lucHV0L2lucHV0cy9rZXlib2FyZC5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L2lucHV0L2lucHV0cy9tb3VzZS5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L2lucHV0L2luZGV4LmpzIiwid2VicGFjazovL0VuZ2luZS8uL2Rpc3QvaW5wdXQvaW5wdXQtZmFjdG9yeS5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3RzbS92ZWM0LmpzIiwid2VicGFjazovL0VuZ2luZS8uL2Rpc3QvdHNtL21hdDQuanMiLCJ3ZWJwYWNrOi8vRW5naW5lLy4vZGlzdC90c20vdmVjMi5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3RzbS9tYXQzLmpzIiwid2VicGFjazovL0VuZ2luZS8uL2Rpc3QvdHNtL3F1YXQuanMiLCJ3ZWJwYWNrOi8vRW5naW5lLy4vZGlzdC90c20vdmVjMy5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3R0cy9vdXRwdXRzL2Jhc2Utb3V0cHV0LmpzIiwid2VicGFjazovL0VuZ2luZS8uL2Rpc3QvdHRzL291dHB1dHMvYXJpYS5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3R0cy9vdXRwdXRzL3dlYnR0cy5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3R0cy9pbmRleC5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3R0cy9vdXRwdXQtZmFjdG9yeS5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3VpL21lbnUvc291bmQtbWFuYWdlci5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3VpL21lbnUva2V5Ym9hcmQtbWFuYWdlci5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3VpL21lbnUvaXRlbXMvYmFzZS1pdGVtLmpzIiwid2VicGFjazovL0VuZ2luZS8uL2Rpc3QvdWkvbWVudS9pdGVtcy9lZGl0LWl0ZW0uanMiLCJ3ZWJwYWNrOi8vRW5naW5lLy4vZGlzdC91aS9tZW51L2l0ZW1zL21lbnUtaXRlbS5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3VpL21lbnUvaXRlbXMvc2VsZWN0b3ItaXRlbS5qcyIsIndlYnBhY2s6Ly9FbmdpbmUvLi9kaXN0L3VpL21lbnUvaXRlbXMvc2xpZGVyLWl0ZW0uanMiLCJ3ZWJwYWNrOi8vRW5naW5lLy4vZGlzdC91aS9tZW51L2l0ZW1zL2NoZWNrYm94LWl0ZW0uanMiLCJ3ZWJwYWNrOi8vRW5naW5lLy4vZGlzdC91aS9tZW51L2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc3RyaWN0JztcblxudmFyIGhhcyA9IE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHlcbiAgLCBwcmVmaXggPSAnfic7XG5cbi8qKlxuICogQ29uc3RydWN0b3IgdG8gY3JlYXRlIGEgc3RvcmFnZSBmb3Igb3VyIGBFRWAgb2JqZWN0cy5cbiAqIEFuIGBFdmVudHNgIGluc3RhbmNlIGlzIGEgcGxhaW4gb2JqZWN0IHdob3NlIHByb3BlcnRpZXMgYXJlIGV2ZW50IG5hbWVzLlxuICpcbiAqIEBjb25zdHJ1Y3RvclxuICogQHByaXZhdGVcbiAqL1xuZnVuY3Rpb24gRXZlbnRzKCkge31cblxuLy9cbi8vIFdlIHRyeSB0byBub3QgaW5oZXJpdCBmcm9tIGBPYmplY3QucHJvdG90eXBlYC4gSW4gc29tZSBlbmdpbmVzIGNyZWF0aW5nIGFuXG4vLyBpbnN0YW5jZSBpbiB0aGlzIHdheSBpcyBmYXN0ZXIgdGhhbiBjYWxsaW5nIGBPYmplY3QuY3JlYXRlKG51bGwpYCBkaXJlY3RseS5cbi8vIElmIGBPYmplY3QuY3JlYXRlKG51bGwpYCBpcyBub3Qgc3VwcG9ydGVkIHdlIHByZWZpeCB0aGUgZXZlbnQgbmFtZXMgd2l0aCBhXG4vLyBjaGFyYWN0ZXIgdG8gbWFrZSBzdXJlIHRoYXQgdGhlIGJ1aWx0LWluIG9iamVjdCBwcm9wZXJ0aWVzIGFyZSBub3Rcbi8vIG92ZXJyaWRkZW4gb3IgdXNlZCBhcyBhbiBhdHRhY2sgdmVjdG9yLlxuLy9cbmlmIChPYmplY3QuY3JlYXRlKSB7XG4gIEV2ZW50cy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuXG4gIC8vXG4gIC8vIFRoaXMgaGFjayBpcyBuZWVkZWQgYmVjYXVzZSB0aGUgYF9fcHJvdG9fX2AgcHJvcGVydHkgaXMgc3RpbGwgaW5oZXJpdGVkIGluXG4gIC8vIHNvbWUgb2xkIGJyb3dzZXJzIGxpa2UgQW5kcm9pZCA0LCBpUGhvbmUgNS4xLCBPcGVyYSAxMSBhbmQgU2FmYXJpIDUuXG4gIC8vXG4gIGlmICghbmV3IEV2ZW50cygpLl9fcHJvdG9fXykgcHJlZml4ID0gZmFsc2U7XG59XG5cbi8qKlxuICogUmVwcmVzZW50YXRpb24gb2YgYSBzaW5nbGUgZXZlbnQgbGlzdGVuZXIuXG4gKlxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gVGhlIGxpc3RlbmVyIGZ1bmN0aW9uLlxuICogQHBhcmFtIHsqfSBjb250ZXh0IFRoZSBjb250ZXh0IHRvIGludm9rZSB0aGUgbGlzdGVuZXIgd2l0aC5cbiAqIEBwYXJhbSB7Qm9vbGVhbn0gW29uY2U9ZmFsc2VdIFNwZWNpZnkgaWYgdGhlIGxpc3RlbmVyIGlzIGEgb25lLXRpbWUgbGlzdGVuZXIuXG4gKiBAY29uc3RydWN0b3JcbiAqIEBwcml2YXRlXG4gKi9cbmZ1bmN0aW9uIEVFKGZuLCBjb250ZXh0LCBvbmNlKSB7XG4gIHRoaXMuZm4gPSBmbjtcbiAgdGhpcy5jb250ZXh0ID0gY29udGV4dDtcbiAgdGhpcy5vbmNlID0gb25jZSB8fCBmYWxzZTtcbn1cblxuLyoqXG4gKiBBZGQgYSBsaXN0ZW5lciBmb3IgYSBnaXZlbiBldmVudC5cbiAqXG4gKiBAcGFyYW0ge0V2ZW50RW1pdHRlcn0gZW1pdHRlciBSZWZlcmVuY2UgdG8gdGhlIGBFdmVudEVtaXR0ZXJgIGluc3RhbmNlLlxuICogQHBhcmFtIHsoU3RyaW5nfFN5bWJvbCl9IGV2ZW50IFRoZSBldmVudCBuYW1lLlxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gVGhlIGxpc3RlbmVyIGZ1bmN0aW9uLlxuICogQHBhcmFtIHsqfSBjb250ZXh0IFRoZSBjb250ZXh0IHRvIGludm9rZSB0aGUgbGlzdGVuZXIgd2l0aC5cbiAqIEBwYXJhbSB7Qm9vbGVhbn0gb25jZSBTcGVjaWZ5IGlmIHRoZSBsaXN0ZW5lciBpcyBhIG9uZS10aW1lIGxpc3RlbmVyLlxuICogQHJldHVybnMge0V2ZW50RW1pdHRlcn1cbiAqIEBwcml2YXRlXG4gKi9cbmZ1bmN0aW9uIGFkZExpc3RlbmVyKGVtaXR0ZXIsIGV2ZW50LCBmbiwgY29udGV4dCwgb25jZSkge1xuICBpZiAodHlwZW9mIGZuICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcignVGhlIGxpc3RlbmVyIG11c3QgYmUgYSBmdW5jdGlvbicpO1xuICB9XG5cbiAgdmFyIGxpc3RlbmVyID0gbmV3IEVFKGZuLCBjb250ZXh0IHx8IGVtaXR0ZXIsIG9uY2UpXG4gICAgLCBldnQgPSBwcmVmaXggPyBwcmVmaXggKyBldmVudCA6IGV2ZW50O1xuXG4gIGlmICghZW1pdHRlci5fZXZlbnRzW2V2dF0pIGVtaXR0ZXIuX2V2ZW50c1tldnRdID0gbGlzdGVuZXIsIGVtaXR0ZXIuX2V2ZW50c0NvdW50Kys7XG4gIGVsc2UgaWYgKCFlbWl0dGVyLl9ldmVudHNbZXZ0XS5mbikgZW1pdHRlci5fZXZlbnRzW2V2dF0ucHVzaChsaXN0ZW5lcik7XG4gIGVsc2UgZW1pdHRlci5fZXZlbnRzW2V2dF0gPSBbZW1pdHRlci5fZXZlbnRzW2V2dF0sIGxpc3RlbmVyXTtcblxuICByZXR1cm4gZW1pdHRlcjtcbn1cblxuLyoqXG4gKiBDbGVhciBldmVudCBieSBuYW1lLlxuICpcbiAqIEBwYXJhbSB7RXZlbnRFbWl0dGVyfSBlbWl0dGVyIFJlZmVyZW5jZSB0byB0aGUgYEV2ZW50RW1pdHRlcmAgaW5zdGFuY2UuXG4gKiBAcGFyYW0geyhTdHJpbmd8U3ltYm9sKX0gZXZ0IFRoZSBFdmVudCBuYW1lLlxuICogQHByaXZhdGVcbiAqL1xuZnVuY3Rpb24gY2xlYXJFdmVudChlbWl0dGVyLCBldnQpIHtcbiAgaWYgKC0tZW1pdHRlci5fZXZlbnRzQ291bnQgPT09IDApIGVtaXR0ZXIuX2V2ZW50cyA9IG5ldyBFdmVudHMoKTtcbiAgZWxzZSBkZWxldGUgZW1pdHRlci5fZXZlbnRzW2V2dF07XG59XG5cbi8qKlxuICogTWluaW1hbCBgRXZlbnRFbWl0dGVyYCBpbnRlcmZhY2UgdGhhdCBpcyBtb2xkZWQgYWdhaW5zdCB0aGUgTm9kZS5qc1xuICogYEV2ZW50RW1pdHRlcmAgaW50ZXJmYWNlLlxuICpcbiAqIEBjb25zdHJ1Y3RvclxuICogQHB1YmxpY1xuICovXG5mdW5jdGlvbiBFdmVudEVtaXR0ZXIoKSB7XG4gIHRoaXMuX2V2ZW50cyA9IG5ldyBFdmVudHMoKTtcbiAgdGhpcy5fZXZlbnRzQ291bnQgPSAwO1xufVxuXG4vKipcbiAqIFJldHVybiBhbiBhcnJheSBsaXN0aW5nIHRoZSBldmVudHMgZm9yIHdoaWNoIHRoZSBlbWl0dGVyIGhhcyByZWdpc3RlcmVkXG4gKiBsaXN0ZW5lcnMuXG4gKlxuICogQHJldHVybnMge0FycmF5fVxuICogQHB1YmxpY1xuICovXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLmV2ZW50TmFtZXMgPSBmdW5jdGlvbiBldmVudE5hbWVzKCkge1xuICB2YXIgbmFtZXMgPSBbXVxuICAgICwgZXZlbnRzXG4gICAgLCBuYW1lO1xuXG4gIGlmICh0aGlzLl9ldmVudHNDb3VudCA9PT0gMCkgcmV0dXJuIG5hbWVzO1xuXG4gIGZvciAobmFtZSBpbiAoZXZlbnRzID0gdGhpcy5fZXZlbnRzKSkge1xuICAgIGlmIChoYXMuY2FsbChldmVudHMsIG5hbWUpKSBuYW1lcy5wdXNoKHByZWZpeCA/IG5hbWUuc2xpY2UoMSkgOiBuYW1lKTtcbiAgfVxuXG4gIGlmIChPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKSB7XG4gICAgcmV0dXJuIG5hbWVzLmNvbmNhdChPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKGV2ZW50cykpO1xuICB9XG5cbiAgcmV0dXJuIG5hbWVzO1xufTtcblxuLyoqXG4gKiBSZXR1cm4gdGhlIGxpc3RlbmVycyByZWdpc3RlcmVkIGZvciBhIGdpdmVuIGV2ZW50LlxuICpcbiAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBldmVudCBUaGUgZXZlbnQgbmFtZS5cbiAqIEByZXR1cm5zIHtBcnJheX0gVGhlIHJlZ2lzdGVyZWQgbGlzdGVuZXJzLlxuICogQHB1YmxpY1xuICovXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLmxpc3RlbmVycyA9IGZ1bmN0aW9uIGxpc3RlbmVycyhldmVudCkge1xuICB2YXIgZXZ0ID0gcHJlZml4ID8gcHJlZml4ICsgZXZlbnQgOiBldmVudFxuICAgICwgaGFuZGxlcnMgPSB0aGlzLl9ldmVudHNbZXZ0XTtcblxuICBpZiAoIWhhbmRsZXJzKSByZXR1cm4gW107XG4gIGlmIChoYW5kbGVycy5mbikgcmV0dXJuIFtoYW5kbGVycy5mbl07XG5cbiAgZm9yICh2YXIgaSA9IDAsIGwgPSBoYW5kbGVycy5sZW5ndGgsIGVlID0gbmV3IEFycmF5KGwpOyBpIDwgbDsgaSsrKSB7XG4gICAgZWVbaV0gPSBoYW5kbGVyc1tpXS5mbjtcbiAgfVxuXG4gIHJldHVybiBlZTtcbn07XG5cbi8qKlxuICogUmV0dXJuIHRoZSBudW1iZXIgb2YgbGlzdGVuZXJzIGxpc3RlbmluZyB0byBhIGdpdmVuIGV2ZW50LlxuICpcbiAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBldmVudCBUaGUgZXZlbnQgbmFtZS5cbiAqIEByZXR1cm5zIHtOdW1iZXJ9IFRoZSBudW1iZXIgb2YgbGlzdGVuZXJzLlxuICogQHB1YmxpY1xuICovXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLmxpc3RlbmVyQ291bnQgPSBmdW5jdGlvbiBsaXN0ZW5lckNvdW50KGV2ZW50KSB7XG4gIHZhciBldnQgPSBwcmVmaXggPyBwcmVmaXggKyBldmVudCA6IGV2ZW50XG4gICAgLCBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbZXZ0XTtcblxuICBpZiAoIWxpc3RlbmVycykgcmV0dXJuIDA7XG4gIGlmIChsaXN0ZW5lcnMuZm4pIHJldHVybiAxO1xuICByZXR1cm4gbGlzdGVuZXJzLmxlbmd0aDtcbn07XG5cbi8qKlxuICogQ2FsbHMgZWFjaCBvZiB0aGUgbGlzdGVuZXJzIHJlZ2lzdGVyZWQgZm9yIGEgZ2l2ZW4gZXZlbnQuXG4gKlxuICogQHBhcmFtIHsoU3RyaW5nfFN5bWJvbCl9IGV2ZW50IFRoZSBldmVudCBuYW1lLlxuICogQHJldHVybnMge0Jvb2xlYW59IGB0cnVlYCBpZiB0aGUgZXZlbnQgaGFkIGxpc3RlbmVycywgZWxzZSBgZmFsc2VgLlxuICogQHB1YmxpY1xuICovXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLmVtaXQgPSBmdW5jdGlvbiBlbWl0KGV2ZW50LCBhMSwgYTIsIGEzLCBhNCwgYTUpIHtcbiAgdmFyIGV2dCA9IHByZWZpeCA/IHByZWZpeCArIGV2ZW50IDogZXZlbnQ7XG5cbiAgaWYgKCF0aGlzLl9ldmVudHNbZXZ0XSkgcmV0dXJuIGZhbHNlO1xuXG4gIHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbZXZ0XVxuICAgICwgbGVuID0gYXJndW1lbnRzLmxlbmd0aFxuICAgICwgYXJnc1xuICAgICwgaTtcblxuICBpZiAobGlzdGVuZXJzLmZuKSB7XG4gICAgaWYgKGxpc3RlbmVycy5vbmNlKSB0aGlzLnJlbW92ZUxpc3RlbmVyKGV2ZW50LCBsaXN0ZW5lcnMuZm4sIHVuZGVmaW5lZCwgdHJ1ZSk7XG5cbiAgICBzd2l0Y2ggKGxlbikge1xuICAgICAgY2FzZSAxOiByZXR1cm4gbGlzdGVuZXJzLmZuLmNhbGwobGlzdGVuZXJzLmNvbnRleHQpLCB0cnVlO1xuICAgICAgY2FzZSAyOiByZXR1cm4gbGlzdGVuZXJzLmZuLmNhbGwobGlzdGVuZXJzLmNvbnRleHQsIGExKSwgdHJ1ZTtcbiAgICAgIGNhc2UgMzogcmV0dXJuIGxpc3RlbmVycy5mbi5jYWxsKGxpc3RlbmVycy5jb250ZXh0LCBhMSwgYTIpLCB0cnVlO1xuICAgICAgY2FzZSA0OiByZXR1cm4gbGlzdGVuZXJzLmZuLmNhbGwobGlzdGVuZXJzLmNvbnRleHQsIGExLCBhMiwgYTMpLCB0cnVlO1xuICAgICAgY2FzZSA1OiByZXR1cm4gbGlzdGVuZXJzLmZuLmNhbGwobGlzdGVuZXJzLmNvbnRleHQsIGExLCBhMiwgYTMsIGE0KSwgdHJ1ZTtcbiAgICAgIGNhc2UgNjogcmV0dXJuIGxpc3RlbmVycy5mbi5jYWxsKGxpc3RlbmVycy5jb250ZXh0LCBhMSwgYTIsIGEzLCBhNCwgYTUpLCB0cnVlO1xuICAgIH1cblxuICAgIGZvciAoaSA9IDEsIGFyZ3MgPSBuZXcgQXJyYXkobGVuIC0xKTsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICBhcmdzW2kgLSAxXSA9IGFyZ3VtZW50c1tpXTtcbiAgICB9XG5cbiAgICBsaXN0ZW5lcnMuZm4uYXBwbHkobGlzdGVuZXJzLmNvbnRleHQsIGFyZ3MpO1xuICB9IGVsc2Uge1xuICAgIHZhciBsZW5ndGggPSBsaXN0ZW5lcnMubGVuZ3RoXG4gICAgICAsIGo7XG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChsaXN0ZW5lcnNbaV0ub25jZSkgdGhpcy5yZW1vdmVMaXN0ZW5lcihldmVudCwgbGlzdGVuZXJzW2ldLmZuLCB1bmRlZmluZWQsIHRydWUpO1xuXG4gICAgICBzd2l0Y2ggKGxlbikge1xuICAgICAgICBjYXNlIDE6IGxpc3RlbmVyc1tpXS5mbi5jYWxsKGxpc3RlbmVyc1tpXS5jb250ZXh0KTsgYnJlYWs7XG4gICAgICAgIGNhc2UgMjogbGlzdGVuZXJzW2ldLmZuLmNhbGwobGlzdGVuZXJzW2ldLmNvbnRleHQsIGExKTsgYnJlYWs7XG4gICAgICAgIGNhc2UgMzogbGlzdGVuZXJzW2ldLmZuLmNhbGwobGlzdGVuZXJzW2ldLmNvbnRleHQsIGExLCBhMik7IGJyZWFrO1xuICAgICAgICBjYXNlIDQ6IGxpc3RlbmVyc1tpXS5mbi5jYWxsKGxpc3RlbmVyc1tpXS5jb250ZXh0LCBhMSwgYTIsIGEzKTsgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgaWYgKCFhcmdzKSBmb3IgKGogPSAxLCBhcmdzID0gbmV3IEFycmF5KGxlbiAtMSk7IGogPCBsZW47IGorKykge1xuICAgICAgICAgICAgYXJnc1tqIC0gMV0gPSBhcmd1bWVudHNbal07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgbGlzdGVuZXJzW2ldLmZuLmFwcGx5KGxpc3RlbmVyc1tpXS5jb250ZXh0LCBhcmdzKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gdHJ1ZTtcbn07XG5cbi8qKlxuICogQWRkIGEgbGlzdGVuZXIgZm9yIGEgZ2l2ZW4gZXZlbnQuXG4gKlxuICogQHBhcmFtIHsoU3RyaW5nfFN5bWJvbCl9IGV2ZW50IFRoZSBldmVudCBuYW1lLlxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gVGhlIGxpc3RlbmVyIGZ1bmN0aW9uLlxuICogQHBhcmFtIHsqfSBbY29udGV4dD10aGlzXSBUaGUgY29udGV4dCB0byBpbnZva2UgdGhlIGxpc3RlbmVyIHdpdGguXG4gKiBAcmV0dXJucyB7RXZlbnRFbWl0dGVyfSBgdGhpc2AuXG4gKiBAcHVibGljXG4gKi9cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUub24gPSBmdW5jdGlvbiBvbihldmVudCwgZm4sIGNvbnRleHQpIHtcbiAgcmV0dXJuIGFkZExpc3RlbmVyKHRoaXMsIGV2ZW50LCBmbiwgY29udGV4dCwgZmFsc2UpO1xufTtcblxuLyoqXG4gKiBBZGQgYSBvbmUtdGltZSBsaXN0ZW5lciBmb3IgYSBnaXZlbiBldmVudC5cbiAqXG4gKiBAcGFyYW0geyhTdHJpbmd8U3ltYm9sKX0gZXZlbnQgVGhlIGV2ZW50IG5hbWUuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBUaGUgbGlzdGVuZXIgZnVuY3Rpb24uXG4gKiBAcGFyYW0geyp9IFtjb250ZXh0PXRoaXNdIFRoZSBjb250ZXh0IHRvIGludm9rZSB0aGUgbGlzdGVuZXIgd2l0aC5cbiAqIEByZXR1cm5zIHtFdmVudEVtaXR0ZXJ9IGB0aGlzYC5cbiAqIEBwdWJsaWNcbiAqL1xuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5vbmNlID0gZnVuY3Rpb24gb25jZShldmVudCwgZm4sIGNvbnRleHQpIHtcbiAgcmV0dXJuIGFkZExpc3RlbmVyKHRoaXMsIGV2ZW50LCBmbiwgY29udGV4dCwgdHJ1ZSk7XG59O1xuXG4vKipcbiAqIFJlbW92ZSB0aGUgbGlzdGVuZXJzIG9mIGEgZ2l2ZW4gZXZlbnQuXG4gKlxuICogQHBhcmFtIHsoU3RyaW5nfFN5bWJvbCl9IGV2ZW50IFRoZSBldmVudCBuYW1lLlxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gT25seSByZW1vdmUgdGhlIGxpc3RlbmVycyB0aGF0IG1hdGNoIHRoaXMgZnVuY3Rpb24uXG4gKiBAcGFyYW0geyp9IGNvbnRleHQgT25seSByZW1vdmUgdGhlIGxpc3RlbmVycyB0aGF0IGhhdmUgdGhpcyBjb250ZXh0LlxuICogQHBhcmFtIHtCb29sZWFufSBvbmNlIE9ubHkgcmVtb3ZlIG9uZS10aW1lIGxpc3RlbmVycy5cbiAqIEByZXR1cm5zIHtFdmVudEVtaXR0ZXJ9IGB0aGlzYC5cbiAqIEBwdWJsaWNcbiAqL1xuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5yZW1vdmVMaXN0ZW5lciA9IGZ1bmN0aW9uIHJlbW92ZUxpc3RlbmVyKGV2ZW50LCBmbiwgY29udGV4dCwgb25jZSkge1xuICB2YXIgZXZ0ID0gcHJlZml4ID8gcHJlZml4ICsgZXZlbnQgOiBldmVudDtcblxuICBpZiAoIXRoaXMuX2V2ZW50c1tldnRdKSByZXR1cm4gdGhpcztcbiAgaWYgKCFmbikge1xuICAgIGNsZWFyRXZlbnQodGhpcywgZXZ0KTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbZXZ0XTtcblxuICBpZiAobGlzdGVuZXJzLmZuKSB7XG4gICAgaWYgKFxuICAgICAgbGlzdGVuZXJzLmZuID09PSBmbiAmJlxuICAgICAgKCFvbmNlIHx8IGxpc3RlbmVycy5vbmNlKSAmJlxuICAgICAgKCFjb250ZXh0IHx8IGxpc3RlbmVycy5jb250ZXh0ID09PSBjb250ZXh0KVxuICAgICkge1xuICAgICAgY2xlYXJFdmVudCh0aGlzLCBldnQpO1xuICAgIH1cbiAgfSBlbHNlIHtcbiAgICBmb3IgKHZhciBpID0gMCwgZXZlbnRzID0gW10sIGxlbmd0aCA9IGxpc3RlbmVycy5sZW5ndGg7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgICAgaWYgKFxuICAgICAgICBsaXN0ZW5lcnNbaV0uZm4gIT09IGZuIHx8XG4gICAgICAgIChvbmNlICYmICFsaXN0ZW5lcnNbaV0ub25jZSkgfHxcbiAgICAgICAgKGNvbnRleHQgJiYgbGlzdGVuZXJzW2ldLmNvbnRleHQgIT09IGNvbnRleHQpXG4gICAgICApIHtcbiAgICAgICAgZXZlbnRzLnB1c2gobGlzdGVuZXJzW2ldKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvL1xuICAgIC8vIFJlc2V0IHRoZSBhcnJheSwgb3IgcmVtb3ZlIGl0IGNvbXBsZXRlbHkgaWYgd2UgaGF2ZSBubyBtb3JlIGxpc3RlbmVycy5cbiAgICAvL1xuICAgIGlmIChldmVudHMubGVuZ3RoKSB0aGlzLl9ldmVudHNbZXZ0XSA9IGV2ZW50cy5sZW5ndGggPT09IDEgPyBldmVudHNbMF0gOiBldmVudHM7XG4gICAgZWxzZSBjbGVhckV2ZW50KHRoaXMsIGV2dCk7XG4gIH1cblxuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogUmVtb3ZlIGFsbCBsaXN0ZW5lcnMsIG9yIHRob3NlIG9mIHRoZSBzcGVjaWZpZWQgZXZlbnQuXG4gKlxuICogQHBhcmFtIHsoU3RyaW5nfFN5bWJvbCl9IFtldmVudF0gVGhlIGV2ZW50IG5hbWUuXG4gKiBAcmV0dXJucyB7RXZlbnRFbWl0dGVyfSBgdGhpc2AuXG4gKiBAcHVibGljXG4gKi9cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUucmVtb3ZlQWxsTGlzdGVuZXJzID0gZnVuY3Rpb24gcmVtb3ZlQWxsTGlzdGVuZXJzKGV2ZW50KSB7XG4gIHZhciBldnQ7XG5cbiAgaWYgKGV2ZW50KSB7XG4gICAgZXZ0ID0gcHJlZml4ID8gcHJlZml4ICsgZXZlbnQgOiBldmVudDtcbiAgICBpZiAodGhpcy5fZXZlbnRzW2V2dF0pIGNsZWFyRXZlbnQodGhpcywgZXZ0KTtcbiAgfSBlbHNlIHtcbiAgICB0aGlzLl9ldmVudHMgPSBuZXcgRXZlbnRzKCk7XG4gICAgdGhpcy5fZXZlbnRzQ291bnQgPSAwO1xuICB9XG5cbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vL1xuLy8gQWxpYXMgbWV0aG9kcyBuYW1lcyBiZWNhdXNlIHBlb3BsZSByb2xsIGxpa2UgdGhhdC5cbi8vXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLm9mZiA9IEV2ZW50RW1pdHRlci5wcm90b3R5cGUucmVtb3ZlTGlzdGVuZXI7XG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLmFkZExpc3RlbmVyID0gRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5vbjtcblxuLy9cbi8vIEV4cG9zZSB0aGUgcHJlZml4LlxuLy9cbkV2ZW50RW1pdHRlci5wcmVmaXhlZCA9IHByZWZpeDtcblxuLy9cbi8vIEFsbG93IGBFdmVudEVtaXR0ZXJgIHRvIGJlIGltcG9ydGVkIGFzIG1vZHVsZSBuYW1lc3BhY2UuXG4vL1xuRXZlbnRFbWl0dGVyLkV2ZW50RW1pdHRlciA9IEV2ZW50RW1pdHRlcjtcblxuLy9cbi8vIEV4cG9zZSB0aGUgbW9kdWxlLlxuLy9cbmlmICgndW5kZWZpbmVkJyAhPT0gdHlwZW9mIG1vZHVsZSkge1xuICBtb2R1bGUuZXhwb3J0cyA9IEV2ZW50RW1pdHRlcjtcbn1cbiIsIi8vIFRoZSBtb2R1bGUgY2FjaGVcbnZhciBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX18gPSB7fTtcblxuLy8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbmZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG5cdHZhciBjYWNoZWRNb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdO1xuXHRpZiAoY2FjaGVkTW9kdWxlICE9PSB1bmRlZmluZWQpIHtcblx0XHRyZXR1cm4gY2FjaGVkTW9kdWxlLmV4cG9ydHM7XG5cdH1cblx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcblx0dmFyIG1vZHVsZSA9IF9fd2VicGFja19tb2R1bGVfY2FjaGVfX1ttb2R1bGVJZF0gPSB7XG5cdFx0Ly8gbm8gbW9kdWxlLmlkIG5lZWRlZFxuXHRcdC8vIG5vIG1vZHVsZS5sb2FkZWQgbmVlZGVkXG5cdFx0ZXhwb3J0czoge31cblx0fTtcblxuXHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cblx0X193ZWJwYWNrX21vZHVsZXNfX1ttb2R1bGVJZF0obW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cblx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcblx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xufVxuXG4iLCIvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuX193ZWJwYWNrX3JlcXVpcmVfXy5uID0gKG1vZHVsZSkgPT4ge1xuXHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cblx0XHQoKSA9PiAobW9kdWxlWydkZWZhdWx0J10pIDpcblx0XHQoKSA9PiAobW9kdWxlKTtcblx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgeyBhOiBnZXR0ZXIgfSk7XG5cdHJldHVybiBnZXR0ZXI7XG59OyIsIi8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb25zIGZvciBoYXJtb255IGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uZCA9IChleHBvcnRzLCBkZWZpbml0aW9uKSA9PiB7XG5cdGZvcih2YXIga2V5IGluIGRlZmluaXRpb24pIHtcblx0XHRpZihfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZGVmaW5pdGlvbiwga2V5KSAmJiAhX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIGtleSkpIHtcblx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBrZXksIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBkZWZpbml0aW9uW2tleV0gfSk7XG5cdFx0fVxuXHR9XG59OyIsIl9fd2VicGFja19yZXF1aXJlX18ubyA9IChvYmosIHByb3ApID0+IChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBwcm9wKSkiLCIvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSAoZXhwb3J0cykgPT4ge1xuXHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcblx0fVxuXHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xufTsiLCJleHBvcnQgZnVuY3Rpb24gYnVpbGRQYXRoKGJhc2VQYXRoLCBwYXRoKSB7XHJcbiAgICBpZiAoIWJhc2VQYXRoKSB7XHJcbiAgICAgICAgcmV0dXJuIHBhdGg7XHJcbiAgICB9XHJcbiAgICBlbHNlIHtcclxuICAgICAgICByZXR1cm4gYCR7YmFzZVBhdGh9LyR7cGF0aH1gO1xyXG4gICAgfVxyXG59XHJcbiIsInZhciBfX2F3YWl0ZXIgPSAodGhpcyAmJiB0aGlzLl9fYXdhaXRlcikgfHwgZnVuY3Rpb24gKHRoaXNBcmcsIF9hcmd1bWVudHMsIFAsIGdlbmVyYXRvcikge1xyXG4gICAgZnVuY3Rpb24gYWRvcHQodmFsdWUpIHsgcmV0dXJuIHZhbHVlIGluc3RhbmNlb2YgUCA/IHZhbHVlIDogbmV3IFAoZnVuY3Rpb24gKHJlc29sdmUpIHsgcmVzb2x2ZSh2YWx1ZSk7IH0pOyB9XHJcbiAgICByZXR1cm4gbmV3IChQIHx8IChQID0gUHJvbWlzZSkpKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHtcclxuICAgICAgICBmdW5jdGlvbiBmdWxmaWxsZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3IubmV4dCh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9XHJcbiAgICAgICAgZnVuY3Rpb24gcmVqZWN0ZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3JbXCJ0aHJvd1wiXSh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9XHJcbiAgICAgICAgZnVuY3Rpb24gc3RlcChyZXN1bHQpIHsgcmVzdWx0LmRvbmUgPyByZXNvbHZlKHJlc3VsdC52YWx1ZSkgOiBhZG9wdChyZXN1bHQudmFsdWUpLnRoZW4oZnVsZmlsbGVkLCByZWplY3RlZCk7IH1cclxuICAgICAgICBzdGVwKChnZW5lcmF0b3IgPSBnZW5lcmF0b3IuYXBwbHkodGhpc0FyZywgX2FyZ3VtZW50cyB8fCBbXSkpLm5leHQoKSk7XHJcbiAgICB9KTtcclxufTtcclxuaW1wb3J0IEV2ZW50RW1pdHRlciBmcm9tICdldmVudGVtaXR0ZXIzJztcclxuaW1wb3J0IHsgYnVpbGRQYXRoIH0gZnJvbSAnLi91dGlscyc7XHJcbmV4cG9ydCBjbGFzcyBEb3dubG9hZGVyIGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcclxuICAgIGNvbnN0cnVjdG9yKHN0b3JhZ2UsIHF1ZXVlLCBiYXNlUGF0aCA9ICcnKSB7XHJcbiAgICAgICAgc3VwZXIoKTtcclxuICAgICAgICB0aGlzLnN0b3JhZ2UgPSBzdG9yYWdlO1xyXG4gICAgICAgIHRoaXMucXVldWUgPSBxdWV1ZTtcclxuICAgICAgICB0aGlzLmJhc2VQYXRoID0gYmFzZVBhdGg7XHJcbiAgICB9XHJcbiAgICBzZXRCYXNlUGF0aChwYXRoKSB7XHJcbiAgICAgICAgdGhpcy5iYXNlUGF0aCA9IHRoaXMuYmFzZVBhdGg7XHJcbiAgICB9XHJcbiAgICBkb3dubG9hZCgpIHtcclxuICAgICAgICByZXR1cm4gX19hd2FpdGVyKHRoaXMsIHZvaWQgMCwgdm9pZCAwLCBmdW5jdGlvbiogKCkge1xyXG4gICAgICAgICAgICBjb25zdCBkb3dubG9hZGVkID0gbmV3IE1hcCgpO1xyXG4gICAgICAgICAgICBsZXQgbnVtRG93bmxvYWRlZCA9IDA7XHJcbiAgICAgICAgICAgIHdoaWxlICh0aGlzLnF1ZXVlLmxlbmd0aCgpID4gMCkge1xyXG4gICAgICAgICAgICAgICAgY29uc3QgcGF0aCA9IHRoaXMucXVldWUucG9wKCk7XHJcbiAgICAgICAgICAgICAgICBjb25zdCBpdGVtID0geWllbGQgdGhpcy5kb3dubG9hZEl0ZW0oYnVpbGRQYXRoKHRoaXMuYmFzZVBhdGgsIHBhdGgpKTtcclxuICAgICAgICAgICAgICAgIGRvd25sb2FkZWQuc2V0KHBhdGgsIGl0ZW0pO1xyXG4gICAgICAgICAgICAgICAgbnVtRG93bmxvYWRlZCsrO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5lbWl0KCdkb3dubG9hZC5wcm9ncmVzcycsIHtcclxuICAgICAgICAgICAgICAgICAgICBkb3dubG9hZGVkOiBudW1Eb3dubG9hZGVkLFxyXG4gICAgICAgICAgICAgICAgICAgIHJlbWFpbmluZzogdGhpcy5xdWV1ZS5sZW5ndGgoKVxyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIGRvd25sb2FkZWQ7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBkb3dubG9hZEl0ZW0ocGF0aCkge1xyXG4gICAgICAgIHJldHVybiBfX2F3YWl0ZXIodGhpcywgdm9pZCAwLCB2b2lkIDAsIGZ1bmN0aW9uKiAoKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGluQ2FjaGUgPSB5aWVsZCB0aGlzLnN0b3JhZ2UuZ2V0KHBhdGgpO1xyXG4gICAgICAgICAgICBpZiAoaW5DYWNoZSkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIGluQ2FjaGU7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSB5aWVsZCBmZXRjaChwYXRoKTtcclxuICAgICAgICAgICAgdGhpcy5zdG9yYWdlLmFkZChwYXRoLCByZXNwb25zZSk7XHJcbiAgICAgICAgICAgIHJldHVybiByZXNwb25zZTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxufVxyXG4iLCJleHBvcnQgY2xhc3MgUXVldWUge1xyXG4gICAgY29uc3RydWN0b3IoKSB7XHJcbiAgICAgICAgdGhpcy5pdGVtcyA9IFtdO1xyXG4gICAgfVxyXG4gICAgYWRkKGZpbGUpIHtcclxuICAgICAgICB0aGlzLml0ZW1zLnB1c2goZmlsZSk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuaXRlbXM7XHJcbiAgICB9XHJcbiAgICByZW1vdmUoZmlsZSkge1xyXG4gICAgICAgIHRoaXMuaXRlbXMgPSB0aGlzLml0ZW1zLmZpbHRlcigoaXRlbSkgPT4gaXRlbSAhPT0gZmlsZSk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuaXRlbXM7XHJcbiAgICB9XHJcbiAgICBwb3AoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuaXRlbXMucG9wKCk7XHJcbiAgICB9XHJcbiAgICBsZW5ndGgoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuaXRlbXMubGVuZ3RoO1xyXG4gICAgfVxyXG59XHJcbiIsInZhciBfX2F3YWl0ZXIgPSAodGhpcyAmJiB0aGlzLl9fYXdhaXRlcikgfHwgZnVuY3Rpb24gKHRoaXNBcmcsIF9hcmd1bWVudHMsIFAsIGdlbmVyYXRvcikge1xyXG4gICAgZnVuY3Rpb24gYWRvcHQodmFsdWUpIHsgcmV0dXJuIHZhbHVlIGluc3RhbmNlb2YgUCA/IHZhbHVlIDogbmV3IFAoZnVuY3Rpb24gKHJlc29sdmUpIHsgcmVzb2x2ZSh2YWx1ZSk7IH0pOyB9XHJcbiAgICByZXR1cm4gbmV3IChQIHx8IChQID0gUHJvbWlzZSkpKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHtcclxuICAgICAgICBmdW5jdGlvbiBmdWxmaWxsZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3IubmV4dCh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9XHJcbiAgICAgICAgZnVuY3Rpb24gcmVqZWN0ZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3JbXCJ0aHJvd1wiXSh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9XHJcbiAgICAgICAgZnVuY3Rpb24gc3RlcChyZXN1bHQpIHsgcmVzdWx0LmRvbmUgPyByZXNvbHZlKHJlc3VsdC52YWx1ZSkgOiBhZG9wdChyZXN1bHQudmFsdWUpLnRoZW4oZnVsZmlsbGVkLCByZWplY3RlZCk7IH1cclxuICAgICAgICBzdGVwKChnZW5lcmF0b3IgPSBnZW5lcmF0b3IuYXBwbHkodGhpc0FyZywgX2FyZ3VtZW50cyB8fCBbXSkpLm5leHQoKSk7XHJcbiAgICB9KTtcclxufTtcclxuaW1wb3J0ICogYXMgeWFtbCBmcm9tICd5YW1sJztcclxuZXhwb3J0IGZ1bmN0aW9uIE1hbmlmZXN0KG1hbmlmZXN0UGF0aCkge1xyXG4gICAgcmV0dXJuIF9fYXdhaXRlcih0aGlzLCB2b2lkIDAsIHZvaWQgMCwgZnVuY3Rpb24qICgpIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IHlpZWxkIGZldGNoKG1hbmlmZXN0UGF0aCk7XHJcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKHJlc3BvbnNlKTtcclxuICAgICAgICAgICAgY29uc3QgZGF0YSA9IHlpZWxkIHJlc3BvbnNlLnRleHQoKTtcclxuICAgICAgICAgICAgY29uc29sZS5sb2coYFBhcnNpbmc6IGAsIGRhdGEpO1xyXG4gICAgICAgICAgICBjb25zdCBtYW5pZmVzdCA9IHlhbWwucGFyc2UoZGF0YSk7XHJcbiAgICAgICAgICAgIHJldHVybiBtYW5pZmVzdDtcclxuICAgICAgICB9XHJcbiAgICAgICAgY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGFsZXJ0KGBFcnJvciBvY2N1cmVkOiAke2Vycm9yLnRvU3RyaW5nKCl9YCk7XHJcbiAgICAgICAgfVxyXG4gICAgfSk7XHJcbn1cclxuZXhwb3J0IGZ1bmN0aW9uIENoZWNrTWFuaWZlc3QobWFuaWZlc3QpIHtcclxuICAgIGNvbnN0IHByZXZNYW5pZmVzdFN0ciA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKCdtYW5pZmVzdCcpO1xyXG4gICAgaWYgKCFwcmV2TWFuaWZlc3RTdHIpIHtcclxuICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSgnbWFuaWZlc3QnLCBKU09OLnN0cmluZ2lmeShtYW5pZmVzdCkpO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuICAgIGNvbnN0IHByZXZNYW5pZmVzdCA9IEpTT04ucGFyc2UocHJldk1hbmlmZXN0U3RyKTtcclxuICAgIGlmIChwcmV2TWFuaWZlc3QudmVyc2lvbiA9PT0gbWFuaWZlc3QudmVyc2lvbikge1xyXG4gICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG4gICAgZWxzZSB7XHJcbiAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oJ21hbmlmZXN0JywgbWFuaWZlc3QpO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxufVxyXG4iLCJ2YXIgX19hd2FpdGVyID0gKHRoaXMgJiYgdGhpcy5fX2F3YWl0ZXIpIHx8IGZ1bmN0aW9uICh0aGlzQXJnLCBfYXJndW1lbnRzLCBQLCBnZW5lcmF0b3IpIHtcclxuICAgIGZ1bmN0aW9uIGFkb3B0KHZhbHVlKSB7IHJldHVybiB2YWx1ZSBpbnN0YW5jZW9mIFAgPyB2YWx1ZSA6IG5ldyBQKGZ1bmN0aW9uIChyZXNvbHZlKSB7IHJlc29sdmUodmFsdWUpOyB9KTsgfVxyXG4gICAgcmV0dXJuIG5ldyAoUCB8fCAoUCA9IFByb21pc2UpKShmdW5jdGlvbiAocmVzb2x2ZSwgcmVqZWN0KSB7XHJcbiAgICAgICAgZnVuY3Rpb24gZnVsZmlsbGVkKHZhbHVlKSB7IHRyeSB7IHN0ZXAoZ2VuZXJhdG9yLm5leHQodmFsdWUpKTsgfSBjYXRjaCAoZSkgeyByZWplY3QoZSk7IH0gfVxyXG4gICAgICAgIGZ1bmN0aW9uIHJlamVjdGVkKHZhbHVlKSB7IHRyeSB7IHN0ZXAoZ2VuZXJhdG9yW1widGhyb3dcIl0odmFsdWUpKTsgfSBjYXRjaCAoZSkgeyByZWplY3QoZSk7IH0gfVxyXG4gICAgICAgIGZ1bmN0aW9uIHN0ZXAocmVzdWx0KSB7IHJlc3VsdC5kb25lID8gcmVzb2x2ZShyZXN1bHQudmFsdWUpIDogYWRvcHQocmVzdWx0LnZhbHVlKS50aGVuKGZ1bGZpbGxlZCwgcmVqZWN0ZWQpOyB9XHJcbiAgICAgICAgc3RlcCgoZ2VuZXJhdG9yID0gZ2VuZXJhdG9yLmFwcGx5KHRoaXNBcmcsIF9hcmd1bWVudHMgfHwgW10pKS5uZXh0KCkpO1xyXG4gICAgfSk7XHJcbn07XHJcbmltcG9ydCB7IENoZWNrTWFuaWZlc3QgfSBmcm9tICcuL21hbmlmZXN0JztcclxuZXhwb3J0IGNsYXNzIEFzc2V0U3RvcmFnZSB7XHJcbiAgICBjb25zdHJ1Y3RvcihpZCkge1xyXG4gICAgICAgIHRoaXMuaWQgPSBpZDtcclxuICAgIH1cclxuICAgIGluaXQoKSB7XHJcbiAgICAgICAgcmV0dXJuIF9fYXdhaXRlcih0aGlzLCB2b2lkIDAsIHZvaWQgMCwgZnVuY3Rpb24qICgpIHtcclxuICAgICAgICAgICAgdGhpcy5jYWNoZSA9IHlpZWxkIGNhY2hlcy5vcGVuKHRoaXMuaWQpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgYWRkKHJlcXVlc3QsIHJlc3BvbnNlKSB7XHJcbiAgICAgICAgcmV0dXJuIF9fYXdhaXRlcih0aGlzLCB2b2lkIDAsIHZvaWQgMCwgZnVuY3Rpb24qICgpIHtcclxuICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0geWllbGQgdGhpcy5jYWNoZS5wdXQocmVxdWVzdCwgcmVzcG9uc2UpO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuICAgIGdldChyZXF1ZXN0KSB7XHJcbiAgICAgICAgcmV0dXJuIF9fYXdhaXRlcih0aGlzLCB2b2lkIDAsIHZvaWQgMCwgZnVuY3Rpb24qICgpIHtcclxuICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0geWllbGQgdGhpcy5jYWNoZS5tYXRjaChyZXF1ZXN0KTtcclxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuICAgIHNldE1hbmlmZXN0KG1hbmlmZXN0KSB7XHJcbiAgICAgICAgcmV0dXJuIF9fYXdhaXRlcih0aGlzLCB2b2lkIDAsIHZvaWQgMCwgZnVuY3Rpb24qICgpIHtcclxuICAgICAgICAgICAgdGhpcy5tYW5pZmVzdCA9IG1hbmlmZXN0O1xyXG4gICAgICAgICAgICBpZiAoIUNoZWNrTWFuaWZlc3QodGhpcy5tYW5pZmVzdCkpIHtcclxuICAgICAgICAgICAgICAgIHlpZWxkIHRoaXMuY2xlYXIoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgY2xlYXIoKSB7XHJcbiAgICAgICAgcmV0dXJuIF9fYXdhaXRlcih0aGlzLCB2b2lkIDAsIHZvaWQgMCwgZnVuY3Rpb24qICgpIHtcclxuICAgICAgICAgICAgY29uc3Qga2V5cyA9IHlpZWxkIHRoaXMuY2FjaGUua2V5cygpO1xyXG4gICAgICAgICAgICBrZXlzLmZvckVhY2goKGtleSkgPT4gX19hd2FpdGVyKHRoaXMsIHZvaWQgMCwgdm9pZCAwLCBmdW5jdGlvbiogKCkge1xyXG4gICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0geWllbGQgdGhpcy5jYWNoZS5kZWxldGUoa2V5KTtcclxuICAgICAgICAgICAgfSkpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG59XHJcbiIsInZhciBfX2F3YWl0ZXIgPSAodGhpcyAmJiB0aGlzLl9fYXdhaXRlcikgfHwgZnVuY3Rpb24gKHRoaXNBcmcsIF9hcmd1bWVudHMsIFAsIGdlbmVyYXRvcikge1xyXG4gICAgZnVuY3Rpb24gYWRvcHQodmFsdWUpIHsgcmV0dXJuIHZhbHVlIGluc3RhbmNlb2YgUCA/IHZhbHVlIDogbmV3IFAoZnVuY3Rpb24gKHJlc29sdmUpIHsgcmVzb2x2ZSh2YWx1ZSk7IH0pOyB9XHJcbiAgICByZXR1cm4gbmV3IChQIHx8IChQID0gUHJvbWlzZSkpKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHtcclxuICAgICAgICBmdW5jdGlvbiBmdWxmaWxsZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3IubmV4dCh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9XHJcbiAgICAgICAgZnVuY3Rpb24gcmVqZWN0ZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3JbXCJ0aHJvd1wiXSh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9XHJcbiAgICAgICAgZnVuY3Rpb24gc3RlcChyZXN1bHQpIHsgcmVzdWx0LmRvbmUgPyByZXNvbHZlKHJlc3VsdC52YWx1ZSkgOiBhZG9wdChyZXN1bHQudmFsdWUpLnRoZW4oZnVsZmlsbGVkLCByZWplY3RlZCk7IH1cclxuICAgICAgICBzdGVwKChnZW5lcmF0b3IgPSBnZW5lcmF0b3IuYXBwbHkodGhpc0FyZywgX2FyZ3VtZW50cyB8fCBbXSkpLm5leHQoKSk7XHJcbiAgICB9KTtcclxufTtcclxuaW1wb3J0IHsgRG93bmxvYWRlciB9IGZyb20gJy4vZG93bmxvYWRlcic7XHJcbmltcG9ydCB7IFF1ZXVlIH0gZnJvbSAnLi9xdWV1ZSc7XHJcbmltcG9ydCB7IE1hbmlmZXN0IH0gZnJvbSAnLi9tYW5pZmVzdCc7XHJcbmltcG9ydCB7IEFzc2V0U3RvcmFnZSB9IGZyb20gJy4vc3RvcmFnZSc7XHJcbmltcG9ydCBFdmVudEVtaXR0ZXIgZnJvbSAnZXZlbnRlbWl0dGVyMyc7XHJcbmltcG9ydCB7IGJ1aWxkUGF0aCB9IGZyb20gJy4vdXRpbHMnO1xyXG5leHBvcnQgY2xhc3MgQXNzZXRNYW5hZ2VyIGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcclxuICAgIGNvbnN0cnVjdG9yKG5hbWUsIGJhc2VQYXRoKSB7XHJcbiAgICAgICAgc3VwZXIoKTtcclxuICAgICAgICB0aGlzLm5hbWUgPSBuYW1lO1xyXG4gICAgICAgIHRoaXMuYmFzZVBhdGggPSBiYXNlUGF0aDtcclxuICAgICAgICB0aGlzLnF1ZXVlID0gbmV3IFF1ZXVlKCk7XHJcbiAgICAgICAgdGhpcy5zdG9yYWdlID0gbmV3IEFzc2V0U3RvcmFnZShuYW1lKTtcclxuICAgICAgICB0aGlzLmRvd25sb2FkZXIgPSBuZXcgRG93bmxvYWRlcih0aGlzLnN0b3JhZ2UsIHRoaXMucXVldWUsIHRoaXMuYmFzZVBhdGgpO1xyXG4gICAgICAgIGNvbnNvbGUubG9nKGBBc3NldCBtYW5hZ2VyIGluaXRpYWxpemVkYCk7XHJcbiAgICB9XHJcbiAgICBpbml0KCkge1xyXG4gICAgICAgIHJldHVybiBfX2F3YWl0ZXIodGhpcywgdm9pZCAwLCB2b2lkIDAsIGZ1bmN0aW9uKiAoKSB7XHJcbiAgICAgICAgICAgIHlpZWxkIHRoaXMuc3RvcmFnZS5pbml0KCk7XHJcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgc2V0TWFuaWZlc3QocGF0aCkge1xyXG4gICAgICAgIHJldHVybiBfX2F3YWl0ZXIodGhpcywgdm9pZCAwLCB2b2lkIDAsIGZ1bmN0aW9uKiAoKSB7XHJcbiAgICAgICAgICAgIHRoaXMubWFuaWZlc3QgPSB5aWVsZCBNYW5pZmVzdChgJHt0aGlzLmJhc2VQYXRofS8ke3BhdGh9YCk7XHJcbiAgICAgICAgICAgIHRoaXMuc3RvcmFnZS5zZXRNYW5pZmVzdCh0aGlzLm1hbmlmZXN0KTtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMubWFuaWZlc3Q7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBlbnF1ZXVlKHBhdGgpIHtcclxuICAgICAgICB0aGlzLnF1ZXVlLmFkZChwYXRoKTtcclxuICAgIH1cclxuICAgIGRvd25sb2FkKCkge1xyXG4gICAgICAgIHJldHVybiBfX2F3YWl0ZXIodGhpcywgdm9pZCAwLCB2b2lkIDAsIGZ1bmN0aW9uKiAoKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IHlpZWxkIHRoaXMuZG93bmxvYWRlci5kb3dubG9hZCgpO1xyXG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgZG93bmxvYWRGcm9tTWFuaWZlc3Qoa2V5KSB7XHJcbiAgICAgICAgcmV0dXJuIF9fYXdhaXRlcih0aGlzLCB2b2lkIDAsIHZvaWQgMCwgZnVuY3Rpb24qICgpIHtcclxuICAgICAgICAgICAgY29uc3QgcGF0aHMgPSB0aGlzLm1hbmlmZXN0W2tleV07XHJcbiAgICAgICAgICAgIHBhdGhzLmZvckVhY2goKHBhdGgpID0+IHRoaXMuZW5xdWV1ZShwYXRoKSk7XHJcbiAgICAgICAgICAgIHRoaXMuZG93bmxvYWRlci5vbignZG93bmxvYWQucHJvZ3Jlc3MnLCAoaW5mbykgPT4gdGhpcy5lbWl0KCdwcm9ncmVzcycsIGluZm8pKTtcclxuICAgICAgICAgICAgY29uc3QgZmlsZXMgPSB5aWVsZCB0aGlzLmRvd25sb2FkZXIuZG93bmxvYWQoKTtcclxuICAgICAgICAgICAgdGhpcy5kb3dubG9hZGVyLm9mZignZG93bmxvYWQucHJvZ3Jlc3MnKTtcclxuICAgICAgICAgICAgcmV0dXJuIGZpbGVzO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgZG93bmxvYWRGaWxlKHBhdGgpIHtcclxuICAgICAgICByZXR1cm4gX19hd2FpdGVyKHRoaXMsIHZvaWQgMCwgdm9pZCAwLCBmdW5jdGlvbiogKCkge1xyXG4gICAgICAgICAgICBjb25zdCByZXN1bHQgPSB5aWVsZCB0aGlzLmRvd25sb2FkZXIuZG93bmxvYWRJdGVtKGJ1aWxkUGF0aCh0aGlzLmJhc2VQYXRoLCBwYXRoKSk7XHJcbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBzZXRCYXNlUGF0aChwYXRoKSB7XHJcbiAgICAgICAgdGhpcy5iYXNlUGF0aCA9IHRoaXMuYmFzZVBhdGg7XHJcbiAgICAgICAgdGhpcy5kb3dubG9hZGVyLnNldEJhc2VQYXRoKHRoaXMuYmFzZVBhdGgpO1xyXG4gICAgfVxyXG4gICAgY2xlYXJDYWNoZSgpIHtcclxuICAgICAgICB0aGlzLnN0b3JhZ2UuY2xlYXIoKTtcclxuICAgIH1cclxufVxyXG4iLCJleHBvcnQgdmFyIFNvdXJjZVR5cGU7XHJcbihmdW5jdGlvbiAoU291cmNlVHlwZSkge1xyXG4gICAgU291cmNlVHlwZVtTb3VyY2VUeXBlW1wiV29ybGRTb3VyY2VcIl0gPSAwXSA9IFwiV29ybGRTb3VyY2VcIjtcclxuICAgIFNvdXJjZVR5cGVbU291cmNlVHlwZVtcIlVJU291cmNlXCJdID0gMV0gPSBcIlVJU291cmNlXCI7XHJcbiAgICBTb3VyY2VUeXBlW1NvdXJjZVR5cGVbXCJNYXN0ZXJTb3VyY2VcIl0gPSAyXSA9IFwiTWFzdGVyU291cmNlXCI7XHJcbn0pKFNvdXJjZVR5cGUgfHwgKFNvdXJjZVR5cGUgPSB7fSkpO1xyXG4iLCJjb25zdCBfX1dFQlBBQ0tfTkFNRVNQQUNFX09CSkVDVF9fID0gdW5kZWZpbmVkOyIsImV4cG9ydCBjbGFzcyBCYXNlRW50aXR5IHtcclxuICAgIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgICAgIHRoaXMuY29tcG9uZW50cyA9IG5ldyBNYXAoKTtcclxuICAgICAgICB0aGlzLmlkID0gMDtcclxuICAgIH1cclxuICAgIGFkZENvbXBvbmVudChjb21wb25lbnQpIHtcclxuICAgICAgICBsZXQgY29tcCA9IG5ldyBjb21wb25lbnQoKTtcclxuICAgICAgICBjb21wLmlkID0gY29tcG9uZW50LmlkO1xyXG4gICAgICAgIHRoaXMuY29tcG9uZW50cy5zZXQoY29tcG9uZW50LmlkLCBjb21wKTtcclxuICAgIH1cclxuICAgIHJlbW92ZUNvbXBvbmVudChjb21wb25lbnQpIHtcclxuICAgICAgICB0aGlzLmNvbXBvbmVudHMuZGVsZXRlKGNvbXBvbmVudC5pZCk7XHJcbiAgICB9XHJcbiAgICBnZXRDb21wb25lbnRJRHMoKSB7XHJcbiAgICAgICAgcmV0dXJuIFsuLi50aGlzLmNvbXBvbmVudHMua2V5cygpXTtcclxuICAgIH1cclxuICAgIGdldENvbXBvbmVudChjb21wb25lbnQpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5jb21wb25lbnRzLmdldChjb21wb25lbnQuaWQpO1xyXG4gICAgfVxyXG4gICAgZ2V0Q29tcG9uZW50QnlJRChpZCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLmNvbXBvbmVudHMuZ2V0KGlkKTtcclxuICAgIH1cclxufVxyXG4iLCJleHBvcnQgY2xhc3MgRXZlbnRCdXMge1xyXG4gICAgY29uc3RydWN0b3IoKSB7XHJcbiAgICAgICAgdGhpcy5ldmVudHMgPSBuZXcgTWFwKCk7XHJcbiAgICB9XHJcbiAgICBlbWl0KGlkLCBkYXRhID0ge30pIHtcclxuICAgICAgICBsZXQgZXYgPSB0aGlzLmV2ZW50cy5nZXQoaWQpO1xyXG4gICAgICAgIGlmICghZXYpIHtcclxuICAgICAgICAgICAgbGV0IGV2ID0gbmV3IEV2ZW50SXRlbShpZCk7XHJcbiAgICAgICAgICAgIHRoaXMuZXZlbnRzLnNldChpZCwgZXYpO1xyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGV2LnN1YnNjcmliZXJzLmZvckVhY2goKHN1YnNjcmliZXIpID0+IHtcclxuICAgICAgICAgICAgc3Vic2NyaWJlcihkYXRhKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuICAgIHN1YnNjcmliZShpZCwgc3Vic2NyaWJlcikge1xyXG4gICAgICAgIGxldCBldiA9IHRoaXMuZXZlbnRzLmdldChpZCk7XHJcbiAgICAgICAgaWYgKCFldikge1xyXG4gICAgICAgICAgICBldiA9IG5ldyBFdmVudEl0ZW0oaWQpO1xyXG4gICAgICAgICAgICB0aGlzLmV2ZW50cy5zZXQoaWQsIGV2KTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZXYuc3Vic2NyaWJlcnMucHVzaChzdWJzY3JpYmVyKTtcclxuICAgIH1cclxuICAgIHVuc3Vic2NyaWJlKGlkLCBzdWJzY3JpYmVyKSB7XHJcbiAgICAgICAgaWYgKHRoaXMuZXZlbnRzLmhhcyhpZCkpIHtcclxuICAgICAgICAgICAgbGV0IGV2ID0gdGhpcy5ldmVudHMuZ2V0KGlkKTtcclxuICAgICAgICAgICAgZXYuc3Vic2NyaWJlcnMgPSBldi5zdWJzY3JpYmVycy5maWx0ZXIoKGZuKSA9PiBmbiAhPT0gc3Vic2NyaWJlcik7XHJcbiAgICAgICAgICAgIGlmIChldi5zdWJzY3JpYmVycy5sZW5ndGggPCAxKSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmV2ZW50cy5kZWxldGUoaWQpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgdW5zdWJzY3JpYmVBbGwoaWQpIHtcclxuICAgICAgICBpZiAodGhpcy5ldmVudHMuaGFzKGlkKSkge1xyXG4gICAgICAgICAgICB0aGlzLmV2ZW50cy5kZWxldGUoaWQpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxufVxyXG5leHBvcnQgY2xhc3MgRXZlbnRJdGVtIHtcclxuICAgIGNvbnN0cnVjdG9yKGlkKSB7XHJcbiAgICAgICAgdGhpcy5pZCA9IGlkO1xyXG4gICAgICAgIHRoaXMuc3Vic2NyaWJlcnMgPSBbXTtcclxuICAgIH1cclxufVxyXG4iLCJleHBvcnQgY2xhc3MgUXVlcnkge1xyXG4gICAgY29uc3RydWN0b3IoaW5jbHVkZSwgZXhjbHVkZSwgd29ybGQpIHtcclxuICAgICAgICB0aGlzLmluY2x1ZGUgPSBpbmNsdWRlO1xyXG4gICAgICAgIHRoaXMuZXhjbHVkZSA9IGV4Y2x1ZGU7XHJcbiAgICAgICAgdGhpcy53b3JsZCA9IHdvcmxkO1xyXG4gICAgICAgIHRoaXMuaXNEaXJ0eSA9IHRydWU7XHJcbiAgICAgICAgdGhpcy5yZXN1bHRzID0gbmV3IEFycmF5KCk7XHJcbiAgICAgICAgdGhpcy5pbmNsdWRlQ29tcG9uZW50SWRzID0gaW5jbHVkZS5tYXAoKGNvbXBvbmVudCkgPT4gY29tcG9uZW50LmlkKTtcclxuICAgICAgICB0aGlzLmV4Y2x1ZGVDb21wb25lbnRJZHMgPSBleGNsdWRlLm1hcCgoY29tcG9uZW50KSA9PiBjb21wb25lbnQuaWQpO1xyXG4gICAgICAgIHRoaXMuaWQgPSAwO1xyXG4gICAgfVxyXG4gICAgZXhlY3V0ZSgpIHtcclxuICAgICAgICBpZiAoIXRoaXMuaXNEaXJ0eSAmJiB0aGlzLnJlc3VsdHMpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMucmVzdWx0cztcclxuICAgICAgICB9XHJcbiAgICAgICAgbGV0IGZpbHRlcmVkO1xyXG4gICAgICAgIHRoaXMuaW5jbHVkZUNvbXBvbmVudElkcyA9IHRoaXMuaW5jbHVkZS5tYXAoKGNvbXBvbmVudCkgPT4gdGhpcy53b3JsZC5jb21wb25lbnROYW1lc1RvSURzLmdldChjb21wb25lbnQubmFtZSkpO1xyXG4gICAgICAgIHRoaXMuZXhjbHVkZUNvbXBvbmVudElkcyA9IHRoaXMuZXhjbHVkZS5tYXAoKGNvbXBvbmVudCkgPT4gdGhpcy53b3JsZC5jb21wb25lbnROYW1lc1RvSURzLmdldChjb21wb25lbnQubmFtZSkpO1xyXG4gICAgICAgIGNvbnN0IGVudGl0aWVzID0gdGhpcy53b3JsZC5lbnRpdGllcy5maWx0ZXIoKGVudGl0eSkgPT4ge1xyXG4gICAgICAgICAgICBsZXQgaWRzID0gZW50aXR5LmdldENvbXBvbmVudElEcygpO1xyXG4gICAgICAgICAgICAvLyBsZXQgaW5jbHVkZXMgPSBpZHMubWFwKGlkID0+IHRoaXMuaW5jbHVkZUNvbXBvbmVudElkcy5pbmNsdWRlcyhpZCkpLmluY2x1ZGVzKHRydWUpO1xyXG4gICAgICAgICAgICBsZXQgZXhjbHVkZXMgPSBpZHNcclxuICAgICAgICAgICAgICAgIC5tYXAoKGlkKSA9PiB0aGlzLmV4Y2x1ZGVDb21wb25lbnRJZHMuaW5jbHVkZXMoaWQpKVxyXG4gICAgICAgICAgICAgICAgLmluY2x1ZGVzKHRydWUpO1xyXG4gICAgICAgICAgICBsZXQgaW5jbHVkZXMgPSBpZHMuZmlsdGVyKChpZCkgPT4gdGhpcy5pbmNsdWRlQ29tcG9uZW50SWRzLmluY2x1ZGVzKGlkKSk7XHJcbiAgICAgICAgICAgIHJldHVybiBpbmNsdWRlcy5sZW5ndGggPT09IHRoaXMuaW5jbHVkZUNvbXBvbmVudElkcy5sZW5ndGggJiYgIWV4Y2x1ZGVzO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIGlmIChlbnRpdGllcy5sZW5ndGggPiAwKSB7XHJcbiAgICAgICAgICAgIHRoaXMuaXNEaXJ0eSA9IGZhbHNlO1xyXG4gICAgICAgICAgICB0aGlzLnJlc3VsdHMgPSBlbnRpdGllcztcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIGVudGl0aWVzO1xyXG4gICAgfVxyXG59XHJcbiIsImV4cG9ydCBjbGFzcyBTeXN0ZW0ge1xyXG4gICAgY29uc3RydWN0b3IoZXhlY3V0b3IpIHtcclxuICAgICAgICB0aGlzLmV4ZWN1dG9yID0gZXhlY3V0b3I7XHJcbiAgICB9XHJcbiAgICBleGVjdXRlKHdvcmxkKSB7XHJcbiAgICAgICAgaWYgKHRoaXMuZXhlY3V0b3IpIHtcclxuICAgICAgICAgICAgdGhpcy5leGVjdXRvcih3b3JsZCk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IEJhc2VFbnRpdHkgfSBmcm9tICcuL2VudGl0eSc7XHJcbmltcG9ydCB7IEV2ZW50QnVzIH0gZnJvbSAnLi4vZXZlbnQtYnVzJztcclxuaW1wb3J0IHsgUXVlcnkgfSBmcm9tICcuL3F1ZXJ5JztcclxuaW1wb3J0IHsgU3lzdGVtIH0gZnJvbSAnLi9zeXN0ZW0nO1xyXG5leHBvcnQgY2xhc3MgV29ybGQge1xyXG4gICAgY29uc3RydWN0b3IoKSB7XHJcbiAgICAgICAgdGhpcy5uZXh0RW50aXR5SUQgPSAwO1xyXG4gICAgICAgIHRoaXMubmV4dENvbXBvbmVudElEID0gMDtcclxuICAgICAgICB0aGlzLm5leHRRdWVyeUlEID0gMDtcclxuICAgICAgICB0aGlzLmVudGl0aWVzID0gbmV3IEFycmF5KCk7XHJcbiAgICAgICAgdGhpcy5zeXN0ZW1zID0gbmV3IFNldCgpO1xyXG4gICAgICAgIHRoaXMuY29tcG9uZW50cyA9IG5ldyBNYXAoKTtcclxuICAgICAgICB0aGlzLmNvbXBvbmVudE5hbWVzVG9JRHMgPSBuZXcgTWFwKCk7XHJcbiAgICAgICAgdGhpcy5xdWVyeUNhY2hlID0gbmV3IEFycmF5KCk7XHJcbiAgICAgICAgdGhpcy5ldmVudEJ1cyA9IG5ldyBFdmVudEJ1cygpO1xyXG4gICAgfVxyXG4gICAgcnVuKCkge1xyXG4gICAgICAgIHRoaXMuc3lzdGVtcy5mb3JFYWNoKChzeXN0ZW0pID0+IHtcclxuICAgICAgICAgICAgc3lzdGVtLmV4ZWN1dGUodGhpcyk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBjcmVhdGVTeXN0ZW0oc3lzdGVtRXhlY3V0b3IpIHtcclxuICAgICAgICBjb25zdCBuZXdTeXN0ZW0gPSBuZXcgU3lzdGVtKHN5c3RlbUV4ZWN1dG9yKTtcclxuICAgICAgICB0aGlzLnN5c3RlbXMuYWRkKG5ld1N5c3RlbSk7XHJcbiAgICB9XHJcbiAgICBhZGRTeXN0ZW0oc3lzdGVtKSB7XHJcbiAgICAgICAgdGhpcy5zeXN0ZW1zLmFkZChzeXN0ZW0pO1xyXG4gICAgfVxyXG4gICAgYWRkRW50aXR5KGVudGl0eSkge1xyXG4gICAgICAgIHRoaXMuZW50aXRpZXMucHVzaChlbnRpdHkpO1xyXG4gICAgICAgIHRoaXMubWFya1F1ZXJpZXNEaXJ0eSgpO1xyXG4gICAgfVxyXG4gICAgcmVtb3ZlRW50aXR5KGVudGl0eVRvUmVtb3ZlKSB7XHJcbiAgICAgICAgdGhpcy5lbnRpdGllcyA9IHRoaXMuZW50aXRpZXMuZmlsdGVyKChlbnRpdHkpID0+IGVudGl0eSAhPT0gZW50aXR5VG9SZW1vdmUpO1xyXG4gICAgICAgIHRoaXMubWFya1F1ZXJpZXNEaXJ0eSgpO1xyXG4gICAgfVxyXG4gICAgY3JlYXRlRW50aXR5KGNvbXBvbmVudHMpIHtcclxuICAgICAgICBjb25zdCBuZXdFbnRpdHkgPSBuZXcgQmFzZUVudGl0eSgpO1xyXG4gICAgICAgIG5ld0VudGl0eS5pZCA9IHRoaXMubmV4dEVudGl0eUlEO1xyXG4gICAgICAgIHRoaXMubmV4dEVudGl0eUlEKys7XHJcbiAgICAgICAgY29tcG9uZW50cy5mb3JFYWNoKChjb21wb25lbnQpID0+IHtcclxuICAgICAgICAgICAgaWYgKHRoaXMuY29tcG9uZW50TmFtZXNUb0lEcy5oYXMoY29tcG9uZW50Lm5hbWUpKSB7XHJcbiAgICAgICAgICAgICAgICBjb21wb25lbnQuaWQgPSB0aGlzLmNvbXBvbmVudE5hbWVzVG9JRHMuZ2V0KGNvbXBvbmVudC5uYW1lKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuY29tcG9uZW50TmFtZXNUb0lEcy5zZXQoY29tcG9uZW50Lm5hbWUsIHRoaXMubmV4dENvbXBvbmVudElEKTtcclxuICAgICAgICAgICAgICAgIGNvbXBvbmVudC5pZCA9IHRoaXMubmV4dENvbXBvbmVudElEO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5uZXh0Q29tcG9uZW50SUQrKztcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBuZXdFbnRpdHkuYWRkQ29tcG9uZW50KGNvbXBvbmVudCk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgdGhpcy5lbnRpdGllcy5wdXNoKG5ld0VudGl0eSk7XHJcbiAgICAgICAgdGhpcy5tYXJrUXVlcmllc0RpcnR5KCk7XHJcbiAgICAgICAgcmV0dXJuIG5ld0VudGl0eTtcclxuICAgIH1cclxuICAgIGV4dGVuZEVudGl0eShlbnRpdHksIGNvbXBvbmVudHMpIHtcclxuICAgICAgICBjb25zdCB0b0Nsb25lID0gdGhpcy5lbnRpdGllcy5maW5kKChmb3VuZCkgPT4gZW50aXR5Lm5hbWUgPT09IGZvdW5kLmNvbnN0cnVjdG9yLm5hbWUpO1xyXG4gICAgICAgIGNvbnN0IGNsb25lZCA9IG5ldyBCYXNlRW50aXR5KCk7XHJcbiAgICAgICAgY2xvbmVkLmlkID0gdGhpcy5uZXh0RW50aXR5SUQ7XHJcbiAgICAgICAgdGhpcy5uZXh0RW50aXR5SUQrKztcclxuICAgICAgICB0b0Nsb25lLmNvbXBvbmVudHMuZm9yRWFjaCgoY29tcG9uZW50KSA9PiB7XHJcbiAgICAgICAgICAgIGNsb25lZC5hZGRDb21wb25lbnQodGhpcy5jb21wb25lbnRzLmdldChjb21wb25lbnQuaWQpKTtcclxuICAgICAgICB9KTtcclxuICAgICAgICBjb21wb25lbnRzLmZvckVhY2goKGNvbXBvbmVudCkgPT4ge1xyXG4gICAgICAgICAgICBpZiAodGhpcy5jb21wb25lbnROYW1lc1RvSURzLmhhcyhjb21wb25lbnQubmFtZSkpIHtcclxuICAgICAgICAgICAgICAgIGNvbXBvbmVudC5pZCA9IHRoaXMuY29tcG9uZW50TmFtZXNUb0lEcy5nZXQoY29tcG9uZW50Lm5hbWUpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5jb21wb25lbnROYW1lc1RvSURzLnNldChjb21wb25lbnQubmFtZSwgdGhpcy5uZXh0Q29tcG9uZW50SUQpO1xyXG4gICAgICAgICAgICAgICAgY29tcG9uZW50LmlkID0gdGhpcy5uZXh0Q29tcG9uZW50SUQ7XHJcbiAgICAgICAgICAgICAgICB0aGlzLm5leHRDb21wb25lbnRJRCsrO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGNsb25lZC5hZGRDb21wb25lbnQoY29tcG9uZW50KTtcclxuICAgICAgICB9KTtcclxuICAgICAgICB0aGlzLmVudGl0aWVzLnB1c2goY2xvbmVkKTtcclxuICAgICAgICByZXR1cm4gY2xvbmVkO1xyXG4gICAgfVxyXG4gICAgY3JlYXRlQ29tcG9uZW50KGNvbXBvbmVudCkge1xyXG4gICAgICAgIGNvbnN0IG5ld0NvbXBvbmVudCA9IGNvbXBvbmVudDtcclxuICAgICAgICBuZXdDb21wb25lbnQuaWQgPSB0aGlzLm5leHRDb21wb25lbnRJRDtcclxuICAgICAgICB0aGlzLm5leHRDb21wb25lbnRJRCsrO1xyXG4gICAgICAgIHRoaXMuY29tcG9uZW50cy5zZXQobmV3Q29tcG9uZW50LmlkLCBuZXdDb21wb25lbnQpO1xyXG4gICAgICAgIHRoaXMuY29tcG9uZW50TmFtZXNUb0lEcy5zZXQoY29tcG9uZW50Lm5hbWUsIGNvbXBvbmVudC5pZCk7XHJcbiAgICAgICAgcmV0dXJuIG5ld0NvbXBvbmVudDtcclxuICAgIH1cclxuICAgIHF1ZXJ5KGluY2x1ZGUsIGV4Y2x1ZGUpIHtcclxuICAgICAgICBjb25zdCBxdWVyeSA9IG5ldyBRdWVyeShpbmNsdWRlLCBleGNsdWRlLCB0aGlzKTtcclxuICAgICAgICBjb25zdCBjYWNoZSA9IHRoaXMucXVlcnlDYWNoZS5maW5kKChpdGVtKSA9PiBpdGVtLmluY2x1ZGUgPT0gaW5jbHVkZSAmJiBpdGVtLmV4Y2x1ZGUgPT0gZXhjbHVkZSk7XHJcbiAgICAgICAgaWYgKGNhY2hlKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBjYWNoZS5leGVjdXRlKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMucXVlcnlDYWNoZS5wdXNoKHF1ZXJ5KTtcclxuICAgICAgICByZXR1cm4gcXVlcnkuZXhlY3V0ZSgpO1xyXG4gICAgfVxyXG4gICAgY3JlYXRlUXVlcnkoaW5jbHVkZSwgZXhjbHVkZSkge1xyXG4gICAgICAgIGNvbnN0IG5ld1F1ZXJ5ID0gbmV3IFF1ZXJ5KGluY2x1ZGUsIGV4Y2x1ZGUsIHRoaXMpO1xyXG4gICAgICAgIG5ld1F1ZXJ5LmlkID0gdGhpcy5uZXh0UXVlcnlJRDtcclxuICAgICAgICB0aGlzLm5leHRRdWVyeUlEKys7XHJcbiAgICAgICAgdGhpcy5xdWVyeUNhY2hlLnB1c2gobmV3UXVlcnkpO1xyXG4gICAgICAgIHJldHVybiBuZXdRdWVyeTtcclxuICAgIH1cclxuICAgIG1hcmtRdWVyaWVzRGlydHkoKSB7XHJcbiAgICAgICAgdGhpcy5xdWVyeUNhY2hlLmZvckVhY2goKHF1ZXJ5KSA9PiAocXVlcnkuaXNEaXJ0eSA9IHRydWUpKTtcclxuICAgIH1cclxufVxyXG4iLCJleHBvcnQgY2xhc3MgQmFzZUlucHV0IHtcclxuICAgIGNvbnN0cnVjdG9yKGVsZW1lbnQpIHtcclxuICAgICAgICB0aGlzLmVsZW1lbnQgPSBlbGVtZW50O1xyXG4gICAgfVxyXG4gICAgZ2V0U3RhdGUoKSB7IH1cclxuICAgIGNhcHR1cmUocHJldmVudERlZmF1bHQpIHtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgICByZWxlYXNlKCkge1xyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxufVxyXG4iLCJpbXBvcnQgeyBCYXNlSW5wdXQgfSBmcm9tICcuL2Jhc2UtaW5wdXQnO1xyXG5leHBvcnQgY2xhc3MgS2V5Ym9hcmQgZXh0ZW5kcyBCYXNlSW5wdXQge1xyXG4gICAgY29uc3RydWN0b3IoZWxlbWVudCkge1xyXG4gICAgICAgIHN1cGVyKGVsZW1lbnQpO1xyXG4gICAgICAgIHRoaXMua2V5c0Rvd24gPSBuZXcgTWFwKCk7XHJcbiAgICAgICAgdGhpcy5rZXlzSnVzdFByZXNzZWQgPSBuZXcgTWFwKCk7XHJcbiAgICAgICAgdGhpcy5rZXlzSnVzdFJlbGVhc2VkID0gbmV3IE1hcCgpO1xyXG4gICAgICAgIHRoaXMuaGFuZGxlS2V5RG93biA9IHRoaXMuaGFuZGxlS2V5RG93bi5iaW5kKHRoaXMpO1xyXG4gICAgICAgIHRoaXMuaGFuZGxlS2V5VXAgPSB0aGlzLmhhbmRsZUtleVVwLmJpbmQodGhpcyk7XHJcbiAgICB9XHJcbiAgICBjYXB0dXJlKHByZXZlbnREZWZhdWx0KSB7XHJcbiAgICAgICAgdGhpcy5hY3RpdmUgPSB0cnVlO1xyXG4gICAgICAgIHRoaXMucHJldmVudERlZmF1bHQgPSBwcmV2ZW50RGVmYXVsdDtcclxuICAgICAgICB0aGlzLmVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigna2V5ZG93bicsIHRoaXMuaGFuZGxlS2V5RG93bik7XHJcbiAgICAgICAgdGhpcy5lbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2tleXVwJywgdGhpcy5oYW5kbGVLZXlVcCk7XHJcbiAgICB9XHJcbiAgICByZWxlYXNlKCkge1xyXG4gICAgICAgIHRoaXMuYWN0aXZlID0gZmFsc2U7XHJcbiAgICAgICAgdGhpcy5lbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2tleWRvd24nLCB0aGlzLmhhbmRsZUtleURvd24pO1xyXG4gICAgICAgIHRoaXMuZWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdrZXl1cCcsIHRoaXMuaGFuZGxlS2V5VXApO1xyXG4gICAgfVxyXG4gICAgZ2V0U3RhdGUoKSB7XHJcbiAgICAgICAgY29uc3Qgc3RhdGUgPSB7XHJcbiAgICAgICAgICAgIGtleXNEb3duOiBuZXcgTWFwKHRoaXMua2V5c0Rvd24pLFxyXG4gICAgICAgICAgICBrZXlzSnVzdFByZXNzZWQ6IG5ldyBNYXAodGhpcy5rZXlzSnVzdFByZXNzZWQpLFxyXG4gICAgICAgICAgICBrZXlzSnVzdFJlbGVhc2VkOiBuZXcgTWFwKHRoaXMua2V5c0p1c3RSZWxlYXNlZClcclxuICAgICAgICB9O1xyXG4gICAgICAgIHRoaXMua2V5c0p1c3RQcmVzc2VkLmNsZWFyKCk7XHJcbiAgICAgICAgdGhpcy5rZXlzSnVzdFJlbGVhc2VkLmNsZWFyKCk7XHJcbiAgICAgICAgcmV0dXJuIHN0YXRlO1xyXG4gICAgfVxyXG4gICAgaGFuZGxlS2V5RG93bihldmVudCkge1xyXG4gICAgICAgIGlmICh0aGlzLmFjdGl2ZSAmJiB0aGlzLnByZXZlbnREZWZhdWx0KVxyXG4gICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgIGlmICh0aGlzLmtleXNEb3duLmdldChldmVudC5rZXlDb2RlKSlcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIHRoaXMua2V5c0Rvd24uc2V0KGV2ZW50LmtleUNvZGUsIHRydWUpO1xyXG4gICAgICAgIHRoaXMua2V5c0p1c3RQcmVzc2VkLnNldChldmVudC5rZXlDb2RlLCB0cnVlKTtcclxuICAgICAgICB0aGlzLmtleXNKdXN0UmVsZWFzZWQuc2V0KGV2ZW50LmtleUNvZGUsIGZhbHNlKTtcclxuICAgIH1cclxuICAgIGhhbmRsZUtleVVwKGV2ZW50KSB7XHJcbiAgICAgICAgaWYgKHRoaXMuYWN0aXZlICYmIHRoaXMucHJldmVudERlZmF1bHQpXHJcbiAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICAgICAgaWYgKCF0aGlzLmtleXNEb3duLmdldChldmVudC5rZXlDb2RlKSlcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIHRoaXMua2V5c0Rvd24uc2V0KGV2ZW50LmtleUNvZGUsIGZhbHNlKTtcclxuICAgICAgICB0aGlzLmtleXNKdXN0UHJlc3NlZC5zZXQoZXZlbnQua2V5Q29kZSwgZmFsc2UpO1xyXG4gICAgICAgIHRoaXMua2V5c0p1c3RSZWxlYXNlZC5zZXQoZXZlbnQua2V5Q29kZSwgdHJ1ZSk7XHJcbiAgICB9XHJcbn1cclxuIiwiaW1wb3J0IHsgQmFzZUlucHV0IH0gZnJvbSAnLi9iYXNlLWlucHV0JztcclxuZXhwb3J0IGNsYXNzIE1vdXNlIGV4dGVuZHMgQmFzZUlucHV0IHtcclxuICAgIGNvbnN0cnVjdG9yKGVsZW1lbnQpIHtcclxuICAgICAgICBzdXBlcihlbGVtZW50KTtcclxuICAgICAgICB0aGlzLm1vdXNlUG9zaXRpb24gPSBuZXcgUG9zaXRpb24oKTtcclxuICAgICAgICB0aGlzLm1vdXNlRGVsdGEgPSBuZXcgRGVsdGEoKTtcclxuICAgICAgICB0aGlzLm1vdXNlV2hlZWwgPSBuZXcgRGVsdGEoKTtcclxuICAgICAgICB0aGlzLm1vdXNlQnV0dG9ucyA9IG5ldyBNb3VzZUJ1dHRvbnMoKTtcclxuICAgIH1cclxuICAgIGNhcHR1cmUoKSB7XHJcbiAgICAgICAgdGhpcy5oYW5kbGVNb3VzZURvd24gPSB0aGlzLmhhbmRsZU1vdXNlRG93bi5iaW5kKHRoaXMpO1xyXG4gICAgICAgIHRoaXMuaGFuZGxlTW91c2VNb3ZlID0gdGhpcy5oYW5kbGVNb3VzZU1vdmUuYmluZCh0aGlzKTtcclxuICAgICAgICB0aGlzLmhhbmRsZU1vdXNlVXAgPSB0aGlzLmhhbmRsZU1vdXNlVXAuYmluZCh0aGlzKTtcclxuICAgICAgICB0aGlzLmhhbmRsZVBvaW50ZXJDaGFuZ2UgPSB0aGlzLmhhbmRsZVBvaW50ZXJDaGFuZ2UuYmluZCh0aGlzKTtcclxuICAgICAgICB0aGlzLmFjdGl2ZSA9IHRydWU7XHJcbiAgICAgICAgdGhpcy5lbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIHRoaXMuaGFuZGxlTW91c2VEb3duKTtcclxuICAgICAgICB0aGlzLmVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vtb3ZlJywgdGhpcy5oYW5kbGVNb3VzZU1vdmUpO1xyXG4gICAgICAgIHRoaXMuZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdtb3VzZXVwJywgdGhpcy5oYW5kbGVNb3VzZVVwKTtcclxuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVybG9ja2NoYW5nZScsIHRoaXMuaGFuZGxlUG9pbnRlckNoYW5nZSk7XHJcbiAgICB9XHJcbiAgICByZWxlYXNlKCkge1xyXG4gICAgICAgIHRoaXMuYWN0aXZlID0gZmFsc2U7XHJcbiAgICAgICAgdGhpcy5lbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIHRoaXMuaGFuZGxlTW91c2VEb3duKTtcclxuICAgICAgICB0aGlzLmVsZW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcignbW91c2Vtb3ZlJywgdGhpcy5oYW5kbGVNb3VzZU1vdmUpO1xyXG4gICAgICAgIHRoaXMuZWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZXVwJywgdGhpcy5oYW5kbGVNb3VzZVVwKTtcclxuICAgIH1cclxuICAgIGdldFN0YXRlKCkge1xyXG4gICAgICAgIGNvbnN0IHsgbW91c2VCdXR0b25zLCBtb3VzZURlbHRhLCBtb3VzZVBvc2l0aW9uLCBtb3VzZVdoZWVsIH0gPSB0aGlzO1xyXG4gICAgICAgIGNvbnN0IHN0YXRlID0ge1xyXG4gICAgICAgICAgICBtb3VzZUJ1dHRvbnM6IHtcclxuICAgICAgICAgICAgICAgIGtleXNEb3duOiBuZXcgTWFwKHRoaXMubW91c2VCdXR0b25zLmtleXNEb3duKSxcclxuICAgICAgICAgICAgICAgIGtleXNKdXN0UHJlc3NlZDogbmV3IE1hcCh0aGlzLm1vdXNlQnV0dG9ucy5rZXlzSnVzdFByZXNzZWQpLFxyXG4gICAgICAgICAgICAgICAga2V5c0p1c3RSZWxlYXNlZDogbmV3IE1hcCh0aGlzLm1vdXNlQnV0dG9ucy5rZXlzSnVzdFJlbGVhc2VkKVxyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICBtb3VzZURlbHRhLFxyXG4gICAgICAgICAgICBtb3VzZVBvc2l0aW9uLFxyXG4gICAgICAgICAgICBtb3VzZVdoZWVsXHJcbiAgICAgICAgfTtcclxuICAgICAgICB0aGlzLm1vdXNlQnV0dG9ucy5rZXlzSnVzdFByZXNzZWQuY2xlYXIoKTtcclxuICAgICAgICB0aGlzLm1vdXNlQnV0dG9ucy5rZXlzSnVzdFJlbGVhc2VkLmNsZWFyKCk7XHJcbiAgICAgICAgdGhpcy5tb3VzZURlbHRhLnggPSAwO1xyXG4gICAgICAgIHRoaXMubW91c2VEZWx0YS55ID0gMDtcclxuICAgICAgICByZXR1cm4gc3RhdGU7XHJcbiAgICB9XHJcbiAgICBoYW5kbGVNb3VzZURvd24oZXZlbnQpIHtcclxuICAgICAgICBpZiAodGhpcy5hY3RpdmUpXHJcbiAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICAgICAgdGhpcy5tb3VzZUJ1dHRvbnMua2V5c0Rvd24uc2V0KGV2ZW50LmJ1dHRvbiwgdHJ1ZSk7XHJcbiAgICAgICAgdGhpcy5tb3VzZUJ1dHRvbnMua2V5c0p1c3RQcmVzc2VkLnNldChldmVudC5idXR0b24sIHRydWUpO1xyXG4gICAgICAgIHRoaXMubW91c2VCdXR0b25zLmtleXNKdXN0UmVsZWFzZWQuc2V0KGV2ZW50LmJ1dHRvbiwgZmFsc2UpO1xyXG4gICAgfVxyXG4gICAgaGFuZGxlTW91c2VNb3ZlKGV2ZW50KSB7XHJcbiAgICAgICAgaWYgKHRoaXMuYWN0aXZlKVxyXG4gICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgIHRoaXMubW91c2VQb3NpdGlvbi54ID0gZXZlbnQuY2xpZW50WDtcclxuICAgICAgICB0aGlzLm1vdXNlUG9zaXRpb24ueSA9IGV2ZW50LmNsaWVudFk7XHJcbiAgICAgICAgdGhpcy5tb3VzZURlbHRhLnggPSBldmVudC5tb3ZlbWVudFg7XHJcbiAgICAgICAgdGhpcy5tb3VzZURlbHRhLnkgPSBldmVudC5tb3ZlbWVudFk7XHJcbiAgICB9XHJcbiAgICBoYW5kbGVNb3VzZVVwKGV2ZW50KSB7XHJcbiAgICAgICAgaWYgKHRoaXMuYWN0aXZlKVxyXG4gICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgIHRoaXMubW91c2VCdXR0b25zLmtleXNKdXN0UmVsZWFzZWQuc2V0KGV2ZW50LmJ1dHRvbiwgdHJ1ZSk7XHJcbiAgICAgICAgdGhpcy5tb3VzZUJ1dHRvbnMua2V5c0Rvd24uc2V0KGV2ZW50LmJ1dHRvbiwgZmFsc2UpO1xyXG4gICAgICAgIHRoaXMubW91c2VCdXR0b25zLmtleXNKdXN0UHJlc3NlZC5zZXQoZXZlbnQuYnV0dG9uLCBmYWxzZSk7XHJcbiAgICB9XHJcbiAgICBoYW5kbGVQb2ludGVyQ2hhbmdlKCkge1xyXG4gICAgICAgIGlmIChkb2N1bWVudC5wb2ludGVyTG9ja0VsZW1lbnQgIT09IHRoaXMuZWxlbWVudCkge1xyXG4gICAgICAgICAgICB0aGlzLmVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmVsZW1lbnQucmVxdWVzdFBvaW50ZXJMb2NrKCk7XHJcbiAgICAgICAgICAgIH0sIHtcclxuICAgICAgICAgICAgICAgIG9uY2U6IHRydWVcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcbmV4cG9ydCBjbGFzcyBQb3NpdGlvbiB7XHJcbn1cclxuZXhwb3J0IGNsYXNzIE1vdXNlQnV0dG9ucyB7XHJcbiAgICBjb25zdHJ1Y3RvcigpIHtcclxuICAgICAgICB0aGlzLmtleXNEb3duID0gbmV3IE1hcCgpO1xyXG4gICAgICAgIHRoaXMua2V5c0p1c3RQcmVzc2VkID0gbmV3IE1hcCgpO1xyXG4gICAgICAgIHRoaXMua2V5c0p1c3RSZWxlYXNlZCA9IG5ldyBNYXAoKTtcclxuICAgIH1cclxufVxyXG5leHBvcnQgY2xhc3MgRGVsdGEge1xyXG59XHJcbiIsImltcG9ydCB7IGNyZWF0ZUlucHV0IH0gZnJvbSAnLi9pbnB1dC1mYWN0b3J5JztcclxuZXhwb3J0IGNsYXNzIElucHV0IHtcclxuICAgIGNvbnN0cnVjdG9yKElucHV0SURzLCBlbGVtZW50KSB7XHJcbiAgICAgICAgdGhpcy5JbnB1dElEcyA9IElucHV0SURzO1xyXG4gICAgICAgIHRoaXMuZWxlbWVudCA9IGVsZW1lbnQ7XHJcbiAgICAgICAgdGhpcy5pbnB1dHMgPSBuZXcgTWFwKCk7XHJcbiAgICAgICAgdGhpcy5pbml0KCk7XHJcbiAgICB9XHJcbiAgICBpbml0KCkge1xyXG4gICAgICAgIHRoaXMuSW5wdXRJRHMuZm9yRWFjaCgoaW5wdXRJRCkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCB0aGluZyA9IGNyZWF0ZUlucHV0KGlucHV0SUQpO1xyXG4gICAgICAgICAgICBjb25zdCBpbnN0YW5jZSA9IG5ldyB0aGluZyh0aGlzLmVsZW1lbnQpO1xyXG4gICAgICAgICAgICB0aGlzLmlucHV0cy5zZXQoaW5wdXRJRCwgaW5zdGFuY2UpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgYWRkSW5wdXQoaWQsIGlucHV0KSB7XHJcbiAgICAgICAgdGhpcy5pbnB1dHMuc2V0KGlkLCBpbnB1dCk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBjYXB0dXJlKHByZXZlbnREZWZhdWx0ID0gdHJ1ZSkge1xyXG4gICAgICAgIHRoaXMuaW5wdXRzLmZvckVhY2goKGlucHV0KSA9PiBpbnB1dC5jYXB0dXJlKHByZXZlbnREZWZhdWx0KSk7XHJcbiAgICB9XHJcbiAgICByZWxlYXNlKCkge1xyXG4gICAgICAgIHRoaXMuaW5wdXRzLmZvckVhY2goKGlucHV0KSA9PiBpbnB1dC5yZWxlYXNlKCkpO1xyXG4gICAgfVxyXG4gICAgZ2V0U3RhdGUoKSB7XHJcbiAgICAgICAgY29uc3Qgc3RhdGUgPSB7fTtcclxuICAgICAgICB0aGlzLmlucHV0cy5mb3JFYWNoKChpbnB1dCwgaW5wdXRJRCkgPT4ge1xyXG4gICAgICAgICAgICBpZiAoaW5wdXQpXHJcbiAgICAgICAgICAgICAgICBzdGF0ZVtpbnB1dElEXSA9IGlucHV0LmdldFN0YXRlKCk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgcmV0dXJuIHN0YXRlO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IEtleWJvYXJkIH0gZnJvbSAnLi9pbnB1dHMva2V5Ym9hcmQnO1xyXG5pbXBvcnQgeyBNb3VzZSB9IGZyb20gJy4vaW5wdXRzL21vdXNlJztcclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUlucHV0KGtleSkge1xyXG4gICAgc3dpdGNoIChrZXkpIHtcclxuICAgICAgICBjYXNlICdrZXlib2FyZCc6XHJcbiAgICAgICAgICAgIHJldHVybiBLZXlib2FyZDtcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSAnbW91c2UnOlxyXG4gICAgICAgICAgICByZXR1cm4gTW91c2U7XHJcbiAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IGVwc2lsb24gfSBmcm9tICcuL2NvbnN0YW50cyc7XHJcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIHZlYzQge1xyXG4gICAgY29uc3RydWN0b3IodmFsdWVzKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KDQpO1xyXG4gICAgICAgIGlmICh2YWx1ZXMgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgICAgICB0aGlzLnh5encgPSB2YWx1ZXM7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgZ2V0IHgoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzBdO1xyXG4gICAgfVxyXG4gICAgZ2V0IHkoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzFdO1xyXG4gICAgfVxyXG4gICAgZ2V0IHooKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzJdO1xyXG4gICAgfVxyXG4gICAgZ2V0IHcoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzNdO1xyXG4gICAgfVxyXG4gICAgZ2V0IHh5KCkge1xyXG4gICAgICAgIHJldHVybiBbdGhpcy52YWx1ZXNbMF0sIHRoaXMudmFsdWVzWzFdXTtcclxuICAgIH1cclxuICAgIGdldCB4eXooKSB7XHJcbiAgICAgICAgcmV0dXJuIFt0aGlzLnZhbHVlc1swXSwgdGhpcy52YWx1ZXNbMV0sIHRoaXMudmFsdWVzWzJdXTtcclxuICAgIH1cclxuICAgIGdldCB4eXp3KCkge1xyXG4gICAgICAgIHJldHVybiBbdGhpcy52YWx1ZXNbMF0sIHRoaXMudmFsdWVzWzFdLCB0aGlzLnZhbHVlc1syXSwgdGhpcy52YWx1ZXNbM11dO1xyXG4gICAgfVxyXG4gICAgc2V0IHgodmFsdWUpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1swXSA9IHZhbHVlO1xyXG4gICAgfVxyXG4gICAgc2V0IHkodmFsdWUpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSA9IHZhbHVlO1xyXG4gICAgfVxyXG4gICAgc2V0IHoodmFsdWUpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1syXSA9IHZhbHVlO1xyXG4gICAgfVxyXG4gICAgc2V0IHcodmFsdWUpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1szXSA9IHZhbHVlO1xyXG4gICAgfVxyXG4gICAgc2V0IHh5KHZhbHVlcykge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzBdID0gdmFsdWVzWzBdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdID0gdmFsdWVzWzFdO1xyXG4gICAgfVxyXG4gICAgc2V0IHh5eih2YWx1ZXMpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1swXSA9IHZhbHVlc1swXTtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSA9IHZhbHVlc1sxXTtcclxuICAgICAgICB0aGlzLnZhbHVlc1syXSA9IHZhbHVlc1syXTtcclxuICAgIH1cclxuICAgIHNldCB4eXp3KHZhbHVlcykge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzBdID0gdmFsdWVzWzBdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdID0gdmFsdWVzWzFdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzJdID0gdmFsdWVzWzJdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzNdID0gdmFsdWVzWzNdO1xyXG4gICAgfVxyXG4gICAgZ2V0IHIoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzBdO1xyXG4gICAgfVxyXG4gICAgZ2V0IGcoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzFdO1xyXG4gICAgfVxyXG4gICAgZ2V0IGIoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzJdO1xyXG4gICAgfVxyXG4gICAgZ2V0IGEoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzNdO1xyXG4gICAgfVxyXG4gICAgZ2V0IHJnKCkge1xyXG4gICAgICAgIHJldHVybiBbdGhpcy52YWx1ZXNbMF0sIHRoaXMudmFsdWVzWzFdXTtcclxuICAgIH1cclxuICAgIGdldCByZ2IoKSB7XHJcbiAgICAgICAgcmV0dXJuIFt0aGlzLnZhbHVlc1swXSwgdGhpcy52YWx1ZXNbMV0sIHRoaXMudmFsdWVzWzJdXTtcclxuICAgIH1cclxuICAgIGdldCByZ2JhKCkge1xyXG4gICAgICAgIHJldHVybiBbdGhpcy52YWx1ZXNbMF0sIHRoaXMudmFsdWVzWzFdLCB0aGlzLnZhbHVlc1syXSwgdGhpcy52YWx1ZXNbM11dO1xyXG4gICAgfVxyXG4gICAgc2V0IHIodmFsdWUpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1swXSA9IHZhbHVlO1xyXG4gICAgfVxyXG4gICAgc2V0IGcodmFsdWUpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSA9IHZhbHVlO1xyXG4gICAgfVxyXG4gICAgc2V0IGIodmFsdWUpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1syXSA9IHZhbHVlO1xyXG4gICAgfVxyXG4gICAgc2V0IGEodmFsdWUpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1szXSA9IHZhbHVlO1xyXG4gICAgfVxyXG4gICAgc2V0IHJnKHZhbHVlcykge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzBdID0gdmFsdWVzWzBdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdID0gdmFsdWVzWzFdO1xyXG4gICAgfVxyXG4gICAgc2V0IHJnYih2YWx1ZXMpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1swXSA9IHZhbHVlc1swXTtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSA9IHZhbHVlc1sxXTtcclxuICAgICAgICB0aGlzLnZhbHVlc1syXSA9IHZhbHVlc1syXTtcclxuICAgIH1cclxuICAgIHNldCByZ2JhKHZhbHVlcykge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzBdID0gdmFsdWVzWzBdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdID0gdmFsdWVzWzFdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzJdID0gdmFsdWVzWzJdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzNdID0gdmFsdWVzWzNdO1xyXG4gICAgfVxyXG4gICAgYXQoaW5kZXgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXNbaW5kZXhdO1xyXG4gICAgfVxyXG4gICAgcmVzZXQoKSB7XHJcbiAgICAgICAgdGhpcy54ID0gMDtcclxuICAgICAgICB0aGlzLnkgPSAwO1xyXG4gICAgICAgIHRoaXMueiA9IDA7XHJcbiAgICAgICAgdGhpcy53ID0gMDtcclxuICAgIH1cclxuICAgIGNvcHkoZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHZlYzQoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVzdC54ID0gdGhpcy54O1xyXG4gICAgICAgIGRlc3QueSA9IHRoaXMueTtcclxuICAgICAgICBkZXN0LnogPSB0aGlzLno7XHJcbiAgICAgICAgZGVzdC53ID0gdGhpcy53O1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgbmVnYXRlKGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IHRoaXM7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlc3QueCA9IC10aGlzLng7XHJcbiAgICAgICAgZGVzdC55ID0gLXRoaXMueTtcclxuICAgICAgICBkZXN0LnogPSAtdGhpcy56O1xyXG4gICAgICAgIGRlc3QudyA9IC10aGlzLnc7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbiAgICBlcXVhbHModmVjdG9yLCB0aHJlc2hvbGQgPSBlcHNpbG9uKSB7XHJcbiAgICAgICAgaWYgKE1hdGguYWJzKHRoaXMueCAtIHZlY3Rvci54KSA+IHRocmVzaG9sZCkge1xyXG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmIChNYXRoLmFicyh0aGlzLnkgLSB2ZWN0b3IueSkgPiB0aHJlc2hvbGQpIHtcclxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoTWF0aC5hYnModGhpcy56IC0gdmVjdG9yLnopID4gdGhyZXNob2xkKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKE1hdGguYWJzKHRoaXMudyAtIHZlY3Rvci53KSA+IHRocmVzaG9sZCkge1xyXG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG4gICAgbGVuZ3RoKCkge1xyXG4gICAgICAgIHJldHVybiBNYXRoLnNxcnQodGhpcy5zcXVhcmVkTGVuZ3RoKCkpO1xyXG4gICAgfVxyXG4gICAgc3F1YXJlZExlbmd0aCgpIHtcclxuICAgICAgICBjb25zdCB4ID0gdGhpcy54O1xyXG4gICAgICAgIGNvbnN0IHkgPSB0aGlzLnk7XHJcbiAgICAgICAgY29uc3QgeiA9IHRoaXMuejtcclxuICAgICAgICBjb25zdCB3ID0gdGhpcy53O1xyXG4gICAgICAgIHJldHVybiB4ICogeCArIHkgKiB5ICsgeiAqIHogKyB3ICogdztcclxuICAgIH1cclxuICAgIGFkZCh2ZWN0b3IpIHtcclxuICAgICAgICB0aGlzLnggKz0gdmVjdG9yLng7XHJcbiAgICAgICAgdGhpcy55ICs9IHZlY3Rvci55O1xyXG4gICAgICAgIHRoaXMueiArPSB2ZWN0b3IuejtcclxuICAgICAgICB0aGlzLncgKz0gdmVjdG9yLnc7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBzdWJ0cmFjdCh2ZWN0b3IpIHtcclxuICAgICAgICB0aGlzLnggLT0gdmVjdG9yLng7XHJcbiAgICAgICAgdGhpcy55IC09IHZlY3Rvci55O1xyXG4gICAgICAgIHRoaXMueiAtPSB2ZWN0b3IuejtcclxuICAgICAgICB0aGlzLncgLT0gdmVjdG9yLnc7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBtdWx0aXBseSh2ZWN0b3IpIHtcclxuICAgICAgICB0aGlzLnggKj0gdmVjdG9yLng7XHJcbiAgICAgICAgdGhpcy55ICo9IHZlY3Rvci55O1xyXG4gICAgICAgIHRoaXMueiAqPSB2ZWN0b3IuejtcclxuICAgICAgICB0aGlzLncgKj0gdmVjdG9yLnc7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBkaXZpZGUodmVjdG9yKSB7XHJcbiAgICAgICAgdGhpcy54IC89IHZlY3Rvci54O1xyXG4gICAgICAgIHRoaXMueSAvPSB2ZWN0b3IueTtcclxuICAgICAgICB0aGlzLnogLz0gdmVjdG9yLno7XHJcbiAgICAgICAgdGhpcy53IC89IHZlY3Rvci53O1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgc2NhbGUodmFsdWUsIGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IHRoaXM7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlc3QueCAqPSB2YWx1ZTtcclxuICAgICAgICBkZXN0LnkgKj0gdmFsdWU7XHJcbiAgICAgICAgZGVzdC56ICo9IHZhbHVlO1xyXG4gICAgICAgIGRlc3QudyAqPSB2YWx1ZTtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIG5vcm1hbGl6ZShkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSB0aGlzO1xyXG4gICAgICAgIH1cclxuICAgICAgICBsZXQgbGVuZ3RoID0gdGhpcy5sZW5ndGgoKTtcclxuICAgICAgICBpZiAobGVuZ3RoID09PSAxKSB7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAobGVuZ3RoID09PSAwKSB7XHJcbiAgICAgICAgICAgIGRlc3QueCAqPSAwO1xyXG4gICAgICAgICAgICBkZXN0LnkgKj0gMDtcclxuICAgICAgICAgICAgZGVzdC56ICo9IDA7XHJcbiAgICAgICAgICAgIGRlc3QudyAqPSAwO1xyXG4gICAgICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgICAgICB9XHJcbiAgICAgICAgbGVuZ3RoID0gMS4wIC8gbGVuZ3RoO1xyXG4gICAgICAgIGRlc3QueCAqPSBsZW5ndGg7XHJcbiAgICAgICAgZGVzdC55ICo9IGxlbmd0aDtcclxuICAgICAgICBkZXN0LnogKj0gbGVuZ3RoO1xyXG4gICAgICAgIGRlc3QudyAqPSBsZW5ndGg7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbiAgICBtdWx0aXBseU1hdDQobWF0cml4LCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSB0aGlzO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gbWF0cml4Lm11bHRpcGx5VmVjNCh0aGlzLCBkZXN0KTtcclxuICAgIH1cclxuICAgIHN0YXRpYyBtaXgodmVjdG9yLCB2ZWN0b3IyLCB0aW1lLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgdmVjNCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBkZXN0LnggPSB2ZWN0b3IueCArIHRpbWUgKiAodmVjdG9yMi54IC0gdmVjdG9yLngpO1xyXG4gICAgICAgIGRlc3QueSA9IHZlY3Rvci55ICsgdGltZSAqICh2ZWN0b3IyLnkgLSB2ZWN0b3IueSk7XHJcbiAgICAgICAgZGVzdC56ID0gdmVjdG9yLnogKyB0aW1lICogKHZlY3RvcjIueiAtIHZlY3Rvci56KTtcclxuICAgICAgICBkZXN0LncgPSB2ZWN0b3IudyArIHRpbWUgKiAodmVjdG9yMi53IC0gdmVjdG9yLncpO1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgc3RhdGljIHN1bSh2ZWN0b3IsIHZlY3RvcjIsIGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IG5ldyB2ZWM0KCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlc3QueCA9IHZlY3Rvci54ICsgdmVjdG9yMi54O1xyXG4gICAgICAgIGRlc3QueSA9IHZlY3Rvci55ICsgdmVjdG9yMi55O1xyXG4gICAgICAgIGRlc3QueiA9IHZlY3Rvci56ICsgdmVjdG9yMi56O1xyXG4gICAgICAgIGRlc3QudyA9IHZlY3Rvci53ICsgdmVjdG9yMi53O1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgc3RhdGljIGRpZmZlcmVuY2UodmVjdG9yLCB2ZWN0b3IyLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgdmVjNCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBkZXN0LnggPSB2ZWN0b3IueCAtIHZlY3RvcjIueDtcclxuICAgICAgICBkZXN0LnkgPSB2ZWN0b3IueSAtIHZlY3RvcjIueTtcclxuICAgICAgICBkZXN0LnogPSB2ZWN0b3IueiAtIHZlY3RvcjIuejtcclxuICAgICAgICBkZXN0LncgPSB2ZWN0b3IudyAtIHZlY3RvcjIudztcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBwcm9kdWN0KHZlY3RvciwgdmVjdG9yMiwgZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHZlYzQoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVzdC54ID0gdmVjdG9yLnggKiB2ZWN0b3IyLng7XHJcbiAgICAgICAgZGVzdC55ID0gdmVjdG9yLnkgKiB2ZWN0b3IyLnk7XHJcbiAgICAgICAgZGVzdC56ID0gdmVjdG9yLnogKiB2ZWN0b3IyLno7XHJcbiAgICAgICAgZGVzdC53ID0gdmVjdG9yLncgKiB2ZWN0b3IyLnc7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbiAgICBzdGF0aWMgcXVvdGllbnQodmVjdG9yLCB2ZWN0b3IyLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgdmVjNCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBkZXN0LnggPSB2ZWN0b3IueCAvIHZlY3RvcjIueDtcclxuICAgICAgICBkZXN0LnkgPSB2ZWN0b3IueSAvIHZlY3RvcjIueTtcclxuICAgICAgICBkZXN0LnogPSB2ZWN0b3IueiAvIHZlY3RvcjIuejtcclxuICAgICAgICBkZXN0LncgPSB2ZWN0b3IudyAvIHZlY3RvcjIudztcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxufVxyXG52ZWM0Lnplcm8gPSBuZXcgdmVjNChbMCwgMCwgMCwgMV0pO1xyXG52ZWM0Lm9uZSA9IG5ldyB2ZWM0KFsxLCAxLCAxLCAxXSk7XHJcbiIsImltcG9ydCBtYXQzIGZyb20gJy4vbWF0Myc7XHJcbmltcG9ydCB2ZWMzIGZyb20gJy4vdmVjMyc7XHJcbmltcG9ydCB2ZWM0IGZyb20gJy4vdmVjNCc7XHJcbmltcG9ydCB7IGVwc2lsb24gfSBmcm9tICcuL2NvbnN0YW50cyc7XHJcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIG1hdDQge1xyXG4gICAgY29uc3RydWN0b3IodmFsdWVzKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KDE2KTtcclxuICAgICAgICBpZiAodmFsdWVzICE9PSB1bmRlZmluZWQpIHtcclxuICAgICAgICAgICAgdGhpcy5pbml0KHZhbHVlcyk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgYXQoaW5kZXgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXNbaW5kZXhdO1xyXG4gICAgfVxyXG4gICAgaW5pdCh2YWx1ZXMpIHtcclxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IDE2OyBpKyspIHtcclxuICAgICAgICAgICAgdGhpcy52YWx1ZXNbaV0gPSB2YWx1ZXNbaV07XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgcmVzZXQoKSB7XHJcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCAxNjsgaSsrKSB7XHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzW2ldID0gMDtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBjb3B5KGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IG5ldyBtYXQ0KCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgMTY7IGkrKykge1xyXG4gICAgICAgICAgICBkZXN0LnZhbHVlc1tpXSA9IHRoaXMudmFsdWVzW2ldO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIGFsbCgpIHtcclxuICAgICAgICBjb25zdCBkYXRhID0gW107XHJcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCAxNjsgaSsrKSB7XHJcbiAgICAgICAgICAgIGRhdGFbaV0gPSB0aGlzLnZhbHVlc1tpXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIGRhdGE7XHJcbiAgICB9XHJcbiAgICByb3coaW5kZXgpIHtcclxuICAgICAgICByZXR1cm4gW1xyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1tpbmRleCAqIDQgKyAwXSxcclxuICAgICAgICAgICAgdGhpcy52YWx1ZXNbaW5kZXggKiA0ICsgMV0sXHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzW2luZGV4ICogNCArIDJdLFxyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1tpbmRleCAqIDQgKyAzXVxyXG4gICAgICAgIF07XHJcbiAgICB9XHJcbiAgICBjb2woaW5kZXgpIHtcclxuICAgICAgICByZXR1cm4gW1xyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1tpbmRleF0sXHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzW2luZGV4ICsgNF0sXHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzW2luZGV4ICsgOF0sXHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzW2luZGV4ICsgMTJdXHJcbiAgICAgICAgXTtcclxuICAgIH1cclxuICAgIGVxdWFscyhtYXRyaXgsIHRocmVzaG9sZCA9IGVwc2lsb24pIHtcclxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IDE2OyBpKyspIHtcclxuICAgICAgICAgICAgaWYgKE1hdGguYWJzKHRoaXMudmFsdWVzW2ldIC0gbWF0cml4LmF0KGkpKSA+IHRocmVzaG9sZCkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG4gICAgZGV0ZXJtaW5hbnQoKSB7XHJcbiAgICAgICAgY29uc3QgYTAwID0gdGhpcy52YWx1ZXNbMF07XHJcbiAgICAgICAgY29uc3QgYTAxID0gdGhpcy52YWx1ZXNbMV07XHJcbiAgICAgICAgY29uc3QgYTAyID0gdGhpcy52YWx1ZXNbMl07XHJcbiAgICAgICAgY29uc3QgYTAzID0gdGhpcy52YWx1ZXNbM107XHJcbiAgICAgICAgY29uc3QgYTEwID0gdGhpcy52YWx1ZXNbNF07XHJcbiAgICAgICAgY29uc3QgYTExID0gdGhpcy52YWx1ZXNbNV07XHJcbiAgICAgICAgY29uc3QgYTEyID0gdGhpcy52YWx1ZXNbNl07XHJcbiAgICAgICAgY29uc3QgYTEzID0gdGhpcy52YWx1ZXNbN107XHJcbiAgICAgICAgY29uc3QgYTIwID0gdGhpcy52YWx1ZXNbOF07XHJcbiAgICAgICAgY29uc3QgYTIxID0gdGhpcy52YWx1ZXNbOV07XHJcbiAgICAgICAgY29uc3QgYTIyID0gdGhpcy52YWx1ZXNbMTBdO1xyXG4gICAgICAgIGNvbnN0IGEyMyA9IHRoaXMudmFsdWVzWzExXTtcclxuICAgICAgICBjb25zdCBhMzAgPSB0aGlzLnZhbHVlc1sxMl07XHJcbiAgICAgICAgY29uc3QgYTMxID0gdGhpcy52YWx1ZXNbMTNdO1xyXG4gICAgICAgIGNvbnN0IGEzMiA9IHRoaXMudmFsdWVzWzE0XTtcclxuICAgICAgICBjb25zdCBhMzMgPSB0aGlzLnZhbHVlc1sxNV07XHJcbiAgICAgICAgY29uc3QgZGV0MDAgPSBhMDAgKiBhMTEgLSBhMDEgKiBhMTA7XHJcbiAgICAgICAgY29uc3QgZGV0MDEgPSBhMDAgKiBhMTIgLSBhMDIgKiBhMTA7XHJcbiAgICAgICAgY29uc3QgZGV0MDIgPSBhMDAgKiBhMTMgLSBhMDMgKiBhMTA7XHJcbiAgICAgICAgY29uc3QgZGV0MDMgPSBhMDEgKiBhMTIgLSBhMDIgKiBhMTE7XHJcbiAgICAgICAgY29uc3QgZGV0MDQgPSBhMDEgKiBhMTMgLSBhMDMgKiBhMTE7XHJcbiAgICAgICAgY29uc3QgZGV0MDUgPSBhMDIgKiBhMTMgLSBhMDMgKiBhMTI7XHJcbiAgICAgICAgY29uc3QgZGV0MDYgPSBhMjAgKiBhMzEgLSBhMjEgKiBhMzA7XHJcbiAgICAgICAgY29uc3QgZGV0MDcgPSBhMjAgKiBhMzIgLSBhMjIgKiBhMzA7XHJcbiAgICAgICAgY29uc3QgZGV0MDggPSBhMjAgKiBhMzMgLSBhMjMgKiBhMzA7XHJcbiAgICAgICAgY29uc3QgZGV0MDkgPSBhMjEgKiBhMzIgLSBhMjIgKiBhMzE7XHJcbiAgICAgICAgY29uc3QgZGV0MTAgPSBhMjEgKiBhMzMgLSBhMjMgKiBhMzE7XHJcbiAgICAgICAgY29uc3QgZGV0MTEgPSBhMjIgKiBhMzMgLSBhMjMgKiBhMzI7XHJcbiAgICAgICAgcmV0dXJuIChkZXQwMCAqIGRldDExIC1cclxuICAgICAgICAgICAgZGV0MDEgKiBkZXQxMCArXHJcbiAgICAgICAgICAgIGRldDAyICogZGV0MDkgK1xyXG4gICAgICAgICAgICBkZXQwMyAqIGRldDA4IC1cclxuICAgICAgICAgICAgZGV0MDQgKiBkZXQwNyArXHJcbiAgICAgICAgICAgIGRldDA1ICogZGV0MDYpO1xyXG4gICAgfVxyXG4gICAgc2V0SWRlbnRpdHkoKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gPSAxO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdID0gMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1syXSA9IDA7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbM10gPSAwO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzRdID0gMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s1XSA9IDE7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNl0gPSAwO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzddID0gMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s4XSA9IDA7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbOV0gPSAwO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzEwXSA9IDE7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMTFdID0gMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxMl0gPSAwO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzEzXSA9IDA7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMTRdID0gMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxNV0gPSAxO1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgdHJhbnNwb3NlKCkge1xyXG4gICAgICAgIGNvbnN0IHRlbXAwMSA9IHRoaXMudmFsdWVzWzFdO1xyXG4gICAgICAgIGNvbnN0IHRlbXAwMiA9IHRoaXMudmFsdWVzWzJdO1xyXG4gICAgICAgIGNvbnN0IHRlbXAwMyA9IHRoaXMudmFsdWVzWzNdO1xyXG4gICAgICAgIGNvbnN0IHRlbXAxMiA9IHRoaXMudmFsdWVzWzZdO1xyXG4gICAgICAgIGNvbnN0IHRlbXAxMyA9IHRoaXMudmFsdWVzWzddO1xyXG4gICAgICAgIGNvbnN0IHRlbXAyMyA9IHRoaXMudmFsdWVzWzExXTtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSA9IHRoaXMudmFsdWVzWzRdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzJdID0gdGhpcy52YWx1ZXNbOF07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbM10gPSB0aGlzLnZhbHVlc1sxMl07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNF0gPSB0ZW1wMDE7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNl0gPSB0aGlzLnZhbHVlc1s5XTtcclxuICAgICAgICB0aGlzLnZhbHVlc1s3XSA9IHRoaXMudmFsdWVzWzEzXTtcclxuICAgICAgICB0aGlzLnZhbHVlc1s4XSA9IHRlbXAwMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1s5XSA9IHRlbXAxMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxMV0gPSB0aGlzLnZhbHVlc1sxNF07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMTJdID0gdGVtcDAzO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzEzXSA9IHRlbXAxMztcclxuICAgICAgICB0aGlzLnZhbHVlc1sxNF0gPSB0ZW1wMjM7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBpbnZlcnNlKCkge1xyXG4gICAgICAgIGNvbnN0IGEwMCA9IHRoaXMudmFsdWVzWzBdO1xyXG4gICAgICAgIGNvbnN0IGEwMSA9IHRoaXMudmFsdWVzWzFdO1xyXG4gICAgICAgIGNvbnN0IGEwMiA9IHRoaXMudmFsdWVzWzJdO1xyXG4gICAgICAgIGNvbnN0IGEwMyA9IHRoaXMudmFsdWVzWzNdO1xyXG4gICAgICAgIGNvbnN0IGExMCA9IHRoaXMudmFsdWVzWzRdO1xyXG4gICAgICAgIGNvbnN0IGExMSA9IHRoaXMudmFsdWVzWzVdO1xyXG4gICAgICAgIGNvbnN0IGExMiA9IHRoaXMudmFsdWVzWzZdO1xyXG4gICAgICAgIGNvbnN0IGExMyA9IHRoaXMudmFsdWVzWzddO1xyXG4gICAgICAgIGNvbnN0IGEyMCA9IHRoaXMudmFsdWVzWzhdO1xyXG4gICAgICAgIGNvbnN0IGEyMSA9IHRoaXMudmFsdWVzWzldO1xyXG4gICAgICAgIGNvbnN0IGEyMiA9IHRoaXMudmFsdWVzWzEwXTtcclxuICAgICAgICBjb25zdCBhMjMgPSB0aGlzLnZhbHVlc1sxMV07XHJcbiAgICAgICAgY29uc3QgYTMwID0gdGhpcy52YWx1ZXNbMTJdO1xyXG4gICAgICAgIGNvbnN0IGEzMSA9IHRoaXMudmFsdWVzWzEzXTtcclxuICAgICAgICBjb25zdCBhMzIgPSB0aGlzLnZhbHVlc1sxNF07XHJcbiAgICAgICAgY29uc3QgYTMzID0gdGhpcy52YWx1ZXNbMTVdO1xyXG4gICAgICAgIGNvbnN0IGRldDAwID0gYTAwICogYTExIC0gYTAxICogYTEwO1xyXG4gICAgICAgIGNvbnN0IGRldDAxID0gYTAwICogYTEyIC0gYTAyICogYTEwO1xyXG4gICAgICAgIGNvbnN0IGRldDAyID0gYTAwICogYTEzIC0gYTAzICogYTEwO1xyXG4gICAgICAgIGNvbnN0IGRldDAzID0gYTAxICogYTEyIC0gYTAyICogYTExO1xyXG4gICAgICAgIGNvbnN0IGRldDA0ID0gYTAxICogYTEzIC0gYTAzICogYTExO1xyXG4gICAgICAgIGNvbnN0IGRldDA1ID0gYTAyICogYTEzIC0gYTAzICogYTEyO1xyXG4gICAgICAgIGNvbnN0IGRldDA2ID0gYTIwICogYTMxIC0gYTIxICogYTMwO1xyXG4gICAgICAgIGNvbnN0IGRldDA3ID0gYTIwICogYTMyIC0gYTIyICogYTMwO1xyXG4gICAgICAgIGNvbnN0IGRldDA4ID0gYTIwICogYTMzIC0gYTIzICogYTMwO1xyXG4gICAgICAgIGNvbnN0IGRldDA5ID0gYTIxICogYTMyIC0gYTIyICogYTMxO1xyXG4gICAgICAgIGNvbnN0IGRldDEwID0gYTIxICogYTMzIC0gYTIzICogYTMxO1xyXG4gICAgICAgIGNvbnN0IGRldDExID0gYTIyICogYTMzIC0gYTIzICogYTMyO1xyXG4gICAgICAgIGxldCBkZXQgPSBkZXQwMCAqIGRldDExIC1cclxuICAgICAgICAgICAgZGV0MDEgKiBkZXQxMCArXHJcbiAgICAgICAgICAgIGRldDAyICogZGV0MDkgK1xyXG4gICAgICAgICAgICBkZXQwMyAqIGRldDA4IC1cclxuICAgICAgICAgICAgZGV0MDQgKiBkZXQwNyArXHJcbiAgICAgICAgICAgIGRldDA1ICogZGV0MDY7XHJcbiAgICAgICAgaWYgKCFkZXQpIHtcclxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRldCA9IDEuMCAvIGRldDtcclxuICAgICAgICB0aGlzLnZhbHVlc1swXSA9IChhMTEgKiBkZXQxMSAtIGExMiAqIGRldDEwICsgYTEzICogZGV0MDkpICogZGV0O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdID0gKC1hMDEgKiBkZXQxMSArIGEwMiAqIGRldDEwIC0gYTAzICogZGV0MDkpICogZGV0O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzJdID0gKGEzMSAqIGRldDA1IC0gYTMyICogZGV0MDQgKyBhMzMgKiBkZXQwMykgKiBkZXQ7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbM10gPSAoLWEyMSAqIGRldDA1ICsgYTIyICogZGV0MDQgLSBhMjMgKiBkZXQwMykgKiBkZXQ7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNF0gPSAoLWExMCAqIGRldDExICsgYTEyICogZGV0MDggLSBhMTMgKiBkZXQwNykgKiBkZXQ7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNV0gPSAoYTAwICogZGV0MTEgLSBhMDIgKiBkZXQwOCArIGEwMyAqIGRldDA3KSAqIGRldDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s2XSA9ICgtYTMwICogZGV0MDUgKyBhMzIgKiBkZXQwMiAtIGEzMyAqIGRldDAxKSAqIGRldDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s3XSA9IChhMjAgKiBkZXQwNSAtIGEyMiAqIGRldDAyICsgYTIzICogZGV0MDEpICogZGV0O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzhdID0gKGExMCAqIGRldDEwIC0gYTExICogZGV0MDggKyBhMTMgKiBkZXQwNikgKiBkZXQ7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbOV0gPSAoLWEwMCAqIGRldDEwICsgYTAxICogZGV0MDggLSBhMDMgKiBkZXQwNikgKiBkZXQ7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMTBdID0gKGEzMCAqIGRldDA0IC0gYTMxICogZGV0MDIgKyBhMzMgKiBkZXQwMCkgKiBkZXQ7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMTFdID0gKC1hMjAgKiBkZXQwNCArIGEyMSAqIGRldDAyIC0gYTIzICogZGV0MDApICogZGV0O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzEyXSA9ICgtYTEwICogZGV0MDkgKyBhMTEgKiBkZXQwNyAtIGExMiAqIGRldDA2KSAqIGRldDtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxM10gPSAoYTAwICogZGV0MDkgLSBhMDEgKiBkZXQwNyArIGEwMiAqIGRldDA2KSAqIGRldDtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxNF0gPSAoLWEzMCAqIGRldDAzICsgYTMxICogZGV0MDEgLSBhMzIgKiBkZXQwMCkgKiBkZXQ7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMTVdID0gKGEyMCAqIGRldDAzIC0gYTIxICogZGV0MDEgKyBhMjIgKiBkZXQwMCkgKiBkZXQ7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBtdWx0aXBseShtYXRyaXgpIHtcclxuICAgICAgICBjb25zdCBhMDAgPSB0aGlzLnZhbHVlc1swXTtcclxuICAgICAgICBjb25zdCBhMDEgPSB0aGlzLnZhbHVlc1sxXTtcclxuICAgICAgICBjb25zdCBhMDIgPSB0aGlzLnZhbHVlc1syXTtcclxuICAgICAgICBjb25zdCBhMDMgPSB0aGlzLnZhbHVlc1szXTtcclxuICAgICAgICBjb25zdCBhMTAgPSB0aGlzLnZhbHVlc1s0XTtcclxuICAgICAgICBjb25zdCBhMTEgPSB0aGlzLnZhbHVlc1s1XTtcclxuICAgICAgICBjb25zdCBhMTIgPSB0aGlzLnZhbHVlc1s2XTtcclxuICAgICAgICBjb25zdCBhMTMgPSB0aGlzLnZhbHVlc1s3XTtcclxuICAgICAgICBjb25zdCBhMjAgPSB0aGlzLnZhbHVlc1s4XTtcclxuICAgICAgICBjb25zdCBhMjEgPSB0aGlzLnZhbHVlc1s5XTtcclxuICAgICAgICBjb25zdCBhMjIgPSB0aGlzLnZhbHVlc1sxMF07XHJcbiAgICAgICAgY29uc3QgYTIzID0gdGhpcy52YWx1ZXNbMTFdO1xyXG4gICAgICAgIGNvbnN0IGEzMCA9IHRoaXMudmFsdWVzWzEyXTtcclxuICAgICAgICBjb25zdCBhMzEgPSB0aGlzLnZhbHVlc1sxM107XHJcbiAgICAgICAgY29uc3QgYTMyID0gdGhpcy52YWx1ZXNbMTRdO1xyXG4gICAgICAgIGNvbnN0IGEzMyA9IHRoaXMudmFsdWVzWzE1XTtcclxuICAgICAgICBsZXQgYjAgPSBtYXRyaXguYXQoMCk7XHJcbiAgICAgICAgbGV0IGIxID0gbWF0cml4LmF0KDEpO1xyXG4gICAgICAgIGxldCBiMiA9IG1hdHJpeC5hdCgyKTtcclxuICAgICAgICBsZXQgYjMgPSBtYXRyaXguYXQoMyk7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gPSBiMCAqIGEwMCArIGIxICogYTEwICsgYjIgKiBhMjAgKyBiMyAqIGEzMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSA9IGIwICogYTAxICsgYjEgKiBhMTEgKyBiMiAqIGEyMSArIGIzICogYTMxO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzJdID0gYjAgKiBhMDIgKyBiMSAqIGExMiArIGIyICogYTIyICsgYjMgKiBhMzI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbM10gPSBiMCAqIGEwMyArIGIxICogYTEzICsgYjIgKiBhMjMgKyBiMyAqIGEzMztcclxuICAgICAgICBiMCA9IG1hdHJpeC5hdCg0KTtcclxuICAgICAgICBiMSA9IG1hdHJpeC5hdCg1KTtcclxuICAgICAgICBiMiA9IG1hdHJpeC5hdCg2KTtcclxuICAgICAgICBiMyA9IG1hdHJpeC5hdCg3KTtcclxuICAgICAgICB0aGlzLnZhbHVlc1s0XSA9IGIwICogYTAwICsgYjEgKiBhMTAgKyBiMiAqIGEyMCArIGIzICogYTMwO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzVdID0gYjAgKiBhMDEgKyBiMSAqIGExMSArIGIyICogYTIxICsgYjMgKiBhMzE7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNl0gPSBiMCAqIGEwMiArIGIxICogYTEyICsgYjIgKiBhMjIgKyBiMyAqIGEzMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1s3XSA9IGIwICogYTAzICsgYjEgKiBhMTMgKyBiMiAqIGEyMyArIGIzICogYTMzO1xyXG4gICAgICAgIGIwID0gbWF0cml4LmF0KDgpO1xyXG4gICAgICAgIGIxID0gbWF0cml4LmF0KDkpO1xyXG4gICAgICAgIGIyID0gbWF0cml4LmF0KDEwKTtcclxuICAgICAgICBiMyA9IG1hdHJpeC5hdCgxMSk7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbOF0gPSBiMCAqIGEwMCArIGIxICogYTEwICsgYjIgKiBhMjAgKyBiMyAqIGEzMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s5XSA9IGIwICogYTAxICsgYjEgKiBhMTEgKyBiMiAqIGEyMSArIGIzICogYTMxO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzEwXSA9IGIwICogYTAyICsgYjEgKiBhMTIgKyBiMiAqIGEyMiArIGIzICogYTMyO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzExXSA9IGIwICogYTAzICsgYjEgKiBhMTMgKyBiMiAqIGEyMyArIGIzICogYTMzO1xyXG4gICAgICAgIGIwID0gbWF0cml4LmF0KDEyKTtcclxuICAgICAgICBiMSA9IG1hdHJpeC5hdCgxMyk7XHJcbiAgICAgICAgYjIgPSBtYXRyaXguYXQoMTQpO1xyXG4gICAgICAgIGIzID0gbWF0cml4LmF0KDE1KTtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxMl0gPSBiMCAqIGEwMCArIGIxICogYTEwICsgYjIgKiBhMjAgKyBiMyAqIGEzMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxM10gPSBiMCAqIGEwMSArIGIxICogYTExICsgYjIgKiBhMjEgKyBiMyAqIGEzMTtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxNF0gPSBiMCAqIGEwMiArIGIxICogYTEyICsgYjIgKiBhMjIgKyBiMyAqIGEzMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxNV0gPSBiMCAqIGEwMyArIGIxICogYTEzICsgYjIgKiBhMjMgKyBiMyAqIGEzMztcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIG11bHRpcGx5VmVjMyh2ZWN0b3IpIHtcclxuICAgICAgICBjb25zdCB4ID0gdmVjdG9yLng7XHJcbiAgICAgICAgY29uc3QgeSA9IHZlY3Rvci55O1xyXG4gICAgICAgIGNvbnN0IHogPSB2ZWN0b3IuejtcclxuICAgICAgICByZXR1cm4gbmV3IHZlYzMoW1xyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1swXSAqIHggK1xyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbNF0gKiB5ICtcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzhdICogeiArXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1sxMl0sXHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzWzFdICogeCArXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1s1XSAqIHkgK1xyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbOV0gKiB6ICtcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzEzXSxcclxuICAgICAgICAgICAgdGhpcy52YWx1ZXNbMl0gKiB4ICtcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzZdICogeSArXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1sxMF0gKiB6ICtcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzE0XVxyXG4gICAgICAgIF0pO1xyXG4gICAgfVxyXG4gICAgbXVsdGlwbHlWZWM0KHZlY3RvciwgZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHZlYzQoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgY29uc3QgeCA9IHZlY3Rvci54O1xyXG4gICAgICAgIGNvbnN0IHkgPSB2ZWN0b3IueTtcclxuICAgICAgICBjb25zdCB6ID0gdmVjdG9yLno7XHJcbiAgICAgICAgY29uc3QgdyA9IHZlY3Rvci53O1xyXG4gICAgICAgIGRlc3QueCA9XHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzWzBdICogeCArXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1s0XSAqIHkgK1xyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbOF0gKiB6ICtcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzEyXSAqIHc7XHJcbiAgICAgICAgZGVzdC55ID1cclxuICAgICAgICAgICAgdGhpcy52YWx1ZXNbMV0gKiB4ICtcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzVdICogeSArXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1s5XSAqIHogK1xyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbMTNdICogdztcclxuICAgICAgICBkZXN0LnogPVxyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1syXSAqIHggK1xyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbNl0gKiB5ICtcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzEwXSAqIHogK1xyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbMTRdICogdztcclxuICAgICAgICBkZXN0LncgPVxyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1szXSAqIHggK1xyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbN10gKiB5ICtcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzExXSAqIHogK1xyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbMTVdICogdztcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHRvTWF0MygpIHtcclxuICAgICAgICByZXR1cm4gbmV3IG1hdDMoW1xyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1swXSxcclxuICAgICAgICAgICAgdGhpcy52YWx1ZXNbMV0sXHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzWzJdLFxyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1s0XSxcclxuICAgICAgICAgICAgdGhpcy52YWx1ZXNbNV0sXHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzWzZdLFxyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1s4XSxcclxuICAgICAgICAgICAgdGhpcy52YWx1ZXNbOV0sXHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzWzEwXVxyXG4gICAgICAgIF0pO1xyXG4gICAgfVxyXG4gICAgdG9JbnZlcnNlTWF0MygpIHtcclxuICAgICAgICBjb25zdCBhMDAgPSB0aGlzLnZhbHVlc1swXTtcclxuICAgICAgICBjb25zdCBhMDEgPSB0aGlzLnZhbHVlc1sxXTtcclxuICAgICAgICBjb25zdCBhMDIgPSB0aGlzLnZhbHVlc1syXTtcclxuICAgICAgICBjb25zdCBhMTAgPSB0aGlzLnZhbHVlc1s0XTtcclxuICAgICAgICBjb25zdCBhMTEgPSB0aGlzLnZhbHVlc1s1XTtcclxuICAgICAgICBjb25zdCBhMTIgPSB0aGlzLnZhbHVlc1s2XTtcclxuICAgICAgICBjb25zdCBhMjAgPSB0aGlzLnZhbHVlc1s4XTtcclxuICAgICAgICBjb25zdCBhMjEgPSB0aGlzLnZhbHVlc1s5XTtcclxuICAgICAgICBjb25zdCBhMjIgPSB0aGlzLnZhbHVlc1sxMF07XHJcbiAgICAgICAgY29uc3QgZGV0MDEgPSBhMjIgKiBhMTEgLSBhMTIgKiBhMjE7XHJcbiAgICAgICAgY29uc3QgZGV0MTEgPSAtYTIyICogYTEwICsgYTEyICogYTIwO1xyXG4gICAgICAgIGNvbnN0IGRldDIxID0gYTIxICogYTEwIC0gYTExICogYTIwO1xyXG4gICAgICAgIGxldCBkZXQgPSBhMDAgKiBkZXQwMSArIGEwMSAqIGRldDExICsgYTAyICogZGV0MjE7XHJcbiAgICAgICAgaWYgKCFkZXQpIHtcclxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRldCA9IDEuMCAvIGRldDtcclxuICAgICAgICByZXR1cm4gbmV3IG1hdDMoW1xyXG4gICAgICAgICAgICBkZXQwMSAqIGRldCxcclxuICAgICAgICAgICAgKC1hMjIgKiBhMDEgKyBhMDIgKiBhMjEpICogZGV0LFxyXG4gICAgICAgICAgICAoYTEyICogYTAxIC0gYTAyICogYTExKSAqIGRldCxcclxuICAgICAgICAgICAgZGV0MTEgKiBkZXQsXHJcbiAgICAgICAgICAgIChhMjIgKiBhMDAgLSBhMDIgKiBhMjApICogZGV0LFxyXG4gICAgICAgICAgICAoLWExMiAqIGEwMCArIGEwMiAqIGExMCkgKiBkZXQsXHJcbiAgICAgICAgICAgIGRldDIxICogZGV0LFxyXG4gICAgICAgICAgICAoLWEyMSAqIGEwMCArIGEwMSAqIGEyMCkgKiBkZXQsXHJcbiAgICAgICAgICAgIChhMTEgKiBhMDAgLSBhMDEgKiBhMTApICogZGV0XHJcbiAgICAgICAgXSk7XHJcbiAgICB9XHJcbiAgICB0cmFuc2xhdGUodmVjdG9yKSB7XHJcbiAgICAgICAgY29uc3QgeCA9IHZlY3Rvci54O1xyXG4gICAgICAgIGNvbnN0IHkgPSB2ZWN0b3IueTtcclxuICAgICAgICBjb25zdCB6ID0gdmVjdG9yLno7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMTJdICs9XHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzWzBdICogeCArIHRoaXMudmFsdWVzWzRdICogeSArIHRoaXMudmFsdWVzWzhdICogejtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxM10gKz1cclxuICAgICAgICAgICAgdGhpcy52YWx1ZXNbMV0gKiB4ICsgdGhpcy52YWx1ZXNbNV0gKiB5ICsgdGhpcy52YWx1ZXNbOV0gKiB6O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzE0XSArPVxyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1syXSAqIHggKyB0aGlzLnZhbHVlc1s2XSAqIHkgKyB0aGlzLnZhbHVlc1sxMF0gKiB6O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzE1XSArPVxyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1szXSAqIHggKyB0aGlzLnZhbHVlc1s3XSAqIHkgKyB0aGlzLnZhbHVlc1sxMV0gKiB6O1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgc2NhbGUodmVjdG9yKSB7XHJcbiAgICAgICAgY29uc3QgeCA9IHZlY3Rvci54O1xyXG4gICAgICAgIGNvbnN0IHkgPSB2ZWN0b3IueTtcclxuICAgICAgICBjb25zdCB6ID0gdmVjdG9yLno7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gKj0geDtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSAqPSB4O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzJdICo9IHg7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbM10gKj0geDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s0XSAqPSB5O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzVdICo9IHk7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNl0gKj0geTtcclxuICAgICAgICB0aGlzLnZhbHVlc1s3XSAqPSB5O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzhdICo9IHo7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbOV0gKj0gejtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxMF0gKj0gejtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxMV0gKj0gejtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIHJvdGF0ZShhbmdsZSwgYXhpcykge1xyXG4gICAgICAgIGxldCB4ID0gYXhpcy54O1xyXG4gICAgICAgIGxldCB5ID0gYXhpcy55O1xyXG4gICAgICAgIGxldCB6ID0gYXhpcy56O1xyXG4gICAgICAgIGxldCBsZW5ndGggPSBNYXRoLnNxcnQoeCAqIHggKyB5ICogeSArIHogKiB6KTtcclxuICAgICAgICBpZiAoIWxlbmd0aCkge1xyXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKGxlbmd0aCAhPT0gMSkge1xyXG4gICAgICAgICAgICBsZW5ndGggPSAxIC8gbGVuZ3RoO1xyXG4gICAgICAgICAgICB4ICo9IGxlbmd0aDtcclxuICAgICAgICAgICAgeSAqPSBsZW5ndGg7XHJcbiAgICAgICAgICAgIHogKj0gbGVuZ3RoO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCBzID0gTWF0aC5zaW4oYW5nbGUpO1xyXG4gICAgICAgIGNvbnN0IGMgPSBNYXRoLmNvcyhhbmdsZSk7XHJcbiAgICAgICAgY29uc3QgdCA9IDEuMCAtIGM7XHJcbiAgICAgICAgY29uc3QgYTAwID0gdGhpcy52YWx1ZXNbMF07XHJcbiAgICAgICAgY29uc3QgYTAxID0gdGhpcy52YWx1ZXNbMV07XHJcbiAgICAgICAgY29uc3QgYTAyID0gdGhpcy52YWx1ZXNbMl07XHJcbiAgICAgICAgY29uc3QgYTAzID0gdGhpcy52YWx1ZXNbM107XHJcbiAgICAgICAgY29uc3QgYTEwID0gdGhpcy52YWx1ZXNbNF07XHJcbiAgICAgICAgY29uc3QgYTExID0gdGhpcy52YWx1ZXNbNV07XHJcbiAgICAgICAgY29uc3QgYTEyID0gdGhpcy52YWx1ZXNbNl07XHJcbiAgICAgICAgY29uc3QgYTEzID0gdGhpcy52YWx1ZXNbN107XHJcbiAgICAgICAgY29uc3QgYTIwID0gdGhpcy52YWx1ZXNbOF07XHJcbiAgICAgICAgY29uc3QgYTIxID0gdGhpcy52YWx1ZXNbOV07XHJcbiAgICAgICAgY29uc3QgYTIyID0gdGhpcy52YWx1ZXNbMTBdO1xyXG4gICAgICAgIGNvbnN0IGEyMyA9IHRoaXMudmFsdWVzWzExXTtcclxuICAgICAgICBjb25zdCBiMDAgPSB4ICogeCAqIHQgKyBjO1xyXG4gICAgICAgIGNvbnN0IGIwMSA9IHkgKiB4ICogdCArIHogKiBzO1xyXG4gICAgICAgIGNvbnN0IGIwMiA9IHogKiB4ICogdCAtIHkgKiBzO1xyXG4gICAgICAgIGNvbnN0IGIxMCA9IHggKiB5ICogdCAtIHogKiBzO1xyXG4gICAgICAgIGNvbnN0IGIxMSA9IHkgKiB5ICogdCArIGM7XHJcbiAgICAgICAgY29uc3QgYjEyID0geiAqIHkgKiB0ICsgeCAqIHM7XHJcbiAgICAgICAgY29uc3QgYjIwID0geCAqIHogKiB0ICsgeSAqIHM7XHJcbiAgICAgICAgY29uc3QgYjIxID0geSAqIHogKiB0IC0geCAqIHM7XHJcbiAgICAgICAgY29uc3QgYjIyID0geiAqIHogKiB0ICsgYztcclxuICAgICAgICB0aGlzLnZhbHVlc1swXSA9IGEwMCAqIGIwMCArIGExMCAqIGIwMSArIGEyMCAqIGIwMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSA9IGEwMSAqIGIwMCArIGExMSAqIGIwMSArIGEyMSAqIGIwMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1syXSA9IGEwMiAqIGIwMCArIGExMiAqIGIwMSArIGEyMiAqIGIwMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1szXSA9IGEwMyAqIGIwMCArIGExMyAqIGIwMSArIGEyMyAqIGIwMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1s0XSA9IGEwMCAqIGIxMCArIGExMCAqIGIxMSArIGEyMCAqIGIxMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1s1XSA9IGEwMSAqIGIxMCArIGExMSAqIGIxMSArIGEyMSAqIGIxMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1s2XSA9IGEwMiAqIGIxMCArIGExMiAqIGIxMSArIGEyMiAqIGIxMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1s3XSA9IGEwMyAqIGIxMCArIGExMyAqIGIxMSArIGEyMyAqIGIxMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1s4XSA9IGEwMCAqIGIyMCArIGExMCAqIGIyMSArIGEyMCAqIGIyMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1s5XSA9IGEwMSAqIGIyMCArIGExMSAqIGIyMSArIGEyMSAqIGIyMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxMF0gPSBhMDIgKiBiMjAgKyBhMTIgKiBiMjEgKyBhMjIgKiBiMjI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMTFdID0gYTAzICogYjIwICsgYTEzICogYjIxICsgYTIzICogYjIyO1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgc3RhdGljIGZydXN0dW0obGVmdCwgcmlnaHQsIGJvdHRvbSwgdG9wLCBuZWFyLCBmYXIpIHtcclxuICAgICAgICBjb25zdCBybCA9IHJpZ2h0IC0gbGVmdDtcclxuICAgICAgICBjb25zdCB0YiA9IHRvcCAtIGJvdHRvbTtcclxuICAgICAgICBjb25zdCBmbiA9IGZhciAtIG5lYXI7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBtYXQ0KFtcclxuICAgICAgICAgICAgKG5lYXIgKiAyKSAvIHJsLFxyXG4gICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAobmVhciAqIDIpIC8gdGIsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIChyaWdodCArIGxlZnQpIC8gcmwsXHJcbiAgICAgICAgICAgICh0b3AgKyBib3R0b20pIC8gdGIsXHJcbiAgICAgICAgICAgIC0oZmFyICsgbmVhcikgLyBmbixcclxuICAgICAgICAgICAgLTEsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIC0oZmFyICogbmVhciAqIDIpIC8gZm4sXHJcbiAgICAgICAgICAgIDBcclxuICAgICAgICBdKTtcclxuICAgIH1cclxuICAgIHN0YXRpYyBwZXJzcGVjdGl2ZShmb3YsIGFzcGVjdCwgbmVhciwgZmFyKSB7XHJcbiAgICAgICAgY29uc3QgdG9wID0gbmVhciAqIE1hdGgudGFuKChmb3YgKiBNYXRoLlBJKSAvIDM2MC4wKTtcclxuICAgICAgICBjb25zdCByaWdodCA9IHRvcCAqIGFzcGVjdDtcclxuICAgICAgICByZXR1cm4gbWF0NC5mcnVzdHVtKC1yaWdodCwgcmlnaHQsIC10b3AsIHRvcCwgbmVhciwgZmFyKTtcclxuICAgIH1cclxuICAgIHN0YXRpYyBvcnRob2dyYXBoaWMobGVmdCwgcmlnaHQsIGJvdHRvbSwgdG9wLCBuZWFyLCBmYXIpIHtcclxuICAgICAgICBjb25zdCBybCA9IHJpZ2h0IC0gbGVmdDtcclxuICAgICAgICBjb25zdCB0YiA9IHRvcCAtIGJvdHRvbTtcclxuICAgICAgICBjb25zdCBmbiA9IGZhciAtIG5lYXI7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBtYXQ0KFtcclxuICAgICAgICAgICAgMiAvIHJsLFxyXG4gICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAyIC8gdGIsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIC0yIC8gZm4sXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIC0obGVmdCArIHJpZ2h0KSAvIHJsLFxyXG4gICAgICAgICAgICAtKHRvcCArIGJvdHRvbSkgLyB0YixcclxuICAgICAgICAgICAgLShmYXIgKyBuZWFyKSAvIGZuLFxyXG4gICAgICAgICAgICAxXHJcbiAgICAgICAgXSk7XHJcbiAgICB9XHJcbiAgICBzdGF0aWMgbG9va0F0KHBvc2l0aW9uLCB0YXJnZXQsIHVwID0gdmVjMy51cCkge1xyXG4gICAgICAgIGlmIChwb3NpdGlvbi5lcXVhbHModGFyZ2V0KSkge1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5pZGVudGl0eTtcclxuICAgICAgICB9XHJcbiAgICAgICAgY29uc3QgeiA9IHZlYzMuZGlmZmVyZW5jZShwb3NpdGlvbiwgdGFyZ2V0KS5ub3JtYWxpemUoKTtcclxuICAgICAgICBjb25zdCB4ID0gdmVjMy5jcm9zcyh1cCwgeikubm9ybWFsaXplKCk7XHJcbiAgICAgICAgY29uc3QgeSA9IHZlYzMuY3Jvc3MoeiwgeCkubm9ybWFsaXplKCk7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBtYXQ0KFtcclxuICAgICAgICAgICAgeC54LFxyXG4gICAgICAgICAgICB5LngsXHJcbiAgICAgICAgICAgIHoueCxcclxuICAgICAgICAgICAgMCxcclxuICAgICAgICAgICAgeC55LFxyXG4gICAgICAgICAgICB5LnksXHJcbiAgICAgICAgICAgIHoueSxcclxuICAgICAgICAgICAgMCxcclxuICAgICAgICAgICAgeC56LFxyXG4gICAgICAgICAgICB5LnosXHJcbiAgICAgICAgICAgIHoueixcclxuICAgICAgICAgICAgMCxcclxuICAgICAgICAgICAgLXZlYzMuZG90KHgsIHBvc2l0aW9uKSxcclxuICAgICAgICAgICAgLXZlYzMuZG90KHksIHBvc2l0aW9uKSxcclxuICAgICAgICAgICAgLXZlYzMuZG90KHosIHBvc2l0aW9uKSxcclxuICAgICAgICAgICAgMVxyXG4gICAgICAgIF0pO1xyXG4gICAgfVxyXG4gICAgc3RhdGljIHByb2R1Y3QobTEsIG0yLCByZXN1bHQpIHtcclxuICAgICAgICBjb25zdCBhMDAgPSBtMS5hdCgwKTtcclxuICAgICAgICBjb25zdCBhMDEgPSBtMS5hdCgxKTtcclxuICAgICAgICBjb25zdCBhMDIgPSBtMS5hdCgyKTtcclxuICAgICAgICBjb25zdCBhMDMgPSBtMS5hdCgzKTtcclxuICAgICAgICBjb25zdCBhMTAgPSBtMS5hdCg0KTtcclxuICAgICAgICBjb25zdCBhMTEgPSBtMS5hdCg1KTtcclxuICAgICAgICBjb25zdCBhMTIgPSBtMS5hdCg2KTtcclxuICAgICAgICBjb25zdCBhMTMgPSBtMS5hdCg3KTtcclxuICAgICAgICBjb25zdCBhMjAgPSBtMS5hdCg4KTtcclxuICAgICAgICBjb25zdCBhMjEgPSBtMS5hdCg5KTtcclxuICAgICAgICBjb25zdCBhMjIgPSBtMS5hdCgxMCk7XHJcbiAgICAgICAgY29uc3QgYTIzID0gbTEuYXQoMTEpO1xyXG4gICAgICAgIGNvbnN0IGEzMCA9IG0xLmF0KDEyKTtcclxuICAgICAgICBjb25zdCBhMzEgPSBtMS5hdCgxMyk7XHJcbiAgICAgICAgY29uc3QgYTMyID0gbTEuYXQoMTQpO1xyXG4gICAgICAgIGNvbnN0IGEzMyA9IG0xLmF0KDE1KTtcclxuICAgICAgICBjb25zdCBiMDAgPSBtMi5hdCgwKTtcclxuICAgICAgICBjb25zdCBiMDEgPSBtMi5hdCgxKTtcclxuICAgICAgICBjb25zdCBiMDIgPSBtMi5hdCgyKTtcclxuICAgICAgICBjb25zdCBiMDMgPSBtMi5hdCgzKTtcclxuICAgICAgICBjb25zdCBiMTAgPSBtMi5hdCg0KTtcclxuICAgICAgICBjb25zdCBiMTEgPSBtMi5hdCg1KTtcclxuICAgICAgICBjb25zdCBiMTIgPSBtMi5hdCg2KTtcclxuICAgICAgICBjb25zdCBiMTMgPSBtMi5hdCg3KTtcclxuICAgICAgICBjb25zdCBiMjAgPSBtMi5hdCg4KTtcclxuICAgICAgICBjb25zdCBiMjEgPSBtMi5hdCg5KTtcclxuICAgICAgICBjb25zdCBiMjIgPSBtMi5hdCgxMCk7XHJcbiAgICAgICAgY29uc3QgYjIzID0gbTIuYXQoMTEpO1xyXG4gICAgICAgIGNvbnN0IGIzMCA9IG0yLmF0KDEyKTtcclxuICAgICAgICBjb25zdCBiMzEgPSBtMi5hdCgxMyk7XHJcbiAgICAgICAgY29uc3QgYjMyID0gbTIuYXQoMTQpO1xyXG4gICAgICAgIGNvbnN0IGIzMyA9IG0yLmF0KDE1KTtcclxuICAgICAgICBpZiAocmVzdWx0KSB7XHJcbiAgICAgICAgICAgIHJlc3VsdC5pbml0KFtcclxuICAgICAgICAgICAgICAgIGIwMCAqIGEwMCArIGIwMSAqIGExMCArIGIwMiAqIGEyMCArIGIwMyAqIGEzMCxcclxuICAgICAgICAgICAgICAgIGIwMCAqIGEwMSArIGIwMSAqIGExMSArIGIwMiAqIGEyMSArIGIwMyAqIGEzMSxcclxuICAgICAgICAgICAgICAgIGIwMCAqIGEwMiArIGIwMSAqIGExMiArIGIwMiAqIGEyMiArIGIwMyAqIGEzMixcclxuICAgICAgICAgICAgICAgIGIwMCAqIGEwMyArIGIwMSAqIGExMyArIGIwMiAqIGEyMyArIGIwMyAqIGEzMyxcclxuICAgICAgICAgICAgICAgIGIxMCAqIGEwMCArIGIxMSAqIGExMCArIGIxMiAqIGEyMCArIGIxMyAqIGEzMCxcclxuICAgICAgICAgICAgICAgIGIxMCAqIGEwMSArIGIxMSAqIGExMSArIGIxMiAqIGEyMSArIGIxMyAqIGEzMSxcclxuICAgICAgICAgICAgICAgIGIxMCAqIGEwMiArIGIxMSAqIGExMiArIGIxMiAqIGEyMiArIGIxMyAqIGEzMixcclxuICAgICAgICAgICAgICAgIGIxMCAqIGEwMyArIGIxMSAqIGExMyArIGIxMiAqIGEyMyArIGIxMyAqIGEzMyxcclxuICAgICAgICAgICAgICAgIGIyMCAqIGEwMCArIGIyMSAqIGExMCArIGIyMiAqIGEyMCArIGIyMyAqIGEzMCxcclxuICAgICAgICAgICAgICAgIGIyMCAqIGEwMSArIGIyMSAqIGExMSArIGIyMiAqIGEyMSArIGIyMyAqIGEzMSxcclxuICAgICAgICAgICAgICAgIGIyMCAqIGEwMiArIGIyMSAqIGExMiArIGIyMiAqIGEyMiArIGIyMyAqIGEzMixcclxuICAgICAgICAgICAgICAgIGIyMCAqIGEwMyArIGIyMSAqIGExMyArIGIyMiAqIGEyMyArIGIyMyAqIGEzMyxcclxuICAgICAgICAgICAgICAgIGIzMCAqIGEwMCArIGIzMSAqIGExMCArIGIzMiAqIGEyMCArIGIzMyAqIGEzMCxcclxuICAgICAgICAgICAgICAgIGIzMCAqIGEwMSArIGIzMSAqIGExMSArIGIzMiAqIGEyMSArIGIzMyAqIGEzMSxcclxuICAgICAgICAgICAgICAgIGIzMCAqIGEwMiArIGIzMSAqIGExMiArIGIzMiAqIGEyMiArIGIzMyAqIGEzMixcclxuICAgICAgICAgICAgICAgIGIzMCAqIGEwMyArIGIzMSAqIGExMyArIGIzMiAqIGEyMyArIGIzMyAqIGEzM1xyXG4gICAgICAgICAgICBdKTtcclxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgIHJldHVybiBuZXcgbWF0NChbXHJcbiAgICAgICAgICAgICAgICBiMDAgKiBhMDAgKyBiMDEgKiBhMTAgKyBiMDIgKiBhMjAgKyBiMDMgKiBhMzAsXHJcbiAgICAgICAgICAgICAgICBiMDAgKiBhMDEgKyBiMDEgKiBhMTEgKyBiMDIgKiBhMjEgKyBiMDMgKiBhMzEsXHJcbiAgICAgICAgICAgICAgICBiMDAgKiBhMDIgKyBiMDEgKiBhMTIgKyBiMDIgKiBhMjIgKyBiMDMgKiBhMzIsXHJcbiAgICAgICAgICAgICAgICBiMDAgKiBhMDMgKyBiMDEgKiBhMTMgKyBiMDIgKiBhMjMgKyBiMDMgKiBhMzMsXHJcbiAgICAgICAgICAgICAgICBiMTAgKiBhMDAgKyBiMTEgKiBhMTAgKyBiMTIgKiBhMjAgKyBiMTMgKiBhMzAsXHJcbiAgICAgICAgICAgICAgICBiMTAgKiBhMDEgKyBiMTEgKiBhMTEgKyBiMTIgKiBhMjEgKyBiMTMgKiBhMzEsXHJcbiAgICAgICAgICAgICAgICBiMTAgKiBhMDIgKyBiMTEgKiBhMTIgKyBiMTIgKiBhMjIgKyBiMTMgKiBhMzIsXHJcbiAgICAgICAgICAgICAgICBiMTAgKiBhMDMgKyBiMTEgKiBhMTMgKyBiMTIgKiBhMjMgKyBiMTMgKiBhMzMsXHJcbiAgICAgICAgICAgICAgICBiMjAgKiBhMDAgKyBiMjEgKiBhMTAgKyBiMjIgKiBhMjAgKyBiMjMgKiBhMzAsXHJcbiAgICAgICAgICAgICAgICBiMjAgKiBhMDEgKyBiMjEgKiBhMTEgKyBiMjIgKiBhMjEgKyBiMjMgKiBhMzEsXHJcbiAgICAgICAgICAgICAgICBiMjAgKiBhMDIgKyBiMjEgKiBhMTIgKyBiMjIgKiBhMjIgKyBiMjMgKiBhMzIsXHJcbiAgICAgICAgICAgICAgICBiMjAgKiBhMDMgKyBiMjEgKiBhMTMgKyBiMjIgKiBhMjMgKyBiMjMgKiBhMzMsXHJcbiAgICAgICAgICAgICAgICBiMzAgKiBhMDAgKyBiMzEgKiBhMTAgKyBiMzIgKiBhMjAgKyBiMzMgKiBhMzAsXHJcbiAgICAgICAgICAgICAgICBiMzAgKiBhMDEgKyBiMzEgKiBhMTEgKyBiMzIgKiBhMjEgKyBiMzMgKiBhMzEsXHJcbiAgICAgICAgICAgICAgICBiMzAgKiBhMDIgKyBiMzEgKiBhMTIgKyBiMzIgKiBhMjIgKyBiMzMgKiBhMzIsXHJcbiAgICAgICAgICAgICAgICBiMzAgKiBhMDMgKyBiMzEgKiBhMTMgKyBiMzIgKiBhMjMgKyBiMzMgKiBhMzNcclxuICAgICAgICAgICAgXSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcbm1hdDQuaWRlbnRpdHkgPSBuZXcgbWF0NCgpLnNldElkZW50aXR5KCk7XHJcbiIsImltcG9ydCB2ZWMzIGZyb20gJy4vdmVjMyc7XHJcbmltcG9ydCB7IGVwc2lsb24gfSBmcm9tICcuL2NvbnN0YW50cyc7XHJcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIHZlYzIge1xyXG4gICAgY29uc3RydWN0b3IodmFsdWVzKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KDIpO1xyXG4gICAgICAgIGlmICh2YWx1ZXMgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgICAgICB0aGlzLnh5ID0gdmFsdWVzO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuICAgIGdldCB4KCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnZhbHVlc1swXTtcclxuICAgIH1cclxuICAgIGdldCB5KCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnZhbHVlc1sxXTtcclxuICAgIH1cclxuICAgIGdldCB4eSgpIHtcclxuICAgICAgICByZXR1cm4gW3RoaXMudmFsdWVzWzBdLCB0aGlzLnZhbHVlc1sxXV07XHJcbiAgICB9XHJcbiAgICBzZXQgeCh2YWx1ZSkge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzBdID0gdmFsdWU7XHJcbiAgICB9XHJcbiAgICBzZXQgeSh2YWx1ZSkge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdID0gdmFsdWU7XHJcbiAgICB9XHJcbiAgICBzZXQgeHkodmFsdWVzKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gPSB2YWx1ZXNbMF07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMV0gPSB2YWx1ZXNbMV07XHJcbiAgICB9XHJcbiAgICBhdChpbmRleCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnZhbHVlc1tpbmRleF07XHJcbiAgICB9XHJcbiAgICByZXNldCgpIHtcclxuICAgICAgICB0aGlzLnggPSAwO1xyXG4gICAgICAgIHRoaXMueSA9IDA7XHJcbiAgICB9XHJcbiAgICBjb3B5KGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IG5ldyB2ZWMyKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlc3QueCA9IHRoaXMueDtcclxuICAgICAgICBkZXN0LnkgPSB0aGlzLnk7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbiAgICBuZWdhdGUoZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gdGhpcztcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVzdC54ID0gLXRoaXMueDtcclxuICAgICAgICBkZXN0LnkgPSAtdGhpcy55O1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgZXF1YWxzKHZlY3RvciwgdGhyZXNob2xkID0gZXBzaWxvbikge1xyXG4gICAgICAgIGlmIChNYXRoLmFicyh0aGlzLnggLSB2ZWN0b3IueCkgPiB0aHJlc2hvbGQpIHtcclxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoTWF0aC5hYnModGhpcy55IC0gdmVjdG9yLnkpID4gdGhyZXNob2xkKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgICBsZW5ndGgoKSB7XHJcbiAgICAgICAgcmV0dXJuIE1hdGguc3FydCh0aGlzLnNxdWFyZWRMZW5ndGgoKSk7XHJcbiAgICB9XHJcbiAgICBzcXVhcmVkTGVuZ3RoKCkge1xyXG4gICAgICAgIGNvbnN0IHggPSB0aGlzLng7XHJcbiAgICAgICAgY29uc3QgeSA9IHRoaXMueTtcclxuICAgICAgICByZXR1cm4geCAqIHggKyB5ICogeTtcclxuICAgIH1cclxuICAgIGFkZCh2ZWN0b3IpIHtcclxuICAgICAgICB0aGlzLnggKz0gdmVjdG9yLng7XHJcbiAgICAgICAgdGhpcy55ICs9IHZlY3Rvci55O1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgc3VidHJhY3QodmVjdG9yKSB7XHJcbiAgICAgICAgdGhpcy54IC09IHZlY3Rvci54O1xyXG4gICAgICAgIHRoaXMueSAtPSB2ZWN0b3IueTtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIG11bHRpcGx5KHZlY3Rvcikge1xyXG4gICAgICAgIHRoaXMueCAqPSB2ZWN0b3IueDtcclxuICAgICAgICB0aGlzLnkgKj0gdmVjdG9yLnk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBkaXZpZGUodmVjdG9yKSB7XHJcbiAgICAgICAgdGhpcy54IC89IHZlY3Rvci54O1xyXG4gICAgICAgIHRoaXMueSAvPSB2ZWN0b3IueTtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIHNjYWxlKHZhbHVlLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSB0aGlzO1xyXG4gICAgICAgIH1cclxuICAgICAgICBkZXN0LnggKj0gdmFsdWU7XHJcbiAgICAgICAgZGVzdC55ICo9IHZhbHVlO1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgbm9ybWFsaXplKGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IHRoaXM7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxldCBsZW5ndGggPSB0aGlzLmxlbmd0aCgpO1xyXG4gICAgICAgIGlmIChsZW5ndGggPT09IDEpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmIChsZW5ndGggPT09IDApIHtcclxuICAgICAgICAgICAgZGVzdC54ID0gMDtcclxuICAgICAgICAgICAgZGVzdC55ID0gMDtcclxuICAgICAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxlbmd0aCA9IDEuMCAvIGxlbmd0aDtcclxuICAgICAgICBkZXN0LnggKj0gbGVuZ3RoO1xyXG4gICAgICAgIGRlc3QueSAqPSBsZW5ndGg7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbiAgICBtdWx0aXBseU1hdDIobWF0cml4LCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSB0aGlzO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gbWF0cml4Lm11bHRpcGx5VmVjMih0aGlzLCBkZXN0KTtcclxuICAgIH1cclxuICAgIG11bHRpcGx5TWF0MyhtYXRyaXgsIGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IHRoaXM7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBtYXRyaXgubXVsdGlwbHlWZWMyKHRoaXMsIGRlc3QpO1xyXG4gICAgfVxyXG4gICAgc3RhdGljIGNyb3NzKHZlY3RvciwgdmVjdG9yMiwgZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHZlYzMoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgY29uc3QgeCA9IHZlY3Rvci54O1xyXG4gICAgICAgIGNvbnN0IHkgPSB2ZWN0b3IueTtcclxuICAgICAgICBjb25zdCB4MiA9IHZlY3RvcjIueDtcclxuICAgICAgICBjb25zdCB5MiA9IHZlY3RvcjIueTtcclxuICAgICAgICBjb25zdCB6ID0geCAqIHkyIC0geSAqIHgyO1xyXG4gICAgICAgIGRlc3QueCA9IDA7XHJcbiAgICAgICAgZGVzdC55ID0gMDtcclxuICAgICAgICBkZXN0LnogPSB6O1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgc3RhdGljIGRvdCh2ZWN0b3IsIHZlY3RvcjIpIHtcclxuICAgICAgICByZXR1cm4gdmVjdG9yLnggKiB2ZWN0b3IyLnggKyB2ZWN0b3IueSAqIHZlY3RvcjIueTtcclxuICAgIH1cclxuICAgIHN0YXRpYyBkaXN0YW5jZSh2ZWN0b3IsIHZlY3RvcjIpIHtcclxuICAgICAgICByZXR1cm4gTWF0aC5zcXJ0KHRoaXMuc3F1YXJlZERpc3RhbmNlKHZlY3RvciwgdmVjdG9yMikpO1xyXG4gICAgfVxyXG4gICAgc3RhdGljIHNxdWFyZWREaXN0YW5jZSh2ZWN0b3IsIHZlY3RvcjIpIHtcclxuICAgICAgICBjb25zdCB4ID0gdmVjdG9yMi54IC0gdmVjdG9yLng7XHJcbiAgICAgICAgY29uc3QgeSA9IHZlY3RvcjIueSAtIHZlY3Rvci55O1xyXG4gICAgICAgIHJldHVybiB4ICogeCArIHkgKiB5O1xyXG4gICAgfVxyXG4gICAgc3RhdGljIGRpcmVjdGlvbih2ZWN0b3IsIHZlY3RvcjIsIGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IG5ldyB2ZWMyKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGNvbnN0IHggPSB2ZWN0b3IueCAtIHZlY3RvcjIueDtcclxuICAgICAgICBjb25zdCB5ID0gdmVjdG9yLnkgLSB2ZWN0b3IyLnk7XHJcbiAgICAgICAgbGV0IGxlbmd0aCA9IE1hdGguc3FydCh4ICogeCArIHkgKiB5KTtcclxuICAgICAgICBpZiAobGVuZ3RoID09PSAwKSB7XHJcbiAgICAgICAgICAgIGRlc3QueCA9IDA7XHJcbiAgICAgICAgICAgIGRlc3QueSA9IDA7XHJcbiAgICAgICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgICAgIH1cclxuICAgICAgICBsZW5ndGggPSAxIC8gbGVuZ3RoO1xyXG4gICAgICAgIGRlc3QueCA9IHggKiBsZW5ndGg7XHJcbiAgICAgICAgZGVzdC55ID0geSAqIGxlbmd0aDtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBtaXgodmVjdG9yLCB2ZWN0b3IyLCB0aW1lLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgdmVjMigpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCB4ID0gdmVjdG9yLng7XHJcbiAgICAgICAgY29uc3QgeSA9IHZlY3Rvci55O1xyXG4gICAgICAgIGNvbnN0IHgyID0gdmVjdG9yMi54O1xyXG4gICAgICAgIGNvbnN0IHkyID0gdmVjdG9yMi55O1xyXG4gICAgICAgIGRlc3QueCA9IHggKyB0aW1lICogKHgyIC0geCk7XHJcbiAgICAgICAgZGVzdC55ID0geSArIHRpbWUgKiAoeTIgLSB5KTtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBzdW0odmVjdG9yLCB2ZWN0b3IyLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgdmVjMigpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBkZXN0LnggPSB2ZWN0b3IueCArIHZlY3RvcjIueDtcclxuICAgICAgICBkZXN0LnkgPSB2ZWN0b3IueSArIHZlY3RvcjIueTtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBkaWZmZXJlbmNlKHZlY3RvciwgdmVjdG9yMiwgZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHZlYzIoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVzdC54ID0gdmVjdG9yLnggLSB2ZWN0b3IyLng7XHJcbiAgICAgICAgZGVzdC55ID0gdmVjdG9yLnkgLSB2ZWN0b3IyLnk7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbiAgICBzdGF0aWMgcHJvZHVjdCh2ZWN0b3IsIHZlY3RvcjIsIGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IG5ldyB2ZWMyKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlc3QueCA9IHZlY3Rvci54ICogdmVjdG9yMi54O1xyXG4gICAgICAgIGRlc3QueSA9IHZlY3Rvci55ICogdmVjdG9yMi55O1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgc3RhdGljIHF1b3RpZW50KHZlY3RvciwgdmVjdG9yMiwgZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHZlYzIoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVzdC54ID0gdmVjdG9yLnggLyB2ZWN0b3IyLng7XHJcbiAgICAgICAgZGVzdC55ID0gdmVjdG9yLnkgLyB2ZWN0b3IyLnk7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbn1cclxudmVjMi56ZXJvID0gbmV3IHZlYzIoWzAsIDBdKTtcclxudmVjMi5vbmUgPSBuZXcgdmVjMihbMSwgMV0pO1xyXG4iLCJpbXBvcnQgbWF0NCBmcm9tICcuL21hdDQnO1xyXG5pbXBvcnQgcXVhdCBmcm9tICcuL3F1YXQnO1xyXG5pbXBvcnQgdmVjMiBmcm9tICcuL3ZlYzInO1xyXG5pbXBvcnQgdmVjMyBmcm9tICcuL3ZlYzMnO1xyXG5pbXBvcnQgeyBlcHNpbG9uIH0gZnJvbSAnLi9jb25zdGFudHMnO1xyXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBtYXQzIHtcclxuICAgIGNvbnN0cnVjdG9yKHZhbHVlcykge1xyXG4gICAgICAgIHRoaXMudmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSg5KTtcclxuICAgICAgICBpZiAodmFsdWVzICE9PSB1bmRlZmluZWQpIHtcclxuICAgICAgICAgICAgdGhpcy5pbml0KHZhbHVlcyk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgYXQoaW5kZXgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXNbaW5kZXhdO1xyXG4gICAgfVxyXG4gICAgaW5pdCh2YWx1ZXMpIHtcclxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IDk7IGkrKykge1xyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1tpXSA9IHZhbHVlc1tpXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICByZXNldCgpIHtcclxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IDk7IGkrKykge1xyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1tpXSA9IDA7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgY29weShkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgbWF0MygpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IDk7IGkrKykge1xyXG4gICAgICAgICAgICBkZXN0LnZhbHVlc1tpXSA9IHRoaXMudmFsdWVzW2ldO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIGFsbCgpIHtcclxuICAgICAgICBjb25zdCBkYXRhID0gW107XHJcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCA5OyBpKyspIHtcclxuICAgICAgICAgICAgZGF0YVtpXSA9IHRoaXMudmFsdWVzW2ldO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gZGF0YTtcclxuICAgIH1cclxuICAgIHJvdyhpbmRleCkge1xyXG4gICAgICAgIHJldHVybiBbXHJcbiAgICAgICAgICAgIHRoaXMudmFsdWVzW2luZGV4ICogMyArIDBdLFxyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1tpbmRleCAqIDMgKyAxXSxcclxuICAgICAgICAgICAgdGhpcy52YWx1ZXNbaW5kZXggKiAzICsgMl1cclxuICAgICAgICBdO1xyXG4gICAgfVxyXG4gICAgY29sKGluZGV4KSB7XHJcbiAgICAgICAgcmV0dXJuIFt0aGlzLnZhbHVlc1tpbmRleF0sIHRoaXMudmFsdWVzW2luZGV4ICsgM10sIHRoaXMudmFsdWVzW2luZGV4ICsgNl1dO1xyXG4gICAgfVxyXG4gICAgZXF1YWxzKG1hdHJpeCwgdGhyZXNob2xkID0gZXBzaWxvbikge1xyXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgOTsgaSsrKSB7XHJcbiAgICAgICAgICAgIGlmIChNYXRoLmFicyh0aGlzLnZhbHVlc1tpXSAtIG1hdHJpeC5hdChpKSkgPiB0aHJlc2hvbGQpIHtcclxuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICAgIGRldGVybWluYW50KCkge1xyXG4gICAgICAgIGNvbnN0IGEwMCA9IHRoaXMudmFsdWVzWzBdO1xyXG4gICAgICAgIGNvbnN0IGEwMSA9IHRoaXMudmFsdWVzWzFdO1xyXG4gICAgICAgIGNvbnN0IGEwMiA9IHRoaXMudmFsdWVzWzJdO1xyXG4gICAgICAgIGNvbnN0IGExMCA9IHRoaXMudmFsdWVzWzNdO1xyXG4gICAgICAgIGNvbnN0IGExMSA9IHRoaXMudmFsdWVzWzRdO1xyXG4gICAgICAgIGNvbnN0IGExMiA9IHRoaXMudmFsdWVzWzVdO1xyXG4gICAgICAgIGNvbnN0IGEyMCA9IHRoaXMudmFsdWVzWzZdO1xyXG4gICAgICAgIGNvbnN0IGEyMSA9IHRoaXMudmFsdWVzWzddO1xyXG4gICAgICAgIGNvbnN0IGEyMiA9IHRoaXMudmFsdWVzWzhdO1xyXG4gICAgICAgIGNvbnN0IGRldDAxID0gYTIyICogYTExIC0gYTEyICogYTIxO1xyXG4gICAgICAgIGNvbnN0IGRldDExID0gLWEyMiAqIGExMCArIGExMiAqIGEyMDtcclxuICAgICAgICBjb25zdCBkZXQyMSA9IGEyMSAqIGExMCAtIGExMSAqIGEyMDtcclxuICAgICAgICByZXR1cm4gYTAwICogZGV0MDEgKyBhMDEgKiBkZXQxMSArIGEwMiAqIGRldDIxO1xyXG4gICAgfVxyXG4gICAgc2V0SWRlbnRpdHkoKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gPSAxO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdID0gMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1syXSA9IDA7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbM10gPSAwO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzRdID0gMTtcclxuICAgICAgICB0aGlzLnZhbHVlc1s1XSA9IDA7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNl0gPSAwO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzddID0gMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s4XSA9IDE7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICB0cmFuc3Bvc2UoKSB7XHJcbiAgICAgICAgY29uc3QgdGVtcDAxID0gdGhpcy52YWx1ZXNbMV07XHJcbiAgICAgICAgY29uc3QgdGVtcDAyID0gdGhpcy52YWx1ZXNbMl07XHJcbiAgICAgICAgY29uc3QgdGVtcDEyID0gdGhpcy52YWx1ZXNbNV07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMV0gPSB0aGlzLnZhbHVlc1szXTtcclxuICAgICAgICB0aGlzLnZhbHVlc1syXSA9IHRoaXMudmFsdWVzWzZdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzNdID0gdGVtcDAxO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzVdID0gdGhpcy52YWx1ZXNbN107XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNl0gPSB0ZW1wMDI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbN10gPSB0ZW1wMTI7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBpbnZlcnNlKCkge1xyXG4gICAgICAgIGNvbnN0IGEwMCA9IHRoaXMudmFsdWVzWzBdO1xyXG4gICAgICAgIGNvbnN0IGEwMSA9IHRoaXMudmFsdWVzWzFdO1xyXG4gICAgICAgIGNvbnN0IGEwMiA9IHRoaXMudmFsdWVzWzJdO1xyXG4gICAgICAgIGNvbnN0IGExMCA9IHRoaXMudmFsdWVzWzNdO1xyXG4gICAgICAgIGNvbnN0IGExMSA9IHRoaXMudmFsdWVzWzRdO1xyXG4gICAgICAgIGNvbnN0IGExMiA9IHRoaXMudmFsdWVzWzVdO1xyXG4gICAgICAgIGNvbnN0IGEyMCA9IHRoaXMudmFsdWVzWzZdO1xyXG4gICAgICAgIGNvbnN0IGEyMSA9IHRoaXMudmFsdWVzWzddO1xyXG4gICAgICAgIGNvbnN0IGEyMiA9IHRoaXMudmFsdWVzWzhdO1xyXG4gICAgICAgIGNvbnN0IGRldDAxID0gYTIyICogYTExIC0gYTEyICogYTIxO1xyXG4gICAgICAgIGNvbnN0IGRldDExID0gLWEyMiAqIGExMCArIGExMiAqIGEyMDtcclxuICAgICAgICBjb25zdCBkZXQyMSA9IGEyMSAqIGExMCAtIGExMSAqIGEyMDtcclxuICAgICAgICBsZXQgZGV0ID0gYTAwICogZGV0MDEgKyBhMDEgKiBkZXQxMSArIGEwMiAqIGRldDIxO1xyXG4gICAgICAgIGlmICghZGV0KSB7XHJcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xyXG4gICAgICAgIH1cclxuICAgICAgICBkZXQgPSAxLjAgLyBkZXQ7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gPSBkZXQwMSAqIGRldDtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSA9ICgtYTIyICogYTAxICsgYTAyICogYTIxKSAqIGRldDtcclxuICAgICAgICB0aGlzLnZhbHVlc1syXSA9IChhMTIgKiBhMDEgLSBhMDIgKiBhMTEpICogZGV0O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzNdID0gZGV0MTEgKiBkZXQ7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNF0gPSAoYTIyICogYTAwIC0gYTAyICogYTIwKSAqIGRldDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s1XSA9ICgtYTEyICogYTAwICsgYTAyICogYTEwKSAqIGRldDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s2XSA9IGRldDIxICogZGV0O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzddID0gKC1hMjEgKiBhMDAgKyBhMDEgKiBhMjApICogZGV0O1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzhdID0gKGExMSAqIGEwMCAtIGEwMSAqIGExMCkgKiBkZXQ7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBtdWx0aXBseShtYXRyaXgpIHtcclxuICAgICAgICBjb25zdCBhMDAgPSB0aGlzLnZhbHVlc1swXTtcclxuICAgICAgICBjb25zdCBhMDEgPSB0aGlzLnZhbHVlc1sxXTtcclxuICAgICAgICBjb25zdCBhMDIgPSB0aGlzLnZhbHVlc1syXTtcclxuICAgICAgICBjb25zdCBhMTAgPSB0aGlzLnZhbHVlc1szXTtcclxuICAgICAgICBjb25zdCBhMTEgPSB0aGlzLnZhbHVlc1s0XTtcclxuICAgICAgICBjb25zdCBhMTIgPSB0aGlzLnZhbHVlc1s1XTtcclxuICAgICAgICBjb25zdCBhMjAgPSB0aGlzLnZhbHVlc1s2XTtcclxuICAgICAgICBjb25zdCBhMjEgPSB0aGlzLnZhbHVlc1s3XTtcclxuICAgICAgICBjb25zdCBhMjIgPSB0aGlzLnZhbHVlc1s4XTtcclxuICAgICAgICBjb25zdCBiMDAgPSBtYXRyaXguYXQoMCk7XHJcbiAgICAgICAgY29uc3QgYjAxID0gbWF0cml4LmF0KDEpO1xyXG4gICAgICAgIGNvbnN0IGIwMiA9IG1hdHJpeC5hdCgyKTtcclxuICAgICAgICBjb25zdCBiMTAgPSBtYXRyaXguYXQoMyk7XHJcbiAgICAgICAgY29uc3QgYjExID0gbWF0cml4LmF0KDQpO1xyXG4gICAgICAgIGNvbnN0IGIxMiA9IG1hdHJpeC5hdCg1KTtcclxuICAgICAgICBjb25zdCBiMjAgPSBtYXRyaXguYXQoNik7XHJcbiAgICAgICAgY29uc3QgYjIxID0gbWF0cml4LmF0KDcpO1xyXG4gICAgICAgIGNvbnN0IGIyMiA9IG1hdHJpeC5hdCg4KTtcclxuICAgICAgICB0aGlzLnZhbHVlc1swXSA9IGIwMCAqIGEwMCArIGIwMSAqIGExMCArIGIwMiAqIGEyMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSA9IGIwMCAqIGEwMSArIGIwMSAqIGExMSArIGIwMiAqIGEyMTtcclxuICAgICAgICB0aGlzLnZhbHVlc1syXSA9IGIwMCAqIGEwMiArIGIwMSAqIGExMiArIGIwMiAqIGEyMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1szXSA9IGIxMCAqIGEwMCArIGIxMSAqIGExMCArIGIxMiAqIGEyMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s0XSA9IGIxMCAqIGEwMSArIGIxMSAqIGExMSArIGIxMiAqIGEyMTtcclxuICAgICAgICB0aGlzLnZhbHVlc1s1XSA9IGIxMCAqIGEwMiArIGIxMSAqIGExMiArIGIxMiAqIGEyMjtcclxuICAgICAgICB0aGlzLnZhbHVlc1s2XSA9IGIyMCAqIGEwMCArIGIyMSAqIGExMCArIGIyMiAqIGEyMDtcclxuICAgICAgICB0aGlzLnZhbHVlc1s3XSA9IGIyMCAqIGEwMSArIGIyMSAqIGExMSArIGIyMiAqIGEyMTtcclxuICAgICAgICB0aGlzLnZhbHVlc1s4XSA9IGIyMCAqIGEwMiArIGIyMSAqIGExMiArIGIyMiAqIGEyMjtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIG11bHRpcGx5VmVjMih2ZWN0b3IsIHJlc3VsdCkge1xyXG4gICAgICAgIGNvbnN0IHggPSB2ZWN0b3IueDtcclxuICAgICAgICBjb25zdCB5ID0gdmVjdG9yLnk7XHJcbiAgICAgICAgaWYgKHJlc3VsdCkge1xyXG4gICAgICAgICAgICByZXN1bHQueHkgPSBbXHJcbiAgICAgICAgICAgICAgICB4ICogdGhpcy52YWx1ZXNbMF0gKyB5ICogdGhpcy52YWx1ZXNbM10gKyB0aGlzLnZhbHVlc1s2XSxcclxuICAgICAgICAgICAgICAgIHggKiB0aGlzLnZhbHVlc1sxXSArIHkgKiB0aGlzLnZhbHVlc1s0XSArIHRoaXMudmFsdWVzWzddXHJcbiAgICAgICAgICAgIF07XHJcbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICByZXR1cm4gbmV3IHZlYzIoW1xyXG4gICAgICAgICAgICAgICAgeCAqIHRoaXMudmFsdWVzWzBdICsgeSAqIHRoaXMudmFsdWVzWzNdICsgdGhpcy52YWx1ZXNbNl0sXHJcbiAgICAgICAgICAgICAgICB4ICogdGhpcy52YWx1ZXNbMV0gKyB5ICogdGhpcy52YWx1ZXNbNF0gKyB0aGlzLnZhbHVlc1s3XVxyXG4gICAgICAgICAgICBdKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBtdWx0aXBseVZlYzModmVjdG9yLCByZXN1bHQpIHtcclxuICAgICAgICBjb25zdCB4ID0gdmVjdG9yLng7XHJcbiAgICAgICAgY29uc3QgeSA9IHZlY3Rvci55O1xyXG4gICAgICAgIGNvbnN0IHogPSB2ZWN0b3IuejtcclxuICAgICAgICBpZiAocmVzdWx0KSB7XHJcbiAgICAgICAgICAgIHJlc3VsdC54eXogPSBbXHJcbiAgICAgICAgICAgICAgICB4ICogdGhpcy52YWx1ZXNbMF0gKyB5ICogdGhpcy52YWx1ZXNbM10gKyB6ICogdGhpcy52YWx1ZXNbNl0sXHJcbiAgICAgICAgICAgICAgICB4ICogdGhpcy52YWx1ZXNbMV0gKyB5ICogdGhpcy52YWx1ZXNbNF0gKyB6ICogdGhpcy52YWx1ZXNbN10sXHJcbiAgICAgICAgICAgICAgICB4ICogdGhpcy52YWx1ZXNbMl0gKyB5ICogdGhpcy52YWx1ZXNbNV0gKyB6ICogdGhpcy52YWx1ZXNbOF1cclxuICAgICAgICAgICAgXTtcclxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgIHJldHVybiBuZXcgdmVjMyhbXHJcbiAgICAgICAgICAgICAgICB4ICogdGhpcy52YWx1ZXNbMF0gKyB5ICogdGhpcy52YWx1ZXNbM10gKyB6ICogdGhpcy52YWx1ZXNbNl0sXHJcbiAgICAgICAgICAgICAgICB4ICogdGhpcy52YWx1ZXNbMV0gKyB5ICogdGhpcy52YWx1ZXNbNF0gKyB6ICogdGhpcy52YWx1ZXNbN10sXHJcbiAgICAgICAgICAgICAgICB4ICogdGhpcy52YWx1ZXNbMl0gKyB5ICogdGhpcy52YWx1ZXNbNV0gKyB6ICogdGhpcy52YWx1ZXNbOF1cclxuICAgICAgICAgICAgXSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgdG9NYXQ0KHJlc3VsdCkge1xyXG4gICAgICAgIGlmIChyZXN1bHQpIHtcclxuICAgICAgICAgICAgcmVzdWx0LmluaXQoW1xyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbMF0sXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1sxXSxcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzJdLFxyXG4gICAgICAgICAgICAgICAgMCxcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzNdLFxyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbNF0sXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1s1XSxcclxuICAgICAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1s2XSxcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzddLFxyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbOF0sXHJcbiAgICAgICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAgICAgMCxcclxuICAgICAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAgICAgMVxyXG4gICAgICAgICAgICBdKTtcclxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgIHJldHVybiBuZXcgbWF0NChbXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1swXSxcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzFdLFxyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbMl0sXHJcbiAgICAgICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbM10sXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1s0XSxcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzVdLFxyXG4gICAgICAgICAgICAgICAgMCxcclxuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzWzZdLFxyXG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNbN10sXHJcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlc1s4XSxcclxuICAgICAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgICAgICAwLFxyXG4gICAgICAgICAgICAgICAgMCxcclxuICAgICAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgICAgICAxXHJcbiAgICAgICAgICAgIF0pO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuICAgIHRvUXVhdCgpIHtcclxuICAgICAgICBjb25zdCBtMDAgPSB0aGlzLnZhbHVlc1swXTtcclxuICAgICAgICBjb25zdCBtMDEgPSB0aGlzLnZhbHVlc1sxXTtcclxuICAgICAgICBjb25zdCBtMDIgPSB0aGlzLnZhbHVlc1syXTtcclxuICAgICAgICBjb25zdCBtMTAgPSB0aGlzLnZhbHVlc1szXTtcclxuICAgICAgICBjb25zdCBtMTEgPSB0aGlzLnZhbHVlc1s0XTtcclxuICAgICAgICBjb25zdCBtMTIgPSB0aGlzLnZhbHVlc1s1XTtcclxuICAgICAgICBjb25zdCBtMjAgPSB0aGlzLnZhbHVlc1s2XTtcclxuICAgICAgICBjb25zdCBtMjEgPSB0aGlzLnZhbHVlc1s3XTtcclxuICAgICAgICBjb25zdCBtMjIgPSB0aGlzLnZhbHVlc1s4XTtcclxuICAgICAgICBjb25zdCBmb3VyWFNxdWFyZWRNaW51czEgPSBtMDAgLSBtMTEgLSBtMjI7XHJcbiAgICAgICAgY29uc3QgZm91cllTcXVhcmVkTWludXMxID0gbTExIC0gbTAwIC0gbTIyO1xyXG4gICAgICAgIGNvbnN0IGZvdXJaU3F1YXJlZE1pbnVzMSA9IG0yMiAtIG0wMCAtIG0xMTtcclxuICAgICAgICBjb25zdCBmb3VyV1NxdWFyZWRNaW51czEgPSBtMDAgKyBtMTEgKyBtMjI7XHJcbiAgICAgICAgbGV0IGJpZ2dlc3RJbmRleCA9IDA7XHJcbiAgICAgICAgbGV0IGZvdXJCaWdnZXN0U3F1YXJlZE1pbnVzMSA9IGZvdXJXU3F1YXJlZE1pbnVzMTtcclxuICAgICAgICBpZiAoZm91clhTcXVhcmVkTWludXMxID4gZm91ckJpZ2dlc3RTcXVhcmVkTWludXMxKSB7XHJcbiAgICAgICAgICAgIGZvdXJCaWdnZXN0U3F1YXJlZE1pbnVzMSA9IGZvdXJYU3F1YXJlZE1pbnVzMTtcclxuICAgICAgICAgICAgYmlnZ2VzdEluZGV4ID0gMTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKGZvdXJZU3F1YXJlZE1pbnVzMSA+IGZvdXJCaWdnZXN0U3F1YXJlZE1pbnVzMSkge1xyXG4gICAgICAgICAgICBmb3VyQmlnZ2VzdFNxdWFyZWRNaW51czEgPSBmb3VyWVNxdWFyZWRNaW51czE7XHJcbiAgICAgICAgICAgIGJpZ2dlc3RJbmRleCA9IDI7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmIChmb3VyWlNxdWFyZWRNaW51czEgPiBmb3VyQmlnZ2VzdFNxdWFyZWRNaW51czEpIHtcclxuICAgICAgICAgICAgZm91ckJpZ2dlc3RTcXVhcmVkTWludXMxID0gZm91clpTcXVhcmVkTWludXMxO1xyXG4gICAgICAgICAgICBiaWdnZXN0SW5kZXggPSAzO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCBiaWdnZXN0VmFsID0gTWF0aC5zcXJ0KGZvdXJCaWdnZXN0U3F1YXJlZE1pbnVzMSArIDEpICogMC41O1xyXG4gICAgICAgIGNvbnN0IG11bHQgPSAwLjI1IC8gYmlnZ2VzdFZhbDtcclxuICAgICAgICBjb25zdCByZXN1bHQgPSBuZXcgcXVhdCgpO1xyXG4gICAgICAgIHN3aXRjaCAoYmlnZ2VzdEluZGV4KSB7XHJcbiAgICAgICAgICAgIGNhc2UgMDpcclxuICAgICAgICAgICAgICAgIHJlc3VsdC53ID0gYmlnZ2VzdFZhbDtcclxuICAgICAgICAgICAgICAgIHJlc3VsdC54ID0gKG0xMiAtIG0yMSkgKiBtdWx0O1xyXG4gICAgICAgICAgICAgICAgcmVzdWx0LnkgPSAobTIwIC0gbTAyKSAqIG11bHQ7XHJcbiAgICAgICAgICAgICAgICByZXN1bHQueiA9IChtMDEgLSBtMTApICogbXVsdDtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICBjYXNlIDE6XHJcbiAgICAgICAgICAgICAgICByZXN1bHQudyA9IChtMTIgLSBtMjEpICogbXVsdDtcclxuICAgICAgICAgICAgICAgIHJlc3VsdC54ID0gYmlnZ2VzdFZhbDtcclxuICAgICAgICAgICAgICAgIHJlc3VsdC55ID0gKG0wMSArIG0xMCkgKiBtdWx0O1xyXG4gICAgICAgICAgICAgICAgcmVzdWx0LnogPSAobTIwICsgbTAyKSAqIG11bHQ7XHJcbiAgICAgICAgICAgICAgICBicmVhaztcclxuICAgICAgICAgICAgY2FzZSAyOlxyXG4gICAgICAgICAgICAgICAgcmVzdWx0LncgPSAobTIwIC0gbTAyKSAqIG11bHQ7XHJcbiAgICAgICAgICAgICAgICByZXN1bHQueCA9IChtMDEgKyBtMTApICogbXVsdDtcclxuICAgICAgICAgICAgICAgIHJlc3VsdC55ID0gYmlnZ2VzdFZhbDtcclxuICAgICAgICAgICAgICAgIHJlc3VsdC56ID0gKG0xMiArIG0yMSkgKiBtdWx0O1xyXG4gICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIGNhc2UgMzpcclxuICAgICAgICAgICAgICAgIHJlc3VsdC53ID0gKG0wMSAtIG0xMCkgKiBtdWx0O1xyXG4gICAgICAgICAgICAgICAgcmVzdWx0LnggPSAobTIwICsgbTAyKSAqIG11bHQ7XHJcbiAgICAgICAgICAgICAgICByZXN1bHQueSA9IChtMTIgKyBtMjEpICogbXVsdDtcclxuICAgICAgICAgICAgICAgIHJlc3VsdC56ID0gYmlnZ2VzdFZhbDtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgfVxyXG4gICAgcm90YXRlKGFuZ2xlLCBheGlzKSB7XHJcbiAgICAgICAgbGV0IHggPSBheGlzLng7XHJcbiAgICAgICAgbGV0IHkgPSBheGlzLnk7XHJcbiAgICAgICAgbGV0IHogPSBheGlzLno7XHJcbiAgICAgICAgbGV0IGxlbmd0aCA9IE1hdGguc3FydCh4ICogeCArIHkgKiB5ICsgeiAqIHopO1xyXG4gICAgICAgIGlmICghbGVuZ3RoKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAobGVuZ3RoICE9PSAxKSB7XHJcbiAgICAgICAgICAgIGxlbmd0aCA9IDEgLyBsZW5ndGg7XHJcbiAgICAgICAgICAgIHggKj0gbGVuZ3RoO1xyXG4gICAgICAgICAgICB5ICo9IGxlbmd0aDtcclxuICAgICAgICAgICAgeiAqPSBsZW5ndGg7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGNvbnN0IHMgPSBNYXRoLnNpbihhbmdsZSk7XHJcbiAgICAgICAgY29uc3QgYyA9IE1hdGguY29zKGFuZ2xlKTtcclxuICAgICAgICBjb25zdCB0ID0gMS4wIC0gYztcclxuICAgICAgICBjb25zdCBhMDAgPSB0aGlzLnZhbHVlc1swXTtcclxuICAgICAgICBjb25zdCBhMDEgPSB0aGlzLnZhbHVlc1sxXTtcclxuICAgICAgICBjb25zdCBhMDIgPSB0aGlzLnZhbHVlc1syXTtcclxuICAgICAgICBjb25zdCBhMTAgPSB0aGlzLnZhbHVlc1s0XTtcclxuICAgICAgICBjb25zdCBhMTEgPSB0aGlzLnZhbHVlc1s1XTtcclxuICAgICAgICBjb25zdCBhMTIgPSB0aGlzLnZhbHVlc1s2XTtcclxuICAgICAgICBjb25zdCBhMjAgPSB0aGlzLnZhbHVlc1s4XTtcclxuICAgICAgICBjb25zdCBhMjEgPSB0aGlzLnZhbHVlc1s5XTtcclxuICAgICAgICBjb25zdCBhMjIgPSB0aGlzLnZhbHVlc1sxMF07XHJcbiAgICAgICAgY29uc3QgYjAwID0geCAqIHggKiB0ICsgYztcclxuICAgICAgICBjb25zdCBiMDEgPSB5ICogeCAqIHQgKyB6ICogcztcclxuICAgICAgICBjb25zdCBiMDIgPSB6ICogeCAqIHQgLSB5ICogcztcclxuICAgICAgICBjb25zdCBiMTAgPSB4ICogeSAqIHQgLSB6ICogcztcclxuICAgICAgICBjb25zdCBiMTEgPSB5ICogeSAqIHQgKyBjO1xyXG4gICAgICAgIGNvbnN0IGIxMiA9IHogKiB5ICogdCArIHggKiBzO1xyXG4gICAgICAgIGNvbnN0IGIyMCA9IHggKiB6ICogdCArIHkgKiBzO1xyXG4gICAgICAgIGNvbnN0IGIyMSA9IHkgKiB6ICogdCAtIHggKiBzO1xyXG4gICAgICAgIGNvbnN0IGIyMiA9IHogKiB6ICogdCArIGM7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gPSBhMDAgKiBiMDAgKyBhMTAgKiBiMDEgKyBhMjAgKiBiMDI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMV0gPSBhMDEgKiBiMDAgKyBhMTEgKiBiMDEgKyBhMjEgKiBiMDI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMl0gPSBhMDIgKiBiMDAgKyBhMTIgKiBiMDEgKyBhMjIgKiBiMDI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbM10gPSBhMDAgKiBiMTAgKyBhMTAgKiBiMTEgKyBhMjAgKiBiMTI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNF0gPSBhMDEgKiBiMTAgKyBhMTEgKiBiMTEgKyBhMjEgKiBiMTI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNV0gPSBhMDIgKiBiMTAgKyBhMTIgKiBiMTEgKyBhMjIgKiBiMTI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbNl0gPSBhMDAgKiBiMjAgKyBhMTAgKiBiMjEgKyBhMjAgKiBiMjI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbN10gPSBhMDEgKiBiMjAgKyBhMTEgKiBiMjEgKyBhMjEgKiBiMjI7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbOF0gPSBhMDIgKiBiMjAgKyBhMTIgKiBiMjEgKyBhMjIgKiBiMjI7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBzdGF0aWMgcHJvZHVjdChtMSwgbTIsIHJlc3VsdCkge1xyXG4gICAgICAgIGNvbnN0IGEwMCA9IG0xLmF0KDApO1xyXG4gICAgICAgIGNvbnN0IGEwMSA9IG0xLmF0KDEpO1xyXG4gICAgICAgIGNvbnN0IGEwMiA9IG0xLmF0KDIpO1xyXG4gICAgICAgIGNvbnN0IGExMCA9IG0xLmF0KDMpO1xyXG4gICAgICAgIGNvbnN0IGExMSA9IG0xLmF0KDQpO1xyXG4gICAgICAgIGNvbnN0IGExMiA9IG0xLmF0KDUpO1xyXG4gICAgICAgIGNvbnN0IGEyMCA9IG0xLmF0KDYpO1xyXG4gICAgICAgIGNvbnN0IGEyMSA9IG0xLmF0KDcpO1xyXG4gICAgICAgIGNvbnN0IGEyMiA9IG0xLmF0KDgpO1xyXG4gICAgICAgIGNvbnN0IGIwMCA9IG0yLmF0KDApO1xyXG4gICAgICAgIGNvbnN0IGIwMSA9IG0yLmF0KDEpO1xyXG4gICAgICAgIGNvbnN0IGIwMiA9IG0yLmF0KDIpO1xyXG4gICAgICAgIGNvbnN0IGIxMCA9IG0yLmF0KDMpO1xyXG4gICAgICAgIGNvbnN0IGIxMSA9IG0yLmF0KDQpO1xyXG4gICAgICAgIGNvbnN0IGIxMiA9IG0yLmF0KDUpO1xyXG4gICAgICAgIGNvbnN0IGIyMCA9IG0yLmF0KDYpO1xyXG4gICAgICAgIGNvbnN0IGIyMSA9IG0yLmF0KDcpO1xyXG4gICAgICAgIGNvbnN0IGIyMiA9IG0yLmF0KDgpO1xyXG4gICAgICAgIGlmIChyZXN1bHQpIHtcclxuICAgICAgICAgICAgcmVzdWx0LmluaXQoW1xyXG4gICAgICAgICAgICAgICAgYjAwICogYTAwICsgYjAxICogYTEwICsgYjAyICogYTIwLFxyXG4gICAgICAgICAgICAgICAgYjAwICogYTAxICsgYjAxICogYTExICsgYjAyICogYTIxLFxyXG4gICAgICAgICAgICAgICAgYjAwICogYTAyICsgYjAxICogYTEyICsgYjAyICogYTIyLFxyXG4gICAgICAgICAgICAgICAgYjEwICogYTAwICsgYjExICogYTEwICsgYjEyICogYTIwLFxyXG4gICAgICAgICAgICAgICAgYjEwICogYTAxICsgYjExICogYTExICsgYjEyICogYTIxLFxyXG4gICAgICAgICAgICAgICAgYjEwICogYTAyICsgYjExICogYTEyICsgYjEyICogYTIyLFxyXG4gICAgICAgICAgICAgICAgYjIwICogYTAwICsgYjIxICogYTEwICsgYjIyICogYTIwLFxyXG4gICAgICAgICAgICAgICAgYjIwICogYTAxICsgYjIxICogYTExICsgYjIyICogYTIxLFxyXG4gICAgICAgICAgICAgICAgYjIwICogYTAyICsgYjIxICogYTEyICsgYjIyICogYTIyXHJcbiAgICAgICAgICAgIF0pO1xyXG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgcmV0dXJuIG5ldyBtYXQzKFtcclxuICAgICAgICAgICAgICAgIGIwMCAqIGEwMCArIGIwMSAqIGExMCArIGIwMiAqIGEyMCxcclxuICAgICAgICAgICAgICAgIGIwMCAqIGEwMSArIGIwMSAqIGExMSArIGIwMiAqIGEyMSxcclxuICAgICAgICAgICAgICAgIGIwMCAqIGEwMiArIGIwMSAqIGExMiArIGIwMiAqIGEyMixcclxuICAgICAgICAgICAgICAgIGIxMCAqIGEwMCArIGIxMSAqIGExMCArIGIxMiAqIGEyMCxcclxuICAgICAgICAgICAgICAgIGIxMCAqIGEwMSArIGIxMSAqIGExMSArIGIxMiAqIGEyMSxcclxuICAgICAgICAgICAgICAgIGIxMCAqIGEwMiArIGIxMSAqIGExMiArIGIxMiAqIGEyMixcclxuICAgICAgICAgICAgICAgIGIyMCAqIGEwMCArIGIyMSAqIGExMCArIGIyMiAqIGEyMCxcclxuICAgICAgICAgICAgICAgIGIyMCAqIGEwMSArIGIyMSAqIGExMSArIGIyMiAqIGEyMSxcclxuICAgICAgICAgICAgICAgIGIyMCAqIGEwMiArIGIyMSAqIGExMiArIGIyMiAqIGEyMlxyXG4gICAgICAgICAgICBdKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbn1cclxubWF0My5pZGVudGl0eSA9IG5ldyBtYXQzKCkuc2V0SWRlbnRpdHkoKTtcclxuIiwiaW1wb3J0IG1hdDMgZnJvbSAnLi9tYXQzJztcclxuaW1wb3J0IG1hdDQgZnJvbSAnLi9tYXQ0JztcclxuaW1wb3J0IHZlYzMgZnJvbSAnLi92ZWMzJztcclxuaW1wb3J0IHsgZXBzaWxvbiB9IGZyb20gJy4vY29uc3RhbnRzJztcclxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgcXVhdCB7XHJcbiAgICBjb25zdHJ1Y3Rvcih2YWx1ZXMpIHtcclxuICAgICAgICB0aGlzLnZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoNCk7XHJcbiAgICAgICAgaWYgKHZhbHVlcyAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICAgIHRoaXMueHl6dyA9IHZhbHVlcztcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBnZXQgeCgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXNbMF07XHJcbiAgICB9XHJcbiAgICBnZXQgeSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXNbMV07XHJcbiAgICB9XHJcbiAgICBnZXQgeigpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXNbMl07XHJcbiAgICB9XHJcbiAgICBnZXQgdygpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXNbM107XHJcbiAgICB9XHJcbiAgICBnZXQgeHkoKSB7XHJcbiAgICAgICAgcmV0dXJuIFt0aGlzLnZhbHVlc1swXSwgdGhpcy52YWx1ZXNbMV1dO1xyXG4gICAgfVxyXG4gICAgZ2V0IHh5eigpIHtcclxuICAgICAgICByZXR1cm4gW3RoaXMudmFsdWVzWzBdLCB0aGlzLnZhbHVlc1sxXSwgdGhpcy52YWx1ZXNbMl1dO1xyXG4gICAgfVxyXG4gICAgZ2V0IHh5encoKSB7XHJcbiAgICAgICAgcmV0dXJuIFt0aGlzLnZhbHVlc1swXSwgdGhpcy52YWx1ZXNbMV0sIHRoaXMudmFsdWVzWzJdLCB0aGlzLnZhbHVlc1szXV07XHJcbiAgICB9XHJcbiAgICBzZXQgeCh2YWx1ZSkge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzBdID0gdmFsdWU7XHJcbiAgICB9XHJcbiAgICBzZXQgeSh2YWx1ZSkge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdID0gdmFsdWU7XHJcbiAgICB9XHJcbiAgICBzZXQgeih2YWx1ZSkge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzJdID0gdmFsdWU7XHJcbiAgICB9XHJcbiAgICBzZXQgdyh2YWx1ZSkge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzNdID0gdmFsdWU7XHJcbiAgICB9XHJcbiAgICBzZXQgeHkodmFsdWVzKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gPSB2YWx1ZXNbMF07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMV0gPSB2YWx1ZXNbMV07XHJcbiAgICB9XHJcbiAgICBzZXQgeHl6KHZhbHVlcykge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzBdID0gdmFsdWVzWzBdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdID0gdmFsdWVzWzFdO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzJdID0gdmFsdWVzWzJdO1xyXG4gICAgfVxyXG4gICAgc2V0IHh5encodmFsdWVzKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gPSB2YWx1ZXNbMF07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMV0gPSB2YWx1ZXNbMV07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMl0gPSB2YWx1ZXNbMl07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbM10gPSB2YWx1ZXNbM107XHJcbiAgICB9XHJcbiAgICBhdChpbmRleCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnZhbHVlc1tpbmRleF07XHJcbiAgICB9XHJcbiAgICByZXNldCgpIHtcclxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IDQ7IGkrKykge1xyXG4gICAgICAgICAgICB0aGlzLnZhbHVlc1tpXSA9IDA7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgY29weShkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgcXVhdCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IDQ7IGkrKykge1xyXG4gICAgICAgICAgICBkZXN0LnZhbHVlc1tpXSA9IHRoaXMudmFsdWVzW2ldO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHJvbGwoKSB7XHJcbiAgICAgICAgY29uc3QgeCA9IHRoaXMueDtcclxuICAgICAgICBjb25zdCB5ID0gdGhpcy55O1xyXG4gICAgICAgIGNvbnN0IHogPSB0aGlzLno7XHJcbiAgICAgICAgY29uc3QgdyA9IHRoaXMudztcclxuICAgICAgICByZXR1cm4gTWF0aC5hdGFuMigyLjAgKiAoeCAqIHkgKyB3ICogeiksIHcgKiB3ICsgeCAqIHggLSB5ICogeSAtIHogKiB6KTtcclxuICAgIH1cclxuICAgIHBpdGNoKCkge1xyXG4gICAgICAgIGNvbnN0IHggPSB0aGlzLng7XHJcbiAgICAgICAgY29uc3QgeSA9IHRoaXMueTtcclxuICAgICAgICBjb25zdCB6ID0gdGhpcy56O1xyXG4gICAgICAgIGNvbnN0IHcgPSB0aGlzLnc7XHJcbiAgICAgICAgcmV0dXJuIE1hdGguYXRhbjIoMi4wICogKHkgKiB6ICsgdyAqIHgpLCB3ICogdyAtIHggKiB4IC0geSAqIHkgKyB6ICogeik7XHJcbiAgICB9XHJcbiAgICB5YXcoKSB7XHJcbiAgICAgICAgcmV0dXJuIE1hdGguYXNpbigyLjAgKiAodGhpcy54ICogdGhpcy56IC0gdGhpcy53ICogdGhpcy55KSk7XHJcbiAgICB9XHJcbiAgICBlcXVhbHModmVjdG9yLCB0aHJlc2hvbGQgPSBlcHNpbG9uKSB7XHJcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCA0OyBpKyspIHtcclxuICAgICAgICAgICAgaWYgKE1hdGguYWJzKHRoaXMudmFsdWVzW2ldIC0gdmVjdG9yLmF0KGkpKSA+IHRocmVzaG9sZCkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG4gICAgc2V0SWRlbnRpdHkoKSB7XHJcbiAgICAgICAgdGhpcy54ID0gMDtcclxuICAgICAgICB0aGlzLnkgPSAwO1xyXG4gICAgICAgIHRoaXMueiA9IDA7XHJcbiAgICAgICAgdGhpcy53ID0gMTtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIGNhbGN1bGF0ZVcoKSB7XHJcbiAgICAgICAgY29uc3QgeCA9IHRoaXMueDtcclxuICAgICAgICBjb25zdCB5ID0gdGhpcy55O1xyXG4gICAgICAgIGNvbnN0IHogPSB0aGlzLno7XHJcbiAgICAgICAgdGhpcy53ID0gLU1hdGguc3FydChNYXRoLmFicygxLjAgLSB4ICogeCAtIHkgKiB5IC0geiAqIHopKTtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIGludmVyc2UoKSB7XHJcbiAgICAgICAgY29uc3QgZG90ID0gcXVhdC5kb3QodGhpcywgdGhpcyk7XHJcbiAgICAgICAgaWYgKCFkb3QpIHtcclxuICAgICAgICAgICAgdGhpcy54eXp3ID0gWzAsIDAsIDAsIDBdO1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcztcclxuICAgICAgICB9XHJcbiAgICAgICAgY29uc3QgaW52RG90ID0gZG90ID8gMS4wIC8gZG90IDogMDtcclxuICAgICAgICB0aGlzLnggKj0gLWludkRvdDtcclxuICAgICAgICB0aGlzLnkgKj0gLWludkRvdDtcclxuICAgICAgICB0aGlzLnogKj0gLWludkRvdDtcclxuICAgICAgICB0aGlzLncgKj0gaW52RG90O1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgY29uanVnYXRlKCkge1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzBdICo9IC0xO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzFdICo9IC0xO1xyXG4gICAgICAgIHRoaXMudmFsdWVzWzJdICo9IC0xO1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgbGVuZ3RoKCkge1xyXG4gICAgICAgIGNvbnN0IHggPSB0aGlzLng7XHJcbiAgICAgICAgY29uc3QgeSA9IHRoaXMueTtcclxuICAgICAgICBjb25zdCB6ID0gdGhpcy56O1xyXG4gICAgICAgIGNvbnN0IHcgPSB0aGlzLnc7XHJcbiAgICAgICAgcmV0dXJuIE1hdGguc3FydCh4ICogeCArIHkgKiB5ICsgeiAqIHogKyB3ICogdyk7XHJcbiAgICB9XHJcbiAgICBub3JtYWxpemUoZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gdGhpcztcclxuICAgICAgICB9XHJcbiAgICAgICAgY29uc3QgeCA9IHRoaXMueDtcclxuICAgICAgICBjb25zdCB5ID0gdGhpcy55O1xyXG4gICAgICAgIGNvbnN0IHogPSB0aGlzLno7XHJcbiAgICAgICAgY29uc3QgdyA9IHRoaXMudztcclxuICAgICAgICBsZXQgbGVuZ3RoID0gTWF0aC5zcXJ0KHggKiB4ICsgeSAqIHkgKyB6ICogeiArIHcgKiB3KTtcclxuICAgICAgICBpZiAoIWxlbmd0aCkge1xyXG4gICAgICAgICAgICBkZXN0LnggPSAwO1xyXG4gICAgICAgICAgICBkZXN0LnkgPSAwO1xyXG4gICAgICAgICAgICBkZXN0LnogPSAwO1xyXG4gICAgICAgICAgICBkZXN0LncgPSAwO1xyXG4gICAgICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgICAgICB9XHJcbiAgICAgICAgbGVuZ3RoID0gMSAvIGxlbmd0aDtcclxuICAgICAgICBkZXN0LnggPSB4ICogbGVuZ3RoO1xyXG4gICAgICAgIGRlc3QueSA9IHkgKiBsZW5ndGg7XHJcbiAgICAgICAgZGVzdC56ID0geiAqIGxlbmd0aDtcclxuICAgICAgICBkZXN0LncgPSB3ICogbGVuZ3RoO1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgYWRkKG90aGVyKSB7XHJcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCA0OyBpKyspIHtcclxuICAgICAgICAgICAgdGhpcy52YWx1ZXNbaV0gKz0gb3RoZXIuYXQoaSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgbXVsdGlwbHkob3RoZXIpIHtcclxuICAgICAgICBjb25zdCBxMXggPSB0aGlzLnZhbHVlc1swXTtcclxuICAgICAgICBjb25zdCBxMXkgPSB0aGlzLnZhbHVlc1sxXTtcclxuICAgICAgICBjb25zdCBxMXogPSB0aGlzLnZhbHVlc1syXTtcclxuICAgICAgICBjb25zdCBxMXcgPSB0aGlzLnZhbHVlc1szXTtcclxuICAgICAgICBjb25zdCBxMnggPSBvdGhlci54O1xyXG4gICAgICAgIGNvbnN0IHEyeSA9IG90aGVyLnk7XHJcbiAgICAgICAgY29uc3QgcTJ6ID0gb3RoZXIuejtcclxuICAgICAgICBjb25zdCBxMncgPSBvdGhlci53O1xyXG4gICAgICAgIHRoaXMueCA9IHExeCAqIHEydyArIHExdyAqIHEyeCArIHExeSAqIHEyeiAtIHExeiAqIHEyeTtcclxuICAgICAgICB0aGlzLnkgPSBxMXkgKiBxMncgKyBxMXcgKiBxMnkgKyBxMXogKiBxMnggLSBxMXggKiBxMno7XHJcbiAgICAgICAgdGhpcy56ID0gcTF6ICogcTJ3ICsgcTF3ICogcTJ6ICsgcTF4ICogcTJ5IC0gcTF5ICogcTJ4O1xyXG4gICAgICAgIHRoaXMudyA9IHExdyAqIHEydyAtIHExeCAqIHEyeCAtIHExeSAqIHEyeSAtIHExeiAqIHEyejtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIG11bHRpcGx5VmVjMyh2ZWN0b3IsIGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IG5ldyB2ZWMzKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGNvbnN0IHggPSB2ZWN0b3IueDtcclxuICAgICAgICBjb25zdCB5ID0gdmVjdG9yLnk7XHJcbiAgICAgICAgY29uc3QgeiA9IHZlY3Rvci56O1xyXG4gICAgICAgIGNvbnN0IHF4ID0gdGhpcy54O1xyXG4gICAgICAgIGNvbnN0IHF5ID0gdGhpcy55O1xyXG4gICAgICAgIGNvbnN0IHF6ID0gdGhpcy56O1xyXG4gICAgICAgIGNvbnN0IHF3ID0gdGhpcy53O1xyXG4gICAgICAgIGNvbnN0IGl4ID0gcXcgKiB4ICsgcXkgKiB6IC0gcXogKiB5O1xyXG4gICAgICAgIGNvbnN0IGl5ID0gcXcgKiB5ICsgcXogKiB4IC0gcXggKiB6O1xyXG4gICAgICAgIGNvbnN0IGl6ID0gcXcgKiB6ICsgcXggKiB5IC0gcXkgKiB4O1xyXG4gICAgICAgIGNvbnN0IGl3ID0gLXF4ICogeCAtIHF5ICogeSAtIHF6ICogejtcclxuICAgICAgICBkZXN0LnggPSBpeCAqIHF3ICsgaXcgKiAtcXggKyBpeSAqIC1xeiAtIGl6ICogLXF5O1xyXG4gICAgICAgIGRlc3QueSA9IGl5ICogcXcgKyBpdyAqIC1xeSArIGl6ICogLXF4IC0gaXggKiAtcXo7XHJcbiAgICAgICAgZGVzdC56ID0gaXogKiBxdyArIGl3ICogLXF6ICsgaXggKiAtcXkgLSBpeSAqIC1xeDtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHRvTWF0MyhkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgbWF0MygpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCB4ID0gdGhpcy54O1xyXG4gICAgICAgIGNvbnN0IHkgPSB0aGlzLnk7XHJcbiAgICAgICAgY29uc3QgeiA9IHRoaXMuejtcclxuICAgICAgICBjb25zdCB3ID0gdGhpcy53O1xyXG4gICAgICAgIGNvbnN0IHgyID0geCArIHg7XHJcbiAgICAgICAgY29uc3QgeTIgPSB5ICsgeTtcclxuICAgICAgICBjb25zdCB6MiA9IHogKyB6O1xyXG4gICAgICAgIGNvbnN0IHh4ID0geCAqIHgyO1xyXG4gICAgICAgIGNvbnN0IHh5ID0geCAqIHkyO1xyXG4gICAgICAgIGNvbnN0IHh6ID0geCAqIHoyO1xyXG4gICAgICAgIGNvbnN0IHl5ID0geSAqIHkyO1xyXG4gICAgICAgIGNvbnN0IHl6ID0geSAqIHoyO1xyXG4gICAgICAgIGNvbnN0IHp6ID0geiAqIHoyO1xyXG4gICAgICAgIGNvbnN0IHd4ID0gdyAqIHgyO1xyXG4gICAgICAgIGNvbnN0IHd5ID0gdyAqIHkyO1xyXG4gICAgICAgIGNvbnN0IHd6ID0gdyAqIHoyO1xyXG4gICAgICAgIGRlc3QuaW5pdChbXHJcbiAgICAgICAgICAgIDEgLSAoeXkgKyB6eiksXHJcbiAgICAgICAgICAgIHh5ICsgd3osXHJcbiAgICAgICAgICAgIHh6IC0gd3ksXHJcbiAgICAgICAgICAgIHh5IC0gd3osXHJcbiAgICAgICAgICAgIDEgLSAoeHggKyB6eiksXHJcbiAgICAgICAgICAgIHl6ICsgd3gsXHJcbiAgICAgICAgICAgIHh6ICsgd3ksXHJcbiAgICAgICAgICAgIHl6IC0gd3gsXHJcbiAgICAgICAgICAgIDEgLSAoeHggKyB5eSlcclxuICAgICAgICBdKTtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHRvTWF0NChkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgbWF0NCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCB4ID0gdGhpcy54O1xyXG4gICAgICAgIGNvbnN0IHkgPSB0aGlzLnk7XHJcbiAgICAgICAgY29uc3QgeiA9IHRoaXMuejtcclxuICAgICAgICBjb25zdCB3ID0gdGhpcy53O1xyXG4gICAgICAgIGNvbnN0IHgyID0geCArIHg7XHJcbiAgICAgICAgY29uc3QgeTIgPSB5ICsgeTtcclxuICAgICAgICBjb25zdCB6MiA9IHogKyB6O1xyXG4gICAgICAgIGNvbnN0IHh4ID0geCAqIHgyO1xyXG4gICAgICAgIGNvbnN0IHh5ID0geCAqIHkyO1xyXG4gICAgICAgIGNvbnN0IHh6ID0geCAqIHoyO1xyXG4gICAgICAgIGNvbnN0IHl5ID0geSAqIHkyO1xyXG4gICAgICAgIGNvbnN0IHl6ID0geSAqIHoyO1xyXG4gICAgICAgIGNvbnN0IHp6ID0geiAqIHoyO1xyXG4gICAgICAgIGNvbnN0IHd4ID0gdyAqIHgyO1xyXG4gICAgICAgIGNvbnN0IHd5ID0gdyAqIHkyO1xyXG4gICAgICAgIGNvbnN0IHd6ID0gdyAqIHoyO1xyXG4gICAgICAgIGRlc3QuaW5pdChbXHJcbiAgICAgICAgICAgIDEgLSAoeXkgKyB6eiksXHJcbiAgICAgICAgICAgIHh5ICsgd3osXHJcbiAgICAgICAgICAgIHh6IC0gd3ksXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIHh5IC0gd3osXHJcbiAgICAgICAgICAgIDEgLSAoeHggKyB6eiksXHJcbiAgICAgICAgICAgIHl6ICsgd3gsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIHh6ICsgd3ksXHJcbiAgICAgICAgICAgIHl6IC0gd3gsXHJcbiAgICAgICAgICAgIDEgLSAoeHggKyB5eSksXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIDAsXHJcbiAgICAgICAgICAgIDFcclxuICAgICAgICBdKTtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBkb3QocTEsIHEyKSB7XHJcbiAgICAgICAgcmV0dXJuIHExLnggKiBxMi54ICsgcTEueSAqIHEyLnkgKyBxMS56ICogcTIueiArIHExLncgKiBxMi53O1xyXG4gICAgfVxyXG4gICAgc3RhdGljIHN1bShxMSwgcTIsIGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IG5ldyBxdWF0KCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlc3QueCA9IHExLnggKyBxMi54O1xyXG4gICAgICAgIGRlc3QueSA9IHExLnkgKyBxMi55O1xyXG4gICAgICAgIGRlc3QueiA9IHExLnogKyBxMi56O1xyXG4gICAgICAgIGRlc3QudyA9IHExLncgKyBxMi53O1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgc3RhdGljIHByb2R1Y3QocTEsIHEyLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgcXVhdCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCBxMXggPSBxMS54O1xyXG4gICAgICAgIGNvbnN0IHExeSA9IHExLnk7XHJcbiAgICAgICAgY29uc3QgcTF6ID0gcTEuejtcclxuICAgICAgICBjb25zdCBxMXcgPSBxMS53O1xyXG4gICAgICAgIGNvbnN0IHEyeCA9IHEyLng7XHJcbiAgICAgICAgY29uc3QgcTJ5ID0gcTIueTtcclxuICAgICAgICBjb25zdCBxMnogPSBxMi56O1xyXG4gICAgICAgIGNvbnN0IHEydyA9IHEyLnc7XHJcbiAgICAgICAgZGVzdC54ID0gcTF4ICogcTJ3ICsgcTF3ICogcTJ4ICsgcTF5ICogcTJ6IC0gcTF6ICogcTJ5O1xyXG4gICAgICAgIGRlc3QueSA9IHExeSAqIHEydyArIHExdyAqIHEyeSArIHExeiAqIHEyeCAtIHExeCAqIHEyejtcclxuICAgICAgICBkZXN0LnogPSBxMXogKiBxMncgKyBxMXcgKiBxMnogKyBxMXggKiBxMnkgLSBxMXkgKiBxMng7XHJcbiAgICAgICAgZGVzdC53ID0gcTF3ICogcTJ3IC0gcTF4ICogcTJ4IC0gcTF5ICogcTJ5IC0gcTF6ICogcTJ6O1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgc3RhdGljIGNyb3NzKHExLCBxMiwgZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHF1YXQoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgY29uc3QgcTF4ID0gcTEueDtcclxuICAgICAgICBjb25zdCBxMXkgPSBxMS55O1xyXG4gICAgICAgIGNvbnN0IHExeiA9IHExLno7XHJcbiAgICAgICAgY29uc3QgcTF3ID0gcTEudztcclxuICAgICAgICBjb25zdCBxMnggPSBxMi54O1xyXG4gICAgICAgIGNvbnN0IHEyeSA9IHEyLnk7XHJcbiAgICAgICAgY29uc3QgcTJ6ID0gcTIuejtcclxuICAgICAgICBjb25zdCBxMncgPSBxMi53O1xyXG4gICAgICAgIGRlc3QueCA9IHExdyAqIHEyeiArIHExeiAqIHEydyArIHExeCAqIHEyeSAtIHExeSAqIHEyeDtcclxuICAgICAgICBkZXN0LnkgPSBxMXcgKiBxMncgLSBxMXggKiBxMnggLSBxMXkgKiBxMnkgLSBxMXogKiBxMno7XHJcbiAgICAgICAgZGVzdC56ID0gcTF3ICogcTJ4ICsgcTF4ICogcTJ3ICsgcTF5ICogcTJ6IC0gcTF6ICogcTJ5O1xyXG4gICAgICAgIGRlc3QudyA9IHExdyAqIHEyeSArIHExeSAqIHEydyArIHExeiAqIHEyeCAtIHExeCAqIHEyejtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBzaG9ydE1peChxMSwgcTIsIHRpbWUsIGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IG5ldyBxdWF0KCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmICh0aW1lIDw9IDAuMCkge1xyXG4gICAgICAgICAgICBkZXN0Lnh5encgPSBxMS54eXp3O1xyXG4gICAgICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSBpZiAodGltZSA+PSAxLjApIHtcclxuICAgICAgICAgICAgZGVzdC54eXp3ID0gcTIueHl6dztcclxuICAgICAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxldCBjb3MgPSBxdWF0LmRvdChxMSwgcTIpO1xyXG4gICAgICAgIGNvbnN0IHEyYSA9IHEyLmNvcHkoKTtcclxuICAgICAgICBpZiAoY29zIDwgMC4wKSB7XHJcbiAgICAgICAgICAgIHEyYS5pbnZlcnNlKCk7XHJcbiAgICAgICAgICAgIGNvcyA9IC1jb3M7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxldCBrMDtcclxuICAgICAgICBsZXQgazE7XHJcbiAgICAgICAgaWYgKGNvcyA+IDAuOTk5OSkge1xyXG4gICAgICAgICAgICBrMCA9IDEgLSB0aW1lO1xyXG4gICAgICAgICAgICBrMSA9IDAgKyB0aW1lO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgY29uc3Qgc2luID0gTWF0aC5zcXJ0KDEgLSBjb3MgKiBjb3MpO1xyXG4gICAgICAgICAgICBjb25zdCBhbmdsZSA9IE1hdGguYXRhbjIoc2luLCBjb3MpO1xyXG4gICAgICAgICAgICBjb25zdCBvbmVPdmVyU2luID0gMSAvIHNpbjtcclxuICAgICAgICAgICAgazAgPSBNYXRoLnNpbigoMSAtIHRpbWUpICogYW5nbGUpICogb25lT3ZlclNpbjtcclxuICAgICAgICAgICAgazEgPSBNYXRoLnNpbigoMCArIHRpbWUpICogYW5nbGUpICogb25lT3ZlclNpbjtcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVzdC54ID0gazAgKiBxMS54ICsgazEgKiBxMmEueDtcclxuICAgICAgICBkZXN0LnkgPSBrMCAqIHExLnkgKyBrMSAqIHEyYS55O1xyXG4gICAgICAgIGRlc3QueiA9IGswICogcTEueiArIGsxICogcTJhLno7XHJcbiAgICAgICAgZGVzdC53ID0gazAgKiBxMS53ICsgazEgKiBxMmEudztcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBtaXgocTEsIHEyLCB0aW1lLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgcXVhdCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCBjb3NIYWxmVGhldGEgPSBxMS54ICogcTIueCArIHExLnkgKiBxMi55ICsgcTEueiAqIHEyLnogKyBxMS53ICogcTIudztcclxuICAgICAgICBpZiAoTWF0aC5hYnMoY29zSGFsZlRoZXRhKSA+PSAxLjApIHtcclxuICAgICAgICAgICAgZGVzdC54eXp3ID0gcTEueHl6dztcclxuICAgICAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGNvbnN0IGhhbGZUaGV0YSA9IE1hdGguYWNvcyhjb3NIYWxmVGhldGEpO1xyXG4gICAgICAgIGNvbnN0IHNpbkhhbGZUaGV0YSA9IE1hdGguc3FydCgxLjAgLSBjb3NIYWxmVGhldGEgKiBjb3NIYWxmVGhldGEpO1xyXG4gICAgICAgIGlmIChNYXRoLmFicyhzaW5IYWxmVGhldGEpIDwgMC4wMDEpIHtcclxuICAgICAgICAgICAgZGVzdC54ID0gcTEueCAqIDAuNSArIHEyLnggKiAwLjU7XHJcbiAgICAgICAgICAgIGRlc3QueSA9IHExLnkgKiAwLjUgKyBxMi55ICogMC41O1xyXG4gICAgICAgICAgICBkZXN0LnogPSBxMS56ICogMC41ICsgcTIueiAqIDAuNTtcclxuICAgICAgICAgICAgZGVzdC53ID0gcTEudyAqIDAuNSArIHEyLncgKiAwLjU7XHJcbiAgICAgICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCByYXRpb0EgPSBNYXRoLnNpbigoMSAtIHRpbWUpICogaGFsZlRoZXRhKSAvIHNpbkhhbGZUaGV0YTtcclxuICAgICAgICBjb25zdCByYXRpb0IgPSBNYXRoLnNpbih0aW1lICogaGFsZlRoZXRhKSAvIHNpbkhhbGZUaGV0YTtcclxuICAgICAgICBkZXN0LnggPSBxMS54ICogcmF0aW9BICsgcTIueCAqIHJhdGlvQjtcclxuICAgICAgICBkZXN0LnkgPSBxMS55ICogcmF0aW9BICsgcTIueSAqIHJhdGlvQjtcclxuICAgICAgICBkZXN0LnogPSBxMS56ICogcmF0aW9BICsgcTIueiAqIHJhdGlvQjtcclxuICAgICAgICBkZXN0LncgPSBxMS53ICogcmF0aW9BICsgcTIudyAqIHJhdGlvQjtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBmcm9tQXhpc0FuZ2xlKGF4aXMsIGFuZ2xlLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgcXVhdCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBhbmdsZSAqPSAwLjU7XHJcbiAgICAgICAgY29uc3Qgc2luID0gTWF0aC5zaW4oYW5nbGUpO1xyXG4gICAgICAgIGRlc3QueCA9IGF4aXMueCAqIHNpbjtcclxuICAgICAgICBkZXN0LnkgPSBheGlzLnkgKiBzaW47XHJcbiAgICAgICAgZGVzdC56ID0gYXhpcy56ICogc2luO1xyXG4gICAgICAgIGRlc3QudyA9IE1hdGguY29zKGFuZ2xlKTtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxufVxyXG5xdWF0LmlkZW50aXR5ID0gbmV3IHF1YXQoKS5zZXRJZGVudGl0eSgpO1xyXG4iLCJpbXBvcnQgcXVhdCBmcm9tICcuL3F1YXQnO1xyXG5pbXBvcnQgeyBlcHNpbG9uIH0gZnJvbSAnLi9jb25zdGFudHMnO1xyXG5leHBvcnQgZGVmYXVsdCBjbGFzcyB2ZWMzIHtcclxuICAgIGNvbnN0cnVjdG9yKHZhbHVlcykge1xyXG4gICAgICAgIHRoaXMudmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSgzKTtcclxuICAgICAgICBpZiAodmFsdWVzICE9PSB1bmRlZmluZWQpIHtcclxuICAgICAgICAgICAgdGhpcy54eXogPSB2YWx1ZXM7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgZ2V0IHgoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzBdO1xyXG4gICAgfVxyXG4gICAgZ2V0IHkoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzFdO1xyXG4gICAgfVxyXG4gICAgZ2V0IHooKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzWzJdO1xyXG4gICAgfVxyXG4gICAgZ2V0IHh5KCkge1xyXG4gICAgICAgIHJldHVybiBbdGhpcy52YWx1ZXNbMF0sIHRoaXMudmFsdWVzWzFdXTtcclxuICAgIH1cclxuICAgIGdldCB4eXooKSB7XHJcbiAgICAgICAgcmV0dXJuIFt0aGlzLnZhbHVlc1swXSwgdGhpcy52YWx1ZXNbMV0sIHRoaXMudmFsdWVzWzJdXTtcclxuICAgIH1cclxuICAgIHNldCB4KHZhbHVlKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gPSB2YWx1ZTtcclxuICAgIH1cclxuICAgIHNldCB5KHZhbHVlKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMV0gPSB2YWx1ZTtcclxuICAgIH1cclxuICAgIHNldCB6KHZhbHVlKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMl0gPSB2YWx1ZTtcclxuICAgIH1cclxuICAgIHNldCB4eSh2YWx1ZXMpIHtcclxuICAgICAgICB0aGlzLnZhbHVlc1swXSA9IHZhbHVlc1swXTtcclxuICAgICAgICB0aGlzLnZhbHVlc1sxXSA9IHZhbHVlc1sxXTtcclxuICAgIH1cclxuICAgIHNldCB4eXoodmFsdWVzKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMF0gPSB2YWx1ZXNbMF07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMV0gPSB2YWx1ZXNbMV07XHJcbiAgICAgICAgdGhpcy52YWx1ZXNbMl0gPSB2YWx1ZXNbMl07XHJcbiAgICB9XHJcbiAgICBhdChpbmRleCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnZhbHVlc1tpbmRleF07XHJcbiAgICB9XHJcbiAgICByZXNldCgpIHtcclxuICAgICAgICB0aGlzLnggPSAwO1xyXG4gICAgICAgIHRoaXMueSA9IDA7XHJcbiAgICAgICAgdGhpcy56ID0gMDtcclxuICAgIH1cclxuICAgIGNvcHkoZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHZlYzMoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVzdC54ID0gdGhpcy54O1xyXG4gICAgICAgIGRlc3QueSA9IHRoaXMueTtcclxuICAgICAgICBkZXN0LnogPSB0aGlzLno7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbiAgICBuZWdhdGUoZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gdGhpcztcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVzdC54ID0gLXRoaXMueDtcclxuICAgICAgICBkZXN0LnkgPSAtdGhpcy55O1xyXG4gICAgICAgIGRlc3QueiA9IC10aGlzLno7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbiAgICBlcXVhbHModmVjdG9yLCB0aHJlc2hvbGQgPSBlcHNpbG9uKSB7XHJcbiAgICAgICAgaWYgKE1hdGguYWJzKHRoaXMueCAtIHZlY3Rvci54KSA+IHRocmVzaG9sZCkge1xyXG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmIChNYXRoLmFicyh0aGlzLnkgLSB2ZWN0b3IueSkgPiB0aHJlc2hvbGQpIHtcclxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoTWF0aC5hYnModGhpcy56IC0gdmVjdG9yLnopID4gdGhyZXNob2xkKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgICBsZW5ndGgoKSB7XHJcbiAgICAgICAgcmV0dXJuIE1hdGguc3FydCh0aGlzLnNxdWFyZWRMZW5ndGgoKSk7XHJcbiAgICB9XHJcbiAgICBzcXVhcmVkTGVuZ3RoKCkge1xyXG4gICAgICAgIGNvbnN0IHggPSB0aGlzLng7XHJcbiAgICAgICAgY29uc3QgeSA9IHRoaXMueTtcclxuICAgICAgICBjb25zdCB6ID0gdGhpcy56O1xyXG4gICAgICAgIHJldHVybiB4ICogeCArIHkgKiB5ICsgeiAqIHo7XHJcbiAgICB9XHJcbiAgICBhZGQodmVjdG9yKSB7XHJcbiAgICAgICAgdGhpcy54ICs9IHZlY3Rvci54O1xyXG4gICAgICAgIHRoaXMueSArPSB2ZWN0b3IueTtcclxuICAgICAgICB0aGlzLnogKz0gdmVjdG9yLno7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBzdWJ0cmFjdCh2ZWN0b3IpIHtcclxuICAgICAgICB0aGlzLnggLT0gdmVjdG9yLng7XHJcbiAgICAgICAgdGhpcy55IC09IHZlY3Rvci55O1xyXG4gICAgICAgIHRoaXMueiAtPSB2ZWN0b3IuejtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIG11bHRpcGx5KHZlY3Rvcikge1xyXG4gICAgICAgIHRoaXMueCAqPSB2ZWN0b3IueDtcclxuICAgICAgICB0aGlzLnkgKj0gdmVjdG9yLnk7XHJcbiAgICAgICAgdGhpcy56ICo9IHZlY3Rvci56O1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgZGl2aWRlKHZlY3Rvcikge1xyXG4gICAgICAgIHRoaXMueCAvPSB2ZWN0b3IueDtcclxuICAgICAgICB0aGlzLnkgLz0gdmVjdG9yLnk7XHJcbiAgICAgICAgdGhpcy56IC89IHZlY3Rvci56O1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgc2NhbGUodmFsdWUsIGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IHRoaXM7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlc3QueCAqPSB2YWx1ZTtcclxuICAgICAgICBkZXN0LnkgKj0gdmFsdWU7XHJcbiAgICAgICAgZGVzdC56ICo9IHZhbHVlO1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgbm9ybWFsaXplKGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IHRoaXM7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxldCBsZW5ndGggPSB0aGlzLmxlbmd0aCgpO1xyXG4gICAgICAgIGlmIChsZW5ndGggPT09IDEpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmIChsZW5ndGggPT09IDApIHtcclxuICAgICAgICAgICAgZGVzdC54ID0gMDtcclxuICAgICAgICAgICAgZGVzdC55ID0gMDtcclxuICAgICAgICAgICAgZGVzdC56ID0gMDtcclxuICAgICAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxlbmd0aCA9IDEuMCAvIGxlbmd0aDtcclxuICAgICAgICBkZXN0LnggKj0gbGVuZ3RoO1xyXG4gICAgICAgIGRlc3QueSAqPSBsZW5ndGg7XHJcbiAgICAgICAgZGVzdC56ICo9IGxlbmd0aDtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIG11bHRpcGx5QnlNYXQzKG1hdHJpeCwgZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gdGhpcztcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIG1hdHJpeC5tdWx0aXBseVZlYzModGhpcywgZGVzdCk7XHJcbiAgICB9XHJcbiAgICBtdWx0aXBseUJ5UXVhdChxdWF0ZXJuaW9uLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSB0aGlzO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gcXVhdGVybmlvbi5tdWx0aXBseVZlYzModGhpcywgZGVzdCk7XHJcbiAgICB9XHJcbiAgICB0b1F1YXQoZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHF1YXQoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgY29uc3QgYyA9IG5ldyB2ZWMzKCk7XHJcbiAgICAgICAgY29uc3QgcyA9IG5ldyB2ZWMzKCk7XHJcbiAgICAgICAgYy54ID0gTWF0aC5jb3ModGhpcy54ICogMC41KTtcclxuICAgICAgICBzLnggPSBNYXRoLnNpbih0aGlzLnggKiAwLjUpO1xyXG4gICAgICAgIGMueSA9IE1hdGguY29zKHRoaXMueSAqIDAuNSk7XHJcbiAgICAgICAgcy55ID0gTWF0aC5zaW4odGhpcy55ICogMC41KTtcclxuICAgICAgICBjLnogPSBNYXRoLmNvcyh0aGlzLnogKiAwLjUpO1xyXG4gICAgICAgIHMueiA9IE1hdGguc2luKHRoaXMueiAqIDAuNSk7XHJcbiAgICAgICAgZGVzdC54ID0gcy54ICogYy55ICogYy56IC0gYy54ICogcy55ICogcy56O1xyXG4gICAgICAgIGRlc3QueSA9IGMueCAqIHMueSAqIGMueiArIHMueCAqIGMueSAqIHMuejtcclxuICAgICAgICBkZXN0LnogPSBjLnggKiBjLnkgKiBzLnogLSBzLnggKiBzLnkgKiBjLno7XHJcbiAgICAgICAgZGVzdC53ID0gYy54ICogYy55ICogYy56ICsgcy54ICogcy55ICogcy56O1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgc3RhdGljIGNyb3NzKHZlY3RvciwgdmVjdG9yMiwgZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHZlYzMoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgY29uc3QgeCA9IHZlY3Rvci54O1xyXG4gICAgICAgIGNvbnN0IHkgPSB2ZWN0b3IueTtcclxuICAgICAgICBjb25zdCB6ID0gdmVjdG9yLno7XHJcbiAgICAgICAgY29uc3QgeDIgPSB2ZWN0b3IyLng7XHJcbiAgICAgICAgY29uc3QgeTIgPSB2ZWN0b3IyLnk7XHJcbiAgICAgICAgY29uc3QgejIgPSB2ZWN0b3IyLno7XHJcbiAgICAgICAgZGVzdC54ID0geSAqIHoyIC0geiAqIHkyO1xyXG4gICAgICAgIGRlc3QueSA9IHogKiB4MiAtIHggKiB6MjtcclxuICAgICAgICBkZXN0LnogPSB4ICogeTIgLSB5ICogeDI7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbiAgICBzdGF0aWMgZG90KHZlY3RvciwgdmVjdG9yMikge1xyXG4gICAgICAgIGNvbnN0IHggPSB2ZWN0b3IueDtcclxuICAgICAgICBjb25zdCB5ID0gdmVjdG9yLnk7XHJcbiAgICAgICAgY29uc3QgeiA9IHZlY3Rvci56O1xyXG4gICAgICAgIGNvbnN0IHgyID0gdmVjdG9yMi54O1xyXG4gICAgICAgIGNvbnN0IHkyID0gdmVjdG9yMi55O1xyXG4gICAgICAgIGNvbnN0IHoyID0gdmVjdG9yMi56O1xyXG4gICAgICAgIHJldHVybiB4ICogeDIgKyB5ICogeTIgKyB6ICogejI7XHJcbiAgICB9XHJcbiAgICBzdGF0aWMgZGlzdGFuY2UodmVjdG9yLCB2ZWN0b3IyKSB7XHJcbiAgICAgICAgY29uc3QgeCA9IHZlY3RvcjIueCAtIHZlY3Rvci54O1xyXG4gICAgICAgIGNvbnN0IHkgPSB2ZWN0b3IyLnkgLSB2ZWN0b3IueTtcclxuICAgICAgICBjb25zdCB6ID0gdmVjdG9yMi56IC0gdmVjdG9yLno7XHJcbiAgICAgICAgcmV0dXJuIE1hdGguc3FydCh0aGlzLnNxdWFyZWREaXN0YW5jZSh2ZWN0b3IsIHZlY3RvcjIpKTtcclxuICAgIH1cclxuICAgIHN0YXRpYyBzcXVhcmVkRGlzdGFuY2UodmVjdG9yLCB2ZWN0b3IyKSB7XHJcbiAgICAgICAgY29uc3QgeCA9IHZlY3RvcjIueCAtIHZlY3Rvci54O1xyXG4gICAgICAgIGNvbnN0IHkgPSB2ZWN0b3IyLnkgLSB2ZWN0b3IueTtcclxuICAgICAgICBjb25zdCB6ID0gdmVjdG9yMi56IC0gdmVjdG9yLno7XHJcbiAgICAgICAgcmV0dXJuIHggKiB4ICsgeSAqIHkgKyB6ICogejtcclxuICAgIH1cclxuICAgIHN0YXRpYyBkaXJlY3Rpb24odmVjdG9yLCB2ZWN0b3IyLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgdmVjMygpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCB4ID0gdmVjdG9yLnggLSB2ZWN0b3IyLng7XHJcbiAgICAgICAgY29uc3QgeSA9IHZlY3Rvci55IC0gdmVjdG9yMi55O1xyXG4gICAgICAgIGNvbnN0IHogPSB2ZWN0b3IueiAtIHZlY3RvcjIuejtcclxuICAgICAgICBsZXQgbGVuZ3RoID0gTWF0aC5zcXJ0KHggKiB4ICsgeSAqIHkgKyB6ICogeik7XHJcbiAgICAgICAgaWYgKGxlbmd0aCA9PT0gMCkge1xyXG4gICAgICAgICAgICBkZXN0LnggPSAwO1xyXG4gICAgICAgICAgICBkZXN0LnkgPSAwO1xyXG4gICAgICAgICAgICBkZXN0LnogPSAwO1xyXG4gICAgICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgICAgICB9XHJcbiAgICAgICAgbGVuZ3RoID0gMSAvIGxlbmd0aDtcclxuICAgICAgICBkZXN0LnggPSB4ICogbGVuZ3RoO1xyXG4gICAgICAgIGRlc3QueSA9IHkgKiBsZW5ndGg7XHJcbiAgICAgICAgZGVzdC56ID0geiAqIGxlbmd0aDtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBtaXgodmVjdG9yLCB2ZWN0b3IyLCB0aW1lLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgdmVjMygpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBkZXN0LnggPSB2ZWN0b3IueCArIHRpbWUgKiAodmVjdG9yMi54IC0gdmVjdG9yLngpO1xyXG4gICAgICAgIGRlc3QueSA9IHZlY3Rvci55ICsgdGltZSAqICh2ZWN0b3IyLnkgLSB2ZWN0b3IueSk7XHJcbiAgICAgICAgZGVzdC56ID0gdmVjdG9yLnogKyB0aW1lICogKHZlY3RvcjIueiAtIHZlY3Rvci56KTtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBzdW0odmVjdG9yLCB2ZWN0b3IyLCBkZXN0KSB7XHJcbiAgICAgICAgaWYgKCFkZXN0KSB7XHJcbiAgICAgICAgICAgIGRlc3QgPSBuZXcgdmVjMygpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBkZXN0LnggPSB2ZWN0b3IueCArIHZlY3RvcjIueDtcclxuICAgICAgICBkZXN0LnkgPSB2ZWN0b3IueSArIHZlY3RvcjIueTtcclxuICAgICAgICBkZXN0LnogPSB2ZWN0b3IueiArIHZlY3RvcjIuejtcclxuICAgICAgICByZXR1cm4gZGVzdDtcclxuICAgIH1cclxuICAgIHN0YXRpYyBkaWZmZXJlbmNlKHZlY3RvciwgdmVjdG9yMiwgZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHZlYzMoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVzdC54ID0gdmVjdG9yLnggLSB2ZWN0b3IyLng7XHJcbiAgICAgICAgZGVzdC55ID0gdmVjdG9yLnkgLSB2ZWN0b3IyLnk7XHJcbiAgICAgICAgZGVzdC56ID0gdmVjdG9yLnogLSB2ZWN0b3IyLno7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbiAgICBzdGF0aWMgcHJvZHVjdCh2ZWN0b3IsIHZlY3RvcjIsIGRlc3QpIHtcclxuICAgICAgICBpZiAoIWRlc3QpIHtcclxuICAgICAgICAgICAgZGVzdCA9IG5ldyB2ZWMzKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlc3QueCA9IHZlY3Rvci54ICogdmVjdG9yMi54O1xyXG4gICAgICAgIGRlc3QueSA9IHZlY3Rvci55ICogdmVjdG9yMi55O1xyXG4gICAgICAgIGRlc3QueiA9IHZlY3Rvci56ICogdmVjdG9yMi56O1xyXG4gICAgICAgIHJldHVybiBkZXN0O1xyXG4gICAgfVxyXG4gICAgc3RhdGljIHF1b3RpZW50KHZlY3RvciwgdmVjdG9yMiwgZGVzdCkge1xyXG4gICAgICAgIGlmICghZGVzdCkge1xyXG4gICAgICAgICAgICBkZXN0ID0gbmV3IHZlYzMoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVzdC54ID0gdmVjdG9yLnggLyB2ZWN0b3IyLng7XHJcbiAgICAgICAgZGVzdC55ID0gdmVjdG9yLnkgLyB2ZWN0b3IyLnk7XHJcbiAgICAgICAgZGVzdC56ID0gdmVjdG9yLnogLyB2ZWN0b3IyLno7XHJcbiAgICAgICAgcmV0dXJuIGRlc3Q7XHJcbiAgICB9XHJcbn1cclxudmVjMy56ZXJvID0gbmV3IHZlYzMoWzAsIDAsIDBdKTtcclxudmVjMy5vbmUgPSBuZXcgdmVjMyhbMSwgMSwgMV0pO1xyXG52ZWMzLnVwID0gbmV3IHZlYzMoWzAsIDEsIDBdKTtcclxudmVjMy5yaWdodCA9IG5ldyB2ZWMzKFsxLCAwLCAwXSk7XHJcbnZlYzMuZm9yd2FyZCA9IG5ldyB2ZWMzKFswLCAwLCAxXSk7XHJcbiIsImV4cG9ydCBjbGFzcyBCYXNlT3V0cHV0IHtcclxuICAgIHNwZWFrKHRleHQpIHtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgICBzdG9wKCkge1xyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIHNldE9wdGlvbnMob3B0aW9ucykge1xyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxufVxyXG4iLCJpbXBvcnQgeyBCYXNlT3V0cHV0IH0gZnJvbSAnLi9iYXNlLW91dHB1dCc7XHJcbmV4cG9ydCBjbGFzcyBBcmlhT3V0cHV0IGV4dGVuZHMgQmFzZU91dHB1dCB7XHJcbiAgICBjb25zdHJ1Y3RvcihvcHRpb25zID0ge30pIHtcclxuICAgICAgICBzdXBlcigpO1xyXG4gICAgICAgIHRoaXMudGltZW91dCA9IDEwMDtcclxuICAgICAgICB0aGlzLnRpbWVvdXQgPSBvcHRpb25zLnRpbWVvdXQgfHwgMTAwO1xyXG4gICAgICAgIHRoaXMuaW5pdCgpO1xyXG4gICAgfVxyXG4gICAgaW5pdCgpIHtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xyXG4gICAgICAgIHRoaXMuY29udGFpbmVyLnNldEF0dHJpYnV0ZSgnYXJpYS1saXZlJywgJ3BvbGl0ZScpO1xyXG4gICAgICAgIHRoaXMuc3BlZWNoRGlzcGxheSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xyXG4gICAgICAgIHRoaXMuc3BlZWNoRGlzcGxheS5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGl2ZScsICdwb2xpdGUnKTtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lci5hcHBlbmQodGhpcy5zcGVlY2hEaXNwbGF5KTtcclxuICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHRoaXMuY29udGFpbmVyKTtcclxuICAgICAgICBkb2N1bWVudC5ib2R5Lmluc2VydEJlZm9yZSh0aGlzLmNvbnRhaW5lciwgZG9jdW1lbnQuYm9keS5maXJzdENoaWxkKTtcclxuICAgIH1cclxuICAgIHNwZWFrKHRleHQpIHtcclxuICAgICAgICB0aGlzLmNsZWFyRGlzcGxheSgpO1xyXG4gICAgICAgIGNvbnN0IG5vZGUgPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZSh0ZXh0KTtcclxuICAgICAgICBjb25zdCBwYXJhID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgncCcpO1xyXG4gICAgICAgIHBhcmEuYXBwZW5kQ2hpbGQobm9kZSk7XHJcbiAgICAgICAgdGhpcy5zcGVlY2hEaXNwbGF5LmFwcGVuZENoaWxkKHBhcmEpO1xyXG4gICAgICAgIHNldFRpbWVvdXQodGhpcy5jbGVhckRpc3BsYXkuYmluZCh0aGlzKSwgdGhpcy50aW1lb3V0KTtcclxuICAgIH1cclxuICAgIHN0b3AoKSB7XHJcbiAgICAgICAgdGhpcy5jbGVhckRpc3BsYXkoKTtcclxuICAgIH1cclxuICAgIGNsZWFyRGlzcGxheSgpIHtcclxuICAgICAgICB0aGlzLnNwZWVjaERpc3BsYXkuaW5uZXJIVE1MID0gJyc7XHJcbiAgICB9XHJcbn1cclxuIiwiaW1wb3J0IHsgQmFzZU91dHB1dCB9IGZyb20gJy4vYmFzZS1vdXRwdXQnO1xyXG5leHBvcnQgY2xhc3MgV2ViVFRTT3V0cHV0IGV4dGVuZHMgQmFzZU91dHB1dCB7XHJcbn1cclxuIiwiaW1wb3J0IHsgY3JlYXRlT3V0cHV0IH0gZnJvbSAnLi9vdXRwdXQtZmFjdG9yeSc7XHJcbmV4cG9ydCBjbGFzcyBUVFMge1xyXG4gICAgY29uc3RydWN0b3Iob3V0cHV0ID0gY3JlYXRlT3V0cHV0KCkpIHtcclxuICAgICAgICB0aGlzLm91dHB1dCA9IG91dHB1dDtcclxuICAgIH1cclxuICAgIHNwZWFrKHRleHQpIHtcclxuICAgICAgICB0aGlzLm91dHB1dC5zcGVhayh0ZXh0KTtcclxuICAgIH1cclxuICAgIHN0b3AoKSB7XHJcbiAgICAgICAgdGhpcy5vdXRwdXQuc3RvcCgpO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IEJhc2VPdXRwdXQgfSBmcm9tICcuL291dHB1dHMvYmFzZS1vdXRwdXQnO1xyXG5pbXBvcnQgeyBBcmlhT3V0cHV0IH0gZnJvbSAnLi9vdXRwdXRzL2FyaWEnO1xyXG5pbXBvcnQgeyBXZWJUVFNPdXRwdXQgfSBmcm9tICcuL291dHB1dHMvd2VidHRzJztcclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU91dHB1dChrZXkgPSAnYXJpYScpIHtcclxuICAgIHN3aXRjaCAoa2V5KSB7XHJcbiAgICAgICAgY2FzZSAnYXJpYSc6XHJcbiAgICAgICAgICAgIHJldHVybiBBcmlhT3V0cHV0O1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICd3ZWJ0dHMnOlxyXG4gICAgICAgICAgICByZXR1cm4gV2ViVFRTT3V0cHV0O1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgICAgICBkZWZhdWx0OlxyXG4gICAgICAgICAgICByZXR1cm4gQXJpYU91dHB1dDtcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICB9XHJcbn1cclxuZXhwb3J0IHsgV2ViVFRTT3V0cHV0LCBBcmlhT3V0cHV0LCBCYXNlT3V0cHV0IH07XHJcbiIsImV4cG9ydCBjbGFzcyBTb3VuZE1hbmFnZXIge1xyXG4gICAgY29uc3RydWN0b3Ioc291bmRTZXQgPSBudWxsKSB7XHJcbiAgICAgICAgdGhpcy5zb3VuZFNldCA9IG51bGw7XHJcbiAgICAgICAgdGhpcy5kYXRhID0gbmV3IE1hcCgpO1xyXG4gICAgICAgIHRoaXMuc291bmRTZXQgPSBzb3VuZFNldDtcclxuICAgIH1cclxuICAgIHNldFNvdW5kU2V0KHNvdW5kU2V0KSB7XHJcbiAgICAgICAgdGhpcy5zb3VuZFNldCA9IHNvdW5kU2V0O1xyXG4gICAgfVxyXG4gICAgaGFuZGxlU291bmQodHlwZSwgZGF0YSA9IG51bGwpIHtcclxuICAgICAgICBzd2l0Y2ggKHR5cGUpIHtcclxuICAgICAgICAgICAgY2FzZSAnZWRpdCc6XHJcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZUVkaXRTb3VuZChkYXRhKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICBjYXNlICdzbGlkZXInOlxyXG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVTbGlkZXJTb3VuZChkYXRhKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICBjYXNlICdzZWxlY3Rvcic6XHJcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZVNlbGVjdG9yU291bmQoZGF0YSk7XHJcbiAgICAgICAgICAgICAgICBicmVhaztcclxuICAgICAgICAgICAgY2FzZSAnY2hlY2tib3gnOlxyXG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVDaGVja2JveFNvdW5kKGRhdGEpO1xyXG4gICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIGNhc2UgJ2ZvY3VzJzpcclxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlRm9jdXNTb3VuZCgpO1xyXG4gICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIGNhc2UgJ2Nob29zZSc6XHJcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZUNob29zZVNvdW5kKCk7XHJcbiAgICAgICAgICAgICAgICBicmVhaztcclxuICAgICAgICAgICAgY2FzZSAnb3Blbic6XHJcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZU9wZW5Tb3VuZCgpO1xyXG4gICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIGNhc2UgJ2Nsb3NlJzpcclxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlQ2xvc2VTb3VuZCgpO1xyXG4gICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICBicmVhaztcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBoYW5kbGVFZGl0U291bmQoZGF0YSkge1xyXG4gICAgICAgIGNvbnN0IHByZXZEYXRhID0gdGhpcy5kYXRhLmdldCgnZWRpdCcpIHx8ICcnO1xyXG4gICAgICAgIGlmIChkYXRhLmxlbmd0aCA8PSBwcmV2RGF0YS5sZW5ndGgpIHtcclxuICAgICAgICAgICAgdGhpcy5zb3VuZFNldC5kZWxldGUgJiYgdGhpcy5zb3VuZFNldC5kZWxldGUucGxheSgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgdGhpcy5zb3VuZFNldC5jaGFyICYmIHRoaXMuc291bmRTZXQuY2hhci5wbGF5KCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMuZGF0YS5zZXQoJ2VkaXQnLCBkYXRhKTtcclxuICAgIH1cclxuICAgIGhhbmRsZVNlbGVjdG9yU291bmQoZGF0YSkge1xyXG4gICAgICAgIHRoaXMuc291bmRTZXQuc2Nyb2xsZXIgJiYgdGhpcy5zb3VuZFNldC5zY3JvbGxlci5wbGF5KCk7XHJcbiAgICB9XHJcbiAgICBoYW5kbGVTbGlkZXJTb3VuZChkYXRhKSB7XHJcbiAgICAgICAgY29uc3QgcHJldkRhdGEgPSB0aGlzLmRhdGEuZ2V0KCdzbGlkZXInKTtcclxuICAgICAgICBpZiAoZGF0YSA8IHByZXZEYXRhKSB7XHJcbiAgICAgICAgICAgIHRoaXMuc291bmRTZXQuc2xpZGVyTGVmdCAmJiB0aGlzLnNvdW5kU2V0LnNsaWRlckxlZnQucGxheSgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgdGhpcy5zb3VuZFNldC5zbGlkZXJSaWdodCAmJiB0aGlzLnNvdW5kU2V0LnNsaWRlclJpZ2h0LnBsYXkoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgdGhpcy5kYXRhLnNldCgnc2xpZGVyJywgZGF0YSk7XHJcbiAgICB9XHJcbiAgICBoYW5kbGVGb2N1c1NvdW5kKCkge1xyXG4gICAgICAgIHRoaXMuc291bmRTZXQubW92ZSAmJiB0aGlzLnNvdW5kU2V0Lm1vdmUucGxheSgpO1xyXG4gICAgfVxyXG4gICAgaGFuZGxlT3BlblNvdW5kKCkge1xyXG4gICAgICAgIHRoaXMuc291bmRTZXQub3BlbiAmJiB0aGlzLnNvdW5kU2V0Lm9wZW4ucGxheSgpO1xyXG4gICAgfVxyXG4gICAgaGFuZGxlQ2xvc2VTb3VuZCgpIHtcclxuICAgICAgICB0aGlzLnNvdW5kU2V0LmNsb3NlICYmIHRoaXMuc291bmRTZXQuY2xvc2UucGxheSgpO1xyXG4gICAgfVxyXG4gICAgaGFuZGxlQ2hvb3NlU291bmQoKSB7XHJcbiAgICAgICAgdGhpcy5zb3VuZFNldC5jaG9vc2UgJiYgdGhpcy5zb3VuZFNldC5jaG9vc2UucGxheSgpO1xyXG4gICAgfVxyXG4gICAgaGFuZGxlQ2hlY2tib3hTb3VuZChkYXRhKSB7XHJcbiAgICAgICAgaWYgKGRhdGEgPT09IHRydWUpIHtcclxuICAgICAgICAgICAgdGhpcy5zb3VuZFNldC5jaGVja2VkICYmIHRoaXMuc291bmRTZXQuY2hlY2tlZC5wbGF5KCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICB0aGlzLnNvdW5kU2V0LnVuY2hlY2tlZCAmJiB0aGlzLnNvdW5kU2V0LnVuY2hlY2tlZC5wbGF5KCk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcbiIsImV4cG9ydCBjbGFzcyBLZXlib2FyZE1hbmFnZXIge1xyXG4gICAgY29uc3RydWN0b3IobWVudSkge1xyXG4gICAgICAgIHRoaXMubWVudSA9IG1lbnU7XHJcbiAgICB9XHJcbiAgICBpbml0KCkge1xyXG4gICAgICAgIHRoaXMubWVudVxyXG4gICAgICAgICAgICAuZ2V0Q29udGFpbmVyKClcclxuICAgICAgICAgICAgLmFkZEV2ZW50TGlzdGVuZXIoJ2tleWRvd24nLCB0aGlzLmhhbmRsZXIuYmluZCh0aGlzKSk7XHJcbiAgICAgICAgLy8gVGhpcyB0cmljayBsZXQncyB1cyBkZXRlY3QgdGhlIHByZXNzIG9mIHRoZSBiYWNrIG9yIGZvcndhcmQgYnV0dG9ucyB0byBleGl0IG91dCBvZiB0aGUgbWVudS5cclxuICAgICAgICB3aW5kb3cub25wb3BzdGF0ZSA9ICgpID0+IHRoaXMubWVudS5jbGlja0NhbmNlbEFjdGlvbigpO1xyXG4gICAgfVxyXG4gICAgaGFuZGxlcihldmVudCkge1xyXG4gICAgICAgIHN3aXRjaCAoZXZlbnQua2V5KSB7XHJcbiAgICAgICAgICAgIGNhc2UgJ0Fycm93RG93bic6XHJcbiAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5tZW51LmZvY3VzTmV4dCgpO1xyXG4gICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIGNhc2UgJ0Fycm93VXAnOlxyXG4gICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgICAgICAgICAgICAgIHRoaXMubWVudS5mb2N1c1ByZXZpb3VzKCk7XHJcbiAgICAgICAgICAgICAgICBicmVhaztcclxuICAgICAgICAgICAgY2FzZSAnRW50ZXInOlxyXG4gICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgICAgICAgICAgICAgIHRoaXMubWVudS5jbGlja0RlZmF1bHRBY3Rpb24oKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICBjYXNlICdFc2NhcGUnOlxyXG4gICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgICAgICAgICAgICAgIHRoaXMubWVudS5jbGlja0NhbmNlbEFjdGlvbigpO1xyXG4gICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICAgICAgICBicmVhaztcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICByZWxlYXNlKCkge1xyXG4gICAgICAgIHRoaXMubWVudVxyXG4gICAgICAgICAgICAuZ2V0Q29udGFpbmVyKClcclxuICAgICAgICAgICAgLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2tleWRvd24nLCB0aGlzLmhhbmRsZXIuYmluZCh0aGlzKSk7XHJcbiAgICAgICAgd2luZG93Lm9ucG9wc3RhdGUgPSBudWxsO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCAqIGFzIEV2ZW50RW1pdHRlciBmcm9tICdldmVudGVtaXR0ZXIzJztcclxuZXhwb3J0IGNsYXNzIEJhc2VJdGVtIGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcclxuICAgIGNvbnN0cnVjdG9yKGlkLCB0aXRsZSkge1xyXG4gICAgICAgIHN1cGVyKCk7XHJcbiAgICAgICAgdGhpcy5pZCA9IGlkO1xyXG4gICAgICAgIHRoaXMudGl0bGUgPSB0aXRsZTtcclxuICAgIH1cclxuICAgIGdldERPTU5vZGUoKSB7XHJcbiAgICAgICAgbGV0IG5vZGUgPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZSh0aGlzLnRpdGxlKTtcclxuICAgICAgICBsZXQgZWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xyXG4gICAgICAgIGVsZW1lbnQuYXBwZW5kQ2hpbGQobm9kZSk7XHJcbiAgICAgICAgcmV0dXJuIGVsZW1lbnQ7XHJcbiAgICB9XHJcbiAgICBnZXRDb250ZW50cygpIHtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgICBvbkZvY3VzKGV2ZW50KSB7XHJcbiAgICAgICAgdGhpcy5lbWl0KCdmb2N1cycsIHRoaXMuaWQpO1xyXG4gICAgfVxyXG4gICAgZm9jdXMoKSB7XHJcbiAgICAgICAgdGhpcy5jb250YWluZXIgJiYgdGhpcy5jb250YWluZXIuZm9jdXMoKTtcclxuICAgIH1cclxuICAgIGNsaWNrKCkge1xyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIGdldElEKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLmlkO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IEJhc2VJdGVtIH0gZnJvbSAnLi9iYXNlLWl0ZW0nO1xyXG5leHBvcnQgY2xhc3MgRWRpdEl0ZW0gZXh0ZW5kcyBCYXNlSXRlbSB7XHJcbiAgICBjb25zdHJ1Y3RvcihpZCwgdGl0bGUsIGluaXRpYWxUZXh0LCBpc1Bhc3N3b3JkID0gZmFsc2UpIHtcclxuICAgICAgICBzdXBlcihpZCwgdGl0bGUpO1xyXG4gICAgICAgIHRoaXMuaW5pdGlhbFRleHQgPSBpbml0aWFsVGV4dDtcclxuICAgICAgICB0aGlzLmlzUGFzc3dvcmQgPSBpc1Bhc3N3b3JkO1xyXG4gICAgICAgIHRoaXMuY29udGVudHMgPSBpbml0aWFsVGV4dDtcclxuICAgIH1cclxuICAgIGdldERPTU5vZGUoKSB7XHJcbiAgICAgICAgY29uc3Qgbm9kZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xyXG4gICAgICAgIGNvbnN0IGxhYmVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnbGFiZWwnKTtcclxuICAgICAgICBsYWJlbC5zZXRBdHRyaWJ1dGUoJ2ZvcicsIGBlZGl0XyR7dGhpcy5pZH1gKTtcclxuICAgICAgICBsYWJlbC50ZXh0Q29udGVudCA9IHRoaXMudGl0bGU7XHJcbiAgICAgICAgY29uc3QgZWRpdEZpZWxkID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW5wdXQnKTtcclxuICAgICAgICBlZGl0RmllbGQuaWQgPSBgZWRpdF8ke3RoaXMuaWR9YDtcclxuICAgICAgICBlZGl0RmllbGQudmFsdWUgPSB0aGlzLmNvbnRlbnRzO1xyXG4gICAgICAgIGVkaXRGaWVsZC5hZGRFdmVudExpc3RlbmVyKCdrZXlkb3duJywgdGhpcy5vbkNoYW5nZS5iaW5kKHRoaXMpKTtcclxuICAgICAgICBlZGl0RmllbGQuYWRkRXZlbnRMaXN0ZW5lcignZm9jdXMnLCB0aGlzLm9uRm9jdXMuYmluZCh0aGlzKSk7XHJcbiAgICAgICAgaWYgKHRoaXMuaXNQYXNzd29yZCkge1xyXG4gICAgICAgICAgICBlZGl0RmllbGQudHlwZSA9ICdwYXNzd29yZCc7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIG5vZGUuYXBwZW5kQ2hpbGQobGFiZWwpO1xyXG4gICAgICAgIG5vZGUuYXBwZW5kQ2hpbGQoZWRpdEZpZWxkKTtcclxuICAgICAgICBub2RlLmFkZEV2ZW50TGlzdGVuZXIoJ2ZvY3VzJywgdGhpcy5vbkZvY3VzLmJpbmQodGhpcykpO1xyXG4gICAgICAgIHRoaXMuZWRpdEZpZWxkID0gZWRpdEZpZWxkO1xyXG4gICAgICAgIHRoaXMubGFiZWwgPSBsYWJlbDtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IG5vZGU7XHJcbiAgICAgICAgcmV0dXJuIG5vZGU7XHJcbiAgICB9XHJcbiAgICBnZXRDb250ZW50cygpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5lZGl0RmllbGQudmFsdWU7XHJcbiAgICB9XHJcbiAgICBvbkNoYW5nZShldmVudCkge1xyXG4gICAgICAgIHRoaXMuZW1pdCgndXBkYXRlJywge1xyXG4gICAgICAgICAgICB0eXBlOiAnZWRpdCcsXHJcbiAgICAgICAgICAgIHZhbHVlOiB0aGlzLmVkaXRGaWVsZC52YWx1ZVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgZm9jdXMoKSB7XHJcbiAgICAgICAgdGhpcy5lZGl0RmllbGQgJiYgdGhpcy5lZGl0RmllbGQuZm9jdXMoKTtcclxuICAgIH1cclxufVxyXG4iLCJpbXBvcnQgeyBCYXNlSXRlbSB9IGZyb20gJy4vYmFzZS1pdGVtJztcclxuZXhwb3J0IGNsYXNzIE1lbnVJdGVtIGV4dGVuZHMgQmFzZUl0ZW0ge1xyXG4gICAgY29uc3RydWN0b3IoaWQsIHRpdGxlKSB7XHJcbiAgICAgICAgc3VwZXIoaWQsIHRpdGxlKTtcclxuICAgIH1cclxuICAgIGdldERPTU5vZGUoKSB7XHJcbiAgICAgICAgY29uc3QgY29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XHJcbiAgICAgICAgY29uc3QgYnV0dG9uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYnV0dG9uJyk7XHJcbiAgICAgICAgYnV0dG9uLnRleHRDb250ZW50ID0gdGhpcy50aXRsZTtcclxuICAgICAgICBidXR0b24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCB0aGlzLmhhbmRsZUNsaWNrLmJpbmQodGhpcykpO1xyXG4gICAgICAgIGJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdmb2N1cycsIHRoaXMub25Gb2N1cy5iaW5kKHRoaXMpKTtcclxuICAgICAgICBjb250YWluZXIuYXBwZW5kQ2hpbGQoYnV0dG9uKTtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IGNvbnRhaW5lcjtcclxuICAgICAgICB0aGlzLmJ1dHRvbiA9IGJ1dHRvbjtcclxuICAgICAgICByZXR1cm4gY29udGFpbmVyO1xyXG4gICAgfVxyXG4gICAgZ2V0Q29udGVudHMoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuaWQ7XHJcbiAgICB9XHJcbiAgICBoYW5kbGVDbGljayhldmVudCkge1xyXG4gICAgICAgIHRoaXMuZW1pdCgnY2hvb3NlJywgdGhpcy5pZCk7XHJcbiAgICB9XHJcbiAgICBmb2N1cygpIHtcclxuICAgICAgICB0aGlzLmJ1dHRvbiAmJiB0aGlzLmJ1dHRvbi5mb2N1cygpO1xyXG4gICAgfVxyXG4gICAgY2xpY2soKSB7XHJcbiAgICAgICAgdGhpcy5idXR0b24uY2xpY2soKTtcclxuICAgIH1cclxufVxyXG4iLCJpbXBvcnQgeyBCYXNlSXRlbSB9IGZyb20gJy4vYmFzZS1pdGVtJztcclxuZXhwb3J0IGNsYXNzIFNlbGVjdG9ySXRlbSBleHRlbmRzIEJhc2VJdGVtIHtcclxuICAgIGNvbnN0cnVjdG9yKGlkLCB0aXRsZSwgaXRlbXMpIHtcclxuICAgICAgICBzdXBlcihpZCwgdGl0bGUpO1xyXG4gICAgICAgIHRoaXMuaXRlbXMgPSBpdGVtcztcclxuICAgICAgICB0aGlzLmVudHJpZXMgPSBbXTtcclxuICAgIH1cclxuICAgIGdldERPTU5vZGUoKSB7XHJcbiAgICAgICAgdGhpcy5jb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuICAgICAgICB0aGlzLmxpc3RDb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd1bCcpO1xyXG4gICAgICAgIHRoaXMubGFiZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsZWdlbmQnKTtcclxuICAgICAgICB0aGlzLmZpZWxkU2V0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZmllbGRzZXQnKTtcclxuICAgICAgICB0aGlzLmZpZWxkU2V0LnNldEF0dHJpYnV0ZSgnY2xhc3MnLCAncmFkaW9ncm91cCcpO1xyXG4gICAgICAgIHRoaXMuZmllbGRTZXQuaWQgPSBgZnNfc2VsZWN0b3JfJHt0aGlzLmlkfWA7XHJcbiAgICAgICAgY29uc3QgbmFtZSA9IGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKHRoaXMudGl0bGUpO1xyXG4gICAgICAgIHRoaXMubGFiZWwuYXBwZW5kQ2hpbGQobmFtZSk7XHJcbiAgICAgICAgdGhpcy5maWVsZFNldC5hcHBlbmRDaGlsZCh0aGlzLmxhYmVsKTtcclxuICAgICAgICB0aGlzLmJ1aWxkRW50cmllcygpO1xyXG4gICAgICAgIHRoaXMuY29udGFpbmVyLmFwcGVuZENoaWxkKHRoaXMuZmllbGRTZXQpO1xyXG4gICAgICAgIHRoaXMuY29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ2ZvY3VzJywgdGhpcy5vbkZvY3VzLmJpbmQodGhpcykpO1xyXG4gICAgICAgIHJldHVybiB0aGlzLmNvbnRhaW5lcjtcclxuICAgIH1cclxuICAgIGJ1aWxkRW50cmllcygpIHtcclxuICAgICAgICB0aGlzLml0ZW1zLmZvckVhY2goKGl0ZW0sIGluZGV4KSA9PiB7XHJcbiAgICAgICAgICAgIGNvbnN0IG5vZGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbnB1dCcpO1xyXG4gICAgICAgICAgICBub2RlLnR5cGUgPSAncmFkaW8nO1xyXG4gICAgICAgICAgICBub2RlLmlkID0gYCR7dGhpcy5pZH1fJHtpdGVtLmlkfWA7XHJcbiAgICAgICAgICAgIG5vZGUubmFtZSA9IHRoaXMuaWQ7XHJcbiAgICAgICAgICAgIG5vZGUudmFsdWUgPSBpdGVtLmlkIHx8IGAke2luZGV4fWA7XHJcbiAgICAgICAgICAgIG5vZGUuYWRkRXZlbnRMaXN0ZW5lcignZm9jdXMnLCB0aGlzLm9uSXRlbUZvY3VzLmJpbmQodGhpcykpO1xyXG4gICAgICAgICAgICBub2RlLmFkZEV2ZW50TGlzdGVuZXIoJ3NlbGVjdCcsIHRoaXMub25TZWxlY3RJdGVtLmJpbmQodGhpcykpO1xyXG4gICAgICAgICAgICBub2RlLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIHRoaXMub25DaGFuZ2VJdGVtLmJpbmQodGhpcykpO1xyXG4gICAgICAgICAgICB0aGlzLmVudHJpZXMucHVzaChub2RlKTtcclxuICAgICAgICAgICAgY29uc3QgbGFiZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsYWJlbCcpO1xyXG4gICAgICAgICAgICBsYWJlbC5zZXRBdHRyaWJ1dGUoJ2ZvcicsIGAke3RoaXMuaWR9XyR7aXRlbS5pZH1gKTtcclxuICAgICAgICAgICAgbGFiZWwudGV4dENvbnRlbnQgPSBpdGVtLnRpdGxlO1xyXG4gICAgICAgICAgICB0aGlzLmZpZWxkU2V0LmFwcGVuZChub2RlKTtcclxuICAgICAgICAgICAgdGhpcy5maWVsZFNldC5hcHBlbmQobGFiZWwpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgb25JdGVtRm9jdXMoZXZlbnQpIHtcclxuICAgICAgICBjb25zb2xlLmxvZyhgSXRlbSBmb2N1c2VkOiBgLCBldmVudCk7XHJcbiAgICAgICAgdGhpcy5lbWl0KCdmb2N1cycsIHRoaXMuaWQpO1xyXG4gICAgfVxyXG4gICAgZ2V0Q29udGVudHMoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuY3VycmVudFZhbHVlO1xyXG4gICAgfVxyXG4gICAgb25TZWxlY3RJdGVtKGV2ZW50KSB7IH1cclxuICAgIG9uQ2hhbmdlSXRlbShldmVudCkge1xyXG4gICAgICAgIGNvbnN0IG5vZGUgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGBpbnB1dFtuYW1lID0gXCIke3RoaXMuaWR9XCJdOmNoZWNrZWRgKTtcclxuICAgICAgICB0aGlzLmN1cnJlbnRWYWx1ZSA9IHRoaXMuaXRlbXMuZmluZCgoaXRlbSkgPT4gYCR7dGhpcy5pZH1fJHtpdGVtLmlkfWAgPT09IG5vZGUuaWQpO1xyXG4gICAgICAgIHRoaXMuZW1pdCgndXBkYXRlJywge1xyXG4gICAgICAgICAgICB0eXBlOiAnc2VsZWN0b3InLFxyXG4gICAgICAgICAgICB2YWx1ZTogdGhpcy5jdXJyZW50VmFsdWVcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuICAgIGZvY3VzKCkge1xyXG4gICAgICAgIGNvbnN0IG5vZGUgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGBpbnB1dFtuYW1lID0gXCIke3RoaXMuaWR9XCJdOmNoZWNrZWRgKSB8fFxyXG4gICAgICAgICAgICB0aGlzLmVudHJpZXNbMF07XHJcbiAgICAgICAgbm9kZS5mb2N1cygpO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IEJhc2VJdGVtIH0gZnJvbSAnLi9iYXNlLWl0ZW0nO1xyXG5leHBvcnQgY2xhc3MgU2xpZGVySXRlbSBleHRlbmRzIEJhc2VJdGVtIHtcclxuICAgIGNvbnN0cnVjdG9yKGlkLCB0aXRsZSwgbWluLCBtYXgsIHN0ZXAsIGRlZmF1bHRWYWx1ZSA9IG51bGwpIHtcclxuICAgICAgICBzdXBlcihpZCwgdGl0bGUpO1xyXG4gICAgICAgIHRoaXMubWluID0gbWluO1xyXG4gICAgICAgIHRoaXMubWF4ID0gbWF4O1xyXG4gICAgICAgIHRoaXMuc3RlcCA9IHN0ZXA7XHJcbiAgICAgICAgdGhpcy5kZWZhdWx0VmFsdWUgPSBkZWZhdWx0VmFsdWU7XHJcbiAgICB9XHJcbiAgICBnZXRET01Ob2RlKCkge1xyXG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XHJcbiAgICAgICAgdGhpcy5sYWJlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xhYmVsJyk7XHJcbiAgICAgICAgdGhpcy5sYWJlbC50ZXh0Q29udGVudCA9IHRoaXMudGl0bGU7XHJcbiAgICAgICAgdGhpcy5sYWJlbC5zZXRBdHRyaWJ1dGUoJ2ZvcicsIGBzbGlkZXJfJHt0aGlzLmlkfWApO1xyXG4gICAgICAgIHRoaXMuc2xpZGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW5wdXQnKTtcclxuICAgICAgICB0aGlzLnNsaWRlci5pZCA9IGBzbGlkZXJfJHt0aGlzLmlkfWA7XHJcbiAgICAgICAgdGhpcy5zbGlkZXIudHlwZSA9ICdyYW5nZSc7XHJcbiAgICAgICAgdGhpcy5zbGlkZXIuc2V0QXR0cmlidXRlKCdtaW4nLCB0aGlzLm1pbi50b1N0cmluZygpKTtcclxuICAgICAgICB0aGlzLnNsaWRlci5zZXRBdHRyaWJ1dGUoJ21heCcsIHRoaXMubWF4LnRvU3RyaW5nKCkpO1xyXG4gICAgICAgIHRoaXMuc2xpZGVyLnNldEF0dHJpYnV0ZSgnc3RlcCcsIHRoaXMuc3RlcC50b1N0cmluZygpKTtcclxuICAgICAgICBpZiAodGhpcy5kZWZhdWx0VmFsdWUpXHJcbiAgICAgICAgICAgIHRoaXMuc2xpZGVyLnZhbHVlID0gdGhpcy5kZWZhdWx0VmFsdWUudG9TdHJpbmcoKTtcclxuICAgICAgICB0aGlzLnNsaWRlci5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCB0aGlzLm9uQ2hhbmdlLmJpbmQodGhpcykpO1xyXG4gICAgICAgIHRoaXMuc2xpZGVyLmFkZEV2ZW50TGlzdGVuZXIoJ2ZvY3VzJywgdGhpcy5vbkZvY3VzLmJpbmQodGhpcykpO1xyXG4gICAgICAgIHRoaXMuY29udGFpbmVyLmFwcGVuZENoaWxkKHRoaXMubGFiZWwpO1xyXG4gICAgICAgIHRoaXMuY29udGFpbmVyLmFwcGVuZENoaWxkKHRoaXMuc2xpZGVyKTtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCdmb2N1cycsIHRoaXMub25Gb2N1cy5iaW5kKHRoaXMpKTtcclxuICAgICAgICByZXR1cm4gdGhpcy5jb250YWluZXI7XHJcbiAgICB9XHJcbiAgICBnZXRDb250ZW50cygpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5zbGlkZXIudmFsdWU7XHJcbiAgICB9XHJcbiAgICBvbkNoYW5nZShldmVudCkge1xyXG4gICAgICAgIHRoaXMuZW1pdCgndXBkYXRlJywge1xyXG4gICAgICAgICAgICB0eXBlOiAnc2xpZGVyJyxcclxuICAgICAgICAgICAgdmFsdWU6IHRoaXMuc2xpZGVyLnZhbHVlXHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBmb2N1cygpIHtcclxuICAgICAgICB0aGlzLnNsaWRlciAmJiB0aGlzLnNsaWRlci5mb2N1cygpO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IEJhc2VJdGVtIH0gZnJvbSAnLi9iYXNlLWl0ZW0nO1xyXG5leHBvcnQgY2xhc3MgQ2hlY2tib3hJdGVtIGV4dGVuZHMgQmFzZUl0ZW0ge1xyXG4gICAgY29uc3RydWN0b3IoaWQsIHRpdGxlKSB7XHJcbiAgICAgICAgc3VwZXIoaWQsIHRpdGxlKTtcclxuICAgIH1cclxuICAgIGdldERPTU5vZGUoKSB7XHJcbiAgICAgICAgdGhpcy5jb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuICAgICAgICB0aGlzLmxhYmVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnbGFiZWwnKTtcclxuICAgICAgICB0aGlzLmxhYmVsLnNldEF0dHJpYnV0ZSgnZm9yJywgYGNoa2J4XyR7dGhpcy5pZH1gKTtcclxuICAgICAgICB0aGlzLmxhYmVsLnRleHRDb250ZW50ID0gdGhpcy50aXRsZTtcclxuICAgICAgICB0aGlzLmNoZWNrYm94RWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lucHV0Jyk7XHJcbiAgICAgICAgdGhpcy5jaGVja2JveEVsZW1lbnQuc2V0QXR0cmlidXRlKCd0eXBlJywgJ2NoZWNrYm94Jyk7XHJcbiAgICAgICAgdGhpcy5jaGVja2JveEVsZW1lbnQuc2V0QXR0cmlidXRlKCdpZCcsIGBjaGtieF8ke3RoaXMuaWR9YCk7XHJcbiAgICAgICAgdGhpcy5jaGVja2JveEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignZm9jdXMnLCB0aGlzLm9uRm9jdXMuYmluZCh0aGlzKSk7XHJcbiAgICAgICAgdGhpcy5jaGVja2JveEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgdGhpcy5vbkNoYW5nZS5iaW5kKHRoaXMpKTtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lci5hcHBlbmRDaGlsZCh0aGlzLmxhYmVsKTtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lci5hcHBlbmRDaGlsZCh0aGlzLmNoZWNrYm94RWxlbWVudCk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuY29udGFpbmVyO1xyXG4gICAgfVxyXG4gICAgZ2V0Q29udGVudHMoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuY2hlY2tib3hFbGVtZW50LmNoZWNrZWQ7XHJcbiAgICB9XHJcbiAgICBvbkNoYW5nZShldmVudCkge1xyXG4gICAgICAgIHRoaXMuZW1pdCgndXBkYXRlJywge1xyXG4gICAgICAgICAgICB0eXBlOiAnY2hlY2tib3gnLFxyXG4gICAgICAgICAgICB2YWx1ZTogdGhpcy5jaGVja2JveEVsZW1lbnQuY2hlY2tlZFxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgZm9jdXMoKSB7XHJcbiAgICAgICAgdGhpcy5jaGVja2JveEVsZW1lbnQuZm9jdXMoKTtcclxuICAgIH1cclxufVxyXG4iLCJ2YXIgX19hd2FpdGVyID0gKHRoaXMgJiYgdGhpcy5fX2F3YWl0ZXIpIHx8IGZ1bmN0aW9uICh0aGlzQXJnLCBfYXJndW1lbnRzLCBQLCBnZW5lcmF0b3IpIHtcclxuICAgIGZ1bmN0aW9uIGFkb3B0KHZhbHVlKSB7IHJldHVybiB2YWx1ZSBpbnN0YW5jZW9mIFAgPyB2YWx1ZSA6IG5ldyBQKGZ1bmN0aW9uIChyZXNvbHZlKSB7IHJlc29sdmUodmFsdWUpOyB9KTsgfVxyXG4gICAgcmV0dXJuIG5ldyAoUCB8fCAoUCA9IFByb21pc2UpKShmdW5jdGlvbiAocmVzb2x2ZSwgcmVqZWN0KSB7XHJcbiAgICAgICAgZnVuY3Rpb24gZnVsZmlsbGVkKHZhbHVlKSB7IHRyeSB7IHN0ZXAoZ2VuZXJhdG9yLm5leHQodmFsdWUpKTsgfSBjYXRjaCAoZSkgeyByZWplY3QoZSk7IH0gfVxyXG4gICAgICAgIGZ1bmN0aW9uIHJlamVjdGVkKHZhbHVlKSB7IHRyeSB7IHN0ZXAoZ2VuZXJhdG9yW1widGhyb3dcIl0odmFsdWUpKTsgfSBjYXRjaCAoZSkgeyByZWplY3QoZSk7IH0gfVxyXG4gICAgICAgIGZ1bmN0aW9uIHN0ZXAocmVzdWx0KSB7IHJlc3VsdC5kb25lID8gcmVzb2x2ZShyZXN1bHQudmFsdWUpIDogYWRvcHQocmVzdWx0LnZhbHVlKS50aGVuKGZ1bGZpbGxlZCwgcmVqZWN0ZWQpOyB9XHJcbiAgICAgICAgc3RlcCgoZ2VuZXJhdG9yID0gZ2VuZXJhdG9yLmFwcGx5KHRoaXNBcmcsIF9hcmd1bWVudHMgfHwgW10pKS5uZXh0KCkpO1xyXG4gICAgfSk7XHJcbn07XHJcbmltcG9ydCAqIGFzIEV2ZW50RW1pdHRlciBmcm9tICdldmVudGVtaXR0ZXIzJztcclxuaW1wb3J0IHsgU291bmRNYW5hZ2VyIH0gZnJvbSAnLi9zb3VuZC1tYW5hZ2VyJztcclxuaW1wb3J0IHsgS2V5Ym9hcmRNYW5hZ2VyIH0gZnJvbSAnLi9rZXlib2FyZC1tYW5hZ2VyJztcclxuZXhwb3J0IGNsYXNzIE1lbnUgZXh0ZW5kcyBFdmVudEVtaXR0ZXIge1xyXG4gICAgY29uc3RydWN0b3IodGl0bGUgPSAnTWVudScsIG1lbnVJdGVtcyA9IFtdLCBzb3VuZFNldCA9IG51bGwsIGRlZmF1bHRBY3Rpb24gPSBudWxsLCBjYW5jZWxBY3Rpb24gPSBudWxsKSB7XHJcbiAgICAgICAgc3VwZXIoKTtcclxuICAgICAgICB0aGlzLnRpdGxlID0gdGl0bGU7XHJcbiAgICAgICAgdGhpcy5tZW51SXRlbXMgPSBtZW51SXRlbXM7XHJcbiAgICAgICAgdGhpcy5zb3VuZFNldCA9IHNvdW5kU2V0O1xyXG4gICAgICAgIHRoaXMuZGVmYXVsdEFjdGlvbiA9IGRlZmF1bHRBY3Rpb247XHJcbiAgICAgICAgdGhpcy5jYW5jZWxBY3Rpb24gPSBjYW5jZWxBY3Rpb247XHJcbiAgICAgICAgdGhpcy5jdXJyZW50SW5kZXggPSAwO1xyXG4gICAgICAgIHRoaXMuRE9NTm9kZXMgPSBbXTtcclxuICAgICAgICB0aGlzLmN1cnJlbnRJbmRleCA9IDA7XHJcbiAgICAgICAgdGhpcy5jdXJyZW50SXRlbSA9IG51bGw7XHJcbiAgICAgICAgdGhpcy5zb3VuZE1hbmFnZXIgPSBuZXcgU291bmRNYW5hZ2VyKHNvdW5kU2V0KTtcclxuICAgICAgICB0aGlzLmtleWJvYXJkTWFuYWdlciA9IG5ldyBLZXlib2FyZE1hbmFnZXIodGhpcyk7XHJcbiAgICAgICAgdGhpcy5pbml0KCk7XHJcbiAgICB9XHJcbiAgICBpbml0KCkge1xyXG4gICAgICAgIHRoaXMubWVudUl0ZW1zW3RoaXMuY3VycmVudEluZGV4XSAmJlxyXG4gICAgICAgICAgICB0aGlzLm1lbnVJdGVtc1t0aGlzLmN1cnJlbnRJbmRleF0uZm9jdXMoKTtcclxuICAgICAgICB0aGlzLmVtaXQoJ2luaXQnKTtcclxuICAgIH1cclxuICAgIGFkZEl0ZW0oaXRlbSkge1xyXG4gICAgICAgIHRoaXMubWVudUl0ZW1zLnB1c2goaXRlbSk7XHJcbiAgICAgICAgdGhpcy5lbWl0KCdpdGVtLmFkZCcsIGl0ZW0pO1xyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfVxyXG4gICAgc2V0VGl0bGUodGl0bGUpIHtcclxuICAgICAgICB0aGlzLnRpdGxlID0gdGl0bGU7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBzZXRTb3VuZFNldChzb3VuZFNldCkge1xyXG4gICAgICAgIHRoaXMuc291bmRTZXQgPSBzb3VuZFNldDtcclxuICAgICAgICB0aGlzLnNvdW5kTWFuYWdlci5zZXRTb3VuZFNldCh0aGlzLnNvdW5kU2V0KTtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIHNldERlZmF1bHRBY3Rpb24oaWQpIHtcclxuICAgICAgICB0aGlzLmRlZmF1bHRBY3Rpb24gPSBpZDtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH1cclxuICAgIHNldENhbmNlbEFjdGlvbihpZCkge1xyXG4gICAgICAgIHRoaXMuY2FuY2VsQWN0aW9uID0gaWQ7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICBydW4oZWxlbWVudCkge1xyXG4gICAgICAgIHJldHVybiBfX2F3YWl0ZXIodGhpcywgdm9pZCAwLCB2b2lkIDAsIGZ1bmN0aW9uKiAoKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmVsZW1lbnQgPSBlbGVtZW50O1xyXG4gICAgICAgICAgICAgICAgdGhpcy5jb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuICAgICAgICAgICAgICAgIHRoaXMudGl0bGVDb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdoMScpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy50aXRsZUNvbnRhaW5lci50ZXh0Q29udGVudCA9IHRoaXMudGl0bGU7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmNvbnRhaW5lci5hcHBlbmRDaGlsZCh0aGlzLnRpdGxlQ29udGFpbmVyKTtcclxuICAgICAgICAgICAgICAgIHRoaXMubWVudUl0ZW1zLmZvckVhY2goKGl0ZW0pID0+IHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLmFwcGVuZFRvQ29udGFpbmVyKGl0ZW0uZ2V0RE9NTm9kZSgpKTtcclxuICAgICAgICAgICAgICAgICAgICBpdGVtLm9uKCd1cGRhdGUnLCB0aGlzLmhhbmRsZUl0ZW1VcGRhdGUuYmluZCh0aGlzKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgaXRlbS5vbignZm9jdXMnLCB0aGlzLm9uSXRlbUZvY3VzLmJpbmQodGhpcykpO1xyXG4gICAgICAgICAgICAgICAgICAgIGl0ZW0ub24oJ2Nob29zZScsIChldmVudCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBtZW51TWFwID0gdGhpcy5jb21waWxlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc291bmRNYW5hZ2VyLmhhbmRsZVNvdW5kKCdjaG9vc2UnKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lbWl0KCdjaG9vc2UnLCBtZW51TWFwKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShtZW51TWFwKTtcclxuICAgICAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgZWxlbWVudC5hcHBlbmRDaGlsZCh0aGlzLmNvbnRhaW5lcik7XHJcbiAgICAgICAgICAgICAgICB0aGlzLnNvdW5kTWFuYWdlci5oYW5kbGVTb3VuZCgnb3BlbicpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5rZXlib2FyZE1hbmFnZXIuaW5pdCgpO1xyXG4gICAgICAgICAgICAgICAgLy8gcHVzaCBzb21lIGRhdGEgb250byB0aGUgaGlzdG9yeSBzdGFjayBzbyB0aGF0IHdlIGNhbiB1c2UgdGhlIGJyb3dzZXIncyBiYWNrIGJ1dHRvbiB0byBleGl0IG91dCBvZiB0aGUgbWVudS5cclxuICAgICAgICAgICAgICAgIGhpc3RvcnkucHVzaFN0YXRlKHsgbWVudTogdHJ1ZSB9LCBudWxsLCBudWxsKTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBjbG9zZSgpIHtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lci5yZW1vdmUoKTtcclxuICAgICAgICB0aGlzLnNvdW5kTWFuYWdlci5oYW5kbGVTb3VuZCgnY2xvc2UnKTtcclxuICAgICAgICB0aGlzLmtleWJvYXJkTWFuYWdlci5yZWxlYXNlKCk7XHJcbiAgICAgICAgdGhpcy5ET01Ob2Rlcy5mb3JFYWNoKChpdGVtKSA9PiB7XHJcbiAgICAgICAgICAgIHRoaXMuY29udGFpbmVyLnJlbW92ZUNoaWxkKGl0ZW0pO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIHRoaXMuZW1pdCgnY2xvc2UnKTtcclxuICAgIH1cclxuICAgIGFwcGVuZFRvQ29udGFpbmVyKG5vZGUpIHtcclxuICAgICAgICB0aGlzLmNvbnRhaW5lci5hcHBlbmRDaGlsZChub2RlKTtcclxuICAgICAgICB0aGlzLkRPTU5vZGVzLnB1c2gobm9kZSk7XHJcbiAgICB9XHJcbiAgICBoYW5kbGVJdGVtVXBkYXRlKHZhbHVlKSB7XHJcbiAgICAgICAgdGhpcy5zb3VuZE1hbmFnZXIuaGFuZGxlU291bmQodmFsdWUudHlwZSwgdmFsdWUudmFsdWUpO1xyXG4gICAgICAgIHRoaXMuZW1pdCgndXBkYXRlJywgdGhpcy5jb21waWxlKCkpO1xyXG4gICAgfVxyXG4gICAgb25JdGVtRm9jdXMoaWQpIHtcclxuICAgICAgICB0aGlzLnNvdW5kTWFuYWdlci5oYW5kbGVTb3VuZCgnZm9jdXMnKTtcclxuICAgICAgICB0aGlzLmN1cnJlbnRJbmRleCA9IHRoaXMubWVudUl0ZW1zLmluZGV4T2YodGhpcy5tZW51SXRlbXMuZmluZCgoaXRlbSkgPT4gaXRlbS5nZXRJRCgpID09IGlkKSk7XHJcbiAgICAgICAgdGhpcy5lbWl0KCdmb2N1cycsIHRoaXMubWVudUl0ZW1zW3RoaXMuY3VycmVudEluZGV4XSk7XHJcbiAgICB9XHJcbiAgICBmb2N1c05leHQoKSB7XHJcbiAgICAgICAgaWYgKHRoaXMuY3VycmVudEluZGV4IDwgdGhpcy5tZW51SXRlbXMubGVuZ3RoIC0gMSkge1xyXG4gICAgICAgICAgICB0aGlzLmN1cnJlbnRJbmRleCsrO1xyXG4gICAgICAgIH1cclxuICAgICAgICB0aGlzLmZvY3VzQ3VycmVudEluZGV4KCk7XHJcbiAgICB9XHJcbiAgICBmb2N1c1ByZXZpb3VzKCkge1xyXG4gICAgICAgIGlmICh0aGlzLmN1cnJlbnRJbmRleCA+IDApIHtcclxuICAgICAgICAgICAgdGhpcy5jdXJyZW50SW5kZXgtLTtcclxuICAgICAgICB9XHJcbiAgICAgICAgdGhpcy5mb2N1c0N1cnJlbnRJbmRleCgpO1xyXG4gICAgfVxyXG4gICAgZm9jdXNDdXJyZW50SW5kZXgoKSB7XHJcbiAgICAgICAgdGhpcy5tZW51SXRlbXNbdGhpcy5jdXJyZW50SW5kZXhdLmZvY3VzKCk7XHJcbiAgICB9XHJcbiAgICBnZXRDdXJyZW50Rm9jdXMoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMubWVudUl0ZW1zW3RoaXMuY3VycmVudEluZGV4XTtcclxuICAgIH1cclxuICAgIGdldENvbnRhaW5lcigpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5jb250YWluZXI7XHJcbiAgICB9XHJcbiAgICBjbGlja0RlZmF1bHRBY3Rpb24oKSB7XHJcbiAgICAgICAgaWYgKCF0aGlzLmRlZmF1bHRBY3Rpb24pXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICBjb25zdCBpdGVtID0gdGhpcy5tZW51SXRlbXMuZmluZCgoaXRlbSkgPT4gaXRlbS5nZXRJRCgpID09PSB0aGlzLmRlZmF1bHRBY3Rpb24pO1xyXG4gICAgICAgIGl0ZW0uY2xpY2soKTtcclxuICAgIH1cclxuICAgIGNsaWNrQ2FuY2VsQWN0aW9uKCkge1xyXG4gICAgICAgIGlmICghdGhpcy5jYW5jZWxBY3Rpb24pXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICBjb25zdCBub2RlID0gdGhpcy5tZW51SXRlbXMuZmluZCgoaXRlbSkgPT4gaXRlbS5nZXRJRCgpID09PSB0aGlzLmNhbmNlbEFjdGlvbik7XHJcbiAgICAgICAgbm9kZS5jbGljaygpO1xyXG4gICAgfVxyXG4gICAgY29tcGlsZSgpIHtcclxuICAgICAgICBjb25zdCBtZW51TWFwID0gbmV3IE1hcCgpO1xyXG4gICAgICAgIHRoaXMubWVudUl0ZW1zLmZvckVhY2goKGl0ZW0pID0+IG1lbnVNYXAuc2V0KGl0ZW0uZ2V0SUQoKSwgaXRlbS5nZXRDb250ZW50cygpKSk7XHJcbiAgICAgICAgbWVudU1hcC5zZXQoJ3NlbGVjdGVkJywgdGhpcy5tZW51SXRlbXNbdGhpcy5jdXJyZW50SW5kZXhdLmdldElEKCkpO1xyXG4gICAgICAgIHJldHVybiBtZW51TWFwO1xyXG4gICAgfVxyXG59XHJcbmV4cG9ydCAqIGZyb20gJy4vaXRlbXMnO1xyXG4iXSwibmFtZXMiOlsiaGFzIiwiT2JqZWN0IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJwcmVmaXgiLCJFdmVudHMiLCJFRSIsImZuIiwiY29udGV4dCIsIm9uY2UiLCJ0aGlzIiwiYWRkTGlzdGVuZXIiLCJlbWl0dGVyIiwiZXZlbnQiLCJUeXBlRXJyb3IiLCJsaXN0ZW5lciIsImV2dCIsIl9ldmVudHMiLCJwdXNoIiwiX2V2ZW50c0NvdW50IiwiY2xlYXJFdmVudCIsIkV2ZW50RW1pdHRlciIsImNyZWF0ZSIsIl9fcHJvdG9fXyIsImV2ZW50TmFtZXMiLCJldmVudHMiLCJuYW1lIiwibmFtZXMiLCJjYWxsIiwic2xpY2UiLCJnZXRPd25Qcm9wZXJ0eVN5bWJvbHMiLCJjb25jYXQiLCJsaXN0ZW5lcnMiLCJoYW5kbGVycyIsImkiLCJsIiwibGVuZ3RoIiwiZWUiLCJBcnJheSIsImxpc3RlbmVyQ291bnQiLCJlbWl0IiwiYTEiLCJhMiIsImEzIiwiYTQiLCJhNSIsImFyZ3MiLCJsZW4iLCJhcmd1bWVudHMiLCJyZW1vdmVMaXN0ZW5lciIsInVuZGVmaW5lZCIsImFwcGx5IiwiaiIsIm9uIiwicmVtb3ZlQWxsTGlzdGVuZXJzIiwib2ZmIiwicHJlZml4ZWQiLCJtb2R1bGUiLCJleHBvcnRzIiwiX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fIiwiX193ZWJwYWNrX3JlcXVpcmVfXyIsIm1vZHVsZUlkIiwiY2FjaGVkTW9kdWxlIiwiX193ZWJwYWNrX21vZHVsZXNfXyIsIm4iLCJnZXR0ZXIiLCJfX2VzTW9kdWxlIiwiZCIsImEiLCJkZWZpbml0aW9uIiwia2V5IiwibyIsImRlZmluZVByb3BlcnR5IiwiZW51bWVyYWJsZSIsImdldCIsIm9iaiIsInByb3AiLCJyIiwiU3ltYm9sIiwidG9TdHJpbmdUYWciLCJ2YWx1ZSIsImJ1aWxkUGF0aCIsImJhc2VQYXRoIiwicGF0aCIsIl9fYXdhaXRlciIsInRoaXNBcmciLCJfYXJndW1lbnRzIiwiUCIsImdlbmVyYXRvciIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwiZnVsZmlsbGVkIiwic3RlcCIsIm5leHQiLCJlIiwicmVqZWN0ZWQiLCJyZXN1bHQiLCJkb25lIiwidGhlbiIsIkRvd25sb2FkZXIiLCJjb25zdHJ1Y3RvciIsInN0b3JhZ2UiLCJxdWV1ZSIsInN1cGVyIiwic2V0QmFzZVBhdGgiLCJkb3dubG9hZCIsImRvd25sb2FkZWQiLCJNYXAiLCJudW1Eb3dubG9hZGVkIiwicG9wIiwiaXRlbSIsImRvd25sb2FkSXRlbSIsInNldCIsInJlbWFpbmluZyIsImluQ2FjaGUiLCJyZXNwb25zZSIsImZldGNoIiwiYWRkIiwiUXVldWUiLCJpdGVtcyIsImZpbGUiLCJyZW1vdmUiLCJmaWx0ZXIiLCJBc3NldFN0b3JhZ2UiLCJpZCIsImluaXQiLCJjYWNoZSIsImNhY2hlcyIsIm9wZW4iLCJyZXF1ZXN0IiwicHV0IiwibWF0Y2giLCJzZXRNYW5pZmVzdCIsIm1hbmlmZXN0IiwicHJldk1hbmlmZXN0U3RyIiwibG9jYWxTdG9yYWdlIiwiZ2V0SXRlbSIsIkpTT04iLCJwYXJzZSIsInZlcnNpb24iLCJzZXRJdGVtIiwic3RyaW5naWZ5IiwiQ2hlY2tNYW5pZmVzdCIsImNsZWFyIiwia2V5cyIsImZvckVhY2giLCJkZWxldGUiLCJTb3VyY2VUeXBlIiwiQXNzZXRNYW5hZ2VyIiwiZG93bmxvYWRlciIsImNvbnNvbGUiLCJsb2ciLCJtYW5pZmVzdFBhdGgiLCJkYXRhIiwidGV4dCIsImVycm9yIiwiYWxlcnQiLCJ0b1N0cmluZyIsIk1hbmlmZXN0IiwiZW5xdWV1ZSIsImRvd25sb2FkRnJvbU1hbmlmZXN0IiwiaW5mbyIsImZpbGVzIiwiZG93bmxvYWRGaWxlIiwiY2xlYXJDYWNoZSIsIkJhc2VFbnRpdHkiLCJjb21wb25lbnRzIiwiYWRkQ29tcG9uZW50IiwiY29tcG9uZW50IiwiY29tcCIsInJlbW92ZUNvbXBvbmVudCIsImdldENvbXBvbmVudElEcyIsImdldENvbXBvbmVudCIsImdldENvbXBvbmVudEJ5SUQiLCJFdmVudEJ1cyIsImV2Iiwic3Vic2NyaWJlcnMiLCJzdWJzY3JpYmVyIiwiRXZlbnRJdGVtIiwic3Vic2NyaWJlIiwidW5zdWJzY3JpYmUiLCJ1bnN1YnNjcmliZUFsbCIsIlF1ZXJ5IiwiaW5jbHVkZSIsImV4Y2x1ZGUiLCJ3b3JsZCIsImlzRGlydHkiLCJyZXN1bHRzIiwiaW5jbHVkZUNvbXBvbmVudElkcyIsIm1hcCIsImV4Y2x1ZGVDb21wb25lbnRJZHMiLCJleGVjdXRlIiwiY29tcG9uZW50TmFtZXNUb0lEcyIsImVudGl0aWVzIiwiZW50aXR5IiwiaWRzIiwiZXhjbHVkZXMiLCJpbmNsdWRlcyIsIlN5c3RlbSIsImV4ZWN1dG9yIiwiV29ybGQiLCJuZXh0RW50aXR5SUQiLCJuZXh0Q29tcG9uZW50SUQiLCJuZXh0UXVlcnlJRCIsInN5c3RlbXMiLCJTZXQiLCJxdWVyeUNhY2hlIiwiZXZlbnRCdXMiLCJydW4iLCJzeXN0ZW0iLCJjcmVhdGVTeXN0ZW0iLCJzeXN0ZW1FeGVjdXRvciIsIm5ld1N5c3RlbSIsImFkZFN5c3RlbSIsImFkZEVudGl0eSIsIm1hcmtRdWVyaWVzRGlydHkiLCJyZW1vdmVFbnRpdHkiLCJlbnRpdHlUb1JlbW92ZSIsImNyZWF0ZUVudGl0eSIsIm5ld0VudGl0eSIsImV4dGVuZEVudGl0eSIsInRvQ2xvbmUiLCJmaW5kIiwiZm91bmQiLCJjbG9uZWQiLCJjcmVhdGVDb21wb25lbnQiLCJuZXdDb21wb25lbnQiLCJxdWVyeSIsImNyZWF0ZVF1ZXJ5IiwibmV3UXVlcnkiLCJCYXNlSW5wdXQiLCJlbGVtZW50IiwiZ2V0U3RhdGUiLCJjYXB0dXJlIiwicHJldmVudERlZmF1bHQiLCJyZWxlYXNlIiwiS2V5Ym9hcmQiLCJrZXlzRG93biIsImtleXNKdXN0UHJlc3NlZCIsImtleXNKdXN0UmVsZWFzZWQiLCJoYW5kbGVLZXlEb3duIiwiYmluZCIsImhhbmRsZUtleVVwIiwiYWN0aXZlIiwiYWRkRXZlbnRMaXN0ZW5lciIsInJlbW92ZUV2ZW50TGlzdGVuZXIiLCJzdGF0ZSIsImtleUNvZGUiLCJNb3VzZSIsIm1vdXNlUG9zaXRpb24iLCJQb3NpdGlvbiIsIm1vdXNlRGVsdGEiLCJEZWx0YSIsIm1vdXNlV2hlZWwiLCJtb3VzZUJ1dHRvbnMiLCJNb3VzZUJ1dHRvbnMiLCJoYW5kbGVNb3VzZURvd24iLCJoYW5kbGVNb3VzZU1vdmUiLCJoYW5kbGVNb3VzZVVwIiwiaGFuZGxlUG9pbnRlckNoYW5nZSIsImRvY3VtZW50IiwieCIsInkiLCJidXR0b24iLCJjbGllbnRYIiwiY2xpZW50WSIsIm1vdmVtZW50WCIsIm1vdmVtZW50WSIsInBvaW50ZXJMb2NrRWxlbWVudCIsInJlcXVlc3RQb2ludGVyTG9jayIsIklucHV0IiwiSW5wdXRJRHMiLCJpbnB1dHMiLCJpbnB1dElEIiwiaW5zdGFuY2UiLCJjcmVhdGVJbnB1dCIsImFkZElucHV0IiwiaW5wdXQiLCJ2ZWM0IiwidmFsdWVzIiwiRmxvYXQzMkFycmF5IiwieHl6dyIsInoiLCJ3IiwieHkiLCJ4eXoiLCJnIiwiYiIsInJnIiwicmdiIiwicmdiYSIsImF0IiwiaW5kZXgiLCJyZXNldCIsImNvcHkiLCJkZXN0IiwibmVnYXRlIiwiZXF1YWxzIiwidmVjdG9yIiwidGhyZXNob2xkIiwiZXBzaWxvbiIsIk1hdGgiLCJhYnMiLCJzcXJ0Iiwic3F1YXJlZExlbmd0aCIsInN1YnRyYWN0IiwibXVsdGlwbHkiLCJkaXZpZGUiLCJzY2FsZSIsIm5vcm1hbGl6ZSIsIm11bHRpcGx5TWF0NCIsIm1hdHJpeCIsIm11bHRpcGx5VmVjNCIsInN0YXRpYyIsInZlY3RvcjIiLCJ0aW1lIiwiemVybyIsIm9uZSIsIm1hdDQiLCJhbGwiLCJyb3ciLCJjb2wiLCJkZXRlcm1pbmFudCIsImEwMCIsImEwMSIsImEwMiIsImEwMyIsImExMCIsImExMSIsImExMiIsImExMyIsImEyMCIsImEyMSIsImEyMiIsImEyMyIsImEzMCIsImEzMSIsImEzMiIsImEzMyIsInNldElkZW50aXR5IiwidHJhbnNwb3NlIiwidGVtcDAxIiwidGVtcDAyIiwidGVtcDAzIiwidGVtcDEyIiwidGVtcDEzIiwidGVtcDIzIiwiaW52ZXJzZSIsImRldDAwIiwiZGV0MDEiLCJkZXQwMiIsImRldDAzIiwiZGV0MDQiLCJkZXQwNSIsImRldDA2IiwiZGV0MDciLCJkZXQwOCIsImRldDA5IiwiZGV0MTAiLCJkZXQxMSIsImRldCIsImIwIiwiYjEiLCJiMiIsImIzIiwibXVsdGlwbHlWZWMzIiwidG9NYXQzIiwibWF0MyIsInRvSW52ZXJzZU1hdDMiLCJkZXQyMSIsInRyYW5zbGF0ZSIsInJvdGF0ZSIsImFuZ2xlIiwiYXhpcyIsInMiLCJzaW4iLCJjIiwiY29zIiwidCIsImIwMCIsImIwMSIsImIwMiIsImIxMCIsImIxMSIsImIxMiIsImIyMCIsImIyMSIsImIyMiIsImxlZnQiLCJyaWdodCIsImJvdHRvbSIsInRvcCIsIm5lYXIiLCJmYXIiLCJybCIsInRiIiwiZm92IiwiYXNwZWN0IiwidGFuIiwiUEkiLCJmcnVzdHVtIiwicG9zaXRpb24iLCJ0YXJnZXQiLCJ1cCIsImlkZW50aXR5IiwibTEiLCJtMiIsImIwMyIsImIxMyIsImIyMyIsImIzMCIsImIzMSIsImIzMiIsImIzMyIsInZlYzIiLCJtdWx0aXBseU1hdDIiLCJtdWx0aXBseVZlYzIiLCJtdWx0aXBseU1hdDMiLCJ4MiIsInNxdWFyZWREaXN0YW5jZSIsInkyIiwidG9NYXQ0IiwidG9RdWF0IiwibTAwIiwibTAxIiwibTAyIiwibTEwIiwibTExIiwibTEyIiwibTIwIiwibTIxIiwibTIyIiwiZm91clhTcXVhcmVkTWludXMxIiwiZm91cllTcXVhcmVkTWludXMxIiwiZm91clpTcXVhcmVkTWludXMxIiwiYmlnZ2VzdEluZGV4IiwiZm91ckJpZ2dlc3RTcXVhcmVkTWludXMxIiwiYmlnZ2VzdFZhbCIsIm11bHQiLCJxdWF0Iiwicm9sbCIsImF0YW4yIiwicGl0Y2giLCJ5YXciLCJhc2luIiwiY2FsY3VsYXRlVyIsImRvdCIsImludkRvdCIsImNvbmp1Z2F0ZSIsIm90aGVyIiwicTF4IiwicTF5IiwicTF6IiwicTF3IiwicTJ4IiwicTJ5IiwicTJ6IiwicTJ3IiwicXgiLCJxeSIsInF6IiwicXciLCJpeCIsIml5IiwiaXoiLCJpdyIsInoyIiwieHgiLCJ4eiIsInl5IiwieXoiLCJ6eiIsInd4Iiwid3kiLCJ3eiIsInExIiwicTIiLCJxMmEiLCJrMCIsImsxIiwib25lT3ZlclNpbiIsImNvc0hhbGZUaGV0YSIsImhhbGZUaGV0YSIsImFjb3MiLCJzaW5IYWxmVGhldGEiLCJyYXRpb0EiLCJyYXRpb0IiLCJtdWx0aXBseUJ5TWF0MyIsIm11bHRpcGx5QnlRdWF0IiwicXVhdGVybmlvbiIsImZvcndhcmQiLCJCYXNlT3V0cHV0Iiwic3BlYWsiLCJzdG9wIiwic2V0T3B0aW9ucyIsIm9wdGlvbnMiLCJBcmlhT3V0cHV0IiwidGltZW91dCIsImNvbnRhaW5lciIsImNyZWF0ZUVsZW1lbnQiLCJzZXRBdHRyaWJ1dGUiLCJzcGVlY2hEaXNwbGF5IiwiYXBwZW5kIiwiYm9keSIsImFwcGVuZENoaWxkIiwiaW5zZXJ0QmVmb3JlIiwiZmlyc3RDaGlsZCIsImNsZWFyRGlzcGxheSIsIm5vZGUiLCJjcmVhdGVUZXh0Tm9kZSIsInBhcmEiLCJzZXRUaW1lb3V0IiwiaW5uZXJIVE1MIiwiV2ViVFRTT3V0cHV0IiwiVFRTIiwib3V0cHV0IiwiY3JlYXRlT3V0cHV0IiwiU291bmRNYW5hZ2VyIiwic291bmRTZXQiLCJzZXRTb3VuZFNldCIsImhhbmRsZVNvdW5kIiwidHlwZSIsImhhbmRsZUVkaXRTb3VuZCIsImhhbmRsZVNsaWRlclNvdW5kIiwiaGFuZGxlU2VsZWN0b3JTb3VuZCIsImhhbmRsZUNoZWNrYm94U291bmQiLCJoYW5kbGVGb2N1c1NvdW5kIiwiaGFuZGxlQ2hvb3NlU291bmQiLCJoYW5kbGVPcGVuU291bmQiLCJoYW5kbGVDbG9zZVNvdW5kIiwicHJldkRhdGEiLCJwbGF5IiwiY2hhciIsInNjcm9sbGVyIiwic2xpZGVyTGVmdCIsInNsaWRlclJpZ2h0IiwibW92ZSIsImNsb3NlIiwiY2hvb3NlIiwiY2hlY2tlZCIsInVuY2hlY2tlZCIsIktleWJvYXJkTWFuYWdlciIsIm1lbnUiLCJnZXRDb250YWluZXIiLCJoYW5kbGVyIiwid2luZG93Iiwib25wb3BzdGF0ZSIsImNsaWNrQ2FuY2VsQWN0aW9uIiwiZm9jdXNOZXh0IiwiZm9jdXNQcmV2aW91cyIsImNsaWNrRGVmYXVsdEFjdGlvbiIsIkJhc2VJdGVtIiwidGl0bGUiLCJnZXRET01Ob2RlIiwiZ2V0Q29udGVudHMiLCJvbkZvY3VzIiwiZm9jdXMiLCJjbGljayIsImdldElEIiwiRWRpdEl0ZW0iLCJpbml0aWFsVGV4dCIsImlzUGFzc3dvcmQiLCJjb250ZW50cyIsImxhYmVsIiwidGV4dENvbnRlbnQiLCJlZGl0RmllbGQiLCJvbkNoYW5nZSIsIk1lbnVJdGVtIiwiaGFuZGxlQ2xpY2siLCJTZWxlY3Rvckl0ZW0iLCJlbnRyaWVzIiwibGlzdENvbnRhaW5lciIsImZpZWxkU2V0IiwiYnVpbGRFbnRyaWVzIiwib25JdGVtRm9jdXMiLCJvblNlbGVjdEl0ZW0iLCJvbkNoYW5nZUl0ZW0iLCJjdXJyZW50VmFsdWUiLCJxdWVyeVNlbGVjdG9yIiwiU2xpZGVySXRlbSIsIm1pbiIsIm1heCIsImRlZmF1bHRWYWx1ZSIsInNsaWRlciIsIkNoZWNrYm94SXRlbSIsImNoZWNrYm94RWxlbWVudCIsIk1lbnUiLCJtZW51SXRlbXMiLCJkZWZhdWx0QWN0aW9uIiwiY2FuY2VsQWN0aW9uIiwiY3VycmVudEluZGV4IiwiRE9NTm9kZXMiLCJjdXJyZW50SXRlbSIsInNvdW5kTWFuYWdlciIsImtleWJvYXJkTWFuYWdlciIsImFkZEl0ZW0iLCJzZXRUaXRsZSIsInNldERlZmF1bHRBY3Rpb24iLCJzZXRDYW5jZWxBY3Rpb24iLCJ0aXRsZUNvbnRhaW5lciIsImFwcGVuZFRvQ29udGFpbmVyIiwiaGFuZGxlSXRlbVVwZGF0ZSIsIm1lbnVNYXAiLCJjb21waWxlIiwiaGlzdG9yeSIsInB1c2hTdGF0ZSIsInJlbW92ZUNoaWxkIiwiaW5kZXhPZiIsImZvY3VzQ3VycmVudEluZGV4IiwiZ2V0Q3VycmVudEZvY3VzIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file diff --git a/src/framework/event-bus/event-bus.d.ts b/src/framework/event-bus/event-bus.d.ts new file mode 100644 index 0000000..24b52fb --- /dev/null +++ b/src/framework/event-bus/event-bus.d.ts @@ -0,0 +1,11 @@ +export declare class EventBus { + private events; + constructor(); + emit(id: string, data: any): void; + subscribe(id: string, subscriber: Function): void; +} +export declare class EventItem { + id: string; + subscribers: Function[]; + constructor(id: string); +} diff --git a/src/framework/event-bus/event-bus.js b/src/framework/event-bus/event-bus.js new file mode 100644 index 0000000..11ee12b --- /dev/null +++ b/src/framework/event-bus/event-bus.js @@ -0,0 +1,30 @@ +export class EventBus { + constructor() { + this.events = new Map(); + } + emit(id, data) { + let ev = this.events.get(id); + if (!ev) { + let ev = new EventItem(id); + this.events.set(id, ev); + return; + } + ev.subscribers.forEach((subscriber) => { + subscriber(data); + }); + } + subscribe(id, subscriber) { + let ev = this.events.get(id); + if (!ev) { + ev = new EventItem(id); + this.events.set(id, ev); + } + ev.subscribers.push(subscriber); + } +} +export class EventItem { + constructor(id) { + this.id = id; + this.subscribers = []; + } +} diff --git a/src/framework/event-bus/index.d.ts b/src/framework/event-bus/index.d.ts new file mode 100644 index 0000000..a3d66e5 --- /dev/null +++ b/src/framework/event-bus/index.d.ts @@ -0,0 +1,13 @@ +export declare class EventBus { + private events; + constructor(); + emit(id: string, data?: any): void; + subscribe(id: string, subscriber: Function): void; + unsubscribe(id: string, subscriber: Function): void; + unsubscribeAll(id: string): void; +} +export declare class EventItem { + id: string; + subscribers: Function[]; + constructor(id: string); +} diff --git a/src/framework/event-bus/index.js b/src/framework/event-bus/index.js new file mode 100644 index 0000000..20afbae --- /dev/null +++ b/src/framework/event-bus/index.js @@ -0,0 +1,44 @@ +export class EventBus { + constructor() { + this.events = new Map(); + } + emit(id, data = {}) { + let ev = this.events.get(id); + if (!ev) { + let ev = new EventItem(id); + this.events.set(id, ev); + return; + } + ev.subscribers.forEach((subscriber) => { + subscriber(data); + }); + } + subscribe(id, subscriber) { + let ev = this.events.get(id); + if (!ev) { + ev = new EventItem(id); + this.events.set(id, ev); + } + ev.subscribers.push(subscriber); + } + unsubscribe(id, subscriber) { + if (this.events.has(id)) { + let ev = this.events.get(id); + ev.subscribers = ev.subscribers.filter((fn) => fn !== subscriber); + if (ev.subscribers.length < 1) { + this.events.delete(id); + } + } + } + unsubscribeAll(id) { + if (this.events.has(id)) { + this.events.delete(id); + } + } +} +export class EventItem { + constructor(id) { + this.id = id; + this.subscribers = []; + } +} diff --git a/src/framework/game/game.d.ts b/src/framework/game/game.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/game/game.js b/src/framework/game/game.js new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/game/index.d.ts b/src/framework/game/index.d.ts new file mode 100644 index 0000000..0de492c --- /dev/null +++ b/src/framework/game/index.d.ts @@ -0,0 +1,26 @@ +import { AssetManager } from '../asset-manager'; +import { Input } from '../input'; +import Resonator from '../resonator'; +import { Scene } from '../scene/scene'; +import { SceneManager } from '../scene/manager'; +import { Scheduler } from '../scheduler'; +import { TTS } from '../tts'; +import { HTTPLoader } from '../resonator/loaders/http-loader'; +import { EventBus } from '../event-bus'; +import { World } from '../world'; +export declare class Game extends EventBus { + assetLoader: HTTPLoader; + assetManager: AssetManager; + resonator: Resonator; + input: Input; + tts: TTS; + sceneManager: SceneManager; + scheduler: Scheduler; + world: World; + constructor(); + init(): void; + start(): void; + addScene(scene: Scene): void; + addDefaultScene(scene: Scene): void; + setWorld(world: World): void; +} diff --git a/src/framework/game/index.js b/src/framework/game/index.js new file mode 100644 index 0000000..91b4aa7 --- /dev/null +++ b/src/framework/game/index.js @@ -0,0 +1,44 @@ +import { AssetManager } from '../asset-manager'; +import { Input } from '../input'; +import Resonator from '../resonator'; +import { SceneManager } from '../scene/manager'; +import { Scheduler } from '../scheduler'; +import { TTS } from '../tts'; +import { AriaOutput } from '../tts/outputs/aria'; +import { HTTPLoader } from '../resonator/loaders/http-loader'; +import { EventBus } from '../event-bus'; +export class Game extends EventBus { + constructor() { + super(); + this.init(); + } + init() { + this.assetManager = new AssetManager('game', ''); + this.assetLoader = new HTTPLoader(); + this.resonator = new Resonator(this.assetLoader); + this.input = new Input(['keyboard'], document.body); + this.tts = new TTS(new AriaOutput()); + this.sceneManager = new SceneManager(); + this.scheduler = new Scheduler(60); + this.emit('ready'); + } + start() { + this.scheduler.start(); + this.scheduler.subscribe('update.logic', (dt) => { + this.sceneManager.currentScene.update(dt); + this.world.update(dt); + }); + this.scheduler.subscribe('update.draw', (dt) => this.sceneManager.currentScene.updateDraw()); + this.sceneManager.init(); + } + addScene(scene) { + this.sceneManager.addScene(scene); + } + addDefaultScene(scene) { + this.sceneManager.addScene(scene); + this.sceneManager.setDefaultScene(scene); + } + setWorld(world) { + this.world = world; + } +} diff --git a/src/framework/game/scenes/ecs-scene.d.ts b/src/framework/game/scenes/ecs-scene.d.ts new file mode 100644 index 0000000..6cee437 --- /dev/null +++ b/src/framework/game/scenes/ecs-scene.d.ts @@ -0,0 +1,29 @@ +import { Scene } from '../../scene/scene'; +import { World } from '../../ecs/index'; +import { Game } from '..'; +import { Component } from '../../ecs/component'; +import { System } from '../../ecs/system'; +import { Entity } from '../../ecs/entity'; +import { Query } from '../../ecs/query'; +import { SceneManager } from '../../scene/manager'; +export declare class ECSScene implements Scene { + instance: Game; + id: string; + world: World; + running: boolean; + data: any; + constructor(instance: Game); + update(): void; + updateDraw(): boolean; + onActivate(manager: SceneManager): void; + onDeactivate(): void; + onSwitch(): void; + createEntity(components: Array>): Entity; + createComponent(props: T): Component; + createSystem(systemExecutor: Function): void; + addSystem(system: System): void; + addEntity(entity: Entity): void; + removeEntity(entity: Entity): void; + createQuery(include: Array>, exclude: Array>): Query; + extendEntity(entity: Entity, components: Array>): Entity; +} diff --git a/src/framework/game/scenes/ecs-scene.js b/src/framework/game/scenes/ecs-scene.js new file mode 100644 index 0000000..4ac7412 --- /dev/null +++ b/src/framework/game/scenes/ecs-scene.js @@ -0,0 +1,49 @@ +import { World } from '../../ecs/index'; +export class ECSScene { + constructor(instance) { + this.instance = instance; + this.running = true; + this.id = 'ECSScene'; + this.world = new World(); + } + update() { + if (this.running) + this.world.run(); + } + updateDraw() { + return true; + } + onActivate(manager) { + this.running = true; + } + onDeactivate() { + this.running = false; + } + onSwitch() { + return; + } + createEntity(components) { + return this.world.createEntity(components); + } + createComponent(props) { + return this.world.createComponent(props); + } + createSystem(systemExecutor) { + return this.world.createSystem(systemExecutor); + } + addSystem(system) { + return this.world.addSystem(system); + } + addEntity(entity) { + return this.world.addEntity(entity); + } + removeEntity(entity) { + return this.world.removeEntity(entity); + } + createQuery(include, exclude) { + return this.world.createQuery(include, exclude); + } + extendEntity(entity, components) { + return this.world.extendEntity(entity, components); + } +} diff --git a/src/framework/index.d.ts b/src/framework/index.d.ts new file mode 100644 index 0000000..513af72 --- /dev/null +++ b/src/framework/index.d.ts @@ -0,0 +1,7 @@ +export * from './asset-manager'; +export * from './ecs'; +export * from './event-bus'; +export * from './input'; +export * from './resonator'; +export * from './tts'; +export * from './ui'; diff --git a/src/framework/index.js b/src/framework/index.js new file mode 100644 index 0000000..513af72 --- /dev/null +++ b/src/framework/index.js @@ -0,0 +1,7 @@ +export * from './asset-manager'; +export * from './ecs'; +export * from './event-bus'; +export * from './input'; +export * from './resonator'; +export * from './tts'; +export * from './ui'; diff --git a/src/framework/input/index.d.ts b/src/framework/input/index.d.ts new file mode 100644 index 0000000..fe7e744 --- /dev/null +++ b/src/framework/input/index.d.ts @@ -0,0 +1,13 @@ +import { BaseInput } from './inputs/base-input'; +import { State } from './interfaces/state'; +export declare class Input { + private InputIDs; + private element; + private inputs; + constructor(InputIDs: string[], element: HTMLElement); + private init; + addInput(id: string, input: BaseInput): this; + capture(preventDefault?: boolean): void; + release(): void; + getState(): State; +} diff --git a/src/framework/input/index.js b/src/framework/input/index.js new file mode 100644 index 0000000..595d456 --- /dev/null +++ b/src/framework/input/index.js @@ -0,0 +1,34 @@ +import { createInput } from './input-factory'; +export class Input { + constructor(InputIDs, element) { + this.InputIDs = InputIDs; + this.element = element; + this.inputs = new Map(); + this.init(); + } + init() { + this.InputIDs.forEach((inputID) => { + const thing = createInput(inputID); + const instance = new thing(this.element); + this.inputs.set(inputID, instance); + }); + } + addInput(id, input) { + this.inputs.set(id, input); + return this; + } + capture(preventDefault = true) { + this.inputs.forEach((input) => input.capture(preventDefault)); + } + release() { + this.inputs.forEach((input) => input.release()); + } + getState() { + const state = {}; + this.inputs.forEach((input, inputID) => { + if (input) + state[inputID] = input.getState(); + }); + return state; + } +} diff --git a/src/framework/input/input-factory.d.ts b/src/framework/input/input-factory.d.ts new file mode 100644 index 0000000..a7e84d2 --- /dev/null +++ b/src/framework/input/input-factory.d.ts @@ -0,0 +1 @@ +export declare function createInput(key: string): any; diff --git a/src/framework/input/input-factory.js b/src/framework/input/input-factory.js new file mode 100644 index 0000000..fe7e98b --- /dev/null +++ b/src/framework/input/input-factory.js @@ -0,0 +1,14 @@ +import { Keyboard } from './inputs/keyboard'; +import { Mouse } from './inputs/mouse'; +export function createInput(key) { + switch (key) { + case 'keyboard': + return Keyboard; + break; + case 'mouse': + return Mouse; + break; + default: + break; + } +} diff --git a/src/framework/input/inputs/base-input.d.ts b/src/framework/input/inputs/base-input.d.ts new file mode 100644 index 0000000..661de29 --- /dev/null +++ b/src/framework/input/inputs/base-input.d.ts @@ -0,0 +1,10 @@ +export declare class BaseInput { + protected element: HTMLElement; + protected active: boolean; + constructor(element: HTMLElement); + getState(): any; + capture(preventDefault: boolean): void; + release(): void; +} +export interface IBaseInput { +} diff --git a/src/framework/input/inputs/base-input.js b/src/framework/input/inputs/base-input.js new file mode 100644 index 0000000..dc21706 --- /dev/null +++ b/src/framework/input/inputs/base-input.js @@ -0,0 +1,12 @@ +export class BaseInput { + constructor(element) { + this.element = element; + } + getState() { } + capture(preventDefault) { + return; + } + release() { + return; + } +} diff --git a/src/framework/input/inputs/joystick.d.ts b/src/framework/input/inputs/joystick.d.ts new file mode 100644 index 0000000..1c4cdd8 --- /dev/null +++ b/src/framework/input/inputs/joystick.d.ts @@ -0,0 +1,6 @@ +import { BaseInput } from './base-input'; +export declare class Joystick extends BaseInput { + constructor(element: HTMLElement); +} +export interface IJoystick { +} diff --git a/src/framework/input/inputs/joystick.js b/src/framework/input/inputs/joystick.js new file mode 100644 index 0000000..76e4e7b --- /dev/null +++ b/src/framework/input/inputs/joystick.js @@ -0,0 +1,6 @@ +import { BaseInput } from './base-input'; +export class Joystick extends BaseInput { + constructor(element) { + super(element); + } +} diff --git a/src/framework/input/inputs/keyboard.d.ts b/src/framework/input/inputs/keyboard.d.ts new file mode 100644 index 0000000..915d9c9 --- /dev/null +++ b/src/framework/input/inputs/keyboard.d.ts @@ -0,0 +1,18 @@ +import { BaseInput } from './base-input'; +export declare class Keyboard extends BaseInput { + private keysDown; + private keysJustPressed; + private keysJustReleased; + private preventDefault; + constructor(element: HTMLElement); + capture(preventDefault: boolean): void; + release(): void; + getState(): IKeyboard; + private handleKeyDown; + private handleKeyUp; +} +export interface IKeyboard { + keysDown: Map; + keysJustPressed: Map; + keysJustReleased: Map; +} diff --git a/src/framework/input/inputs/keyboard.js b/src/framework/input/inputs/keyboard.js new file mode 100644 index 0000000..7ea29c7 --- /dev/null +++ b/src/framework/input/inputs/keyboard.js @@ -0,0 +1,50 @@ +import { BaseInput } from './base-input'; +export class Keyboard extends BaseInput { + constructor(element) { + super(element); + this.keysDown = new Map(); + this.keysJustPressed = new Map(); + this.keysJustReleased = new Map(); + this.handleKeyDown = this.handleKeyDown.bind(this); + this.handleKeyUp = this.handleKeyUp.bind(this); + } + capture(preventDefault) { + this.active = true; + this.preventDefault = preventDefault; + this.element.addEventListener('keydown', this.handleKeyDown); + this.element.addEventListener('keyup', this.handleKeyUp); + } + release() { + this.active = false; + this.element.removeEventListener('keydown', this.handleKeyDown); + this.element.removeEventListener('keyup', this.handleKeyUp); + } + getState() { + const state = { + keysDown: new Map(this.keysDown), + keysJustPressed: new Map(this.keysJustPressed), + keysJustReleased: new Map(this.keysJustReleased) + }; + this.keysJustPressed.clear(); + this.keysJustReleased.clear(); + return state; + } + handleKeyDown(event) { + if (this.active && this.preventDefault) + event.preventDefault(); + if (this.keysDown.get(event.keyCode)) + return; + this.keysDown.set(event.keyCode, true); + this.keysJustPressed.set(event.keyCode, true); + this.keysJustReleased.set(event.keyCode, false); + } + handleKeyUp(event) { + if (this.active && this.preventDefault) + event.preventDefault(); + if (!this.keysDown.get(event.keyCode)) + return; + this.keysDown.set(event.keyCode, false); + this.keysJustPressed.set(event.keyCode, false); + this.keysJustReleased.set(event.keyCode, true); + } +} diff --git a/src/framework/input/inputs/mouse.d.ts b/src/framework/input/inputs/mouse.d.ts new file mode 100644 index 0000000..51ad7ea --- /dev/null +++ b/src/framework/input/inputs/mouse.d.ts @@ -0,0 +1,34 @@ +import { BaseInput } from './base-input'; +export declare class Mouse extends BaseInput { + private mousePosition; + private mouseDelta; + private mouseWheel; + private mouseButtons; + constructor(element: HTMLElement); + capture(): void; + release(): void; + getState(): IMouse; + private handleMouseDown; + private handleMouseMove; + private handleMouseUp; + private handlePointerChange; +} +export declare class Position { + x: number; + y: number; +} +export declare class MouseButtons { + keysDown: Map; + keysJustPressed: Map; + keysJustReleased: Map; +} +export declare class Delta { + x: number; + y: number; +} +export interface IMouse { + mouseButtons: MouseButtons; + mousePosition: Position; + mouseWheel: Delta; + mouseDelta: Delta; +} diff --git a/src/framework/input/inputs/mouse.js b/src/framework/input/inputs/mouse.js new file mode 100644 index 0000000..e14b4e9 --- /dev/null +++ b/src/framework/input/inputs/mouse.js @@ -0,0 +1,87 @@ +import { BaseInput } from './base-input'; +export class Mouse extends BaseInput { + constructor(element) { + super(element); + this.mousePosition = new Position(); + this.mouseDelta = new Delta(); + this.mouseWheel = new Delta(); + this.mouseButtons = new MouseButtons(); + } + capture() { + this.handleMouseDown = this.handleMouseDown.bind(this); + this.handleMouseMove = this.handleMouseMove.bind(this); + this.handleMouseUp = this.handleMouseUp.bind(this); + this.handlePointerChange = this.handlePointerChange.bind(this); + this.active = true; + this.element.addEventListener('mousedown', this.handleMouseDown); + this.element.addEventListener('mousemove', this.handleMouseMove); + this.element.addEventListener('mouseup', this.handleMouseUp); + document.addEventListener('pointerlockchange', this.handlePointerChange); + } + release() { + this.active = false; + this.element.removeEventListener('mousedown', this.handleMouseDown); + this.element.removeEventListener('mousemove', this.handleMouseMove); + this.element.removeEventListener('mouseup', this.handleMouseUp); + } + getState() { + const { mouseButtons, mouseDelta, mousePosition, mouseWheel } = this; + const state = { + mouseButtons: { + keysDown: new Map(this.mouseButtons.keysDown), + keysJustPressed: new Map(this.mouseButtons.keysJustPressed), + keysJustReleased: new Map(this.mouseButtons.keysJustReleased) + }, + mouseDelta, + mousePosition, + mouseWheel + }; + this.mouseButtons.keysJustPressed.clear(); + this.mouseButtons.keysJustReleased.clear(); + this.mouseDelta.x = 0; + this.mouseDelta.y = 0; + return state; + } + handleMouseDown(event) { + if (this.active) + event.preventDefault(); + this.mouseButtons.keysDown.set(event.button, true); + this.mouseButtons.keysJustPressed.set(event.button, true); + this.mouseButtons.keysJustReleased.set(event.button, false); + } + handleMouseMove(event) { + if (this.active) + event.preventDefault(); + this.mousePosition.x = event.clientX; + this.mousePosition.y = event.clientY; + this.mouseDelta.x = event.movementX; + this.mouseDelta.y = event.movementY; + } + handleMouseUp(event) { + if (this.active) + event.preventDefault(); + this.mouseButtons.keysJustReleased.set(event.button, true); + this.mouseButtons.keysDown.set(event.button, false); + this.mouseButtons.keysJustPressed.set(event.button, false); + } + handlePointerChange() { + if (document.pointerLockElement !== this.element) { + this.element.addEventListener('click', () => { + this.element.requestPointerLock(); + }, { + once: true + }); + } + } +} +export class Position { +} +export class MouseButtons { + constructor() { + this.keysDown = new Map(); + this.keysJustPressed = new Map(); + this.keysJustReleased = new Map(); + } +} +export class Delta { +} diff --git a/src/framework/input/interfaces/state.d.ts b/src/framework/input/interfaces/state.d.ts new file mode 100644 index 0000000..55fe5d4 --- /dev/null +++ b/src/framework/input/interfaces/state.d.ts @@ -0,0 +1,4 @@ +import { IKeyboard } from '../inputs/keyboard'; +export interface State { + keyboard?: IKeyboard; +} diff --git a/src/framework/input/interfaces/state.js b/src/framework/input/interfaces/state.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/input/interfaces/state.js @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/input/keycodes.d.ts b/src/framework/input/keycodes.d.ts new file mode 100644 index 0000000..3e0ca85 --- /dev/null +++ b/src/framework/input/keycodes.d.ts @@ -0,0 +1,122 @@ +export declare let KeyCodes: { + CANCEL: number; + HELP: number; + BACK_SPACE: number; + TAB: number; + CLEAR: number; + RETURN: number; + ENTER: number; + SHIFT: number; + CONTROL: number; + ALT: number; + PAUSE: number; + CAPS_LOCK: number; + ESCAPE: number; + SPACE: number; + PAGE_UP: number; + PAGE_DOWN: number; + END: number; + HOME: number; + LEFT: number; + UP: number; + RIGHT: number; + DOWN: number; + PRINTSCREEN: number; + INSERT: number; + DELETE: number; + 0: number; + 1: number; + 2: number; + 3: number; + 4: number; + 5: number; + 6: number; + 7: number; + 8: number; + 9: number; + SEMICOLON: number; + EQUALS: number; + A: number; + B: number; + C: number; + D: number; + E: number; + F: number; + G: number; + H: number; + I: number; + J: number; + K: number; + L: number; + M: number; + N: number; + O: number; + P: number; + Q: number; + R: number; + S: number; + T: number; + U: number; + V: number; + W: number; + X: number; + Y: number; + Z: number; + CONTEXT_MENU: number; + NUMPAD0: number; + NUMPAD1: number; + NUMPAD2: number; + NUMPAD3: number; + NUMPAD4: number; + NUMPAD5: number; + NUMPAD6: number; + NUMPAD7: number; + NUMPAD8: number; + NUMPAD9: number; + MULTIPLY: number; + ADD: number; + SEPARATOR: number; + SUBTRACT: number; + DECIMAL: number; + DIVIDE: number; + F1: number; + F2: number; + F3: number; + F4: number; + F5: number; + F6: number; + F7: number; + F8: number; + F9: number; + F10: number; + F11: number; + F12: number; + F13: number; + F14: number; + F15: number; + F16: number; + F17: number; + F18: number; + F19: number; + F20: number; + F21: number; + F22: number; + F23: number; + F24: number; + NUM_LOCK: number; + SCROLL_LOCK: number; + COMMA: number; + PERIOD: number; + SLASH: number; + BACK_QUOTE: number; + OPEN_BRACKET: number; + BACK_SLASH: number; + CLOSE_BRACKET: number; + QUOTE: number; + META: number; +}; +export declare let MouseCodes: { + LEFT: number; + RIGHT: number; + MIDDLE: number; +}; diff --git a/src/framework/input/keycodes.js b/src/framework/input/keycodes.js new file mode 100644 index 0000000..6984435 --- /dev/null +++ b/src/framework/input/keycodes.js @@ -0,0 +1,122 @@ +export let KeyCodes = { + CANCEL: 3, + HELP: 6, + BACK_SPACE: 8, + TAB: 9, + CLEAR: 12, + RETURN: 13, + ENTER: 14, + SHIFT: 16, + CONTROL: 17, + ALT: 18, + PAUSE: 19, + CAPS_LOCK: 20, + ESCAPE: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + PRINTSCREEN: 44, + INSERT: 45, + DELETE: 46, + 0: 48, + 1: 49, + 2: 50, + 3: 51, + 4: 52, + 5: 53, + 6: 54, + 7: 55, + 8: 56, + 9: 57, + SEMICOLON: 59, + EQUALS: 61, + A: 65, + B: 66, + C: 67, + D: 68, + E: 69, + F: 70, + G: 71, + H: 72, + I: 73, + J: 74, + K: 75, + L: 76, + M: 77, + N: 78, + O: 79, + P: 80, + Q: 81, + R: 82, + S: 83, + T: 84, + U: 85, + V: 86, + W: 87, + X: 88, + Y: 89, + Z: 90, + CONTEXT_MENU: 93, + NUMPAD0: 96, + NUMPAD1: 97, + NUMPAD2: 98, + NUMPAD3: 99, + NUMPAD4: 100, + NUMPAD5: 101, + NUMPAD6: 102, + NUMPAD7: 103, + NUMPAD8: 104, + NUMPAD9: 105, + MULTIPLY: 106, + ADD: 107, + SEPARATOR: 108, + SUBTRACT: 109, + DECIMAL: 110, + DIVIDE: 111, + F1: 112, + F2: 113, + F3: 114, + F4: 115, + F5: 116, + F6: 117, + F7: 118, + F8: 119, + F9: 120, + F10: 121, + F11: 122, + F12: 123, + F13: 124, + F14: 125, + F15: 126, + F16: 127, + F17: 128, + F18: 129, + F19: 130, + F20: 131, + F21: 132, + F22: 133, + F23: 134, + F24: 135, + NUM_LOCK: 144, + SCROLL_LOCK: 145, + COMMA: 188, + PERIOD: 190, + SLASH: 191, + BACK_QUOTE: 192, + OPEN_BRACKET: 219, + BACK_SLASH: 220, + CLOSE_BRACKET: 221, + QUOTE: 222, + META: 224 +}; +export let MouseCodes = { + LEFT: 0, + RIGHT: 1, + MIDDLE: 2 +}; diff --git a/src/framework/package.json b/src/framework/package.json new file mode 100644 index 0000000..f3248d1 --- /dev/null +++ b/src/framework/package.json @@ -0,0 +1,33 @@ +{ + "name": "audiogame-tools", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "lint": "eslint . --ext .ts", + "format": "prettier --config .prettierrc engine/**/*.ts --write", + "compile:engine": "tsc", + "watch:engine": "tsc -w", + "bundle:engine": "webpack --config webpack.engine.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "eventemitter3": "^4.0.7", + "yaml": "^1.10.0" + }, + "devDependencies": { + "@babel/core": "^7.12.3", + "@babel/preset-env": "^7.12.1", + "@typescript-eslint/eslint-plugin": "^4.7.0", + "@typescript-eslint/parser": "^4.7.0", + "eslint": "^7.13.0", + "prettier": "^2.1.2", + "ts-loader": "^8.0.11", + "typescript": "^4.0.5", + "webpack": "^5.4.0", + "webpack-cli": "^4.2.0" + } +} diff --git a/src/framework/physics/aabb.d.ts b/src/framework/physics/aabb.d.ts new file mode 100644 index 0000000..31fa46c --- /dev/null +++ b/src/framework/physics/aabb.d.ts @@ -0,0 +1,2 @@ +import { PhysicsObject } from './object'; +export declare function AABB(obj1: PhysicsObject, obj2: PhysicsObject): boolean; diff --git a/src/framework/physics/aabb.js b/src/framework/physics/aabb.js new file mode 100644 index 0000000..d9c5ab0 --- /dev/null +++ b/src/framework/physics/aabb.js @@ -0,0 +1,17 @@ +export function AABB(obj1, obj2) { + if (checkOverlap(obj1.position.x, obj1.dimensions.x, obj2.position.x, obj2.dimensions.x)) { + return true; + } + else if (checkOverlap(obj1.position.y, obj1.dimensions.y, obj2.position.y, obj2.dimensions.y)) { + return true; + } + else if (checkOverlap(obj1.position.z, obj1.dimensions.z, obj2.position.z, obj2.dimensions.z)) { + return true; + } + return false; +} +function checkOverlap(x, w, yx, yw) { + if (x > yx || x < yx + yw || x + w > x || x + w < yx + yw) { + return true; + } +} diff --git a/src/framework/physics/index.d.ts b/src/framework/physics/index.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/physics/index.js b/src/framework/physics/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/physics/object.d.ts b/src/framework/physics/object.d.ts new file mode 100644 index 0000000..564fa43 --- /dev/null +++ b/src/framework/physics/object.d.ts @@ -0,0 +1,7 @@ +import { Vec3 } from './vec3'; +export declare class PhysicsObject { + position: Vec3; + dimensions: Vec3; + velocity: Vec3; + affectedByGravity: boolean; +} diff --git a/src/framework/physics/object.js b/src/framework/physics/object.js new file mode 100644 index 0000000..01da65f --- /dev/null +++ b/src/framework/physics/object.js @@ -0,0 +1,2 @@ +export class PhysicsObject { +} diff --git a/src/framework/physics/octree.d.ts b/src/framework/physics/octree.d.ts new file mode 100644 index 0000000..7da8ee5 --- /dev/null +++ b/src/framework/physics/octree.d.ts @@ -0,0 +1,39 @@ +import { PhysicsObject } from './object'; +import { Vec3 } from './vec3'; +export declare class Octree { + private dimensions; + private maxObjects; + private maxLevels; + root: OcTreeNode; + constructor(dimensions: Vec3, maxObjects?: number, maxLevels?: number); + insert(obj: PhysicsObject): void; + find(position: Vec3, dimensions: Vec3): PhysicsObject[]; +} +export declare class OcTreeNode { + private position; + private dimensions; + private maxLevels; + private maxObjects; + private currentLevel; + objects: PhysicsObject[]; + nodes: OcTreeNode[]; + constructor(position: Vec3, dimensions: Vec3, maxLevels: number, maxObjects: number, currentLevel?: number); + insert(obj: PhysicsObject): any; + find(x: number, y: number, z: number, xw: number, yh: number, zd: number): PhysicsObject[]; + split(): void; + private distributeObjectsToNodes; + getIndex(x: number, y: number, z: number): Direction; + getIndeciesForRect(x: number, y: number, z: number, xw: number, yh: number, zd: number): Direction[]; +} +declare enum Direction { + AboveUpperLeft = 0, + AboveUpperRight = 1, + AboveLowerRight = 2, + AboveLowerLeft = 3, + BelowUpperLeft = 4, + BelowUpperRight = 5, + BelowLowerRight = 6, + BelowLowerLeft = 7, + Here = 8 +} +export {}; diff --git a/src/framework/physics/octree.js b/src/framework/physics/octree.js new file mode 100644 index 0000000..8e1c511 --- /dev/null +++ b/src/framework/physics/octree.js @@ -0,0 +1,206 @@ +import { Vec3 } from './vec3'; +export class Octree { + constructor(dimensions, maxObjects = 10, maxLevels = 10) { + this.dimensions = dimensions; + this.maxObjects = maxObjects; + this.maxLevels = maxLevels; + this.root = new OcTreeNode(new Vec3({ + x: 0, + y: 0, + z: 0 + }), this.dimensions, maxLevels, maxObjects, 0); + } + insert(obj) { + this.root.insert(obj); + } + find(position, dimensions) { + return this.root.find(position.x, position.y, position.z, dimensions.x, dimensions.y, dimensions.z); + } +} +export class OcTreeNode { + constructor(position, dimensions, maxLevels, maxObjects, currentLevel = 0) { + this.position = position; + this.dimensions = dimensions; + this.maxLevels = maxLevels; + this.maxObjects = maxObjects; + this.currentLevel = currentLevel; + this.objects = []; + this.nodes = []; + } + insert(obj) { + const index = this.getIndex(obj.position.x, obj.position.y, obj.position.z); + if (index === Direction.Here) { + this.objects.push(obj); + } + else { + return this.nodes[index].insert(obj); + } + if (this.objects.length > this.maxObjects && + this.currentLevel < this.maxLevels) { + this.split(); + } + } + find(x, y, z, xw, yh, zd) { + if (this.nodes.length < 1) { + return this.objects; + } + const indecies = this.getIndeciesForRect(x, y, z, xw, yh, zd); + let results = []; + for (let i = 0; i < indecies.length - 1; i++) { + let res = this.nodes[indecies[i]].find(x, y, z, xw, yh, zd); + results.push([...res]); + } + return results; + } + split() { + const halfWidth = this.dimensions.x / 2; + const halfHeight = this.dimensions.y / 2; + const halfDepth = this.dimensions.z / 2; + this.nodes[Direction.AboveUpperLeft] = new OcTreeNode(new Vec3({ + x: this.position.x, + y: this.position.y, + z: this.position.z + halfDepth + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects, this.currentLevel++); + this.nodes[Direction.AboveUpperRight] = new OcTreeNode(new Vec3({ + x: this.position.x + halfWidth, + y: this.position.y, + z: this.position.z + halfDepth + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects, this.currentLevel++); + this.nodes[Direction.AboveLowerRight] = new OcTreeNode(new Vec3({ + x: this.position.x + halfWidth, + y: this.position.y + halfHeight, + z: this.position.z + halfDepth + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects, this.currentLevel++); + this.nodes[Direction.AboveLowerLeft] = new OcTreeNode(new Vec3({ + x: this.position.x, + y: this.position.y + halfHeight, + z: this.position.z + halfDepth + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects, this.currentLevel++); + this.nodes[Direction.BelowUpperLeft] = new OcTreeNode(new Vec3({ + x: this.position.x, + y: this.position.y, + z: this.position.z + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects, this.currentLevel++); + this.nodes[Direction.BelowUpperRight] = new OcTreeNode(new Vec3({ + x: this.position.x + halfWidth, + y: this.position.y, + z: this.position.z + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects, this.currentLevel++); + this.nodes[Direction.BelowLowerRight] = new OcTreeNode(new Vec3({ + x: this.position.x + halfWidth, + y: this.position.y + halfHeight, + z: this.position.z + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects, this.currentLevel++); + this.nodes[Direction.BelowLowerLeft] = new OcTreeNode(new Vec3({ + x: this.position.x, + y: this.position.y + halfHeight, + z: this.position.z + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects, this.currentLevel++); + this.distributeObjectsToNodes(); + } + distributeObjectsToNodes() { + if (this.nodes.length < 8) { + this.split(); + return; + } + this.objects.forEach((obj) => { + const direction = this.getIndex(obj.position.x, obj.position.y, obj.position.z); + this.nodes[direction].insert(obj); + }); + this.objects = []; + } + getIndex(x, y, z) { + if (this.nodes.length === 0) { + return Direction.Here; + } + const halfWidth = this.dimensions.x / 2; + const halfHeight = this.dimensions.y / 2; + const halfDepth = this.dimensions.z / 2; + const isBelow = z < this.position.z + halfDepth; + const isLeft = x < this.position.x + halfWidth; + const isUpper = y > this.position.y + halfHeight; + if (isBelow) { + if (isLeft) { + if (isUpper) + return Direction.AboveUpperLeft; + else + return Direction.AboveLowerLeft; + } + else { + if (isUpper) + return Direction.AboveUpperRight; + else + return Direction.AboveLowerRight; + } + } + else { + if (isLeft) { + if (isUpper) + return Direction.BelowUpperLeft; + else + return Direction.BelowLowerLeft; + } + else { + if (isUpper) + return Direction.BelowUpperRight; + else + return Direction.BelowLowerRight; + } + } + } + getIndeciesForRect(x, y, z, xw, yh, zd) { + if (!(x > this.position.x && x < this.position.x + this.dimensions.x) || + !(y > this.position.y && y < this.position.y + this.dimensions.y) || + !(z > this.position.z && z < this.position.z + this.dimensions.z)) { + return []; + } + let indecies = []; + indecies.push(this.getIndex(x, y, z)); + indecies.push(this.getIndex(x + xw, y + yh, z + zd)); + return indecies; + } +} +var Direction; +(function (Direction) { + Direction[Direction["AboveUpperLeft"] = 0] = "AboveUpperLeft"; + Direction[Direction["AboveUpperRight"] = 1] = "AboveUpperRight"; + Direction[Direction["AboveLowerRight"] = 2] = "AboveLowerRight"; + Direction[Direction["AboveLowerLeft"] = 3] = "AboveLowerLeft"; + Direction[Direction["BelowUpperLeft"] = 4] = "BelowUpperLeft"; + Direction[Direction["BelowUpperRight"] = 5] = "BelowUpperRight"; + Direction[Direction["BelowLowerRight"] = 6] = "BelowLowerRight"; + Direction[Direction["BelowLowerLeft"] = 7] = "BelowLowerLeft"; + Direction[Direction["Here"] = 8] = "Here"; +})(Direction || (Direction = {})); diff --git a/src/framework/physics/octtree.d.ts b/src/framework/physics/octtree.d.ts new file mode 100644 index 0000000..17b248d --- /dev/null +++ b/src/framework/physics/octtree.d.ts @@ -0,0 +1,38 @@ +import { PhysicsObject } from "./object"; +import { Vec3 } from "./vec3"; +export declare class Octtree { + private dimensions; + private maxObjects; + private maxLevels; + root: OctTreeNode; + constructor(dimensions: Vec3, maxObjects?: number, maxLevels?: number); + insert(obj: PhysicsObject): void; + find(position: Vec3, dimensions: Vec3): PhysicsObject[]; +} +export declare class OctTreeNode { + private position; + private dimensions; + private maxLevels; + private maxObjects; + objects: PhysicsObject[]; + nodes: OctTreeNode[]; + constructor(position: Vec3, dimensions: Vec3, maxLevels: number, maxObjects: number); + insert(obj: PhysicsObject): void; + find(position: Vec3, dimensions: Vec3): PhysicsObject[]; + split(): void; + private distributeObjectsToNodes; + getIndex(x: number, y: number, z: number): Direction; + getIndeciesForRect(position: Vec3, dimensions: Vec3): Set; +} +declare enum Direction { + AboveUpperLeft = 0, + AboveUpperRight = 1, + AboveLowerRight = 2, + AboveLowerLeft = 3, + BelowUpperLeft = 4, + BelowUpperRight = 5, + BelowLowerRight = 6, + BelowLowerLeft = 7, + Here = 8 +} +export {}; diff --git a/src/framework/physics/octtree.js b/src/framework/physics/octtree.js new file mode 100644 index 0000000..46feed5 --- /dev/null +++ b/src/framework/physics/octtree.js @@ -0,0 +1,193 @@ +import { Vec3 } from "./vec3"; +export class Octtree { + constructor(dimensions, maxObjects = 10, maxLevels = 10) { + this.dimensions = dimensions; + this.maxObjects = maxObjects; + this.maxLevels = maxLevels; + this.root = new OctTreeNode(new Vec3({ + x: 0, + y: 0, + z: 0 + }), this.dimensions, maxLevels, maxObjects); + } + insert(obj) { + this.root.insert(obj); + } + find(position, dimensions) { + return this.root.find(position, dimensions); + } +} +export class OctTreeNode { + constructor(position, dimensions, maxLevels, maxObjects) { + this.position = position; + this.dimensions = dimensions; + this.maxLevels = maxLevels; + this.maxObjects = maxObjects; + this.objects = []; + this.nodes = []; + } + insert(obj) { + this.objects.push(obj); + if (this.objects.length > this.maxObjects) { + this.split(); + } + } + find(position, dimensions) { + if (!this.nodes.length) { + return this.objects; + } + const indecies = this.getIndeciesForRect(position, dimensions); + let results = []; + indecies.forEach((index) => { + let res = this.nodes[index].find(position, dimensions); + res.forEach((obj) => results.push(obj)); + }); + return results; + } + split() { + const halfWidth = this.dimensions.x / 2; + const halfHeight = this.dimensions.y / 2; + const halfDepth = this.dimensions.z / 2; + this.nodes[Direction.AboveUpperLeft] = new OctTreeNode(new Vec3({ + x: this.position.x, + y: this.position.y, + z: this.position.z + halfDepth + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects); + this.nodes[Direction.AboveUpperRight] = new OctTreeNode(new Vec3({ + x: this.position.x + halfWidth, + y: this.position.y, + z: this.position.z + halfDepth + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects); + this.nodes[Direction.AboveLowerRight] = new OctTreeNode(new Vec3({ + x: this.position.x + halfWidth, + y: this.position.y + halfHeight, + z: this.position.z + halfDepth + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects); + this.nodes[Direction.AboveLowerLeft] = new OctTreeNode(new Vec3({ + x: this.position.x, + y: this.position.y + halfHeight, + z: this.position.z + halfDepth + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects); + this.nodes[Direction.BelowUpperLeft] = new OctTreeNode(new Vec3({ + x: this.position.x, + y: this.position.y, + z: this.position.z + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects); + this.nodes[Direction.BelowUpperRight] = new OctTreeNode(new Vec3({ + x: this.position.x + halfWidth, + y: this.position.y, + z: this.position.z + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects); + this.nodes[Direction.BelowLowerRight] = new OctTreeNode(new Vec3({ + x: this.position.x + halfWidth, + y: this.position.y + halfHeight, + z: this.position.z + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects); + this.nodes[Direction.BelowLowerLeft] = new OctTreeNode(new Vec3({ + x: this.position.x, + y: this.position.y + halfHeight, + z: this.position.z + }), new Vec3({ + x: halfWidth, + y: halfHeight, + z: halfDepth + }), this.maxLevels, this.maxObjects); + this.distributeObjectsToNodes(); + } + distributeObjectsToNodes() { + if (this.nodes.length < 8) { + this.split(); + return; + } + this.objects.forEach((obj) => { + const direction = this.getIndex(obj.position.x, obj.position.y, obj.position.z); + this.nodes[direction].insert(obj); + }); + this.objects = []; + } + getIndex(x, y, z) { + if (this.nodes.length === 0) { + return Direction.Here; + } + const halfWidth = this.dimensions.x / 2; + const halfHeight = this.dimensions.y / 2; + const halfDepth = this.dimensions.z / 2; + const isBelow = (z < this.position.z + halfDepth); + const isLeft = (x < this.position.x + halfWidth); + const isUpper = (y > this.position.y + halfHeight); + if (isBelow) { + if (isLeft) { + if (isUpper) + return Direction.AboveUpperLeft; + else + return Direction.AboveLowerLeft; + } + else { + if (isUpper) + return Direction.AboveUpperRight; + else + return Direction.AboveLowerRight; + } + } + else { + if (isLeft) { + if (isUpper) + return Direction.BelowUpperLeft; + else + return Direction.BelowLowerLeft; + } + else { + if (isUpper) + return Direction.BelowUpperRight; + else + return Direction.BelowLowerRight; + } + } + } + getIndeciesForRect(position, dimensions) { + let indecies = new Set(); + indecies.add(this.getIndex(position.x, position.y, position.z)); + indecies.add(this.getIndex(position.x + dimensions.x, position.y + dimensions.y, position.z + dimensions.z)); + return indecies; + } +} +var Direction; +(function (Direction) { + Direction[Direction["AboveUpperLeft"] = 0] = "AboveUpperLeft"; + Direction[Direction["AboveUpperRight"] = 1] = "AboveUpperRight"; + Direction[Direction["AboveLowerRight"] = 2] = "AboveLowerRight"; + Direction[Direction["AboveLowerLeft"] = 3] = "AboveLowerLeft"; + Direction[Direction["BelowUpperLeft"] = 4] = "BelowUpperLeft"; + Direction[Direction["BelowUpperRight"] = 5] = "BelowUpperRight"; + Direction[Direction["BelowLowerRight"] = 6] = "BelowLowerRight"; + Direction[Direction["BelowLowerLeft"] = 7] = "BelowLowerLeft"; + Direction[Direction["Here"] = 8] = "Here"; +})(Direction || (Direction = {})); diff --git a/src/framework/physics/quadtree.d.ts b/src/framework/physics/quadtree.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/physics/quadtree.js b/src/framework/physics/quadtree.js new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/physics/vec3.d.ts b/src/framework/physics/vec3.d.ts new file mode 100644 index 0000000..f7f50a3 --- /dev/null +++ b/src/framework/physics/vec3.d.ts @@ -0,0 +1,13 @@ +export declare class Vec3 { + x: number; + y: number; + z: number; + constructor(values?: { + x: number; + y: number; + z: number; + }); + add(vector: Vec3): void; + multiply(vector: Vec3): void; + clone(): Vec3; +} diff --git a/src/framework/physics/vec3.js b/src/framework/physics/vec3.js new file mode 100644 index 0000000..abcc389 --- /dev/null +++ b/src/framework/physics/vec3.js @@ -0,0 +1,28 @@ +export class Vec3 { + constructor(values = { + x: 0, + y: 0, + z: 0 + }) { + this.x = values.x; + this.y = values.y; + this.z = values.z; + } + add(vector) { + this.x += vector.x; + this.y += vector.y; + this.z += vector.z; + } + multiply(vector) { + this.x *= vector.x; + this.y *= vector.y; + this.z *= vector.z; + } + clone() { + return new Vec3({ + x: this.x, + y: this.y, + z: this.z + }); + } +} diff --git a/src/framework/physics/world.d.ts b/src/framework/physics/world.d.ts new file mode 100644 index 0000000..4366f98 --- /dev/null +++ b/src/framework/physics/world.d.ts @@ -0,0 +1,23 @@ +import { Octree } from './octree'; +import { EventBus } from '../event-bus'; +import { PhysicsObject } from './object'; +import { Vec3 } from './vec3'; +export declare class World extends EventBus { + objects: PhysicsObject[]; + gravity: Vec3; + dimensions: Vec3; + octreeOptions: OctreeOptions; + constructor(dimensions: Vec3, octreeOptions: OctreeOptions); + setGravity(grav: Vec3): void; + addObject(obj: PhysicsObject): void; + removeObject(obj: PhysicsObject): void; + step(dt: number): void; + checkCollisions(obj: PhysicsObject, octree: Octree): void; +} +interface OctreeOptions { + position: Vec3; + dimensions: Vec3; + maxObjects: number; + maxLevels: number; +} +export {}; diff --git a/src/framework/physics/world.js b/src/framework/physics/world.js new file mode 100644 index 0000000..445a17c --- /dev/null +++ b/src/framework/physics/world.js @@ -0,0 +1,54 @@ +import { Octree } from './octree'; +import { EventBus } from '../event-bus'; +import { Vec3 } from './vec3'; +import { AABB } from './aabb'; +export class World extends EventBus { + constructor(dimensions, octreeOptions) { + super(); + if (!octreeOptions) { + this.octreeOptions = { + position: new Vec3({ x: 0, y: 0, z: 0 }), + dimensions: new Vec3(this.dimensions), + maxLevels: 50, + maxObjects: 50 + }; + } + else { + this.octreeOptions = octreeOptions; + } + this.dimensions = dimensions; + this.objects = []; + } + setGravity(grav) { + this.gravity = grav; + } + addObject(obj) { + this.objects.push(obj); + } + removeObject(obj) { + this.objects = this.objects.filter((val) => val !== obj); + } + step(dt) { + const octree = new Octree(this.octreeOptions.dimensions, this.octreeOptions.maxObjects, this.octreeOptions.maxLevels); + this.objects.forEach((obj) => octree.insert(obj)); + this.objects.forEach((obj) => { + let velocity = obj.velocity.clone(); + velocity.multiply(new Vec3({ x: dt, y: dt, z: dt })); + let gravity = this.gravity.clone(); + gravity.multiply(new Vec3({ x: dt, y: dt, z: dt })); + obj.position.add(velocity); + if (obj.affectedByGravity) { + obj.velocity.add(gravity); + } + this.checkCollisions(obj, octree); + }); + } + checkCollisions(obj, octree) { + const potentialCandidates = octree.find(obj.position, obj.dimensions); + potentialCandidates.forEach((candidate) => { + if (AABB(obj, candidate)) { + this.emit('collision', [obj, candidate]); + } + }); + } +} diff --git a/src/framework/resonator/audio-context.d.ts b/src/framework/resonator/audio-context.d.ts new file mode 100644 index 0000000..b88251b --- /dev/null +++ b/src/framework/resonator/audio-context.d.ts @@ -0,0 +1,11 @@ +export default class ResonatorAudioContext { + private context; + constructor(); + getContext(): AudioContext; + createGain(): any; + getOutputDestination(): AudioNode; + createBufferSource(): AudioBufferSourceNode; + decodeAudioData(data: ArrayBuffer): Promise; + createPanner(): any; + createMediaElementSource(element: HTMLMediaElement): MediaElementAudioSourceNode; +} diff --git a/src/framework/resonator/audio-context.js b/src/framework/resonator/audio-context.js new file mode 100644 index 0000000..fbe0d3b --- /dev/null +++ b/src/framework/resonator/audio-context.js @@ -0,0 +1,39 @@ +// simple wrapper around the AudioContext +// eventually will be used to deal with cross browser issues +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +export default class ResonatorAudioContext { + constructor() { + this.context = new AudioContext(); + } + getContext() { + return this.context; + } + createGain() { + return this.context.createGain(); + } + getOutputDestination() { + return this.context.destination; + } + createBufferSource() { + return this.context.createBufferSource(); + } + decodeAudioData(data) { + return __awaiter(this, void 0, void 0, function* () { + return yield this.context.decodeAudioData(data); + }); + } + createPanner() { + return this.context.createPanner(); + } + createMediaElementSource(element) { + return this.context.createMediaElementSource(element); + } +} diff --git a/src/framework/resonator/audio-graph.d.ts b/src/framework/resonator/audio-graph.d.ts new file mode 100644 index 0000000..22bef61 --- /dev/null +++ b/src/framework/resonator/audio-graph.d.ts @@ -0,0 +1,21 @@ +import ResonatorScene from './scenes/webaudio-scene'; +import ResonatorAudioContext from './audio-context'; +import BaseEffect from './effects/base-effect'; +export default class AudioGraph { + private master; + private effectsBus; + private worldBus; + private secondaryBus; + private effects; + private scene; + private context; + private swapChannels; + private channelSplitter; + private channelMerger; + constructor(scene: ResonatorScene, context: ResonatorAudioContext, swapChannels?: boolean); + init(): void; + connectToMaster(input: any): void; + connectToUI(input: AudioNode): void; + applyEffect(effect: BaseEffect): void; + removeEffect(effect: BaseEffect): void; +} diff --git a/src/framework/resonator/audio-graph.js b/src/framework/resonator/audio-graph.js new file mode 100644 index 0000000..2d72be1 --- /dev/null +++ b/src/framework/resonator/audio-graph.js @@ -0,0 +1,46 @@ +// this is the mixer that takes all the different outputs and mixes them into the 2 busses: +// WorldBus: The directional audio +// SecondaryBus: All the UI and things that are non directional +import EffectChain from './effect-chain'; +export default class AudioGraph { + constructor(scene, context, swapChannels = false) { + this.scene = scene; + this.context = context; + this.swapChannels = swapChannels; + this.init(); + } + init() { + this.effectsBus = this.context.createGain(); + this.worldBus = this.context.createGain(); + this.secondaryBus = this.context.createGain(); + this.master = this.context.createGain(); + this.scene.getOutput().connect(this.worldBus); + // this.worldBus.connect(this.master); + this.worldBus.connect(this.effectsBus); + this.effects = new EffectChain(this.context, this, this.effectsBus, this.master); + this.secondaryBus.connect(this.master); + if (this.swapChannels) { + this.channelSplitter = this.context.getContext().createChannelSplitter(2); + this.channelMerger = this.context.getContext().createChannelMerger(2); + this.master.connect(this.channelSplitter); + this.channelSplitter.connect(this.channelMerger, 0, 1); + this.channelSplitter.connect(this.channelMerger, 1, 0); + this.channelMerger.connect(this.context.getOutputDestination()); + } + else { + this.master.connect(this.context.getOutputDestination()); + } + } + connectToMaster(input) { + input.connect(this.master); + } + connectToUI(input) { + input.connect(this.secondaryBus); + } + applyEffect(effect) { + this.effects.applyEffect(effect); + } + removeEffect(effect) { + this.effects.removeEffect(effect); + } +} diff --git a/src/framework/resonator/data-pool-item.d.ts b/src/framework/resonator/data-pool-item.d.ts new file mode 100644 index 0000000..3dd2f4f --- /dev/null +++ b/src/framework/resonator/data-pool-item.d.ts @@ -0,0 +1,12 @@ +export default class DataPoolItem { + private name; + private data; + private decodedData; + constructor(name: string, data?: any, decodedData?: any); + getData(): any; + setData(data: any): void; + getDecodedData(): any; + setDecodedData(data: any): void; + getName(): string; + setName(name: string): void; +} diff --git a/src/framework/resonator/data-pool-item.js b/src/framework/resonator/data-pool-item.js new file mode 100644 index 0000000..03b6099 --- /dev/null +++ b/src/framework/resonator/data-pool-item.js @@ -0,0 +1,26 @@ +// An item in the data pool +export default class DataPoolItem { + constructor(name, data = null, decodedData = null) { + this.name = name; + this.data = data; + this.decodedData = decodedData; + } + getData() { + return this.data; + } + setData(data) { + this.data = data; + } + getDecodedData() { + return this.decodedData; + } + setDecodedData(data) { + this.decodedData = this.decodedData; + } + getName() { + return this.name; + } + setName(name) { + this.name = name; + } +} diff --git a/src/framework/resonator/data-pool.d.ts b/src/framework/resonator/data-pool.d.ts new file mode 100644 index 0000000..6478e98 --- /dev/null +++ b/src/framework/resonator/data-pool.d.ts @@ -0,0 +1,12 @@ +import EventEmitter from 'eventemitter3'; +import ResonatorAudioContext from './audio-context'; +import { BaseLoader } from './loaders/base-loader'; +export default class DataPool extends EventEmitter { + private loader; + private data; + private maxData; + private context; + constructor(context: ResonatorAudioContext, loader?: BaseLoader, maxData?: number); + get(path: string): Promise; + clear(): void; +} diff --git a/src/framework/resonator/data-pool.js b/src/framework/resonator/data-pool.js new file mode 100644 index 0000000..c8109f8 --- /dev/null +++ b/src/framework/resonator/data-pool.js @@ -0,0 +1,48 @@ +// a data pool holds frequently played sounds in memory together with decoded audio data to no longer have to decode them from the cache when loaded again +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import EventEmitter from 'eventemitter3'; +import DataPoolItem from './data-pool-item'; +import { HTTPLoader } from './loaders/http-loader'; +export default class DataPool extends EventEmitter { + constructor(context, loader = new HTTPLoader(), maxData = 512) { + super(); + this.loader = loader; + this.data = {}; + this.maxData = maxData; + this.context = context; + } + get(path) { + return __awaiter(this, void 0, void 0, function* () { + if (this.data[path]) { + return this.data[path].getDecodedData(); + } + else { + const buffer = yield this.loader.get(path); + const decoded = yield this.context.decodeAudioData(buffer); + const item = new DataPoolItem(path, buffer, decoded); + const length = Object.keys(this.data).length; + if (length < this.maxData) { + this.data[path] = item; + } + else { + // TODO: figure out a more clever solution than just removing the first loaded data. Like tracking how much certain data is needed and prioritize them. + // const paths: string[] = Object.keys(this.data); + // delete this.data[paths[0]]; + this.data[path] = item; + } + return item.getDecodedData(); + } + }); + } + clear() { + this.data = {}; + } +} diff --git a/src/framework/resonator/effect-bus.d.ts b/src/framework/resonator/effect-bus.d.ts new file mode 100644 index 0000000..c940477 --- /dev/null +++ b/src/framework/resonator/effect-bus.d.ts @@ -0,0 +1,8 @@ +import ResonatorAudioContext from './audio-context'; +export default class EffectBus { + private context; + private inputNode; + private channelMerger; + constructor(context: ResonatorAudioContext, input: AudioNode, output: AudioNode); + connect(node: AudioNode): void; +} diff --git a/src/framework/resonator/effect-bus.js b/src/framework/resonator/effect-bus.js new file mode 100644 index 0000000..48c0fe5 --- /dev/null +++ b/src/framework/resonator/effect-bus.js @@ -0,0 +1,12 @@ +// Currently unused, but eventually all the effect stuff will be moved from audio graph to here to make it easier to work on +export default class EffectBus { + constructor(context, input, output) { + this.context = context; + this.inputNode = input; + this.channelMerger = this.context.getContext().createChannelMerger(1); + this.inputNode.connect(this.channelMerger); + } + connect(node) { + this.channelMerger.connect(node); + } +} diff --git a/src/framework/resonator/effect-chain.d.ts b/src/framework/resonator/effect-chain.d.ts new file mode 100644 index 0000000..7eab8de --- /dev/null +++ b/src/framework/resonator/effect-chain.d.ts @@ -0,0 +1,14 @@ +import ResonatorAudioContext from './audio-context'; +import AudioGraph from './audio-graph'; +import BaseEffect from './effects/base-effect'; +export default class EffectChain { + private context; + private graph; + private effects; + private inputNode; + private outputNode; + constructor(context: ResonatorAudioContext, graph: AudioGraph, input: AudioNode, output: AudioNode); + applyEffect(effect: BaseEffect): void; + removeEffect(effect: BaseEffect): void; + private updateConnections; +} diff --git a/src/framework/resonator/effect-chain.js b/src/framework/resonator/effect-chain.js new file mode 100644 index 0000000..7e61834 --- /dev/null +++ b/src/framework/resonator/effect-chain.js @@ -0,0 +1,45 @@ +// A chain of effects that connect to the effect bus +export default class EffectChain { + constructor(context, graph, input, output) { + this.effects = []; + this.context = context; + this.graph = graph; + this.inputNode = input; + this.outputNode = output; + this.updateConnections(); + } + applyEffect(effect) { + this.effects.push(effect); + this.updateConnections(); + } + removeEffect(effect) { + this.effects.forEach((currEffect) => { + if (effect === currEffect) { + currEffect.disconnect(); + } + }); + this.effects = this.effects.filter((currEffect) => effect !== currEffect); + this.updateConnections(); + } + updateConnections() { + if (this.effects.length == 0) { + this.inputNode.connect(this.outputNode); + return; + } + let current = null; + let previous = null; + this.effects.forEach((effect) => { + current = effect; + if (previous) { + current.connectInput(previous.getOutput()); + } + else { + current.connectInput(this.inputNode); + } + previous = current; + }); + if (current) { + current.connectOutput(this.outputNode); + } + } +} diff --git a/src/framework/resonator/effects/base-effect.d.ts b/src/framework/resonator/effects/base-effect.d.ts new file mode 100644 index 0000000..3a163ff --- /dev/null +++ b/src/framework/resonator/effects/base-effect.d.ts @@ -0,0 +1,15 @@ +import ResonatorAudioContext from '../audio-context'; +import AudioGraph from '../audio-graph'; +export default class BaseEffect { + protected ready: boolean; + protected effectNode: any; + protected effectParams: any; + protected context: ResonatorAudioContext; + protected graph: AudioGraph; + protected inputNode: AudioNode; + constructor(context: ResonatorAudioContext, graph: AudioGraph, params: any); + connectOutput(node: AudioNode): void; + connectInput(node: AudioNode): void; + getOutput(): AudioNode; + disconnect(): void; +} diff --git a/src/framework/resonator/effects/base-effect.js b/src/framework/resonator/effects/base-effect.js new file mode 100644 index 0000000..2622e95 --- /dev/null +++ b/src/framework/resonator/effects/base-effect.js @@ -0,0 +1,23 @@ +export default class BaseEffect { + constructor(context, graph, params) { + this.graph = graph; + this.context = context; + this.effectParams = params; + } + connectOutput(node) { + this.effectNode.connect(node); + } + connectInput(node) { + this.inputNode = node; + if (this.effectNode) { + this.inputNode.connect(this.effectNode); + } + } + getOutput() { + return this.effectNode; + } + disconnect() { + this.inputNode.disconnect(); + this.effectNode.disconnect(); + } +} diff --git a/src/framework/resonator/effects/convolver.d.ts b/src/framework/resonator/effects/convolver.d.ts new file mode 100644 index 0000000..b61a265 --- /dev/null +++ b/src/framework/resonator/effects/convolver.d.ts @@ -0,0 +1,10 @@ +import BaseEffect from './base-effect'; +import ResonatorAudioContext from '../audio-context'; +import AudioGraph from '../audio-graph'; +export default class Convolver extends BaseEffect { + private buffer; + private channelSplitter; + private channelMerger; + constructor(context: ResonatorAudioContext, graph: AudioGraph, params: any); + connectInput(node: AudioNode): void; +} diff --git a/src/framework/resonator/effects/convolver.js b/src/framework/resonator/effects/convolver.js new file mode 100644 index 0000000..b36cbec --- /dev/null +++ b/src/framework/resonator/effects/convolver.js @@ -0,0 +1,19 @@ +import BaseEffect from './base-effect'; +export default class Convolver extends BaseEffect { + constructor(context, graph, params) { + super(context, graph, params); + console.log(`Creating convolver`); + this.effectNode = this.context.getContext().createConvolver(); + this.effectNode.buffer = this.effectParams.buffer; + } + connectInput(node) { + this.channelSplitter = this.context.getContext().createChannelSplitter(2); + this.channelMerger = this.context.getContext().createChannelMerger(2); + this.channelSplitter.connect(this.channelMerger, 0, 0); + this.channelSplitter.connect(this.channelMerger, 1, 0); + this.channelSplitter.connect(this.channelMerger, 0, 1); + this.channelSplitter.connect(this.channelMerger, 1, 1); + node.connect(this.channelSplitter); + this.channelMerger.connect(this.effectNode); + } +} diff --git a/src/framework/resonator/index.d.ts b/src/framework/resonator/index.d.ts new file mode 100644 index 0000000..7de5ff4 --- /dev/null +++ b/src/framework/resonator/index.d.ts @@ -0,0 +1,22 @@ +import AudioSource from './sources/audio-source'; +import { BaseLoader } from './loaders/base-loader'; +import { BaseSource } from './sources/base-source'; +import { SourceType } from './sources/source-type'; +import { StreamingSource } from './sources/streaming-source'; +export default class Resonator { + private loader; + private context; + private scene; + private graph; + private dataPool; + private environmentImpulse; + constructor(loader?: BaseLoader); + load(path: string, type?: SourceType): Promise; + loadImmediate(path: string, type?: SourceType): AudioSource; + stream(path: string, type?: SourceType): StreamingSource; + private createSource; + setEnvironmentImpulse(file: string): Promise; + setListenerPosition(x: number, y: number, z: number): void; + setListenerOrientation(forward: any, up: any): void; + clearDataPool(): void; +} diff --git a/src/framework/resonator/index.js b/src/framework/resonator/index.js new file mode 100644 index 0000000..0bade61 --- /dev/null +++ b/src/framework/resonator/index.js @@ -0,0 +1,77 @@ +// the main module for Resonator +// API, etc. +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import ResonatorAudioContext from './audio-context'; +import ResonatorScene from './scenes/webaudio-scene'; +import AudioGraph from './audio-graph'; +import AudioSource from './sources/audio-source'; +import DataPool from './data-pool'; +import Convolver from './effects/convolver'; +import { HTTPLoader } from './loaders/http-loader'; +import { SourceType } from './sources/source-type'; +import { StreamingSource } from './sources/streaming-source'; +export default class Resonator { + constructor(loader = new HTTPLoader()) { + this.loader = loader; + this.environmentImpulse = null; + this.context = new ResonatorAudioContext(); + this.scene = new ResonatorScene(this.context); + this.graph = new AudioGraph(this.scene, this.context, false); + this.dataPool = new DataPool(this.context, this.loader); + } + load(path, type = SourceType.WorldSource) { + return __awaiter(this, void 0, void 0, function* () { + const data = yield this.dataPool.get(path); + const source = this.createSource(type, data); + return source; + }); + } + loadImmediate(path, type = SourceType.WorldSource) { + const source = new AudioSource(this.graph, this.scene, this.context, null, type); + this.dataPool.get(path).then((data) => { + source.setBuffer(data); + }); + return source; + } + stream(path, type = SourceType.MasterSource) { + const element = new Audio(path); + element.crossOrigin = 'anonymous'; + const source = new StreamingSource(this.graph, this.scene, this.context, element, type); + return source; + } + createSource(type, data) { + return new AudioSource(this.graph, this.scene, this.context, data); + } + setEnvironmentImpulse(file) { + return __awaiter(this, void 0, void 0, function* () { + if (this.environmentImpulse) { + this.graph.removeEffect(this.environmentImpulse); + } + if (file === null) { + return; + } + const buffer = yield this.dataPool.get(file); + this.environmentImpulse = new Convolver(this.context, this.graph, { + buffer + }); + this.graph.applyEffect(this.environmentImpulse); + }); + } + setListenerPosition(x, y, z) { + this.scene.setListenerPosition(x, y, z); + } + setListenerOrientation(forward, up) { + this.scene.setListenerOrientation(forward, up); + } + clearDataPool() { + this.dataPool.clear(); + } +} diff --git a/src/framework/resonator/loaders/asset-loader.d.ts b/src/framework/resonator/loaders/asset-loader.d.ts new file mode 100644 index 0000000..012be16 --- /dev/null +++ b/src/framework/resonator/loaders/asset-loader.d.ts @@ -0,0 +1,10 @@ +import { AssetManager } from '../../asset-manager'; +import { BaseLoader } from './base-loader'; +export declare class AssetLoader implements BaseLoader { + private name; + private manager; + private assetManager; + constructor(name: string, manager?: AssetManager); + init(): Promise; + get(path: string): Promise; +} diff --git a/src/framework/resonator/loaders/asset-loader.js b/src/framework/resonator/loaders/asset-loader.js new file mode 100644 index 0000000..4b6920e --- /dev/null +++ b/src/framework/resonator/loaders/asset-loader.js @@ -0,0 +1,36 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +// TODO fix path when actually properly linking the packages together +import { AssetManager } from '../../asset-manager'; +export class AssetLoader { + constructor(name, manager = null) { + this.name = name; + this.manager = manager; + if (manager) { + this.assetManager = manager; + } + else { + this.assetManager = new AssetManager(name, ''); + } + } + init() { + return __awaiter(this, void 0, void 0, function* () { + yield this.assetManager.init(); + }); + } + get(path) { + return __awaiter(this, void 0, void 0, function* () { + const result = yield this.assetManager.downloadFile(path); + console.log(result); + const buffer = yield result.arrayBuffer(); + return buffer; + }); + } +} diff --git a/src/framework/resonator/loaders/base-loader.d.ts b/src/framework/resonator/loaders/base-loader.d.ts new file mode 100644 index 0000000..c586898 --- /dev/null +++ b/src/framework/resonator/loaders/base-loader.d.ts @@ -0,0 +1,3 @@ +export interface BaseLoader { + get(path: string): Promise; +} diff --git a/src/framework/resonator/loaders/base-loader.js b/src/framework/resonator/loaders/base-loader.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/resonator/loaders/base-loader.js @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/resonator/loaders/http-loader.d.ts b/src/framework/resonator/loaders/http-loader.d.ts new file mode 100644 index 0000000..a3ff342 --- /dev/null +++ b/src/framework/resonator/loaders/http-loader.d.ts @@ -0,0 +1,4 @@ +import { BaseLoader } from './base-loader'; +export declare class HTTPLoader implements BaseLoader { + get(path: string): Promise; +} diff --git a/src/framework/resonator/loaders/http-loader.js b/src/framework/resonator/loaders/http-loader.js new file mode 100644 index 0000000..de67b13 --- /dev/null +++ b/src/framework/resonator/loaders/http-loader.js @@ -0,0 +1,18 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +export class HTTPLoader { + get(path) { + return __awaiter(this, void 0, void 0, function* () { + const result = yield fetch(path); + const buffer = yield result.arrayBuffer(); + return buffer; + }); + } +} diff --git a/src/framework/resonator/scenes/resonance-scene.d.ts b/src/framework/resonator/scenes/resonance-scene.d.ts new file mode 100644 index 0000000..967d131 --- /dev/null +++ b/src/framework/resonator/scenes/resonance-scene.d.ts @@ -0,0 +1,14 @@ +import ResonatorAudioContext from '../audio-context'; +import EventEmitter from 'eventemitter3'; +export default class Scene extends EventEmitter { + scene: any; + context: ResonatorAudioContext; + listener: AudioListener; + constructor(context: ResonatorAudioContext); + init(): void; + createSource(): any; + getOutput(): any; + getInput(): any; + setListenerPosition(x: number, y: number, z: number): void; + setListenerOrientation(forward: any, rawup: any): void; +} diff --git a/src/framework/resonator/scenes/resonance-scene.js b/src/framework/resonator/scenes/resonance-scene.js new file mode 100644 index 0000000..f885e91 --- /dev/null +++ b/src/framework/resonator/scenes/resonance-scene.js @@ -0,0 +1,40 @@ +// The code that deals with 3d audio +import ResonanceAudio from '../vendor/resonance-es6/main'; +import EventEmitter from 'eventemitter3'; +import vec3 from '../../tsm/vec3'; +export default class Scene extends EventEmitter { + constructor(context) { + super(); + this.context = context; + this.scene = new ResonanceAudio(this.context.getContext(), { + ambisonicOrder: 3 + }); + this.listener = this.context.getContext().listener; + this.init(); + } + init() { + // this.scene.output.connect(this.context.getOutputDestination()); + } + createSource() { + const source = this.scene.createSource(); + return Object.assign(Object.assign({}, source), { getInput: () => source.input }); + } + getOutput() { + return this.scene.output; + } + getInput() { + return this.scene.input; + } + setListenerPosition(x, y, z) { + this.scene.setListenerPosition(x, y, z); + } + setListenerOrientation(forward, rawup) { + let fwd = new vec3([forward.x, forward.y, forward.z]); + let up = fwd.copy(); + vec3.cross(up, new vec3([rawup.x, rawup.y, rawup.z]), up); + vec3.cross(up, fwd, up); + fwd.normalize(); + up.normalize(); + this.scene.setListenerOrientation(forward.x, forward.y, forward.z, rawup.x, rawup.y, rawup.z); + } +} diff --git a/src/framework/resonator/scenes/webaudio-scene.d.ts b/src/framework/resonator/scenes/webaudio-scene.d.ts new file mode 100644 index 0000000..6ae3a1f --- /dev/null +++ b/src/framework/resonator/scenes/webaudio-scene.d.ts @@ -0,0 +1,14 @@ +import ResonatorAudioContext from '../audio-context'; +import EventEmitter from 'eventemitter3'; +export default class ResonatorScene extends EventEmitter { + scene: GainNode; + context: ResonatorAudioContext; + listener: AudioListener; + constructor(context: ResonatorAudioContext); + init(): void; + createSource(): any; + getOutput(): any; + getInput(): any; + setListenerPosition(x: number, y: number, z: number): void; + setListenerOrientation(forward: any, rawup: any): void; +} diff --git a/src/framework/resonator/scenes/webaudio-scene.js b/src/framework/resonator/scenes/webaudio-scene.js new file mode 100644 index 0000000..19fb5a4 --- /dev/null +++ b/src/framework/resonator/scenes/webaudio-scene.js @@ -0,0 +1,42 @@ +// The code that deals with 3d audio +import EventEmitter from 'eventemitter3'; +import vec3 from '../../tsm/vec3'; +export default class ResonatorScene extends EventEmitter { + constructor(context) { + super(); + this.context = context; + this.scene = this.context.getContext().createGain(); + this.listener = this.context.getContext().listener; + this.init(); + } + init() { + // this.scene.output.connect(this.context.getOutputDestination()); + } + createSource() { + const node = this.context.getContext().createPanner(); + node.panningModel = 'HRTF'; + node.distanceModel = 'linear'; + node.maxDistance = 20; + node.refDistance = 2; + node.connect(this.scene); + return node; + } + getOutput() { + return this.scene; + } + getInput() { + return this.scene; + } + setListenerPosition(x, y, z) { + this.listener.setPosition(x, y, z); + } + setListenerOrientation(forward, rawup) { + let fwd = new vec3([forward.x, forward.y, forward.z]); + let up = fwd.copy(); + vec3.cross(up, new vec3([rawup.x, rawup.y, rawup.z]), up); + vec3.cross(up, fwd, up); + fwd.normalize(); + up.normalize(); + this.listener.setOrientation(fwd.x, fwd.y, fwd.z, up.x, up.y, up.z); + } +} diff --git a/src/framework/resonator/sources/audio-source.d.ts b/src/framework/resonator/sources/audio-source.d.ts new file mode 100644 index 0000000..66dab95 --- /dev/null +++ b/src/framework/resonator/sources/audio-source.d.ts @@ -0,0 +1,35 @@ +import ResonatorAudioContext from '../audio-context'; +import AudioGraph from '../audio-graph'; +import ResonatorScene from '../scenes/webaudio-scene'; +import { BaseSource } from './base-source'; +import { SourceType } from './source-type'; +export default class AudioSource implements BaseSource { + playing: boolean; + looping: boolean; + private node; + private sceneNode; + private buffer; + private context; + private graph; + private scene; + private playOnLoad; + private position; + private playbackRate; + private volume; + private gain; + private type; + constructor(graph: AudioGraph, scene: ResonatorScene, context: ResonatorAudioContext, buffer?: AudioBuffer, type?: SourceType); + init(): void; + getBuffer(): AudioBuffer; + setBuffer(data: AudioBuffer): void; + play(when?: number, offset?: number, duration?: number): void; + setPosition(x: number, y: number, z: number): void; + setPlaybackRate(rate: number): void; + getPlaybackRate(): number; + setVolume(volume: number): void; + getVolume(): number; + private createConnections; + stop(): void; + destroy(): void; + loop(value: boolean): void; +} diff --git a/src/framework/resonator/sources/audio-source.js b/src/framework/resonator/sources/audio-source.js new file mode 100644 index 0000000..3c3c961 --- /dev/null +++ b/src/framework/resonator/sources/audio-source.js @@ -0,0 +1,132 @@ +// an audio source +// This is the actual sound +import { SourceType } from './source-type'; +export default class AudioSource { + constructor(graph, scene, context, buffer = null, type = SourceType.WorldSource) { + this.position = { + x: 0, + y: 0, + z: 0 + }; + this.buffer = buffer; + this.context = context; + this.scene = scene; + this.graph = graph; + this.type = type; + this.playbackRate = 1; + this.init(); + } + init() { + this.gain = this.context.createGain(); + // bind methods so we can add and removve them from event listeners + this.stop = this.stop.bind(this); + } + getBuffer() { + return this.buffer; + } + setBuffer(data) { + this.buffer = data; + if (this.playOnLoad) { + this.play(); + this.playOnLoad = false; + } + } + play(when = 0, offset = 0, duration = this.buffer ? this.buffer.duration : 0) { + if (this.playing && this.node) { + this.stop(); + } + if (!this.buffer) { + this.playOnLoad = true; + return; + } + if (!this.node) { + this.node = this.context.createBufferSource(); + this.node.buffer = this.buffer; + this.createConnections(); + } + if (this.node) { + this.node.playbackRate.value = this.playbackRate; + this.node.start(when, offset, duration); + this.node.loop = this.looping; + this.playing = true; + if (this.sceneNode) { + this.sceneNode.setPosition(this.position.x, this.position.y, this.position.z); + } + this.node.addEventListener('ended', this.stop); + } + } + setPosition(x, y, z) { + this.position = { + x, + y, + z + }; + if (this.sceneNode) + this.sceneNode.setPosition(x, y, z); + } + setPlaybackRate(rate) { + this.playbackRate = rate; + if (this.node) + this.node.playbackRate.value = rate; + } + getPlaybackRate() { + return this.playbackRate; + } + setVolume(volume) { + this.volume = volume; + if (this.gain) + this.gain.gain.value = volume; + } + getVolume() { + return this.volume; + } + createConnections() { + switch (this.type) { + case SourceType.WorldSource: + if (!this.sceneNode) { + this.sceneNode = this.scene.createSource(); + } + this.node.connect(this.gain); + this.gain.connect(this.sceneNode); + break; + case SourceType.UISource: + this.node.connect(this.gain); + this.graph.connectToUI(this.gain); + break; + default: + this.node.connect(this.gain); + this.graph.connectToMaster(this.gain); + break; + } + } + stop() { + this.playing = false; + if (this.node) { + this.node.removeEventListener('ended', this.stop); + this.node.stop(); + this.node.disconnect(); + this.node = null; + this.playing = false; + if (this.sceneNode) { + this.sceneNode.disconnect(); + this.sceneNode = null; + } + } + } + destroy() { + this.stop(); + // set all refs to null to encourage gc + this.node = null; + this.sceneNode = null; + this.buffer = null; + this.context = null; + this.graph = null; + this.scene = null; + } + loop(value) { + this.looping = value; + if (this.node) { + this.node.loop = value; + } + } +} diff --git a/src/framework/resonator/sources/base-source.d.ts b/src/framework/resonator/sources/base-source.d.ts new file mode 100644 index 0000000..a1615fc --- /dev/null +++ b/src/framework/resonator/sources/base-source.d.ts @@ -0,0 +1,10 @@ +export interface BaseSource { + play(when: number, offset: number, duration: number): void; + stop(): void; + setPlaybackRate(value: number): void; + getPlaybackRate(): number; + setVolume(value: number): void; + getVolume(): number; + loop(value: boolean): void; + destroy(): void; +} diff --git a/src/framework/resonator/sources/base-source.js b/src/framework/resonator/sources/base-source.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/resonator/sources/base-source.js @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/resonator/sources/source-type.d.ts b/src/framework/resonator/sources/source-type.d.ts new file mode 100644 index 0000000..c0304b1 --- /dev/null +++ b/src/framework/resonator/sources/source-type.d.ts @@ -0,0 +1,5 @@ +export declare enum SourceType { + WorldSource = 0, + UISource = 1, + MasterSource = 2 +} diff --git a/src/framework/resonator/sources/source-type.js b/src/framework/resonator/sources/source-type.js new file mode 100644 index 0000000..2d58552 --- /dev/null +++ b/src/framework/resonator/sources/source-type.js @@ -0,0 +1,6 @@ +export var SourceType; +(function (SourceType) { + SourceType[SourceType["WorldSource"] = 0] = "WorldSource"; + SourceType[SourceType["UISource"] = 1] = "UISource"; + SourceType[SourceType["MasterSource"] = 2] = "MasterSource"; +})(SourceType || (SourceType = {})); diff --git a/src/framework/resonator/sources/streaming-source.d.ts b/src/framework/resonator/sources/streaming-source.d.ts new file mode 100644 index 0000000..7403688 --- /dev/null +++ b/src/framework/resonator/sources/streaming-source.d.ts @@ -0,0 +1,30 @@ +import { BaseSource } from './base-source'; +import AudioGraph from '../audio-graph'; +import ResonatorScene from '../scenes/webaudio-scene'; +import ResonatorAudioContext from '../audio-context'; +import { SourceType } from './source-type'; +export declare class StreamingSource implements BaseSource { + private graph; + private scene; + private context; + private element; + private type; + playing: boolean; + private playOnAvailable; + private node; + private canPlay; + private sceneNode; + private position; + constructor(graph: AudioGraph, scene: ResonatorScene, context: ResonatorAudioContext, element: HTMLAudioElement, type?: SourceType); + private init; + play(when?: number, offset?: number, duration?: number): void; + stop(): void; + getVolume(): number; + setVolume(value: number): void; + getPlaybackRate(): number; + setPlaybackRate(value: number): void; + private createConnections; + setPosition(x: number, y: number, z: number): void; + destroy(): void; + loop(value: boolean): void; +} diff --git a/src/framework/resonator/sources/streaming-source.js b/src/framework/resonator/sources/streaming-source.js new file mode 100644 index 0000000..7ff3302 --- /dev/null +++ b/src/framework/resonator/sources/streaming-source.js @@ -0,0 +1,81 @@ +import { SourceType } from './source-type'; +export class StreamingSource { + constructor(graph, scene, context, element, type = SourceType.MasterSource) { + this.graph = graph; + this.scene = scene; + this.context = context; + this.element = element; + this.type = type; + this.position = { + x: 0, + y: 0, + z: 0 + }; + this.init(); + } + init() { + this.node = this.context.createMediaElementSource(this.element); + this.createConnections(); + this.element.addEventListener('canplay', (event) => { + this.canPlay = true; + if (this.playOnAvailable) { + this.play(); + } + }); + } + play(when = 0, offset = 0, duration = 0) { + if (this.canPlay) { + this.element.play(); + } + this.playOnAvailable = true; + } + stop() { + this.element.pause(); + } + getVolume() { + return this.element.volume; + } + setVolume(value) { + this.element.volume = value; + } + getPlaybackRate() { + return this.element.playbackRate; + } + setPlaybackRate(value) { + this.element.playbackRate = value; + } + createConnections() { + switch (this.type) { + case SourceType.WorldSource: + if (!this.sceneNode) { + this.sceneNode = this.scene.createSource(); + } + this.node.connect(this.sceneNode); + break; + default: + this.graph.connectToMaster(this.node); + break; + } + } + setPosition(x, y, z) { + this.position = { + x, + y, + z + }; + if (this.sceneNode) + this.sceneNode.setPosition(x, y, z); + } + destroy() { + this.stop(); + this.element = null; + this.graph = null; + this.context = null; + this.node = null; + this.sceneNode = null; + this.scene = null; + } + loop(value) { + this.element.loop = true; + } +} diff --git a/src/framework/resonator/vendor/resonance-es6/attenuation.d.ts b/src/framework/resonator/vendor/resonance-es6/attenuation.d.ts new file mode 100644 index 0000000..e42c1c0 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/attenuation.d.ts @@ -0,0 +1,40 @@ +export default Attenuation; +/** + * @class Attenuation + * @description Distance-based attenuation filter. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Number} options.minDistance + * Min. distance (in meters). Defaults to + * {@linkcode Utils.DEFAULT_MIN_DISTANCE DEFAULT_MIN_DISTANCE}. + * @param {Number} options.maxDistance + * Max. distance (in meters). Defaults to + * {@linkcode Utils.DEFAULT_MAX_DISTANCE DEFAULT_MAX_DISTANCE}. + * @param {string} options.rolloff + * Rolloff model to use, chosen from options in + * {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. Defaults to + * {@linkcode Utils.DEFAULT_ATTENUATION_ROLLOFF DEFAULT_ATTENUATION_ROLLOFF}. + */ +declare class Attenuation { + constructor(context: any, options: any); + minDistance: any; + maxDistance: any; + _gainNode: any; + input: any; + output: any; + /** + * Set distance from the listener. + * @param {Number} distance Distance (in meters). + */ + setDistance(distance: number): void; + /** + * Set rolloff. + * @param {string} rolloff + * Rolloff model to use, chosen from options in + * {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. + */ + setRolloff(rolloff: string): void; + _rolloff: string; +} diff --git a/src/framework/resonator/vendor/resonance-es6/attenuation.js b/src/framework/resonator/vendor/resonance-es6/attenuation.js new file mode 100644 index 0000000..abb8584 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/attenuation.js @@ -0,0 +1,151 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Distance-based attenuation filter. + * @author Andrew Allen + */ +'use strict'; +// Internal dependencies. +import Utils from './utils.js'; +/** + * @class Attenuation + * @description Distance-based attenuation filter. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Number} options.minDistance + * Min. distance (in meters). Defaults to + * {@linkcode Utils.DEFAULT_MIN_DISTANCE DEFAULT_MIN_DISTANCE}. + * @param {Number} options.maxDistance + * Max. distance (in meters). Defaults to + * {@linkcode Utils.DEFAULT_MAX_DISTANCE DEFAULT_MAX_DISTANCE}. + * @param {string} options.rolloff + * Rolloff model to use, chosen from options in + * {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. Defaults to + * {@linkcode Utils.DEFAULT_ATTENUATION_ROLLOFF DEFAULT_ATTENUATION_ROLLOFF}. + */ +class Attenuation { + constructor(context, options) { + // Public variables. + /** + * Min. distance (in meters). + * @member {Number} minDistance + * @memberof Attenuation + * @instance + */ + /** + * Max. distance (in meters). + * @member {Number} maxDistance + * @memberof Attenuation + * @instance + */ + /** + * Mono (1-channel) input {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} input + * @memberof Attenuation + * @instance + */ + /** + * Mono (1-channel) output {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} output + * @memberof Attenuation + * @instance + */ + // Use defaults for undefined arguments. + if (options == undefined) { + options = {}; + } + if (options.minDistance == undefined) { + options.minDistance = Utils.DEFAULT_MIN_DISTANCE; + } + if (options.maxDistance == undefined) { + options.maxDistance = Utils.DEFAULT_MAX_DISTANCE; + } + if (options.rolloff == undefined) { + options.rolloff = Utils.DEFAULT_ATTENUATION_ROLLOFF; + } + // Assign values. + this.minDistance = options.minDistance; + this.maxDistance = options.maxDistance; + this.setRolloff(options.rolloff); + // Create node. + this._gainNode = context.createGain(); + // Initialize distance to max distance. + this.setDistance(options.maxDistance); + // Input/Output proxy. + this.input = this._gainNode; + this.output = this._gainNode; + } + /** + * Set distance from the listener. + * @param {Number} distance Distance (in meters). + */ + setDistance(distance) { + let gain = 1; + if (this._rolloff == 'logarithmic') { + if (distance > this.maxDistance) { + gain = 0; + } + else if (distance > this.minDistance) { + let range = this.maxDistance - this.minDistance; + if (range > Utils.EPSILON_FLOAT) { + // Compute the distance attenuation value by the logarithmic curve + // "1 / (d + 1)" with an offset of |minDistance|. + let relativeDistance = distance - this.minDistance; + let attenuation = 1 / (relativeDistance + 1); + let attenuationMax = 1 / (range + 1); + gain = (attenuation - attenuationMax) / (1 - attenuationMax); + } + } + } + else if (this._rolloff == 'linear') { + if (distance > this.maxDistance) { + gain = 0; + } + else if (distance > this.minDistance) { + let range = this.maxDistance - this.minDistance; + if (range > Utils.EPSILON_FLOAT) { + gain = (this.maxDistance - distance) / range; + } + } + } + this._gainNode.gain.value = gain; + } + /** + * Set rolloff. + * @param {string} rolloff + * Rolloff model to use, chosen from options in + * {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. + */ + setRolloff(rolloff) { + let isValidModel = ~Utils.ATTENUATION_ROLLOFFS.indexOf(rolloff); + if (rolloff == undefined || !isValidModel) { + if (!isValidModel) { + Utils.log('Invalid rolloff model (\"' + rolloff + + '\"). Using default: \"' + Utils.DEFAULT_ATTENUATION_ROLLOFF + '\".'); + } + rolloff = Utils.DEFAULT_ATTENUATION_ROLLOFF; + } + else { + rolloff = rolloff.toString().toLowerCase(); + } + this._rolloff = rolloff; + } +} +export default Attenuation; diff --git a/src/framework/resonator/vendor/resonance-es6/d.d.ts b/src/framework/resonator/vendor/resonance-es6/d.d.ts new file mode 100644 index 0000000..cba0b49 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/d.d.ts @@ -0,0 +1,217 @@ +declare module 'resonance-audio' { + namespace ResonanceAudio { + /** Options for constructing a new ResonanceAudio scene */ + interface Options { + /** Desired ambisonic Order */ + ambisonicOrder?: number; + /** The listener's initial position (in meters), where origin is the center of + * the room */ + listenerPosition?: Float32Array; + /** The listener's initial forward vector */ + listenerForward?: Float32Array; + /** The listener's initial up vector */ + listenerUp?: Float32Array; + /** Room dimensions (in meters) */ + dimensions?: Utils.RoomDimensions; + /** Named acoustic materials per wall */ + materials?: Utils.RoomMaterials; + /** (in meters/second) */ + speedOfSound?: number; + } + } + /** Main class for managing sources, room and listener models */ + class ResonanceAudio { + /** Binaurally-rendered stereo (2-channel) output */ + output: AudioNode; + /** Ambisonic (multichannel) input */ + ambisonicInput: AudioNode; + /** Ambisonic (multichannel) output */ + ambisonicOutput: AudioNode; + constructor(context: AudioContext, options?: ResonanceAudio.Options); + /** + * Create a new source for the scene. + * @param options + * Options for constructing a new Source. + */ + createSource(options?: Source.Options): Source; + /** + * Set the scene's desired ambisonic order. + * @param ambisonicOrder Desired ambisonic order. + */ + setAmbisonicOrder(ambisonicOrder: any): void; + /** + * Set the room's dimensions and wall materials. + * @param dimensions Room dimensions (in meters). + * @param materials Named acoustic materials per wall. + */ + setRoomProperties(dimensions: Utils.RoomDimensions, materials: Utils.RoomMaterials): void; + /** + * Set the listener's position (in meters), where origin is the center of + * the room. + */ + setListenerPosition(x: number, y: number, z: number): any; + /** Set the source's orientation using forward and up vectors. */ + setOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void; + /** + * Set the listener's position and orientation using a Three.js Matrix4 object. + * @param matrix + * The Three.js Matrix4 object representing the listener's world transform. + */ + setListenerFromMatrix(matrix4: Float32Array): void; + /** + * Set the speed of sound. + */ + setSpeedOfSound(speedOfSound: number): void; + } + namespace Source { + /** Options for constructing a new Source. */ + interface Options { + /** The source's initial position (in meters), where origin is the center of + * the room */ + position?: Float32Array; + /** The source's initial forward vector */ + forward?: Float32Array; + /** The source's initial up vector */ + up?: Float32Array; + /** Min. distance (in meters) */ + minDistance?: number; + /** Max. distance (in meters) */ + maxDistance?: number; + /** Rolloff model to use */ + rolloff?: string; + /** Input gain (linear) */ + gain?: number; + /** Directivity alpha */ + alpha?: number; + /** Directivity sharpness */ + sharpness?: number; + /** Source width (in degrees). Where 0 degrees is a point source and 360 degrees + * is an omnidirectional source */ + sourceWidth?: number; + } + } + /** + * Source model to spatialize an audio buffer. + */ + class Source { + constructor(scene: ResonanceAudio, options?: Source.Options); + /** Mono (1-channel) input */ + input: AudioNode; + /** + * Set source's position (in meters), where origin is the center of + * the room. + */ + setPosition(x: number, y: number, z: number): void; + /** Set source's rolloff. */ + setRolloff(rolloff: string): void; + /** Set source's minimum distance (in meters). */ + setMinDistance(minDistance: number): void; + /** Set source's maximum distance (in meters). */ + setMaxDistance(maxDistance: number): void; + /** Set source's gain (linear). */ + setGain(gain: number): void; + /** Set the source's orientation using forward and up vectors. */ + setOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void; + /** Set source's position and orientation using a + * Three.js modelViewMatrix object */ + setFromMatrix(matrix4: Float32Array): void; + /** Set the source width (in degrees). Where 0 degrees is a point source and 360 + * degrees is an omnidirectional source */ + setSourceWidth(sourceWidth: number): void; + /** + * Set source's directivity pattern (defined by alpha), where 0 is an + * omnidirectional pattern, 1 is a bidirectional pattern, 0.5 is a cardiod + * pattern. The sharpness of the pattern is increased exponentially + * @param alpha + * Determines directivity pattern (0 to 1). + * @param sharpness + * Determines the sharpness of the directivity pattern (1 to Inf). + */ + setDirectivityPattern(alpha: number, sharpness: number): void; + } + namespace Room { + interface Options { + /** The listener's initial position (in meters), where origin is the center of + * the room */ + listenerPosition?: Float32Array; + /** Room dimensions (in meters) */ + dimensions?: Utils.RoomDimensions; + /** Named acoustic materials per wall */ + materials?: Utils.RoomMaterials; + /** (in meters/second) */ + speedOfSound?: number; + } + } + /** + * Model that manages early and late reflections using acoustic + * properties and listener position relative to a rectangular room. + */ + class Room { + constructor(context: AudioContext, options?: Room.Options); + /** + * Set the room's dimensions and wall materials. + * @param dimensions Room dimensions (in meters) + * @param materials Named acoustic materials per wall + */ + setProperties(dimensions: Utils.RoomDimensions, materials: Utils.RoomMaterials): void; + /** + * Set the listener's position (in meters), where origin is the center of + * the room. + */ + setListenerPosition(x: number, y: number, z: number): void; + /** + * Compute distance outside room of provided position (in meters). + * @return + * Distance outside room (in meters). Returns 0 if inside room. + */ + getDistanceOutsideRoom(x: number, y: number, z: number): number; + } + namespace Listener { + interface Options { + /** Desired ambisonic order */ + ambisonicOrder: number; + /** Initial position (in meters), where origin is the center of + * the room */ + position?: Float32Array; + /** The listener's initial forward vector */ + forward?: Float32Array; + /** The listener's initial up vector */ + up?: Float32Array; + } + } + /** Listener model to spatialize sources in an environment */ + class Listener { + /** Position (in meters) */ + position: Float32Array; + /** Ambisonic (multichannel) input */ + input: AudioNode; + /** Binaurally-rendered stereo (2-channel) output */ + output: AudioNode; + /** Ambisonic (multichannel) output */ + ambisonicOutput: AudioNode; + /** + * Set the listener's orientation using forward and up vectors. + */ + setOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void; + /** Set listener's position and orientation using a + * Three.js modelViewMatrix object */ + setFromMatrix(matrix4: Float32Array): void; + } + namespace Utils { + /** Properties describing the geometry of a room. */ + interface RoomDimensions { + width: number; + height: number; + depth: number; + } + /** Properties describing the wall materials */ + interface RoomMaterials { + left: string; + right: string; + front: string; + back: string; + down: string; + up: string; + } + } +} diff --git a/src/framework/resonator/vendor/resonance-es6/d.js b/src/framework/resonator/vendor/resonance-es6/d.js new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/resonator/vendor/resonance-es6/directivity.d.ts b/src/framework/resonator/vendor/resonance-es6/directivity.d.ts new file mode 100644 index 0000000..a280dec --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/directivity.d.ts @@ -0,0 +1,47 @@ +export default Directivity; +/** + * @class Directivity + * @description Directivity/occlusion filter. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Number} options.alpha + * Determines directivity pattern (0 to 1). See + * {@link Directivity#setPattern setPattern} for more details. Defaults to + * {@linkcode Utils.DEFAULT_DIRECTIVITY_ALPHA DEFAULT_DIRECTIVITY_ALPHA}. + * @param {Number} options.sharpness + * Determines the sharpness of the directivity pattern (1 to Inf). See + * {@link Directivity#setPattern setPattern} for more details. Defaults to + * {@linkcode Utils.DEFAULT_DIRECTIVITY_SHARPNESS + * DEFAULT_DIRECTIVITY_SHARPNESS}. + */ +declare class Directivity { + constructor(context: any, options: any); + _context: any; + _lowpass: any; + _cosTheta: number; + input: any; + output: any; + /** + * Compute the filter using the source's forward orientation and the listener's + * position. + * @param {Float32Array} forward The source's forward vector. + * @param {Float32Array} direction The direction from the source to the + * listener. + */ + computeAngle(forward: Float32Array, direction: Float32Array): void; + /** + * Set source's directivity pattern (defined by alpha), where 0 is an + * omnidirectional pattern, 1 is a bidirectional pattern, 0.5 is a cardiod + * pattern. The sharpness of the pattern is increased exponenentially. + * @param {Number} alpha + * Determines directivity pattern (0 to 1). + * @param {Number} sharpness + * Determines the sharpness of the directivity pattern (1 to Inf). + * DEFAULT_DIRECTIVITY_SHARPNESS}. + */ + setPattern(alpha: number, sharpness: number): void; + _alpha: number; + _sharpness: number; +} diff --git a/src/framework/resonator/vendor/resonance-es6/directivity.js b/src/framework/resonator/vendor/resonance-es6/directivity.js new file mode 100644 index 0000000..3a29d74 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/directivity.js @@ -0,0 +1,117 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Directivity/occlusion filter. + * @author Andrew Allen + */ +'use strict'; +// Internal dependencies. +import Utils from './utils.js'; +/** + * @class Directivity + * @description Directivity/occlusion filter. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Number} options.alpha + * Determines directivity pattern (0 to 1). See + * {@link Directivity#setPattern setPattern} for more details. Defaults to + * {@linkcode Utils.DEFAULT_DIRECTIVITY_ALPHA DEFAULT_DIRECTIVITY_ALPHA}. + * @param {Number} options.sharpness + * Determines the sharpness of the directivity pattern (1 to Inf). See + * {@link Directivity#setPattern setPattern} for more details. Defaults to + * {@linkcode Utils.DEFAULT_DIRECTIVITY_SHARPNESS + * DEFAULT_DIRECTIVITY_SHARPNESS}. + */ +class Directivity { + constructor(context, options) { + // Public variables. + /** + * Mono (1-channel) input {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} input + * @memberof Directivity + * @instance + */ + /** + * Mono (1-channel) output {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} output + * @memberof Directivity + * @instance + */ + // Use defaults for undefined arguments. + if (options == undefined) { + options = {}; + } + if (options.alpha == undefined) { + options.alpha = Utils.DEFAULT_DIRECTIVITY_ALPHA; + } + if (options.sharpness == undefined) { + options.sharpness = Utils.DEFAULT_DIRECTIVITY_SHARPNESS; + } + // Create audio node. + this._context = context; + this._lowpass = context.createBiquadFilter(); + // Initialize filter coefficients. + this._lowpass.type = 'lowpass'; + this._lowpass.Q.value = 0; + this._lowpass.frequency.value = context.sampleRate * 0.5; + this._cosTheta = 0; + this.setPattern(options.alpha, options.sharpness); + // Input/Output proxy. + this.input = this._lowpass; + this.output = this._lowpass; + } + /** + * Compute the filter using the source's forward orientation and the listener's + * position. + * @param {Float32Array} forward The source's forward vector. + * @param {Float32Array} direction The direction from the source to the + * listener. + */ + computeAngle(forward, direction) { + let forwardNorm = Utils.normalizeVector(forward); + let directionNorm = Utils.normalizeVector(direction); + let coeff = 1; + if (this._alpha > Utils.EPSILON_FLOAT) { + let cosTheta = forwardNorm[0] * directionNorm[0] + + forwardNorm[1] * directionNorm[1] + forwardNorm[2] * directionNorm[2]; + coeff = (1 - this._alpha) + this._alpha * cosTheta; + coeff = Math.pow(Math.abs(coeff), this._sharpness); + } + this._lowpass.frequency.value = this._context.sampleRate * 0.5 * coeff; + } + /** + * Set source's directivity pattern (defined by alpha), where 0 is an + * omnidirectional pattern, 1 is a bidirectional pattern, 0.5 is a cardiod + * pattern. The sharpness of the pattern is increased exponenentially. + * @param {Number} alpha + * Determines directivity pattern (0 to 1). + * @param {Number} sharpness + * Determines the sharpness of the directivity pattern (1 to Inf). + * DEFAULT_DIRECTIVITY_SHARPNESS}. + */ + setPattern(alpha, sharpness) { + // Clamp and set values. + this._alpha = Math.min(1, Math.max(0, alpha)); + this._sharpness = Math.max(1, sharpness); + // Update angle calculation using new values. + this.computeAngle([this._cosTheta * this._cosTheta, 0, 0], [1, 0, 0]); + } +} +export default Directivity; diff --git a/src/framework/resonator/vendor/resonance-es6/early-reflections.d.ts b/src/framework/resonator/vendor/resonance-es6/early-reflections.d.ts new file mode 100644 index 0000000..847009d --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/early-reflections.d.ts @@ -0,0 +1,56 @@ +export default EarlyReflections; +/** + * @class EarlyReflections + * @description Ray-tracing-based early reflections model. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Utils~RoomDimensions} options.dimensions + * Room dimensions (in meters). Defaults to + * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + * @param {Object} options.coefficients + * Frequency-independent reflection coeffs per wall. Defaults to + * {@linkcode Utils.DEFAULT_REFLECTION_COEFFICIENTS + * DEFAULT_REFLECTION_COEFFICIENTS}. + * @param {Number} options.speedOfSound + * (in meters / second). Defaults to {@linkcode Utils.DEFAULT_SPEED_OF_SOUND + * DEFAULT_SPEED_OF_SOUND}. + * @param {Float32Array} options.listenerPosition + * (in meters). Defaults to + * {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}. + */ +declare class EarlyReflections { + constructor(context: any, options: any); + speedOfSound: any; + input: any; + output: any; + _lowpass: any; + _delays: {}; + _gains: {}; + _inverters: {}; + _merger: any; + _listenerPosition: any; + /** + * Set the listener's position (in meters), + * where [0,0,0] is the center of the room. + * @param {Number} x + * @param {Number} y + * @param {Number} z + */ + setListenerPosition(x: number, y: number, z: number): void; + /** + * Set the room's properties which determines the characteristics of + * reflections. + * @param {Utils~RoomDimensions} dimensions + * Room dimensions (in meters). Defaults to + * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + * @param {Object} coefficients + * Frequency-independent reflection coeffs per wall. Defaults to + * {@linkcode Utils.DEFAULT_REFLECTION_COEFFICIENTS + * DEFAULT_REFLECTION_COEFFICIENTS}. + */ + setRoomProperties(dimensions: any, coefficients: any): void; + _coefficients: any; + _halfDimensions: {}; +} diff --git a/src/framework/resonator/vendor/resonance-es6/early-reflections.js b/src/framework/resonator/vendor/resonance-es6/early-reflections.js new file mode 100644 index 0000000..c3c2919 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/early-reflections.js @@ -0,0 +1,212 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Ray-tracing-based early reflections model. + * @author Andrew Allen + */ +'use strict'; +// Internal dependencies. +import Utils from './utils.js'; +/** + * @class EarlyReflections + * @description Ray-tracing-based early reflections model. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Utils~RoomDimensions} options.dimensions + * Room dimensions (in meters). Defaults to + * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + * @param {Object} options.coefficients + * Frequency-independent reflection coeffs per wall. Defaults to + * {@linkcode Utils.DEFAULT_REFLECTION_COEFFICIENTS + * DEFAULT_REFLECTION_COEFFICIENTS}. + * @param {Number} options.speedOfSound + * (in meters / second). Defaults to {@linkcode Utils.DEFAULT_SPEED_OF_SOUND + * DEFAULT_SPEED_OF_SOUND}. + * @param {Float32Array} options.listenerPosition + * (in meters). Defaults to + * {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}. + */ +class EarlyReflections { + constructor(context, options) { + // Public variables. + /** + * The room's speed of sound (in meters/second). + * @member {Number} speedOfSound + * @memberof EarlyReflections + * @instance + */ + /** + * Mono (1-channel) input {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} input + * @memberof EarlyReflections + * @instance + */ + /** + * First-order ambisonic (4-channel) output {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} output + * @memberof EarlyReflections + * @instance + */ + // Use defaults for undefined arguments. + if (options == undefined) { + options = {}; + } + if (options.speedOfSound == undefined) { + options.speedOfSound = Utils.DEFAULT_SPEED_OF_SOUND; + } + if (options.listenerPosition == undefined) { + options.listenerPosition = Utils.DEFAULT_POSITION.slice(); + } + if (options.coefficients == undefined) { + options.coefficients = {}; + Object.assign(options.coefficients, Utils.DEFAULT_REFLECTION_COEFFICIENTS); + } + // Assign room's speed of sound. + this.speedOfSound = options.speedOfSound; + // Create nodes. + this.input = context.createGain(); + this.output = context.createGain(); + this._lowpass = context.createBiquadFilter(); + this._delays = {}; + this._gains = {}; // gainPerWall = (ReflectionCoeff / Attenuation) + this._inverters = {}; // 3 of these are needed for right/back/down walls. + this._merger = context.createChannelMerger(4); // First-order encoding only. + // Connect audio graph for each wall reflection. + for (let property in Utils.DEFAULT_REFLECTION_COEFFICIENTS) { + if (Utils.DEFAULT_REFLECTION_COEFFICIENTS + .hasOwnProperty(property)) { + this._delays[property] = + context.createDelay(Utils.MAX_DURATION); + this._gains[property] = context.createGain(); + } + } + this._inverters.right = context.createGain(); + this._inverters.down = context.createGain(); + this._inverters.back = context.createGain(); + // Initialize lowpass filter. + this._lowpass.type = 'lowpass'; + this._lowpass.frequency.value = Utils.DEFAULT_REFLECTION_CUTOFF_FREQUENCY; + this._lowpass.Q.value = 0; + // Initialize encoder directions, set delay times and gains to 0. + for (let property in Utils.DEFAULT_REFLECTION_COEFFICIENTS) { + if (Utils.DEFAULT_REFLECTION_COEFFICIENTS + .hasOwnProperty(property)) { + this._delays[property].delayTime.value = 0; + this._gains[property].gain.value = 0; + } + } + // Initialize inverters for opposite walls ('right', 'down', 'back' only). + this._inverters.right.gain.value = -1; + this._inverters.down.gain.value = -1; + this._inverters.back.gain.value = -1; + // Connect nodes. + this.input.connect(this._lowpass); + for (let property in Utils.DEFAULT_REFLECTION_COEFFICIENTS) { + if (Utils.DEFAULT_REFLECTION_COEFFICIENTS + .hasOwnProperty(property)) { + this._lowpass.connect(this._delays[property]); + this._delays[property].connect(this._gains[property]); + this._gains[property].connect(this._merger, 0, 0); + } + } + // Connect gains to ambisonic channel output. + // Left: [1 1 0 0] + // Right: [1 -1 0 0] + // Up: [1 0 1 0] + // Down: [1 0 -1 0] + // Front: [1 0 0 1] + // Back: [1 0 0 -1] + this._gains.left.connect(this._merger, 0, 1); + this._gains.right.connect(this._inverters.right); + this._inverters.right.connect(this._merger, 0, 1); + this._gains.up.connect(this._merger, 0, 2); + this._gains.down.connect(this._inverters.down); + this._inverters.down.connect(this._merger, 0, 2); + this._gains.front.connect(this._merger, 0, 3); + this._gains.back.connect(this._inverters.back); + this._inverters.back.connect(this._merger, 0, 3); + this._merger.connect(this.output); + // Initialize. + this._listenerPosition = options.listenerPosition; + this.setRoomProperties(options.dimensions, options.coefficients); + } + /** + * Set the listener's position (in meters), + * where [0,0,0] is the center of the room. + * @param {Number} x + * @param {Number} y + * @param {Number} z + */ + setListenerPosition(x, y, z) { + // Assign listener position. + this._listenerPosition = [x, y, z]; + // Determine distances to each wall. + let distances = { + left: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.width + x) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE, + right: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.width - x) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE, + front: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.depth + z) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE, + back: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.depth - z) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE, + down: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.height + y) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE, + up: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.height - y) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE, + }; + // Assign delay & attenuation values using distances. + for (let property in Utils.DEFAULT_REFLECTION_COEFFICIENTS) { + if (Utils.DEFAULT_REFLECTION_COEFFICIENTS + .hasOwnProperty(property)) { + // Compute and assign delay (in seconds). + let delayInSecs = distances[property] / this.speedOfSound; + this._delays[property].delayTime.value = delayInSecs; + // Compute and assign gain, uses logarithmic rolloff: "g = R / (d + 1)" + let attenuation = this._coefficients[property] / distances[property]; + this._gains[property].gain.value = attenuation; + } + } + } + /** + * Set the room's properties which determines the characteristics of + * reflections. + * @param {Utils~RoomDimensions} dimensions + * Room dimensions (in meters). Defaults to + * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + * @param {Object} coefficients + * Frequency-independent reflection coeffs per wall. Defaults to + * {@linkcode Utils.DEFAULT_REFLECTION_COEFFICIENTS + * DEFAULT_REFLECTION_COEFFICIENTS}. + */ + setRoomProperties(dimensions, coefficients) { + if (dimensions == undefined) { + dimensions = {}; + Object.assign(dimensions, Utils.DEFAULT_ROOM_DIMENSIONS); + } + if (coefficients == undefined) { + coefficients = {}; + Object.assign(coefficients, Utils.DEFAULT_REFLECTION_COEFFICIENTS); + } + this._coefficients = coefficients; + // Sanitize dimensions and store half-dimensions. + this._halfDimensions = {}; + this._halfDimensions.width = dimensions.width * 0.5; + this._halfDimensions.height = dimensions.height * 0.5; + this._halfDimensions.depth = dimensions.depth * 0.5; + // Update listener position with new room properties. + this.setListenerPosition(this._listenerPosition[0], this._listenerPosition[1], this._listenerPosition[2]); + } +} +export default EarlyReflections; diff --git a/src/framework/resonator/vendor/resonance-es6/encoder.d.ts b/src/framework/resonator/vendor/resonance-es6/encoder.d.ts new file mode 100644 index 0000000..059f638 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/encoder.d.ts @@ -0,0 +1,64 @@ +export default Encoder; +/** + * @class Encoder + * @description Spatially encodes input using weighted spherical harmonics. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Number} options.ambisonicOrder + * Desired ambisonic order. Defaults to + * {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}. + * @param {Number} options.azimuth + * Azimuth (in degrees). Defaults to + * {@linkcode Utils.DEFAULT_AZIMUTH DEFAULT_AZIMUTH}. + * @param {Number} options.elevation + * Elevation (in degrees). Defaults to + * {@linkcode Utils.DEFAULT_ELEVATION DEFAULT_ELEVATION}. + * @param {Number} options.sourceWidth + * Source width (in degrees). Where 0 degrees is a point source and 360 degrees + * is an omnidirectional source. Defaults to + * {@linkcode Utils.DEFAULT_SOURCE_WIDTH DEFAULT_SOURCE_WIDTH}. + */ +declare class Encoder { + constructor(context: any, options: any); + _context: any; + input: any; + _channelGain: any[]; + _merger: any; + output: any; + _azimuth: any; + _elevation: any; + /** + * Set the desired ambisonic order. + * @param {Number} ambisonicOrder Desired ambisonic order. + */ + setAmbisonicOrder(ambisonicOrder: number): void; + _ambisonicOrder: number; + /** + * Set the direction of the encoded source signal. + * @param {Number} azimuth + * Azimuth (in degrees). Defaults to + * {@linkcode Utils.DEFAULT_AZIMUTH DEFAULT_AZIMUTH}. + * @param {Number} elevation + * Elevation (in degrees). Defaults to + * {@linkcode Utils.DEFAULT_ELEVATION DEFAULT_ELEVATION}. + */ + setDirection(azimuth: number, elevation: number): void; + /** + * Set the source width (in degrees). Where 0 degrees is a point source and 360 + * degrees is an omnidirectional source. + * @param {Number} sourceWidth (in degrees). + */ + setSourceWidth(sourceWidth: number): void; + _spreadIndex: number; +} +declare namespace Encoder { + /** + * Validate the provided ambisonic order. + * @param {Number} ambisonicOrder Desired ambisonic order. + * @return {Number} Validated/adjusted ambisonic order. + * @private + */ + function validateAmbisonicOrder(ambisonicOrder: number): number; +} diff --git a/src/framework/resonator/vendor/resonance-es6/encoder.js b/src/framework/resonator/vendor/resonance-es6/encoder.js new file mode 100644 index 0000000..2fdaf3d --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/encoder.js @@ -0,0 +1,194 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Spatially encodes input using weighted spherical harmonics. + * @author Andrew Allen + */ +'use strict'; +// Internal dependencies. +import Tables from './tables.js'; +import Utils from './utils.js'; +/** + * @class Encoder + * @description Spatially encodes input using weighted spherical harmonics. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Number} options.ambisonicOrder + * Desired ambisonic order. Defaults to + * {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}. + * @param {Number} options.azimuth + * Azimuth (in degrees). Defaults to + * {@linkcode Utils.DEFAULT_AZIMUTH DEFAULT_AZIMUTH}. + * @param {Number} options.elevation + * Elevation (in degrees). Defaults to + * {@linkcode Utils.DEFAULT_ELEVATION DEFAULT_ELEVATION}. + * @param {Number} options.sourceWidth + * Source width (in degrees). Where 0 degrees is a point source and 360 degrees + * is an omnidirectional source. Defaults to + * {@linkcode Utils.DEFAULT_SOURCE_WIDTH DEFAULT_SOURCE_WIDTH}. + */ +class Encoder { + constructor(context, options) { + // Public variables. + /** + * Mono (1-channel) input {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} input + * @memberof Encoder + * @instance + */ + /** + * Ambisonic (multichannel) output {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} output + * @memberof Encoder + * @instance + */ + // Use defaults for undefined arguments. + if (options == undefined) { + options = {}; + } + if (options.ambisonicOrder == undefined) { + options.ambisonicOrder = Utils.DEFAULT_AMBISONIC_ORDER; + } + if (options.azimuth == undefined) { + options.azimuth = Utils.DEFAULT_AZIMUTH; + } + if (options.elevation == undefined) { + options.elevation = Utils.DEFAULT_ELEVATION; + } + if (options.sourceWidth == undefined) { + options.sourceWidth = Utils.DEFAULT_SOURCE_WIDTH; + } + this._context = context; + // Create I/O nodes. + this.input = context.createGain(); + this._channelGain = []; + this._merger = undefined; + this.output = context.createGain(); + // Set initial order, angle and source width. + this.setAmbisonicOrder(options.ambisonicOrder); + this._azimuth = options.azimuth; + this._elevation = options.elevation; + this.setSourceWidth(options.sourceWidth); + } + /** + * Set the desired ambisonic order. + * @param {Number} ambisonicOrder Desired ambisonic order. + */ + setAmbisonicOrder(ambisonicOrder) { + this._ambisonicOrder = Encoder.validateAmbisonicOrder(ambisonicOrder); + this.input.disconnect(); + for (let i = 0; i < this._channelGain.length; i++) { + this._channelGain[i].disconnect(); + } + if (this._merger != undefined) { + this._merger.disconnect(); + } + delete this._channelGain; + delete this._merger; + // Create audio graph. + let numChannels = (this._ambisonicOrder + 1) * (this._ambisonicOrder + 1); + this._merger = this._context.createChannelMerger(numChannels); + this._channelGain = new Array(numChannels); + for (let i = 0; i < numChannels; i++) { + this._channelGain[i] = this._context.createGain(); + this.input.connect(this._channelGain[i]); + this._channelGain[i].connect(this._merger, 0, i); + } + this._merger.connect(this.output); + } + /** + * Set the direction of the encoded source signal. + * @param {Number} azimuth + * Azimuth (in degrees). Defaults to + * {@linkcode Utils.DEFAULT_AZIMUTH DEFAULT_AZIMUTH}. + * @param {Number} elevation + * Elevation (in degrees). Defaults to + * {@linkcode Utils.DEFAULT_ELEVATION DEFAULT_ELEVATION}. + */ + setDirection(azimuth, elevation) { + // Format input direction to nearest indices. + if (azimuth == undefined || isNaN(azimuth)) { + azimuth = Utils.DEFAULT_AZIMUTH; + } + if (elevation == undefined || isNaN(elevation)) { + elevation = Utils.DEFAULT_ELEVATION; + } + // Store the formatted input (for updating source width). + this._azimuth = azimuth; + this._elevation = elevation; + // Format direction for index lookups. + azimuth = Math.round(azimuth % 360); + if (azimuth < 0) { + azimuth += 360; + } + elevation = Math.round(Math.min(90, Math.max(-90, elevation))) + 90; + // Assign gains to each output. + this._channelGain[0].gain.value = Tables.MAX_RE_WEIGHTS[this._spreadIndex][0]; + for (let i = 1; i <= this._ambisonicOrder; i++) { + let degreeWeight = Tables.MAX_RE_WEIGHTS[this._spreadIndex][i]; + for (let j = -i; j <= i; j++) { + let acnChannel = (i * i) + i + j; + let elevationIndex = i * (i + 1) / 2 + Math.abs(j) - 1; + let val = Tables.SPHERICAL_HARMONICS[1][elevation][elevationIndex]; + if (j != 0) { + let azimuthIndex = Tables.SPHERICAL_HARMONICS_MAX_ORDER + j - 1; + if (j < 0) { + azimuthIndex = Tables.SPHERICAL_HARMONICS_MAX_ORDER + j; + } + val *= Tables.SPHERICAL_HARMONICS[0][azimuth][azimuthIndex]; + } + this._channelGain[acnChannel].gain.value = val * degreeWeight; + } + } + } + /** + * Set the source width (in degrees). Where 0 degrees is a point source and 360 + * degrees is an omnidirectional source. + * @param {Number} sourceWidth (in degrees). + */ + setSourceWidth(sourceWidth) { + // The MAX_RE_WEIGHTS is a 360 x (Tables.SPHERICAL_HARMONICS_MAX_ORDER+1) + // size table. + this._spreadIndex = Math.min(359, Math.max(0, Math.round(sourceWidth))); + this.setDirection(this._azimuth, this._elevation); + } +} +/** + * Validate the provided ambisonic order. + * @param {Number} ambisonicOrder Desired ambisonic order. + * @return {Number} Validated/adjusted ambisonic order. + * @private + */ +Encoder.validateAmbisonicOrder = ambisonicOrder => { + if (isNaN(ambisonicOrder) || ambisonicOrder == undefined) { + Utils.log('Error: Invalid ambisonic order', options.ambisonicOrder, '\nUsing ambisonicOrder=1 instead.'); + ambisonicOrder = 1; + } + else if (ambisonicOrder < 1) { + Utils.log('Error: Unable to render ambisonic order', options.ambisonicOrder, '(Min order is 1)', '\nUsing min order instead.'); + ambisonicOrder = 1; + } + else if (ambisonicOrder > Tables.SPHERICAL_HARMONICS_MAX_ORDER) { + Utils.log('Error: Unable to render ambisonic order', options.ambisonicOrder, '(Max order is', Tables.SPHERICAL_HARMONICS_MAX_ORDER, ')\nUsing max order instead.'); + options.ambisonicOrder = Tables.SPHERICAL_HARMONICS_MAX_ORDER; + } + return ambisonicOrder; +}; +export default Encoder; diff --git a/src/framework/resonator/vendor/resonance-es6/late-reflections.d.ts b/src/framework/resonator/vendor/resonance-es6/late-reflections.d.ts new file mode 100644 index 0000000..e91ca38 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/late-reflections.d.ts @@ -0,0 +1,42 @@ +export default LateReflections; +/** + * @class LateReflections + * @description Late-reflections reverberation filter for Ambisonic content. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Array} options.durations + * Multiband RT60 durations (in seconds) for each frequency band, listed as + * {@linkcode Utils.DEFAULT_REVERB_FREQUENCY_BANDS + * FREQUDEFAULT_REVERB_FREQUENCY_BANDSENCY_BANDS}. Defaults to + * {@linkcode Utils.DEFAULT_REVERB_DURATIONS DEFAULT_REVERB_DURATIONS}. + * @param {Number} options.predelay Pre-delay (in milliseconds). Defaults to + * {@linkcode Utils.DEFAULT_REVERB_PREDELAY DEFAULT_REVERB_PREDELAY}. + * @param {Number} options.gain Output gain (linear). Defaults to + * {@linkcode Utils.DEFAULT_REVERB_GAIN DEFAULT_REVERB_GAIN}. + * @param {Number} options.bandwidth Bandwidth (in octaves) for each frequency + * band. Defaults to + * {@linkcode Utils.DEFAULT_REVERB_BANDWIDTH DEFAULT_REVERB_BANDWIDTH}. + * @param {Number} options.tailonset Length (in milliseconds) of impulse + * response to apply a half-Hann window. Defaults to + * {@linkcode Utils.DEFAULT_REVERB_TAIL_ONSET DEFAULT_REVERB_TAIL_ONSET}. + */ +declare class LateReflections { + constructor(context: any, options: any); + _bandwidthCoeff: number; + _tailonsetSamples: number; + _context: any; + input: any; + _predelay: any; + _convolver: any; + output: any; + /** + * Re-compute a new impulse response by providing Multiband RT60 durations. + * @param {Array} durations + * Multiband RT60 durations (in seconds) for each frequency band, listed as + * {@linkcode Utils.DEFAULT_REVERB_FREQUENCY_BANDS + * DEFAULT_REVERB_FREQUENCY_BANDS}. + */ + setDurations(durations: any[]): void; +} diff --git a/src/framework/resonator/vendor/resonance-es6/late-reflections.js b/src/framework/resonator/vendor/resonance-es6/late-reflections.js new file mode 100644 index 0000000..3ed0d00 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/late-reflections.js @@ -0,0 +1,187 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Late reverberation filter for Ambisonic content. + * @author Andrew Allen + */ +'use strict'; +// Internal dependencies. +import Utils from './utils.js'; +/** + * @class LateReflections + * @description Late-reflections reverberation filter for Ambisonic content. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Array} options.durations + * Multiband RT60 durations (in seconds) for each frequency band, listed as + * {@linkcode Utils.DEFAULT_REVERB_FREQUENCY_BANDS + * FREQUDEFAULT_REVERB_FREQUENCY_BANDSENCY_BANDS}. Defaults to + * {@linkcode Utils.DEFAULT_REVERB_DURATIONS DEFAULT_REVERB_DURATIONS}. + * @param {Number} options.predelay Pre-delay (in milliseconds). Defaults to + * {@linkcode Utils.DEFAULT_REVERB_PREDELAY DEFAULT_REVERB_PREDELAY}. + * @param {Number} options.gain Output gain (linear). Defaults to + * {@linkcode Utils.DEFAULT_REVERB_GAIN DEFAULT_REVERB_GAIN}. + * @param {Number} options.bandwidth Bandwidth (in octaves) for each frequency + * band. Defaults to + * {@linkcode Utils.DEFAULT_REVERB_BANDWIDTH DEFAULT_REVERB_BANDWIDTH}. + * @param {Number} options.tailonset Length (in milliseconds) of impulse + * response to apply a half-Hann window. Defaults to + * {@linkcode Utils.DEFAULT_REVERB_TAIL_ONSET DEFAULT_REVERB_TAIL_ONSET}. + */ +class LateReflections { + constructor(context, options) { + // Public variables. + /** + * Mono (1-channel) input {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} input + * @memberof LateReflections + * @instance + */ + /** + * Mono (1-channel) output {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} output + * @memberof LateReflections + * @instance + */ + // Use defaults for undefined arguments. + if (options == undefined) { + options = {}; + } + if (options.durations == undefined) { + options.durations = Utils.DEFAULT_REVERB_DURATIONS.slice(); + } + if (options.predelay == undefined) { + options.predelay = Utils.DEFAULT_REVERB_PREDELAY; + } + if (options.gain == undefined) { + options.gain = Utils.DEFAULT_REVERB_GAIN; + } + if (options.bandwidth == undefined) { + options.bandwidth = Utils.DEFAULT_REVERB_BANDWIDTH; + } + if (options.tailonset == undefined) { + options.tailonset = Utils.DEFAULT_REVERB_TAIL_ONSET; + } + // Assign pre-computed variables. + let delaySecs = options.predelay / 1000; + this._bandwidthCoeff = options.bandwidth * Utils.LOG2_DIV2; + this._tailonsetSamples = options.tailonset / 1000; + // Create nodes. + this._context = context; + this.input = context.createGain(); + this._predelay = context.createDelay(delaySecs); + this._convolver = context.createConvolver(); + this.output = context.createGain(); + // Set reverb attenuation. + this.output.gain.value = options.gain; + // Disable normalization. + this._convolver.normalize = false; + // Connect nodes. + this.input.connect(this._predelay); + this._predelay.connect(this._convolver); + this._convolver.connect(this.output); + // Compute IR using RT60 values. + this.setDurations(options.durations); + } + /** + * Re-compute a new impulse response by providing Multiband RT60 durations. + * @param {Array} durations + * Multiband RT60 durations (in seconds) for each frequency band, listed as + * {@linkcode Utils.DEFAULT_REVERB_FREQUENCY_BANDS + * DEFAULT_REVERB_FREQUENCY_BANDS}. + */ + setDurations(durations) { + if (durations.length !== Utils.NUMBER_REVERB_FREQUENCY_BANDS) { + Utils.log('Warning: invalid number of RT60 values provided to reverb.'); + return; + } + // Compute impulse response. + let durationsSamples = new Float32Array(Utils.NUMBER_REVERB_FREQUENCY_BANDS); + let sampleRate = this._context.sampleRate; + for (let i = 0; i < durations.length; i++) { + // Clamp within suitable range. + durations[i] = + Math.max(0, Math.min(Utils.DEFAULT_REVERB_MAX_DURATION, durations[i])); + // Convert seconds to samples. + durationsSamples[i] = Math.round(durations[i] * sampleRate * + Utils.DEFAULT_REVERB_DURATION_MULTIPLIER); + } + ; + // Determine max RT60 length in samples. + let durationsSamplesMax = 0; + for (let i = 0; i < durationsSamples.length; i++) { + if (durationsSamples[i] > durationsSamplesMax) { + durationsSamplesMax = durationsSamples[i]; + } + } + // Skip this step if there is no reverberation to compute. + if (durationsSamplesMax < 1) { + durationsSamplesMax = 1; + } + // Create impulse response buffer. + let buffer = this._context.createBuffer(1, durationsSamplesMax, sampleRate); + let bufferData = buffer.getChannelData(0); + // Create noise signal (computed once, referenced in each band's routine). + let noiseSignal = new Float32Array(durationsSamplesMax); + for (let i = 0; i < durationsSamplesMax; i++) { + noiseSignal[i] = Math.random() * 2 - 1; + } + // Compute the decay rate per-band and filter the decaying noise signal. + for (let i = 0; i < Utils.NUMBER_REVERB_FREQUENCY_BANDS; i++) { + // Compute decay rate. + let decayRate = -Utils.LOG1000 / durationsSamples[i]; + // Construct a standard one-zero, two-pole bandpass filter: + // H(z) = (b0 * z^0 + b1 * z^-1 + b2 * z^-2) / (1 + a1 * z^-1 + a2 * z^-2) + let omega = Utils.TWO_PI * + Utils.DEFAULT_REVERB_FREQUENCY_BANDS[i] / sampleRate; + let sinOmega = Math.sin(omega); + let alpha = sinOmega * Math.sinh(this._bandwidthCoeff * omega / sinOmega); + let a0CoeffReciprocal = 1 / (1 + alpha); + let b0Coeff = alpha * a0CoeffReciprocal; + let a1Coeff = -2 * Math.cos(omega) * a0CoeffReciprocal; + let a2Coeff = (1 - alpha) * a0CoeffReciprocal; + // We optimize since b2 = -b0, b1 = 0. + // Update equation for two-pole bandpass filter: + // u[n] = x[n] - a1 * x[n-1] - a2 * x[n-2] + // y[n] = b0 * (u[n] - u[n-2]) + let um1 = 0; + let um2 = 0; + for (let j = 0; j < durationsSamples[i]; j++) { + // Exponentially-decaying white noise. + let x = noiseSignal[j] * Math.exp(decayRate * j); + // Filter signal with bandpass filter and add to output. + let u = x - a1Coeff * um1 - a2Coeff * um2; + bufferData[j] += b0Coeff * (u - um2); + // Update coefficients. + um2 = um1; + um1 = u; + } + } + // Create and apply half of a Hann window to the beginning of the + // impulse response. + let halfHannLength = Math.round(this._tailonsetSamples); + for (let i = 0; i < Math.min(bufferData.length, halfHannLength); i++) { + let halfHann = 0.5 * (1 - Math.cos(Utils.TWO_PI * i / (2 * halfHannLength - 1))); + bufferData[i] *= halfHann; + } + this._convolver.buffer = buffer; + } +} +export default LateReflections; diff --git a/src/framework/resonator/vendor/resonance-es6/listener.d.ts b/src/framework/resonator/vendor/resonance-es6/listener.d.ts new file mode 100644 index 0000000..2b98e59 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/listener.d.ts @@ -0,0 +1,49 @@ +export default Listener; +/** + * @class Listener + * @description Listener model to spatialize sources in an environment. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Number} options.ambisonicOrder + * Desired ambisonic order. Defaults to + * {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}. + * @param {Float32Array} options.position + * Initial position (in meters), where origin is the center of + * the room. Defaults to + * {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}. + * @param {Float32Array} options.forward + * The listener's initial forward vector. Defaults to + * {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}. + * @param {Float32Array} options.up + * The listener's initial up vector. Defaults to + * {@linkcode Utils.DEFAULT_UP DEFAULT_UP}. + */ +declare class Listener { + constructor(context: any, options: any); + position: Float32Array; + _tempMatrix3: Float32Array; + _ambisonicOrder: number; + _context: any; + _renderer: any; + input: any; + output: any; + ambisonicOutput: any; + /** + * Set the source's orientation using forward and up vectors. + * @param {Number} forwardX + * @param {Number} forwardY + * @param {Number} forwardZ + * @param {Number} upX + * @param {Number} upY + * @param {Number} upZ + */ + setOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void; + /** + * Set the listener's position and orientation using a Three.js Matrix4 object. + * @param {Object} matrix4 + * The Three.js Matrix4 object representing the listener's world transform. + */ + setFromMatrix(matrix4: any): void; +} diff --git a/src/framework/resonator/vendor/resonance-es6/listener.js b/src/framework/resonator/vendor/resonance-es6/listener.js new file mode 100644 index 0000000..2e6b15a --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/listener.js @@ -0,0 +1,168 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Listener model to spatialize sources in an environment. + * @author Andrew Allen + */ +'use strict'; +// Internal dependencies. +import Omnitone from 'omnitone/build/omnitone.esm'; +import Encoder from './encoder.js'; +import Utils from './utils.js'; +/** + * @class Listener + * @description Listener model to spatialize sources in an environment. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Number} options.ambisonicOrder + * Desired ambisonic order. Defaults to + * {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}. + * @param {Float32Array} options.position + * Initial position (in meters), where origin is the center of + * the room. Defaults to + * {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}. + * @param {Float32Array} options.forward + * The listener's initial forward vector. Defaults to + * {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}. + * @param {Float32Array} options.up + * The listener's initial up vector. Defaults to + * {@linkcode Utils.DEFAULT_UP DEFAULT_UP}. + */ +class Listener { + constructor(context, options) { + // Public variables. + /** + * Position (in meters). + * @member {Float32Array} position + * @memberof Listener + * @instance + */ + /** + * Ambisonic (multichannel) input {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} input + * @memberof Listener + * @instance + */ + /** + * Binaurally-rendered stereo (2-channel) output {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} output + * @memberof Listener + * @instance + */ + /** + * Ambisonic (multichannel) output {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} ambisonicOutput + * @memberof Listener + * @instance + */ + // Use defaults for undefined arguments. + if (options == undefined) { + options = {}; + } + if (options.ambisonicOrder == undefined) { + options.ambisonicOrder = Utils.DEFAULT_AMBISONIC_ORDER; + } + if (options.position == undefined) { + options.position = Utils.DEFAULT_POSITION.slice(); + } + if (options.forward == undefined) { + options.forward = Utils.DEFAULT_FORWARD.slice(); + } + if (options.up == undefined) { + options.up = Utils.DEFAULT_UP.slice(); + } + // Member variables. + this.position = new Float32Array(3); + this._tempMatrix3 = new Float32Array(9); + // Select the appropriate HRIR filters using 2-channel chunks since + // multichannel audio is not yet supported by a majority of browsers. + this._ambisonicOrder = + Encoder.validateAmbisonicOrder(options.ambisonicOrder); + // Create audio nodes. + this._context = context; + if (this._ambisonicOrder == 1) { + this._renderer = Omnitone.createFOARenderer(context, {}); + } + else if (this._ambisonicOrder > 1) { + this._renderer = Omnitone.createHOARenderer(context, { + ambisonicOrder: this._ambisonicOrder, + }); + } + // These nodes are created in order to safely asynchronously load Omnitone + // while the rest of the scene is being created. + this.input = context.createGain(); + this.output = context.createGain(); + this.ambisonicOutput = context.createGain(); + // Initialize Omnitone (async) and connect to audio graph when complete. + let that = this; + this._renderer.initialize().then(() => { + // Connect pre-rotated soundfield to renderer. + that.input.connect(that._renderer.input); + // Connect rotated soundfield to ambisonic output. + if (that._ambisonicOrder > 1) { + that._renderer._hoaRotator.output.connect(that.ambisonicOutput); + } + else { + that._renderer._foaRotator.output.connect(that.ambisonicOutput); + } + // Connect binaurally-rendered soundfield to binaural output. + that._renderer.output.connect(that.output); + }); + // Set orientation and update rotation matrix accordingly. + this.setOrientation(options.forward[0], options.forward[1], options.forward[2], options.up[0], options.up[1], options.up[2]); + } + /** + * Set the source's orientation using forward and up vectors. + * @param {Number} forwardX + * @param {Number} forwardY + * @param {Number} forwardZ + * @param {Number} upX + * @param {Number} upY + * @param {Number} upZ + */ + setOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ) { + let right = Utils.crossProduct([forwardX, forwardY, forwardZ], [upX, upY, upZ]); + this._tempMatrix3[0] = right[0]; + this._tempMatrix3[1] = right[1]; + this._tempMatrix3[2] = right[2]; + this._tempMatrix3[3] = upX; + this._tempMatrix3[4] = upY; + this._tempMatrix3[5] = upZ; + this._tempMatrix3[6] = forwardX; + this._tempMatrix3[7] = forwardY; + this._tempMatrix3[8] = forwardZ; + this._renderer.setRotationMatrix3(this._tempMatrix3); + } + /** + * Set the listener's position and orientation using a Three.js Matrix4 object. + * @param {Object} matrix4 + * The Three.js Matrix4 object representing the listener's world transform. + */ + setFromMatrix(matrix4) { + // Update ambisonic rotation matrix internally. + this._renderer.setRotationMatrix4(matrix4.elements); + // Extract position from matrix. + this.position[0] = matrix4.elements[12]; + this.position[1] = matrix4.elements[13]; + this.position[2] = matrix4.elements[14]; + } +} +export default Listener; diff --git a/src/framework/resonator/vendor/resonance-es6/main.d.ts b/src/framework/resonator/vendor/resonance-es6/main.d.ts new file mode 100644 index 0000000..384e86e --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/main.d.ts @@ -0,0 +1,2 @@ +export default ResonanceAudio; +import ResonanceAudio from "./resonance-audio"; diff --git a/src/framework/resonator/vendor/resonance-es6/main.js b/src/framework/resonator/vendor/resonance-es6/main.js new file mode 100644 index 0000000..8afdc68 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/main.js @@ -0,0 +1,23 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Primary namespace for ResonanceAudio library. + * @author Andrew Allen + */ +'use strict'; +import ResonanceAudio from './resonance-audio'; +// Main module. +export default ResonanceAudio; diff --git a/src/framework/resonator/vendor/resonance-es6/resonance-audio.d.ts b/src/framework/resonator/vendor/resonance-es6/resonance-audio.d.ts new file mode 100644 index 0000000..7c2194e --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/resonance-audio.d.ts @@ -0,0 +1,130 @@ +export default ResonanceAudio; +/** + * ~ResonanceAudioOptions + */ +export type ResonanceAudio = { + /** + * Desired ambisonic Order. Defaults to + * {@link Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}. + */ + ambisonicOrder: number; + /** + * The listener's initial position (in meters), where origin is the center of + * the room. Defaults to {@link Utils.DEFAULT_POSITION DEFAULT_POSITION}. + */ + listenerPosition: Float32Array; + /** + * The listener's initial forward vector. + * Defaults to {@link Utils.DEFAULT_FORWARD DEFAULT_FORWARD}. + */ + listenerForward: Float32Array; + /** + * The listener's initial up vector. + * Defaults to {@link Utils.DEFAULT_UP DEFAULT_UP}. + */ + listenerUp: Float32Array; + /** + * ~RoomDimensions} dimensions Room dimensions (in meters). Defaults to + * {@link Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + */ + "": Utils; + /** + * (in meters/second). Defaults to + * {@link Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}. + */ + speedOfSound: number; +}; +/** + * Options for constructing a new ResonanceAudio scene. + * @typedef {Object} ResonanceAudio~ResonanceAudioOptions + * @property {Number} ambisonicOrder + * Desired ambisonic Order. Defaults to + * {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}. + * @property {Float32Array} listenerPosition + * The listener's initial position (in meters), where origin is the center of + * the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}. + * @property {Float32Array} listenerForward + * The listener's initial forward vector. + * Defaults to {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}. + * @property {Float32Array} listenerUp + * The listener's initial up vector. + * Defaults to {@linkcode Utils.DEFAULT_UP DEFAULT_UP}. + * @property {Utils~RoomDimensions} dimensions Room dimensions (in meters). Defaults to + * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + * @property {Utils~RoomMaterials} materials Named acoustic materials per wall. + * Defaults to {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}. + * @property {Number} speedOfSound + * (in meters/second). Defaults to + * {@linkcode Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}. + */ +/** + * @class ResonanceAudio + * @description Main class for managing sources, room and listener models. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {ResonanceAudio~ResonanceAudioOptions} options + * Options for constructing a new ResonanceAudio scene. + */ +declare class ResonanceAudio { + constructor(context: any, options: any); + _ambisonicOrder: number; + _sources: any[]; + _room: Room; + _listener: Listener; + _context: any; + output: any; + ambisonicOutput: any; + ambisonicInput: any; + /** + * Create a new source for the scene. + * @param {Source~SourceOptions} options + * Options for constructing a new Source. + * @return {Source} + */ + createSource(options: any): Source; + /** + * Set the scene's desired ambisonic order. + * @param {Number} ambisonicOrder Desired ambisonic order. + */ + setAmbisonicOrder(ambisonicOrder: number): void; + /** + * Set the room's dimensions and wall materials. + * @param {Object} dimensions Room dimensions (in meters). + * @param {Object} materials Named acoustic materials per wall. + */ + setRoomProperties(dimensions: any, materials: any): void; + /** + * Set the listener's position (in meters), where origin is the center of + * the room. + * @param {Number} x + * @param {Number} y + * @param {Number} z + */ + setListenerPosition(x: number, y: number, z: number): void; + /** + * Set the source's orientation using forward and up vectors. + * @param {Number} forwardX + * @param {Number} forwardY + * @param {Number} forwardZ + * @param {Number} upX + * @param {Number} upY + * @param {Number} upZ + */ + setListenerOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void; + /** + * Set the listener's position and orientation using a Three.js Matrix4 object. + * @param {Object} matrix + * The Three.js Matrix4 object representing the listener's world transform. + */ + setListenerFromMatrix(matrix: any): void; + /** + * Set the speed of sound. + * @param {Number} speedOfSound + */ + setSpeedOfSound(speedOfSound: number): void; +} +import Utils from "./utils.js"; +import Room from "./room.js"; +import Listener from "./listener.js"; +import Source from "./source.js"; diff --git a/src/framework/resonator/vendor/resonance-es6/resonance-audio.js b/src/framework/resonator/vendor/resonance-es6/resonance-audio.js new file mode 100644 index 0000000..32d14f3 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/resonance-audio.js @@ -0,0 +1,213 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ResonanceAudio library name space and common utilities. + * @author Andrew Allen + */ +'use strict'; +// Internal dependencies. +import Listener from './listener.js'; +import Source from './source.js'; +import Room from './room.js'; +import Encoder from './encoder.js'; +import Utils from './utils.js'; +/** + * Options for constructing a new ResonanceAudio scene. + * @typedef {Object} ResonanceAudio~ResonanceAudioOptions + * @property {Number} ambisonicOrder + * Desired ambisonic Order. Defaults to + * {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}. + * @property {Float32Array} listenerPosition + * The listener's initial position (in meters), where origin is the center of + * the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}. + * @property {Float32Array} listenerForward + * The listener's initial forward vector. + * Defaults to {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}. + * @property {Float32Array} listenerUp + * The listener's initial up vector. + * Defaults to {@linkcode Utils.DEFAULT_UP DEFAULT_UP}. + * @property {Utils~RoomDimensions} dimensions Room dimensions (in meters). Defaults to + * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + * @property {Utils~RoomMaterials} materials Named acoustic materials per wall. + * Defaults to {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}. + * @property {Number} speedOfSound + * (in meters/second). Defaults to + * {@linkcode Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}. + */ +/** + * @class ResonanceAudio + * @description Main class for managing sources, room and listener models. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {ResonanceAudio~ResonanceAudioOptions} options + * Options for constructing a new ResonanceAudio scene. + */ +class ResonanceAudio { + constructor(context, options) { + // Public variables. + /** + * Binaurally-rendered stereo (2-channel) output {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} output + * @memberof ResonanceAudio + * @instance + */ + /** + * Ambisonic (multichannel) input {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode} + * (For rendering input soundfields). + * @member {AudioNode} ambisonicInput + * @memberof ResonanceAudio + * @instance + */ + /** + * Ambisonic (multichannel) output {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode} + * (For allowing external rendering / post-processing). + * @member {AudioNode} ambisonicOutput + * @memberof ResonanceAudio + * @instance + */ + // Use defaults for undefined arguments. + if (options == undefined) { + options = {}; + } + if (options.ambisonicOrder == undefined) { + options.ambisonicOrder = Utils.DEFAULT_AMBISONIC_ORDER; + } + if (options.listenerPosition == undefined) { + options.listenerPosition = Utils.DEFAULT_POSITION.slice(); + } + if (options.listenerForward == undefined) { + options.listenerForward = Utils.DEFAULT_FORWARD.slice(); + } + if (options.listenerUp == undefined) { + options.listenerUp = Utils.DEFAULT_UP.slice(); + } + if (options.dimensions == undefined) { + options.dimensions = {}; + Object.assign(options.dimensions, Utils.DEFAULT_ROOM_DIMENSIONS); + } + if (options.materials == undefined) { + options.materials = {}; + Object.assign(options.materials, Utils.DEFAULT_ROOM_MATERIALS); + } + if (options.speedOfSound == undefined) { + options.speedOfSound = Utils.DEFAULT_SPEED_OF_SOUND; + } + // Create member submodules. + this._ambisonicOrder = Encoder.validateAmbisonicOrder(options.ambisonicOrder); + this._sources = []; + this._room = new Room(context, { + listenerPosition: options.listenerPosition, + dimensions: options.dimensions, + materials: options.materials, + speedOfSound: options.speedOfSound, + }); + this._listener = new Listener(context, { + ambisonicOrder: options.ambisonicOrder, + position: options.listenerPosition, + forward: options.listenerForward, + up: options.listenerUp, + }); + // Create auxillary audio nodes. + this._context = context; + this.output = context.createGain(); + this.ambisonicOutput = context.createGain(); + this.ambisonicInput = this._listener.input; + // Connect audio graph. + this._room.output.connect(this._listener.input); + this._listener.output.connect(this.output); + this._listener.ambisonicOutput.connect(this.ambisonicOutput); + } + /** + * Create a new source for the scene. + * @param {Source~SourceOptions} options + * Options for constructing a new Source. + * @return {Source} + */ + createSource(options) { + // Create a source and push it to the internal sources array, returning + // the object's reference to the user. + let source = new Source(this, options); + this._sources[this._sources.length] = source; + return source; + } + /** + * Set the scene's desired ambisonic order. + * @param {Number} ambisonicOrder Desired ambisonic order. + */ + setAmbisonicOrder(ambisonicOrder) { + this._ambisonicOrder = Encoder.validateAmbisonicOrder(ambisonicOrder); + } + /** + * Set the room's dimensions and wall materials. + * @param {Object} dimensions Room dimensions (in meters). + * @param {Object} materials Named acoustic materials per wall. + */ + setRoomProperties(dimensions, materials) { + this._room.setProperties(dimensions, materials); + } + /** + * Set the listener's position (in meters), where origin is the center of + * the room. + * @param {Number} x + * @param {Number} y + * @param {Number} z + */ + setListenerPosition(x, y, z) { + // Update listener position. + this._listener.position[0] = x; + this._listener.position[1] = y; + this._listener.position[2] = z; + this._room.setListenerPosition(x, y, z); + // Update sources with new listener position. + this._sources.forEach(element => { + element._update(); + }); + } + /** + * Set the source's orientation using forward and up vectors. + * @param {Number} forwardX + * @param {Number} forwardY + * @param {Number} forwardZ + * @param {Number} upX + * @param {Number} upY + * @param {Number} upZ + */ + setListenerOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ) { + this._listener.setOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ); + } + /** + * Set the listener's position and orientation using a Three.js Matrix4 object. + * @param {Object} matrix + * The Three.js Matrix4 object representing the listener's world transform. + */ + setListenerFromMatrix(matrix) { + this._listener.setFromMatrix(matrix); + // Update the rest of the scene using new listener position. + this.setListenerPosition(this._listener.position[0], this._listener.position[1], this._listener.position[2]); + } + /** + * Set the speed of sound. + * @param {Number} speedOfSound + */ + setSpeedOfSound(speedOfSound) { + this._room.speedOfSound = speedOfSound; + } +} +export default ResonanceAudio; diff --git a/src/framework/resonator/vendor/resonance-es6/room.d.ts b/src/framework/resonator/vendor/resonance-es6/room.d.ts new file mode 100644 index 0000000..a04788c --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/room.d.ts @@ -0,0 +1,55 @@ +export default Room; +/** + * @class Room + * @description Model that manages early and late reflections using acoustic + * properties and listener position relative to a rectangular room. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Float32Array} options.listenerPosition + * The listener's initial position (in meters), where origin is the center of + * the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}. + * @param {Utils~RoomDimensions} options.dimensions Room dimensions (in meters). Defaults to + * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + * @param {Utils~RoomMaterials} options.materials Named acoustic materials per wall. + * Defaults to {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}. + * @param {Number} options.speedOfSound + * (in meters/second). Defaults to + * {@linkcode Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}. + */ +declare class Room { + constructor(context: any, options: any); + early: EarlyReflections; + late: LateReflections; + speedOfSound: any; + output: any; + _merger: any; + /** + * Set the room's dimensions and wall materials. + * @param {Utils~RoomDimensions} dimensions Room dimensions (in meters). Defaults to + * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + * @param {Utils~RoomMaterials} materials Named acoustic materials per wall. Defaults to + * {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}. + */ + setProperties(dimensions: any, materials: any): void; + /** + * Set the listener's position (in meters), where origin is the center of + * the room. + * @param {Number} x + * @param {Number} y + * @param {Number} z + */ + setListenerPosition(x: number, y: number, z: number): void; + /** + * Compute distance outside room of provided position (in meters). + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @return {Number} + * Distance outside room (in meters). Returns 0 if inside room. + */ + getDistanceOutsideRoom(x: number, y: number, z: number): number; +} +import EarlyReflections from "./early-reflections.js"; +import LateReflections from "./late-reflections.js"; diff --git a/src/framework/resonator/vendor/resonance-es6/room.js b/src/framework/resonator/vendor/resonance-es6/room.js new file mode 100644 index 0000000..e2a54c5 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/room.js @@ -0,0 +1,300 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Complete room model with early and late reflections. + * @author Andrew Allen + */ +'use strict'; +// Internal dependencies. +import LateReflections from './late-reflections.js'; +import EarlyReflections from './early-reflections.js'; +import Utils from './utils.js'; +/** + * Generate absorption coefficients from material names. + * @param {Object} materials + * @return {Object} + */ +function _getCoefficientsFromMaterials(materials) { + // Initialize coefficients to use defaults. + let coefficients = {}; + for (let property in Utils.DEFAULT_ROOM_MATERIALS) { + if (Utils.DEFAULT_ROOM_MATERIALS.hasOwnProperty(property)) { + coefficients[property] = Utils.ROOM_MATERIAL_COEFFICIENTS[Utils.DEFAULT_ROOM_MATERIALS[property]]; + } + } + // Sanitize materials. + if (materials == undefined) { + materials = {}; + Object.assign(materials, Utils.DEFAULT_ROOM_MATERIALS); + } + // Assign coefficients using provided materials. + for (let property in Utils.DEFAULT_ROOM_MATERIALS) { + if (Utils.DEFAULT_ROOM_MATERIALS.hasOwnProperty(property) && + materials.hasOwnProperty(property)) { + if (materials[property] in Utils.ROOM_MATERIAL_COEFFICIENTS) { + coefficients[property] = + Utils.ROOM_MATERIAL_COEFFICIENTS[materials[property]]; + } + else { + Utils.log('Material \"' + materials[property] + '\" on wall \"' + + property + '\" not found. Using \"' + + Utils.DEFAULT_ROOM_MATERIALS[property] + '\".'); + } + } + else { + Utils.log('Wall \"' + property + '\" is not defined. Default used.'); + } + } + return coefficients; +} +/** + * Sanitize coefficients. + * @param {Object} coefficients + * @return {Object} + */ +function _sanitizeCoefficients(coefficients) { + if (coefficients == undefined) { + coefficients = {}; + } + for (let property in Utils.DEFAULT_ROOM_MATERIALS) { + if (!(coefficients.hasOwnProperty(property))) { + // If element is not present, use default coefficients. + coefficients[property] = Utils.ROOM_MATERIAL_COEFFICIENTS[Utils.DEFAULT_ROOM_MATERIALS[property]]; + } + } + return coefficients; +} +/** + * Sanitize dimensions. + * @param {Utils~RoomDimensions} dimensions + * @return {Utils~RoomDimensions} + */ +function _sanitizeDimensions(dimensions) { + if (dimensions == undefined) { + dimensions = {}; + } + for (let property in Utils.DEFAULT_ROOM_DIMENSIONS) { + if (!(dimensions.hasOwnProperty(property))) { + dimensions[property] = Utils.DEFAULT_ROOM_DIMENSIONS[property]; + } + } + return dimensions; +} +/** + * Compute frequency-dependent reverb durations. + * @param {Utils~RoomDimensions} dimensions + * @param {Object} coefficients + * @param {Number} speedOfSound + * @return {Array} + */ +function _getDurationsFromProperties(dimensions, coefficients, speedOfSound) { + let durations = new Float32Array(Utils.NUMBER_REVERB_FREQUENCY_BANDS); + // Sanitize inputs. + dimensions = _sanitizeDimensions(dimensions); + coefficients = _sanitizeCoefficients(coefficients); + if (speedOfSound == undefined) { + speedOfSound = Utils.DEFAULT_SPEED_OF_SOUND; + } + // Acoustic constant. + let k = Utils.TWENTY_FOUR_LOG10 / speedOfSound; + // Compute volume, skip if room is not present. + let volume = dimensions.width * dimensions.height * dimensions.depth; + if (volume < Utils.ROOM_MIN_VOLUME) { + return durations; + } + // Room surface area. + let leftRightArea = dimensions.width * dimensions.height; + let floorCeilingArea = dimensions.width * dimensions.depth; + let frontBackArea = dimensions.depth * dimensions.height; + let totalArea = 2 * (leftRightArea + floorCeilingArea + frontBackArea); + for (let i = 0; i < Utils.NUMBER_REVERB_FREQUENCY_BANDS; i++) { + // Effective absorptive area. + let absorbtionArea = (coefficients.left[i] + coefficients.right[i]) * leftRightArea + + (coefficients.down[i] + coefficients.up[i]) * floorCeilingArea + + (coefficients.front[i] + coefficients.back[i]) * frontBackArea; + let meanAbsorbtionArea = absorbtionArea / totalArea; + // Compute reverberation using Eyring equation [1]. + // [1] Beranek, Leo L. "Analysis of Sabine and Eyring equations and their + // application to concert hall audience and chair absorption." The + // Journal of the Acoustical Society of America, Vol. 120, No. 3. + // (2006), pp. 1399-1399. + durations[i] = Utils.ROOM_EYRING_CORRECTION_COEFFICIENT * k * volume / + (-totalArea * Math.log(1 - meanAbsorbtionArea) + 4 * + Utils.ROOM_AIR_ABSORPTION_COEFFICIENTS[i] * volume); + } + return durations; +} +/** + * Compute reflection coefficients from absorption coefficients. + * @param {Object} absorptionCoefficients + * @return {Object} + */ +function _computeReflectionCoefficients(absorptionCoefficients) { + let reflectionCoefficients = []; + for (let property in Utils.DEFAULT_REFLECTION_COEFFICIENTS) { + if (Utils.DEFAULT_REFLECTION_COEFFICIENTS + .hasOwnProperty(property)) { + // Compute average absorption coefficient (per wall). + reflectionCoefficients[property] = 0; + for (let j = 0; j < Utils.NUMBER_REFLECTION_AVERAGING_BANDS; j++) { + let bandIndex = j + Utils.ROOM_STARTING_AVERAGING_BAND; + reflectionCoefficients[property] += + absorptionCoefficients[property][bandIndex]; + } + reflectionCoefficients[property] /= + Utils.NUMBER_REFLECTION_AVERAGING_BANDS; + // Convert absorption coefficient to reflection coefficient. + reflectionCoefficients[property] = + Math.sqrt(1 - reflectionCoefficients[property]); + } + } + return reflectionCoefficients; +} +/** + * @class Room + * @description Model that manages early and late reflections using acoustic + * properties and listener position relative to a rectangular room. + * @param {AudioContext} context + * Associated {@link +https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}. + * @param {Object} options + * @param {Float32Array} options.listenerPosition + * The listener's initial position (in meters), where origin is the center of + * the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}. + * @param {Utils~RoomDimensions} options.dimensions Room dimensions (in meters). Defaults to + * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + * @param {Utils~RoomMaterials} options.materials Named acoustic materials per wall. + * Defaults to {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}. + * @param {Number} options.speedOfSound + * (in meters/second). Defaults to + * {@linkcode Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}. + */ +class Room { + constructor(context, options) { + // Public variables. + /** + * EarlyReflections {@link EarlyReflections EarlyReflections} submodule. + * @member {AudioNode} early + * @memberof Room + * @instance + */ + /** + * LateReflections {@link LateReflections LateReflections} submodule. + * @member {AudioNode} late + * @memberof Room + * @instance + */ + /** + * Ambisonic (multichannel) output {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} output + * @memberof Room + * @instance + */ + // Use defaults for undefined arguments. + if (options == undefined) { + options = {}; + } + if (options.listenerPosition == undefined) { + options.listenerPosition = Utils.DEFAULT_POSITION.slice(); + } + if (options.dimensions == undefined) { + options.dimensions = {}; + Object.assign(options.dimensions, Utils.DEFAULT_ROOM_DIMENSIONS); + } + if (options.materials == undefined) { + options.materials = {}; + Object.assign(options.materials, Utils.DEFAULT_ROOM_MATERIALS); + } + if (options.speedOfSound == undefined) { + options.speedOfSound = Utils.DEFAULT_SPEED_OF_SOUND; + } + // Sanitize room-properties-related arguments. + options.dimensions = _sanitizeDimensions(options.dimensions); + let absorptionCoefficients = _getCoefficientsFromMaterials(options.materials); + let reflectionCoefficients = _computeReflectionCoefficients(absorptionCoefficients); + let durations = _getDurationsFromProperties(options.dimensions, absorptionCoefficients, options.speedOfSound); + // Construct submodules for early and late reflections. + this.early = new EarlyReflections(context, { + dimensions: options.dimensions, + coefficients: reflectionCoefficients, + speedOfSound: options.speedOfSound, + listenerPosition: options.listenerPosition, + }); + this.late = new LateReflections(context, { + durations: durations, + }); + this.speedOfSound = options.speedOfSound; + // Construct auxillary audio nodes. + this.output = context.createGain(); + this.early.output.connect(this.output); + this._merger = context.createChannelMerger(4); + this.late.output.connect(this._merger, 0, 0); + this._merger.connect(this.output); + } + /** + * Set the room's dimensions and wall materials. + * @param {Utils~RoomDimensions} dimensions Room dimensions (in meters). Defaults to + * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}. + * @param {Utils~RoomMaterials} materials Named acoustic materials per wall. Defaults to + * {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}. + */ + setProperties(dimensions, materials) { + // Compute late response. + let absorptionCoefficients = _getCoefficientsFromMaterials(materials); + let durations = _getDurationsFromProperties(dimensions, absorptionCoefficients, this.speedOfSound); + this.late.setDurations(durations); + // Compute early response. + this.early.speedOfSound = this.speedOfSound; + let reflectionCoefficients = _computeReflectionCoefficients(absorptionCoefficients); + this.early.setRoomProperties(dimensions, reflectionCoefficients); + } + /** + * Set the listener's position (in meters), where origin is the center of + * the room. + * @param {Number} x + * @param {Number} y + * @param {Number} z + */ + setListenerPosition(x, y, z) { + this.early.speedOfSound = this.speedOfSound; + this.early.setListenerPosition(x, y, z); + // Disable room effects if the listener is outside the room boundaries. + let distance = this.getDistanceOutsideRoom(x, y, z); + let gain = 1; + if (distance > Utils.EPSILON_FLOAT) { + gain = 1 - distance / Utils.LISTENER_MAX_OUTSIDE_ROOM_DISTANCE; + // Clamp gain between 0 and 1. + gain = Math.max(0, Math.min(1, gain)); + } + this.output.gain.value = gain; + } + /** + * Compute distance outside room of provided position (in meters). + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @return {Number} + * Distance outside room (in meters). Returns 0 if inside room. + */ + getDistanceOutsideRoom(x, y, z) { + let dx = Math.max(0, -this.early._halfDimensions.width - x, x - this.early._halfDimensions.width); + let dy = Math.max(0, -this.early._halfDimensions.height - y, y - this.early._halfDimensions.height); + let dz = Math.max(0, -this.early._halfDimensions.depth - z, z - this.early._halfDimensions.depth); + return Math.sqrt(dx * dx + dy * dy + dz * dz); + } +} +export default Room; diff --git a/src/framework/resonator/vendor/resonance-es6/source.d.ts b/src/framework/resonator/vendor/resonance-es6/source.d.ts new file mode 100644 index 0000000..bf24786 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/source.d.ts @@ -0,0 +1,182 @@ +export default Source; +/** + * ~SourceOptions + */ +export type Source = { + /** + * The source's initial position (in meters), where origin is the center of + * the room. Defaults to {@link Utils.DEFAULT_POSITION DEFAULT_POSITION}. + */ + position: Float32Array; + /** + * The source's initial forward vector. Defaults to + * {@link Utils.DEFAULT_FORWARD DEFAULT_FORWARD}. + */ + forward: Float32Array; + /** + * The source's initial up vector. Defaults to + * {@link Utils.DEFAULT_UP DEFAULT_UP}. + */ + up: Float32Array; + /** + * Min. distance (in meters). Defaults to + * {@link Utils.DEFAULT_MIN_DISTANCE DEFAULT_MIN_DISTANCE}. + */ + minDistance: number; + /** + * Max. distance (in meters). Defaults to + * {@link Utils.DEFAULT_MAX_DISTANCE DEFAULT_MAX_DISTANCE}. + */ + maxDistance: number; + /** + * Rolloff model to use, chosen from options in + * {@link Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. Defaults to + * {@link Utils.DEFAULT_ATTENUATION_ROLLOFF DEFAULT_ATTENUATION_ROLLOFF}. + */ + rolloff: string; + /** + * Input gain (linear). Defaults to + * {@link Utils.DEFAULT_SOURCE_GAIN DEFAULT_SOURCE_GAIN}. + */ + gain: number; + /** + * Directivity alpha. Defaults to + * {@link Utils.DEFAULT_DIRECTIVITY_ALPHA DEFAULT_DIRECTIVITY_ALPHA}. + */ + alpha: number; + /** + * Directivity sharpness. Defaults to + * {@link Utils.DEFAULT_DIRECTIVITY_SHARPNESS * DEFAULT_DIRECTIVITY_SHARPNESS}. + */ + sharpness: number; + /** + * Source width (in degrees). Where 0 degrees is a point source and 360 degrees + * is an omnidirectional source. Defaults to + * {@link Utils.DEFAULT_SOURCE_WIDTH DEFAULT_SOURCE_WIDTH}. + */ + sourceWidth: number; +}; +/** + * Options for constructing a new Source. + * @typedef {Object} Source~SourceOptions + * @property {Float32Array} position + * The source's initial position (in meters), where origin is the center of + * the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}. + * @property {Float32Array} forward + * The source's initial forward vector. Defaults to + * {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}. + * @property {Float32Array} up + * The source's initial up vector. Defaults to + * {@linkcode Utils.DEFAULT_UP DEFAULT_UP}. + * @property {Number} minDistance + * Min. distance (in meters). Defaults to + * {@linkcode Utils.DEFAULT_MIN_DISTANCE DEFAULT_MIN_DISTANCE}. + * @property {Number} maxDistance + * Max. distance (in meters). Defaults to + * {@linkcode Utils.DEFAULT_MAX_DISTANCE DEFAULT_MAX_DISTANCE}. + * @property {string} rolloff + * Rolloff model to use, chosen from options in + * {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. Defaults to + * {@linkcode Utils.DEFAULT_ATTENUATION_ROLLOFF DEFAULT_ATTENUATION_ROLLOFF}. + * @property {Number} gain Input gain (linear). Defaults to + * {@linkcode Utils.DEFAULT_SOURCE_GAIN DEFAULT_SOURCE_GAIN}. + * @property {Number} alpha Directivity alpha. Defaults to + * {@linkcode Utils.DEFAULT_DIRECTIVITY_ALPHA DEFAULT_DIRECTIVITY_ALPHA}. + * @property {Number} sharpness Directivity sharpness. Defaults to + * {@linkcode Utils.DEFAULT_DIRECTIVITY_SHARPNESS + * DEFAULT_DIRECTIVITY_SHARPNESS}. + * @property {Number} sourceWidth + * Source width (in degrees). Where 0 degrees is a point source and 360 degrees + * is an omnidirectional source. Defaults to + * {@linkcode Utils.DEFAULT_SOURCE_WIDTH DEFAULT_SOURCE_WIDTH}. + */ +/** + * @class Source + * @description Source model to spatialize an audio buffer. + * @param {ResonanceAudio} scene Associated {@link ResonanceAudio + * ResonanceAudio} instance. + * @param {Source~SourceOptions} options + * Options for constructing a new Source. + */ +declare class Source { + constructor(scene: any, options: any); + _scene: any; + _position: any; + _forward: any; + _up: any; + _dx: Float32Array; + _right: Float32Array; + input: any; + _directivity: Directivity; + _toEarly: any; + _toLate: any; + _attenuation: Attenuation; + _encoder: Encoder; + /** + * Set source's position (in meters), where origin is the center of + * the room. + * @param {Number} x + * @param {Number} y + * @param {Number} z + */ + setPosition(x: number, y: number, z: number): void; + _update(): void; + /** + * Set source's rolloff. + * @param {string} rolloff + * Rolloff model to use, chosen from options in + * {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. + */ + setRolloff(rolloff: string): void; + /** + * Set source's minimum distance (in meters). + * @param {Number} minDistance + */ + setMinDistance(minDistance: number): void; + /** + * Set source's maximum distance (in meters). + * @param {Number} maxDistance + */ + setMaxDistance(maxDistance: number): void; + /** + * Set source's gain (linear). + * @param {Number} gain + */ + setGain(gain: number): void; + /** + * Set the source's orientation using forward and up vectors. + * @param {Number} forwardX + * @param {Number} forwardY + * @param {Number} forwardZ + * @param {Number} upX + * @param {Number} upY + * @param {Number} upZ + */ + setOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void; + /** + * Set source's position and orientation using a + * Three.js modelViewMatrix object. + * @param {Float32Array} matrix4 + * The Matrix4 representing the object position and rotation in world space. + */ + setFromMatrix(matrix4: Float32Array): void; + /** + * Set the source width (in degrees). Where 0 degrees is a point source and 360 + * degrees is an omnidirectional source. + * @param {Number} sourceWidth (in degrees). + */ + setSourceWidth(sourceWidth: number): void; + /** + * Set source's directivity pattern (defined by alpha), where 0 is an + * omnidirectional pattern, 1 is a bidirectional pattern, 0.5 is a cardiod + * pattern. The sharpness of the pattern is increased exponentially. + * @param {Number} alpha + * Determines directivity pattern (0 to 1). + * @param {Number} sharpness + * Determines the sharpness of the directivity pattern (1 to Inf). + */ + setDirectivityPattern(alpha: number, sharpness: number): void; +} +import Directivity from "./directivity.js"; +import Attenuation from "./attenuation.js"; +import Encoder from "./encoder.js"; diff --git a/src/framework/resonator/vendor/resonance-es6/source.js b/src/framework/resonator/vendor/resonance-es6/source.js new file mode 100644 index 0000000..510a9ca --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/source.js @@ -0,0 +1,308 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Source model to spatialize an audio buffer. + * @author Andrew Allen + */ +'use strict'; +// Internal dependencies. +import Directivity from './directivity.js'; +import Attenuation from './attenuation.js'; +import Encoder from './encoder.js'; +import Utils from './utils.js'; +/** + * Options for constructing a new Source. + * @typedef {Object} Source~SourceOptions + * @property {Float32Array} position + * The source's initial position (in meters), where origin is the center of + * the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}. + * @property {Float32Array} forward + * The source's initial forward vector. Defaults to + * {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}. + * @property {Float32Array} up + * The source's initial up vector. Defaults to + * {@linkcode Utils.DEFAULT_UP DEFAULT_UP}. + * @property {Number} minDistance + * Min. distance (in meters). Defaults to + * {@linkcode Utils.DEFAULT_MIN_DISTANCE DEFAULT_MIN_DISTANCE}. + * @property {Number} maxDistance + * Max. distance (in meters). Defaults to + * {@linkcode Utils.DEFAULT_MAX_DISTANCE DEFAULT_MAX_DISTANCE}. + * @property {string} rolloff + * Rolloff model to use, chosen from options in + * {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. Defaults to + * {@linkcode Utils.DEFAULT_ATTENUATION_ROLLOFF DEFAULT_ATTENUATION_ROLLOFF}. + * @property {Number} gain Input gain (linear). Defaults to + * {@linkcode Utils.DEFAULT_SOURCE_GAIN DEFAULT_SOURCE_GAIN}. + * @property {Number} alpha Directivity alpha. Defaults to + * {@linkcode Utils.DEFAULT_DIRECTIVITY_ALPHA DEFAULT_DIRECTIVITY_ALPHA}. + * @property {Number} sharpness Directivity sharpness. Defaults to + * {@linkcode Utils.DEFAULT_DIRECTIVITY_SHARPNESS + * DEFAULT_DIRECTIVITY_SHARPNESS}. + * @property {Number} sourceWidth + * Source width (in degrees). Where 0 degrees is a point source and 360 degrees + * is an omnidirectional source. Defaults to + * {@linkcode Utils.DEFAULT_SOURCE_WIDTH DEFAULT_SOURCE_WIDTH}. + */ +/** + * @class Source + * @description Source model to spatialize an audio buffer. + * @param {ResonanceAudio} scene Associated {@link ResonanceAudio + * ResonanceAudio} instance. + * @param {Source~SourceOptions} options + * Options for constructing a new Source. + */ +class Source { + constructor(scene, options) { + // Public variables. + /** + * Mono (1-channel) input {@link + * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}. + * @member {AudioNode} input + * @memberof Source + * @instance + */ + /** + * + */ + // Use defaults for undefined arguments. + if (options == undefined) { + options = {}; + } + if (options.position == undefined) { + options.position = Utils.DEFAULT_POSITION.slice(); + } + if (options.forward == undefined) { + options.forward = Utils.DEFAULT_FORWARD.slice(); + } + if (options.up == undefined) { + options.up = Utils.DEFAULT_UP.slice(); + } + if (options.minDistance == undefined) { + options.minDistance = Utils.DEFAULT_MIN_DISTANCE; + } + if (options.maxDistance == undefined) { + options.maxDistance = Utils.DEFAULT_MAX_DISTANCE; + } + if (options.rolloff == undefined) { + options.rolloff = Utils.DEFAULT_ROLLOFF; + } + if (options.gain == undefined) { + options.gain = Utils.DEFAULT_SOURCE_GAIN; + } + if (options.alpha == undefined) { + options.alpha = Utils.DEFAULT_DIRECTIVITY_ALPHA; + } + if (options.sharpness == undefined) { + options.sharpness = Utils.DEFAULT_DIRECTIVITY_SHARPNESS; + } + if (options.sourceWidth == undefined) { + options.sourceWidth = Utils.DEFAULT_SOURCE_WIDTH; + } + // Member variables. + this._scene = scene; + this._position = options.position; + this._forward = options.forward; + this._up = options.up; + this._dx = new Float32Array(3); + this._right = Utils.crossProduct(this._forward, this._up); + // Create audio nodes. + let context = scene._context; + this.input = context.createGain(); + this._directivity = new Directivity(context, { + alpha: options.alpha, + sharpness: options.sharpness, + }); + this._toEarly = context.createGain(); + this._toLate = context.createGain(); + this._attenuation = new Attenuation(context, { + minDistance: options.minDistance, + maxDistance: options.maxDistance, + rolloff: options.rolloff, + }); + this._encoder = new Encoder(context, { + ambisonicOrder: scene._ambisonicOrder, + sourceWidth: options.sourceWidth, + }); + // Connect nodes. + this.input.connect(this._toLate); + this._toLate.connect(scene._room.late.input); + this.input.connect(this._attenuation.input); + this._attenuation.output.connect(this._toEarly); + this._toEarly.connect(scene._room.early.input); + this._attenuation.output.connect(this._directivity.input); + this._directivity.output.connect(this._encoder.input); + this._encoder.output.connect(scene._listener.input); + // Assign initial conditions. + this.setPosition(options.position[0], options.position[1], options.position[2]); + this.input.gain.value = options.gain; + } + /** + * Set source's position (in meters), where origin is the center of + * the room. + * @param {Number} x + * @param {Number} y + * @param {Number} z + */ + setPosition(x, y, z) { + // Assign new position. + this._position[0] = x; + this._position[1] = y; + this._position[2] = z; + // Handle far-field effect. + let distance = this._scene._room.getDistanceOutsideRoom(this._position[0], this._position[1], this._position[2]); + let gain = _computeDistanceOutsideRoom(distance); + this._toLate.gain.value = gain; + this._toEarly.gain.value = gain; + this._update(); + } + // Update the source when changing the listener's position. + _update() { + // Compute distance to listener. + for (let i = 0; i < 3; i++) { + this._dx[i] = this._position[i] - this._scene._listener.position[i]; + } + let distance = Math.sqrt(this._dx[0] * this._dx[0] + + this._dx[1] * this._dx[1] + this._dx[2] * this._dx[2]); + if (distance > 0) { + // Normalize direction vector. + this._dx[0] /= distance; + this._dx[1] /= distance; + this._dx[2] /= distance; + } + // Compuete angle of direction vector. + let azimuth = Math.atan2(-this._dx[0], this._dx[2]) * + Utils.RADIANS_TO_DEGREES; + let elevation = Math.atan2(this._dx[1], Math.sqrt(this._dx[0] * this._dx[0] + + this._dx[2] * this._dx[2])) * Utils.RADIANS_TO_DEGREES; + // Set distance/directivity/direction values. + this._attenuation.setDistance(distance); + this._directivity.computeAngle(this._forward, this._dx); + this._encoder.setDirection(azimuth, elevation); + } + /** + * Set source's rolloff. + * @param {string} rolloff + * Rolloff model to use, chosen from options in + * {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. + */ + setRolloff(rolloff) { + this._attenuation.setRolloff(rolloff); + } + /** + * Set source's minimum distance (in meters). + * @param {Number} minDistance + */ + setMinDistance(minDistance) { + this._attenuation.minDistance = minDistance; + } + /** + * Set source's maximum distance (in meters). + * @param {Number} maxDistance + */ + setMaxDistance(maxDistance) { + this._attenuation.maxDistance = maxDistance; + } + /** + * Set source's gain (linear). + * @param {Number} gain + */ + setGain(gain) { + this.input.gain.value = gain; + } + /** + * Set the source's orientation using forward and up vectors. + * @param {Number} forwardX + * @param {Number} forwardY + * @param {Number} forwardZ + * @param {Number} upX + * @param {Number} upY + * @param {Number} upZ + */ + setOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ) { + this._forward[0] = forwardX; + this._forward[1] = forwardY; + this._forward[2] = forwardZ; + this._up[0] = upX; + this._up[1] = upY; + this._up[2] = upZ; + this._right = Utils.crossProduct(this._forward, this._up); + } + // TODO(bitllama): Make sure this works with Three.js as intended. + /** + * Set source's position and orientation using a + * Three.js modelViewMatrix object. + * @param {Float32Array} matrix4 + * The Matrix4 representing the object position and rotation in world space. + */ + setFromMatrix(matrix4) { + this._right[0] = matrix4.elements[0]; + this._right[1] = matrix4.elements[1]; + this._right[2] = matrix4.elements[2]; + this._up[0] = matrix4.elements[4]; + this._up[1] = matrix4.elements[5]; + this._up[2] = matrix4.elements[6]; + this._forward[0] = matrix4.elements[8]; + this._forward[1] = matrix4.elements[9]; + this._forward[2] = matrix4.elements[10]; + // Normalize to remove scaling. + this._right = Utils.normalizeVector(this._right); + this._up = Utils.normalizeVector(this._up); + this._forward = Utils.normalizeVector(this._forward); + // Update position. + this.setPosition(matrix4.elements[12], matrix4.elements[13], matrix4.elements[14]); + } + /** + * Set the source width (in degrees). Where 0 degrees is a point source and 360 + * degrees is an omnidirectional source. + * @param {Number} sourceWidth (in degrees). + */ + setSourceWidth(sourceWidth) { + this._encoder.setSourceWidth(sourceWidth); + this.setPosition(this._position[0], this._position[1], this._position[2]); + } + /** + * Set source's directivity pattern (defined by alpha), where 0 is an + * omnidirectional pattern, 1 is a bidirectional pattern, 0.5 is a cardiod + * pattern. The sharpness of the pattern is increased exponentially. + * @param {Number} alpha + * Determines directivity pattern (0 to 1). + * @param {Number} sharpness + * Determines the sharpness of the directivity pattern (1 to Inf). + */ + setDirectivityPattern(alpha, sharpness) { + this._directivity.setPattern(alpha, sharpness); + this.setPosition(this._position[0], this._position[1], this._position[2]); + } +} +/** + * Determine the distance a source is outside of a room. Attenuate gain going + * to the reflections and reverb when the source is outside of the room. + * @param {Number} distance Distance in meters. + * @return {Number} Gain (linear) of source. + * @private + */ +function _computeDistanceOutsideRoom(distance) { + // We apply a linear ramp from 1 to 0 as the source is up to 1m outside. + let gain = 1; + if (distance > Utils.EPSILON_FLOAT) { + gain = 1 - distance / Utils.SOURCE_MAX_OUTSIDE_ROOM_DISTANCE; + // Clamp gain between 0 and 1. + gain = Math.max(0, Math.min(1, gain)); + } + return gain; +} +export default Source; diff --git a/src/framework/resonator/vendor/resonance-es6/tables.d.ts b/src/framework/resonator/vendor/resonance-es6/tables.d.ts new file mode 100644 index 0000000..4116808 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/tables.d.ts @@ -0,0 +1,38 @@ +declare namespace _default { + export { SPHERICAL_HARMONICS }; + export { SPHERICAL_HARMONICS_AZIMUTH_RESOLUTION }; + export { SPHERICAL_HARMONICS_ELEVATION_RESOLUTION }; + export { SPHERICAL_HARMONICS_MAX_ORDER }; + export { MAX_RE_WEIGHTS }; + export { MAX_RE_WEIGHTS_RESOLUTION }; +} +export default _default; +/** + * Pre-computed Spherical Harmonics Coefficients. + * + * This function generates an efficient lookup table of SH coefficients. It + * exploits the way SHs are generated (i.e. Ylm = Nlm * Plm * Em). Since Nlm + * & Plm coefficients only depend on theta, and Em only depends on phi, we + * can separate the equation along these lines. Em does not depend on + * degree, so we only need to compute (2 * l) per azimuth Em total and + * Nlm * Plm is symmetrical across indexes, so only positive indexes are + * computed ((l + 1) * (l + 2) / 2 - 1) per elevation. + * @type {Float32Array} + */ +declare const SPHERICAL_HARMONICS: Float32Array; +/** @type {Number} */ +declare const SPHERICAL_HARMONICS_AZIMUTH_RESOLUTION: number; +/** @type {Number} */ +declare const SPHERICAL_HARMONICS_ELEVATION_RESOLUTION: number; +/** + * The maximum allowed ambisonic order. + * @type {Number} + */ +declare const SPHERICAL_HARMONICS_MAX_ORDER: number; +/** + * Pre-computed per-band weighting coefficients for producing energy-preserving + * Max-Re sources. + */ +declare const MAX_RE_WEIGHTS: number[][]; +/** @type {Number} */ +declare const MAX_RE_WEIGHTS_RESOLUTION: number; diff --git a/src/framework/resonator/vendor/resonance-es6/tables.js b/src/framework/resonator/vendor/resonance-es6/tables.js new file mode 100644 index 0000000..90117bf --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/tables.js @@ -0,0 +1,1144 @@ +/** + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Pre-computed lookup tables for encoding ambisonic sources. + * @author Andrew Allen + */ +'use strict'; +/** + * Pre-computed Spherical Harmonics Coefficients. + * + * This function generates an efficient lookup table of SH coefficients. It + * exploits the way SHs are generated (i.e. Ylm = Nlm * Plm * Em). Since Nlm + * & Plm coefficients only depend on theta, and Em only depends on phi, we + * can separate the equation along these lines. Em does not depend on + * degree, so we only need to compute (2 * l) per azimuth Em total and + * Nlm * Plm is symmetrical across indexes, so only positive indexes are + * computed ((l + 1) * (l + 2) / 2 - 1) per elevation. + * @type {Float32Array} + */ +const SPHERICAL_HARMONICS = [ + [ + [0.000000, 0.000000, 0.000000, 1.000000, 1.000000, 1.000000], + [0.052336, 0.034899, 0.017452, 0.999848, 0.999391, 0.998630], + [0.104528, 0.069756, 0.034899, 0.999391, 0.997564, 0.994522], + [0.156434, 0.104528, 0.052336, 0.998630, 0.994522, 0.987688], + [0.207912, 0.139173, 0.069756, 0.997564, 0.990268, 0.978148], + [0.258819, 0.173648, 0.087156, 0.996195, 0.984808, 0.965926], + [0.309017, 0.207912, 0.104528, 0.994522, 0.978148, 0.951057], + [0.358368, 0.241922, 0.121869, 0.992546, 0.970296, 0.933580], + [0.406737, 0.275637, 0.139173, 0.990268, 0.961262, 0.913545], + [0.453990, 0.309017, 0.156434, 0.987688, 0.951057, 0.891007], + [0.500000, 0.342020, 0.173648, 0.984808, 0.939693, 0.866025], + [0.544639, 0.374607, 0.190809, 0.981627, 0.927184, 0.838671], + [0.587785, 0.406737, 0.207912, 0.978148, 0.913545, 0.809017], + [0.629320, 0.438371, 0.224951, 0.974370, 0.898794, 0.777146], + [0.669131, 0.469472, 0.241922, 0.970296, 0.882948, 0.743145], + [0.707107, 0.500000, 0.258819, 0.965926, 0.866025, 0.707107], + [0.743145, 0.529919, 0.275637, 0.961262, 0.848048, 0.669131], + [0.777146, 0.559193, 0.292372, 0.956305, 0.829038, 0.629320], + [0.809017, 0.587785, 0.309017, 0.951057, 0.809017, 0.587785], + [0.838671, 0.615661, 0.325568, 0.945519, 0.788011, 0.544639], + [0.866025, 0.642788, 0.342020, 0.939693, 0.766044, 0.500000], + [0.891007, 0.669131, 0.358368, 0.933580, 0.743145, 0.453990], + [0.913545, 0.694658, 0.374607, 0.927184, 0.719340, 0.406737], + [0.933580, 0.719340, 0.390731, 0.920505, 0.694658, 0.358368], + [0.951057, 0.743145, 0.406737, 0.913545, 0.669131, 0.309017], + [0.965926, 0.766044, 0.422618, 0.906308, 0.642788, 0.258819], + [0.978148, 0.788011, 0.438371, 0.898794, 0.615661, 0.207912], + [0.987688, 0.809017, 0.453990, 0.891007, 0.587785, 0.156434], + [0.994522, 0.829038, 0.469472, 0.882948, 0.559193, 0.104528], + [0.998630, 0.848048, 0.484810, 0.874620, 0.529919, 0.052336], + [1.000000, 0.866025, 0.500000, 0.866025, 0.500000, 0.000000], + [0.998630, 0.882948, 0.515038, 0.857167, 0.469472, -0.052336], + [0.994522, 0.898794, 0.529919, 0.848048, 0.438371, -0.104528], + [0.987688, 0.913545, 0.544639, 0.838671, 0.406737, -0.156434], + [0.978148, 0.927184, 0.559193, 0.829038, 0.374607, -0.207912], + [0.965926, 0.939693, 0.573576, 0.819152, 0.342020, -0.258819], + [0.951057, 0.951057, 0.587785, 0.809017, 0.309017, -0.309017], + [0.933580, 0.961262, 0.601815, 0.798636, 0.275637, -0.358368], + [0.913545, 0.970296, 0.615661, 0.788011, 0.241922, -0.406737], + [0.891007, 0.978148, 0.629320, 0.777146, 0.207912, -0.453990], + [0.866025, 0.984808, 0.642788, 0.766044, 0.173648, -0.500000], + [0.838671, 0.990268, 0.656059, 0.754710, 0.139173, -0.544639], + [0.809017, 0.994522, 0.669131, 0.743145, 0.104528, -0.587785], + [0.777146, 0.997564, 0.681998, 0.731354, 0.069756, -0.629320], + [0.743145, 0.999391, 0.694658, 0.719340, 0.034899, -0.669131], + [0.707107, 1.000000, 0.707107, 0.707107, 0.000000, -0.707107], + [0.669131, 0.999391, 0.719340, 0.694658, -0.034899, -0.743145], + [0.629320, 0.997564, 0.731354, 0.681998, -0.069756, -0.777146], + [0.587785, 0.994522, 0.743145, 0.669131, -0.104528, -0.809017], + [0.544639, 0.990268, 0.754710, 0.656059, -0.139173, -0.838671], + [0.500000, 0.984808, 0.766044, 0.642788, -0.173648, -0.866025], + [0.453990, 0.978148, 0.777146, 0.629320, -0.207912, -0.891007], + [0.406737, 0.970296, 0.788011, 0.615661, -0.241922, -0.913545], + [0.358368, 0.961262, 0.798636, 0.601815, -0.275637, -0.933580], + [0.309017, 0.951057, 0.809017, 0.587785, -0.309017, -0.951057], + [0.258819, 0.939693, 0.819152, 0.573576, -0.342020, -0.965926], + [0.207912, 0.927184, 0.829038, 0.559193, -0.374607, -0.978148], + [0.156434, 0.913545, 0.838671, 0.544639, -0.406737, -0.987688], + [0.104528, 0.898794, 0.848048, 0.529919, -0.438371, -0.994522], + [0.052336, 0.882948, 0.857167, 0.515038, -0.469472, -0.998630], + [0.000000, 0.866025, 0.866025, 0.500000, -0.500000, -1.000000], + [-0.052336, 0.848048, 0.874620, 0.484810, -0.529919, -0.998630], + [-0.104528, 0.829038, 0.882948, 0.469472, -0.559193, -0.994522], + [-0.156434, 0.809017, 0.891007, 0.453990, -0.587785, -0.987688], + [-0.207912, 0.788011, 0.898794, 0.438371, -0.615661, -0.978148], + [-0.258819, 0.766044, 0.906308, 0.422618, -0.642788, -0.965926], + [-0.309017, 0.743145, 0.913545, 0.406737, -0.669131, -0.951057], + [-0.358368, 0.719340, 0.920505, 0.390731, -0.694658, -0.933580], + [-0.406737, 0.694658, 0.927184, 0.374607, -0.719340, -0.913545], + [-0.453990, 0.669131, 0.933580, 0.358368, -0.743145, -0.891007], + [-0.500000, 0.642788, 0.939693, 0.342020, -0.766044, -0.866025], + [-0.544639, 0.615661, 0.945519, 0.325568, -0.788011, -0.838671], + [-0.587785, 0.587785, 0.951057, 0.309017, -0.809017, -0.809017], + [-0.629320, 0.559193, 0.956305, 0.292372, -0.829038, -0.777146], + [-0.669131, 0.529919, 0.961262, 0.275637, -0.848048, -0.743145], + [-0.707107, 0.500000, 0.965926, 0.258819, -0.866025, -0.707107], + [-0.743145, 0.469472, 0.970296, 0.241922, -0.882948, -0.669131], + [-0.777146, 0.438371, 0.974370, 0.224951, -0.898794, -0.629320], + [-0.809017, 0.406737, 0.978148, 0.207912, -0.913545, -0.587785], + [-0.838671, 0.374607, 0.981627, 0.190809, -0.927184, -0.544639], + [-0.866025, 0.342020, 0.984808, 0.173648, -0.939693, -0.500000], + [-0.891007, 0.309017, 0.987688, 0.156434, -0.951057, -0.453990], + [-0.913545, 0.275637, 0.990268, 0.139173, -0.961262, -0.406737], + [-0.933580, 0.241922, 0.992546, 0.121869, -0.970296, -0.358368], + [-0.951057, 0.207912, 0.994522, 0.104528, -0.978148, -0.309017], + [-0.965926, 0.173648, 0.996195, 0.087156, -0.984808, -0.258819], + [-0.978148, 0.139173, 0.997564, 0.069756, -0.990268, -0.207912], + [-0.987688, 0.104528, 0.998630, 0.052336, -0.994522, -0.156434], + [-0.994522, 0.069756, 0.999391, 0.034899, -0.997564, -0.104528], + [-0.998630, 0.034899, 0.999848, 0.017452, -0.999391, -0.052336], + [-1.000000, 0.000000, 1.000000, 0.000000, -1.000000, -0.000000], + [-0.998630, -0.034899, 0.999848, -0.017452, -0.999391, 0.052336], + [-0.994522, -0.069756, 0.999391, -0.034899, -0.997564, 0.104528], + [-0.987688, -0.104528, 0.998630, -0.052336, -0.994522, 0.156434], + [-0.978148, -0.139173, 0.997564, -0.069756, -0.990268, 0.207912], + [-0.965926, -0.173648, 0.996195, -0.087156, -0.984808, 0.258819], + [-0.951057, -0.207912, 0.994522, -0.104528, -0.978148, 0.309017], + [-0.933580, -0.241922, 0.992546, -0.121869, -0.970296, 0.358368], + [-0.913545, -0.275637, 0.990268, -0.139173, -0.961262, 0.406737], + [-0.891007, -0.309017, 0.987688, -0.156434, -0.951057, 0.453990], + [-0.866025, -0.342020, 0.984808, -0.173648, -0.939693, 0.500000], + [-0.838671, -0.374607, 0.981627, -0.190809, -0.927184, 0.544639], + [-0.809017, -0.406737, 0.978148, -0.207912, -0.913545, 0.587785], + [-0.777146, -0.438371, 0.974370, -0.224951, -0.898794, 0.629320], + [-0.743145, -0.469472, 0.970296, -0.241922, -0.882948, 0.669131], + [-0.707107, -0.500000, 0.965926, -0.258819, -0.866025, 0.707107], + [-0.669131, -0.529919, 0.961262, -0.275637, -0.848048, 0.743145], + [-0.629320, -0.559193, 0.956305, -0.292372, -0.829038, 0.777146], + [-0.587785, -0.587785, 0.951057, -0.309017, -0.809017, 0.809017], + [-0.544639, -0.615661, 0.945519, -0.325568, -0.788011, 0.838671], + [-0.500000, -0.642788, 0.939693, -0.342020, -0.766044, 0.866025], + [-0.453990, -0.669131, 0.933580, -0.358368, -0.743145, 0.891007], + [-0.406737, -0.694658, 0.927184, -0.374607, -0.719340, 0.913545], + [-0.358368, -0.719340, 0.920505, -0.390731, -0.694658, 0.933580], + [-0.309017, -0.743145, 0.913545, -0.406737, -0.669131, 0.951057], + [-0.258819, -0.766044, 0.906308, -0.422618, -0.642788, 0.965926], + [-0.207912, -0.788011, 0.898794, -0.438371, -0.615661, 0.978148], + [-0.156434, -0.809017, 0.891007, -0.453990, -0.587785, 0.987688], + [-0.104528, -0.829038, 0.882948, -0.469472, -0.559193, 0.994522], + [-0.052336, -0.848048, 0.874620, -0.484810, -0.529919, 0.998630], + [-0.000000, -0.866025, 0.866025, -0.500000, -0.500000, 1.000000], + [0.052336, -0.882948, 0.857167, -0.515038, -0.469472, 0.998630], + [0.104528, -0.898794, 0.848048, -0.529919, -0.438371, 0.994522], + [0.156434, -0.913545, 0.838671, -0.544639, -0.406737, 0.987688], + [0.207912, -0.927184, 0.829038, -0.559193, -0.374607, 0.978148], + [0.258819, -0.939693, 0.819152, -0.573576, -0.342020, 0.965926], + [0.309017, -0.951057, 0.809017, -0.587785, -0.309017, 0.951057], + [0.358368, -0.961262, 0.798636, -0.601815, -0.275637, 0.933580], + [0.406737, -0.970296, 0.788011, -0.615661, -0.241922, 0.913545], + [0.453990, -0.978148, 0.777146, -0.629320, -0.207912, 0.891007], + [0.500000, -0.984808, 0.766044, -0.642788, -0.173648, 0.866025], + [0.544639, -0.990268, 0.754710, -0.656059, -0.139173, 0.838671], + [0.587785, -0.994522, 0.743145, -0.669131, -0.104528, 0.809017], + [0.629320, -0.997564, 0.731354, -0.681998, -0.069756, 0.777146], + [0.669131, -0.999391, 0.719340, -0.694658, -0.034899, 0.743145], + [0.707107, -1.000000, 0.707107, -0.707107, -0.000000, 0.707107], + [0.743145, -0.999391, 0.694658, -0.719340, 0.034899, 0.669131], + [0.777146, -0.997564, 0.681998, -0.731354, 0.069756, 0.629320], + [0.809017, -0.994522, 0.669131, -0.743145, 0.104528, 0.587785], + [0.838671, -0.990268, 0.656059, -0.754710, 0.139173, 0.544639], + [0.866025, -0.984808, 0.642788, -0.766044, 0.173648, 0.500000], + [0.891007, -0.978148, 0.629320, -0.777146, 0.207912, 0.453990], + [0.913545, -0.970296, 0.615661, -0.788011, 0.241922, 0.406737], + [0.933580, -0.961262, 0.601815, -0.798636, 0.275637, 0.358368], + [0.951057, -0.951057, 0.587785, -0.809017, 0.309017, 0.309017], + [0.965926, -0.939693, 0.573576, -0.819152, 0.342020, 0.258819], + [0.978148, -0.927184, 0.559193, -0.829038, 0.374607, 0.207912], + [0.987688, -0.913545, 0.544639, -0.838671, 0.406737, 0.156434], + [0.994522, -0.898794, 0.529919, -0.848048, 0.438371, 0.104528], + [0.998630, -0.882948, 0.515038, -0.857167, 0.469472, 0.052336], + [1.000000, -0.866025, 0.500000, -0.866025, 0.500000, 0.000000], + [0.998630, -0.848048, 0.484810, -0.874620, 0.529919, -0.052336], + [0.994522, -0.829038, 0.469472, -0.882948, 0.559193, -0.104528], + [0.987688, -0.809017, 0.453990, -0.891007, 0.587785, -0.156434], + [0.978148, -0.788011, 0.438371, -0.898794, 0.615661, -0.207912], + [0.965926, -0.766044, 0.422618, -0.906308, 0.642788, -0.258819], + [0.951057, -0.743145, 0.406737, -0.913545, 0.669131, -0.309017], + [0.933580, -0.719340, 0.390731, -0.920505, 0.694658, -0.358368], + [0.913545, -0.694658, 0.374607, -0.927184, 0.719340, -0.406737], + [0.891007, -0.669131, 0.358368, -0.933580, 0.743145, -0.453990], + [0.866025, -0.642788, 0.342020, -0.939693, 0.766044, -0.500000], + [0.838671, -0.615661, 0.325568, -0.945519, 0.788011, -0.544639], + [0.809017, -0.587785, 0.309017, -0.951057, 0.809017, -0.587785], + [0.777146, -0.559193, 0.292372, -0.956305, 0.829038, -0.629320], + [0.743145, -0.529919, 0.275637, -0.961262, 0.848048, -0.669131], + [0.707107, -0.500000, 0.258819, -0.965926, 0.866025, -0.707107], + [0.669131, -0.469472, 0.241922, -0.970296, 0.882948, -0.743145], + [0.629320, -0.438371, 0.224951, -0.974370, 0.898794, -0.777146], + [0.587785, -0.406737, 0.207912, -0.978148, 0.913545, -0.809017], + [0.544639, -0.374607, 0.190809, -0.981627, 0.927184, -0.838671], + [0.500000, -0.342020, 0.173648, -0.984808, 0.939693, -0.866025], + [0.453990, -0.309017, 0.156434, -0.987688, 0.951057, -0.891007], + [0.406737, -0.275637, 0.139173, -0.990268, 0.961262, -0.913545], + [0.358368, -0.241922, 0.121869, -0.992546, 0.970296, -0.933580], + [0.309017, -0.207912, 0.104528, -0.994522, 0.978148, -0.951057], + [0.258819, -0.173648, 0.087156, -0.996195, 0.984808, -0.965926], + [0.207912, -0.139173, 0.069756, -0.997564, 0.990268, -0.978148], + [0.156434, -0.104528, 0.052336, -0.998630, 0.994522, -0.987688], + [0.104528, -0.069756, 0.034899, -0.999391, 0.997564, -0.994522], + [0.052336, -0.034899, 0.017452, -0.999848, 0.999391, -0.998630], + [0.000000, -0.000000, 0.000000, -1.000000, 1.000000, -1.000000], + [-0.052336, 0.034899, -0.017452, -0.999848, 0.999391, -0.998630], + [-0.104528, 0.069756, -0.034899, -0.999391, 0.997564, -0.994522], + [-0.156434, 0.104528, -0.052336, -0.998630, 0.994522, -0.987688], + [-0.207912, 0.139173, -0.069756, -0.997564, 0.990268, -0.978148], + [-0.258819, 0.173648, -0.087156, -0.996195, 0.984808, -0.965926], + [-0.309017, 0.207912, -0.104528, -0.994522, 0.978148, -0.951057], + [-0.358368, 0.241922, -0.121869, -0.992546, 0.970296, -0.933580], + [-0.406737, 0.275637, -0.139173, -0.990268, 0.961262, -0.913545], + [-0.453990, 0.309017, -0.156434, -0.987688, 0.951057, -0.891007], + [-0.500000, 0.342020, -0.173648, -0.984808, 0.939693, -0.866025], + [-0.544639, 0.374607, -0.190809, -0.981627, 0.927184, -0.838671], + [-0.587785, 0.406737, -0.207912, -0.978148, 0.913545, -0.809017], + [-0.629320, 0.438371, -0.224951, -0.974370, 0.898794, -0.777146], + [-0.669131, 0.469472, -0.241922, -0.970296, 0.882948, -0.743145], + [-0.707107, 0.500000, -0.258819, -0.965926, 0.866025, -0.707107], + [-0.743145, 0.529919, -0.275637, -0.961262, 0.848048, -0.669131], + [-0.777146, 0.559193, -0.292372, -0.956305, 0.829038, -0.629320], + [-0.809017, 0.587785, -0.309017, -0.951057, 0.809017, -0.587785], + [-0.838671, 0.615661, -0.325568, -0.945519, 0.788011, -0.544639], + [-0.866025, 0.642788, -0.342020, -0.939693, 0.766044, -0.500000], + [-0.891007, 0.669131, -0.358368, -0.933580, 0.743145, -0.453990], + [-0.913545, 0.694658, -0.374607, -0.927184, 0.719340, -0.406737], + [-0.933580, 0.719340, -0.390731, -0.920505, 0.694658, -0.358368], + [-0.951057, 0.743145, -0.406737, -0.913545, 0.669131, -0.309017], + [-0.965926, 0.766044, -0.422618, -0.906308, 0.642788, -0.258819], + [-0.978148, 0.788011, -0.438371, -0.898794, 0.615661, -0.207912], + [-0.987688, 0.809017, -0.453990, -0.891007, 0.587785, -0.156434], + [-0.994522, 0.829038, -0.469472, -0.882948, 0.559193, -0.104528], + [-0.998630, 0.848048, -0.484810, -0.874620, 0.529919, -0.052336], + [-1.000000, 0.866025, -0.500000, -0.866025, 0.500000, 0.000000], + [-0.998630, 0.882948, -0.515038, -0.857167, 0.469472, 0.052336], + [-0.994522, 0.898794, -0.529919, -0.848048, 0.438371, 0.104528], + [-0.987688, 0.913545, -0.544639, -0.838671, 0.406737, 0.156434], + [-0.978148, 0.927184, -0.559193, -0.829038, 0.374607, 0.207912], + [-0.965926, 0.939693, -0.573576, -0.819152, 0.342020, 0.258819], + [-0.951057, 0.951057, -0.587785, -0.809017, 0.309017, 0.309017], + [-0.933580, 0.961262, -0.601815, -0.798636, 0.275637, 0.358368], + [-0.913545, 0.970296, -0.615661, -0.788011, 0.241922, 0.406737], + [-0.891007, 0.978148, -0.629320, -0.777146, 0.207912, 0.453990], + [-0.866025, 0.984808, -0.642788, -0.766044, 0.173648, 0.500000], + [-0.838671, 0.990268, -0.656059, -0.754710, 0.139173, 0.544639], + [-0.809017, 0.994522, -0.669131, -0.743145, 0.104528, 0.587785], + [-0.777146, 0.997564, -0.681998, -0.731354, 0.069756, 0.629320], + [-0.743145, 0.999391, -0.694658, -0.719340, 0.034899, 0.669131], + [-0.707107, 1.000000, -0.707107, -0.707107, 0.000000, 0.707107], + [-0.669131, 0.999391, -0.719340, -0.694658, -0.034899, 0.743145], + [-0.629320, 0.997564, -0.731354, -0.681998, -0.069756, 0.777146], + [-0.587785, 0.994522, -0.743145, -0.669131, -0.104528, 0.809017], + [-0.544639, 0.990268, -0.754710, -0.656059, -0.139173, 0.838671], + [-0.500000, 0.984808, -0.766044, -0.642788, -0.173648, 0.866025], + [-0.453990, 0.978148, -0.777146, -0.629320, -0.207912, 0.891007], + [-0.406737, 0.970296, -0.788011, -0.615661, -0.241922, 0.913545], + [-0.358368, 0.961262, -0.798636, -0.601815, -0.275637, 0.933580], + [-0.309017, 0.951057, -0.809017, -0.587785, -0.309017, 0.951057], + [-0.258819, 0.939693, -0.819152, -0.573576, -0.342020, 0.965926], + [-0.207912, 0.927184, -0.829038, -0.559193, -0.374607, 0.978148], + [-0.156434, 0.913545, -0.838671, -0.544639, -0.406737, 0.987688], + [-0.104528, 0.898794, -0.848048, -0.529919, -0.438371, 0.994522], + [-0.052336, 0.882948, -0.857167, -0.515038, -0.469472, 0.998630], + [-0.000000, 0.866025, -0.866025, -0.500000, -0.500000, 1.000000], + [0.052336, 0.848048, -0.874620, -0.484810, -0.529919, 0.998630], + [0.104528, 0.829038, -0.882948, -0.469472, -0.559193, 0.994522], + [0.156434, 0.809017, -0.891007, -0.453990, -0.587785, 0.987688], + [0.207912, 0.788011, -0.898794, -0.438371, -0.615661, 0.978148], + [0.258819, 0.766044, -0.906308, -0.422618, -0.642788, 0.965926], + [0.309017, 0.743145, -0.913545, -0.406737, -0.669131, 0.951057], + [0.358368, 0.719340, -0.920505, -0.390731, -0.694658, 0.933580], + [0.406737, 0.694658, -0.927184, -0.374607, -0.719340, 0.913545], + [0.453990, 0.669131, -0.933580, -0.358368, -0.743145, 0.891007], + [0.500000, 0.642788, -0.939693, -0.342020, -0.766044, 0.866025], + [0.544639, 0.615661, -0.945519, -0.325568, -0.788011, 0.838671], + [0.587785, 0.587785, -0.951057, -0.309017, -0.809017, 0.809017], + [0.629320, 0.559193, -0.956305, -0.292372, -0.829038, 0.777146], + [0.669131, 0.529919, -0.961262, -0.275637, -0.848048, 0.743145], + [0.707107, 0.500000, -0.965926, -0.258819, -0.866025, 0.707107], + [0.743145, 0.469472, -0.970296, -0.241922, -0.882948, 0.669131], + [0.777146, 0.438371, -0.974370, -0.224951, -0.898794, 0.629320], + [0.809017, 0.406737, -0.978148, -0.207912, -0.913545, 0.587785], + [0.838671, 0.374607, -0.981627, -0.190809, -0.927184, 0.544639], + [0.866025, 0.342020, -0.984808, -0.173648, -0.939693, 0.500000], + [0.891007, 0.309017, -0.987688, -0.156434, -0.951057, 0.453990], + [0.913545, 0.275637, -0.990268, -0.139173, -0.961262, 0.406737], + [0.933580, 0.241922, -0.992546, -0.121869, -0.970296, 0.358368], + [0.951057, 0.207912, -0.994522, -0.104528, -0.978148, 0.309017], + [0.965926, 0.173648, -0.996195, -0.087156, -0.984808, 0.258819], + [0.978148, 0.139173, -0.997564, -0.069756, -0.990268, 0.207912], + [0.987688, 0.104528, -0.998630, -0.052336, -0.994522, 0.156434], + [0.994522, 0.069756, -0.999391, -0.034899, -0.997564, 0.104528], + [0.998630, 0.034899, -0.999848, -0.017452, -0.999391, 0.052336], + [1.000000, 0.000000, -1.000000, -0.000000, -1.000000, 0.000000], + [0.998630, -0.034899, -0.999848, 0.017452, -0.999391, -0.052336], + [0.994522, -0.069756, -0.999391, 0.034899, -0.997564, -0.104528], + [0.987688, -0.104528, -0.998630, 0.052336, -0.994522, -0.156434], + [0.978148, -0.139173, -0.997564, 0.069756, -0.990268, -0.207912], + [0.965926, -0.173648, -0.996195, 0.087156, -0.984808, -0.258819], + [0.951057, -0.207912, -0.994522, 0.104528, -0.978148, -0.309017], + [0.933580, -0.241922, -0.992546, 0.121869, -0.970296, -0.358368], + [0.913545, -0.275637, -0.990268, 0.139173, -0.961262, -0.406737], + [0.891007, -0.309017, -0.987688, 0.156434, -0.951057, -0.453990], + [0.866025, -0.342020, -0.984808, 0.173648, -0.939693, -0.500000], + [0.838671, -0.374607, -0.981627, 0.190809, -0.927184, -0.544639], + [0.809017, -0.406737, -0.978148, 0.207912, -0.913545, -0.587785], + [0.777146, -0.438371, -0.974370, 0.224951, -0.898794, -0.629320], + [0.743145, -0.469472, -0.970296, 0.241922, -0.882948, -0.669131], + [0.707107, -0.500000, -0.965926, 0.258819, -0.866025, -0.707107], + [0.669131, -0.529919, -0.961262, 0.275637, -0.848048, -0.743145], + [0.629320, -0.559193, -0.956305, 0.292372, -0.829038, -0.777146], + [0.587785, -0.587785, -0.951057, 0.309017, -0.809017, -0.809017], + [0.544639, -0.615661, -0.945519, 0.325568, -0.788011, -0.838671], + [0.500000, -0.642788, -0.939693, 0.342020, -0.766044, -0.866025], + [0.453990, -0.669131, -0.933580, 0.358368, -0.743145, -0.891007], + [0.406737, -0.694658, -0.927184, 0.374607, -0.719340, -0.913545], + [0.358368, -0.719340, -0.920505, 0.390731, -0.694658, -0.933580], + [0.309017, -0.743145, -0.913545, 0.406737, -0.669131, -0.951057], + [0.258819, -0.766044, -0.906308, 0.422618, -0.642788, -0.965926], + [0.207912, -0.788011, -0.898794, 0.438371, -0.615661, -0.978148], + [0.156434, -0.809017, -0.891007, 0.453990, -0.587785, -0.987688], + [0.104528, -0.829038, -0.882948, 0.469472, -0.559193, -0.994522], + [0.052336, -0.848048, -0.874620, 0.484810, -0.529919, -0.998630], + [0.000000, -0.866025, -0.866025, 0.500000, -0.500000, -1.000000], + [-0.052336, -0.882948, -0.857167, 0.515038, -0.469472, -0.998630], + [-0.104528, -0.898794, -0.848048, 0.529919, -0.438371, -0.994522], + [-0.156434, -0.913545, -0.838671, 0.544639, -0.406737, -0.987688], + [-0.207912, -0.927184, -0.829038, 0.559193, -0.374607, -0.978148], + [-0.258819, -0.939693, -0.819152, 0.573576, -0.342020, -0.965926], + [-0.309017, -0.951057, -0.809017, 0.587785, -0.309017, -0.951057], + [-0.358368, -0.961262, -0.798636, 0.601815, -0.275637, -0.933580], + [-0.406737, -0.970296, -0.788011, 0.615661, -0.241922, -0.913545], + [-0.453990, -0.978148, -0.777146, 0.629320, -0.207912, -0.891007], + [-0.500000, -0.984808, -0.766044, 0.642788, -0.173648, -0.866025], + [-0.544639, -0.990268, -0.754710, 0.656059, -0.139173, -0.838671], + [-0.587785, -0.994522, -0.743145, 0.669131, -0.104528, -0.809017], + [-0.629320, -0.997564, -0.731354, 0.681998, -0.069756, -0.777146], + [-0.669131, -0.999391, -0.719340, 0.694658, -0.034899, -0.743145], + [-0.707107, -1.000000, -0.707107, 0.707107, -0.000000, -0.707107], + [-0.743145, -0.999391, -0.694658, 0.719340, 0.034899, -0.669131], + [-0.777146, -0.997564, -0.681998, 0.731354, 0.069756, -0.629320], + [-0.809017, -0.994522, -0.669131, 0.743145, 0.104528, -0.587785], + [-0.838671, -0.990268, -0.656059, 0.754710, 0.139173, -0.544639], + [-0.866025, -0.984808, -0.642788, 0.766044, 0.173648, -0.500000], + [-0.891007, -0.978148, -0.629320, 0.777146, 0.207912, -0.453990], + [-0.913545, -0.970296, -0.615661, 0.788011, 0.241922, -0.406737], + [-0.933580, -0.961262, -0.601815, 0.798636, 0.275637, -0.358368], + [-0.951057, -0.951057, -0.587785, 0.809017, 0.309017, -0.309017], + [-0.965926, -0.939693, -0.573576, 0.819152, 0.342020, -0.258819], + [-0.978148, -0.927184, -0.559193, 0.829038, 0.374607, -0.207912], + [-0.987688, -0.913545, -0.544639, 0.838671, 0.406737, -0.156434], + [-0.994522, -0.898794, -0.529919, 0.848048, 0.438371, -0.104528], + [-0.998630, -0.882948, -0.515038, 0.857167, 0.469472, -0.052336], + [-1.000000, -0.866025, -0.500000, 0.866025, 0.500000, -0.000000], + [-0.998630, -0.848048, -0.484810, 0.874620, 0.529919, 0.052336], + [-0.994522, -0.829038, -0.469472, 0.882948, 0.559193, 0.104528], + [-0.987688, -0.809017, -0.453990, 0.891007, 0.587785, 0.156434], + [-0.978148, -0.788011, -0.438371, 0.898794, 0.615661, 0.207912], + [-0.965926, -0.766044, -0.422618, 0.906308, 0.642788, 0.258819], + [-0.951057, -0.743145, -0.406737, 0.913545, 0.669131, 0.309017], + [-0.933580, -0.719340, -0.390731, 0.920505, 0.694658, 0.358368], + [-0.913545, -0.694658, -0.374607, 0.927184, 0.719340, 0.406737], + [-0.891007, -0.669131, -0.358368, 0.933580, 0.743145, 0.453990], + [-0.866025, -0.642788, -0.342020, 0.939693, 0.766044, 0.500000], + [-0.838671, -0.615661, -0.325568, 0.945519, 0.788011, 0.544639], + [-0.809017, -0.587785, -0.309017, 0.951057, 0.809017, 0.587785], + [-0.777146, -0.559193, -0.292372, 0.956305, 0.829038, 0.629320], + [-0.743145, -0.529919, -0.275637, 0.961262, 0.848048, 0.669131], + [-0.707107, -0.500000, -0.258819, 0.965926, 0.866025, 0.707107], + [-0.669131, -0.469472, -0.241922, 0.970296, 0.882948, 0.743145], + [-0.629320, -0.438371, -0.224951, 0.974370, 0.898794, 0.777146], + [-0.587785, -0.406737, -0.207912, 0.978148, 0.913545, 0.809017], + [-0.544639, -0.374607, -0.190809, 0.981627, 0.927184, 0.838671], + [-0.500000, -0.342020, -0.173648, 0.984808, 0.939693, 0.866025], + [-0.453990, -0.309017, -0.156434, 0.987688, 0.951057, 0.891007], + [-0.406737, -0.275637, -0.139173, 0.990268, 0.961262, 0.913545], + [-0.358368, -0.241922, -0.121869, 0.992546, 0.970296, 0.933580], + [-0.309017, -0.207912, -0.104528, 0.994522, 0.978148, 0.951057], + [-0.258819, -0.173648, -0.087156, 0.996195, 0.984808, 0.965926], + [-0.207912, -0.139173, -0.069756, 0.997564, 0.990268, 0.978148], + [-0.156434, -0.104528, -0.052336, 0.998630, 0.994522, 0.987688], + [-0.104528, -0.069756, -0.034899, 0.999391, 0.997564, 0.994522], + [-0.052336, -0.034899, -0.017452, 0.999848, 0.999391, 0.998630], + ], + [ + [-1.000000, -0.000000, 1.000000, -0.000000, 0.000000, + -1.000000, -0.000000, 0.000000, -0.000000], + [-0.999848, 0.017452, 0.999543, -0.030224, 0.000264, + -0.999086, 0.042733, -0.000590, 0.000004], + [-0.999391, 0.034899, 0.998173, -0.060411, 0.001055, + -0.996348, 0.085356, -0.002357, 0.000034], + [-0.998630, 0.052336, 0.995891, -0.090524, 0.002372, + -0.991791, 0.127757, -0.005297, 0.000113], + [-0.997564, 0.069756, 0.992701, -0.120527, 0.004214, + -0.985429, 0.169828, -0.009400, 0.000268], + [-0.996195, 0.087156, 0.988606, -0.150384, 0.006578, + -0.977277, 0.211460, -0.014654, 0.000523], + [-0.994522, 0.104528, 0.983611, -0.180057, 0.009462, + -0.967356, 0.252544, -0.021043, 0.000903], + [-0.992546, 0.121869, 0.977722, -0.209511, 0.012862, + -0.955693, 0.292976, -0.028547, 0.001431], + [-0.990268, 0.139173, 0.970946, -0.238709, 0.016774, + -0.942316, 0.332649, -0.037143, 0.002131], + [-0.987688, 0.156434, 0.963292, -0.267617, 0.021193, + -0.927262, 0.371463, -0.046806, 0.003026], + [-0.984808, 0.173648, 0.954769, -0.296198, 0.026114, + -0.910569, 0.409317, -0.057505, 0.004140], + [-0.981627, 0.190809, 0.945388, -0.324419, 0.031530, + -0.892279, 0.446114, -0.069209, 0.005492], + [-0.978148, 0.207912, 0.935159, -0.352244, 0.037436, + -0.872441, 0.481759, -0.081880, 0.007105], + [-0.974370, 0.224951, 0.924096, -0.379641, 0.043823, + -0.851105, 0.516162, -0.095481, 0.008999], + [-0.970296, 0.241922, 0.912211, -0.406574, 0.050685, + -0.828326, 0.549233, -0.109969, 0.011193], + [-0.965926, 0.258819, 0.899519, -0.433013, 0.058013, + -0.804164, 0.580889, -0.125300, 0.013707], + [-0.961262, 0.275637, 0.886036, -0.458924, 0.065797, + -0.778680, 0.611050, -0.141427, 0.016556], + [-0.956305, 0.292372, 0.871778, -0.484275, 0.074029, + -0.751940, 0.639639, -0.158301, 0.019758], + [-0.951057, 0.309017, 0.856763, -0.509037, 0.082698, + -0.724012, 0.666583, -0.175868, 0.023329], + [-0.945519, 0.325568, 0.841008, -0.533178, 0.091794, + -0.694969, 0.691816, -0.194075, 0.027281], + [-0.939693, 0.342020, 0.824533, -0.556670, 0.101306, + -0.664885, 0.715274, -0.212865, 0.031630], + [-0.933580, 0.358368, 0.807359, -0.579484, 0.111222, + -0.633837, 0.736898, -0.232180, 0.036385], + [-0.927184, 0.374607, 0.789505, -0.601592, 0.121529, + -0.601904, 0.756637, -0.251960, 0.041559], + [-0.920505, 0.390731, 0.770994, -0.622967, 0.132217, + -0.569169, 0.774442, -0.272143, 0.047160], + [-0.913545, 0.406737, 0.751848, -0.643582, 0.143271, + -0.535715, 0.790270, -0.292666, 0.053196], + [-0.906308, 0.422618, 0.732091, -0.663414, 0.154678, + -0.501627, 0.804083, -0.313464, 0.059674], + [-0.898794, 0.438371, 0.711746, -0.682437, 0.166423, + -0.466993, 0.815850, -0.334472, 0.066599], + [-0.891007, 0.453990, 0.690839, -0.700629, 0.178494, + -0.431899, 0.825544, -0.355623, 0.073974], + [-0.882948, 0.469472, 0.669395, -0.717968, 0.190875, + -0.396436, 0.833145, -0.376851, 0.081803], + [-0.874620, 0.484810, 0.647439, -0.734431, 0.203551, + -0.360692, 0.838638, -0.398086, 0.090085], + [-0.866025, 0.500000, 0.625000, -0.750000, 0.216506, + -0.324760, 0.842012, -0.419263, 0.098821], + [-0.857167, 0.515038, 0.602104, -0.764655, 0.229726, + -0.288728, 0.843265, -0.440311, 0.108009], + [-0.848048, 0.529919, 0.578778, -0.778378, 0.243192, + -0.252688, 0.842399, -0.461164, 0.117644], + [-0.838671, 0.544639, 0.555052, -0.791154, 0.256891, + -0.216730, 0.839422, -0.481753, 0.127722], + [-0.829038, 0.559193, 0.530955, -0.802965, 0.270803, + -0.180944, 0.834347, -0.502011, 0.138237], + [-0.819152, 0.573576, 0.506515, -0.813798, 0.284914, + -0.145420, 0.827194, -0.521871, 0.149181], + [-0.809017, 0.587785, 0.481763, -0.823639, 0.299204, + -0.110246, 0.817987, -0.541266, 0.160545], + [-0.798636, 0.601815, 0.456728, -0.832477, 0.313658, + -0.075508, 0.806757, -0.560132, 0.172317], + [-0.788011, 0.615661, 0.431441, -0.840301, 0.328257, + -0.041294, 0.793541, -0.578405, 0.184487], + [-0.777146, 0.629320, 0.405934, -0.847101, 0.342984, + -0.007686, 0.778379, -0.596021, 0.197040], + [-0.766044, 0.642788, 0.380236, -0.852869, 0.357821, + 0.025233, 0.761319, -0.612921, 0.209963], + [-0.754710, 0.656059, 0.354380, -0.857597, 0.372749, + 0.057383, 0.742412, -0.629044, 0.223238], + [-0.743145, 0.669131, 0.328396, -0.861281, 0.387751, + 0.088686, 0.721714, -0.644334, 0.236850], + [-0.731354, 0.681998, 0.302317, -0.863916, 0.402807, + 0.119068, 0.699288, -0.658734, 0.250778], + [-0.719340, 0.694658, 0.276175, -0.865498, 0.417901, + 0.148454, 0.675199, -0.672190, 0.265005], + [-0.707107, 0.707107, 0.250000, -0.866025, 0.433013, + 0.176777, 0.649519, -0.684653, 0.279508], + [-0.694658, 0.719340, 0.223825, -0.865498, 0.448125, + 0.203969, 0.622322, -0.696073, 0.294267], + [-0.681998, 0.731354, 0.197683, -0.863916, 0.463218, + 0.229967, 0.593688, -0.706405, 0.309259], + [-0.669131, 0.743145, 0.171604, -0.861281, 0.478275, + 0.254712, 0.563700, -0.715605, 0.324459], + [-0.656059, 0.754710, 0.145620, -0.857597, 0.493276, + 0.278147, 0.532443, -0.723633, 0.339844], + [-0.642788, 0.766044, 0.119764, -0.852869, 0.508205, + 0.300221, 0.500009, -0.730451, 0.355387], + [-0.629320, 0.777146, 0.094066, -0.847101, 0.523041, + 0.320884, 0.466490, -0.736025, 0.371063], + [-0.615661, 0.788011, 0.068559, -0.840301, 0.537768, + 0.340093, 0.431982, -0.740324, 0.386845], + [-0.601815, 0.798636, 0.043272, -0.832477, 0.552367, + 0.357807, 0.396584, -0.743320, 0.402704], + [-0.587785, 0.809017, 0.018237, -0.823639, 0.566821, + 0.373991, 0.360397, -0.744989, 0.418613], + [-0.573576, 0.819152, -0.006515, -0.813798, 0.581112, + 0.388612, 0.323524, -0.745308, 0.434544], + [-0.559193, 0.829038, -0.030955, -0.802965, 0.595222, + 0.401645, 0.286069, -0.744262, 0.450467], + [-0.544639, 0.838671, -0.055052, -0.791154, 0.609135, + 0.413066, 0.248140, -0.741835, 0.466352], + [-0.529919, 0.848048, -0.078778, -0.778378, 0.622833, + 0.422856, 0.209843, -0.738017, 0.482171], + [-0.515038, 0.857167, -0.102104, -0.764655, 0.636300, + 0.431004, 0.171288, -0.732801, 0.497894], + [-0.500000, 0.866025, -0.125000, -0.750000, 0.649519, + 0.437500, 0.132583, -0.726184, 0.513490], + [-0.484810, 0.874620, -0.147439, -0.734431, 0.662474, + 0.442340, 0.093837, -0.718167, 0.528929], + [-0.469472, 0.882948, -0.169395, -0.717968, 0.675150, + 0.445524, 0.055160, -0.708753, 0.544183], + [-0.453990, 0.891007, -0.190839, -0.700629, 0.687531, + 0.447059, 0.016662, -0.697950, 0.559220], + [-0.438371, 0.898794, -0.211746, -0.682437, 0.699602, + 0.446953, -0.021550, -0.685769, 0.574011], + [-0.422618, 0.906308, -0.232091, -0.663414, 0.711348, + 0.445222, -0.059368, -0.672226, 0.588528], + [-0.406737, 0.913545, -0.251848, -0.643582, 0.722755, + 0.441884, -0.096684, -0.657339, 0.602741], + [-0.390731, 0.920505, -0.270994, -0.622967, 0.733809, + 0.436964, -0.133395, -0.641130, 0.616621], + [-0.374607, 0.927184, -0.289505, -0.601592, 0.744496, + 0.430488, -0.169397, -0.623624, 0.630141], + [-0.358368, 0.933580, -0.307359, -0.579484, 0.754804, + 0.422491, -0.204589, -0.604851, 0.643273], + [-0.342020, 0.939693, -0.324533, -0.556670, 0.764720, + 0.413008, -0.238872, -0.584843, 0.655990], + [-0.325568, 0.945519, -0.341008, -0.533178, 0.774231, + 0.402081, -0.272150, -0.563635, 0.668267], + [-0.309017, 0.951057, -0.356763, -0.509037, 0.783327, + 0.389754, -0.304329, -0.541266, 0.680078], + [-0.292372, 0.956305, -0.371778, -0.484275, 0.791997, + 0.376077, -0.335319, -0.517778, 0.691399], + [-0.275637, 0.961262, -0.386036, -0.458924, 0.800228, + 0.361102, -0.365034, -0.493216, 0.702207], + [-0.258819, 0.965926, -0.399519, -0.433013, 0.808013, + 0.344885, -0.393389, -0.467627, 0.712478], + [-0.241922, 0.970296, -0.412211, -0.406574, 0.815340, + 0.327486, -0.420306, -0.441061, 0.722191], + [-0.224951, 0.974370, -0.424096, -0.379641, 0.822202, + 0.308969, -0.445709, -0.413572, 0.731327], + [-0.207912, 0.978148, -0.435159, -0.352244, 0.828589, + 0.289399, -0.469527, -0.385215, 0.739866], + [-0.190809, 0.981627, -0.445388, -0.324419, 0.834495, + 0.268846, -0.491693, -0.356047, 0.747790], + [-0.173648, 0.984808, -0.454769, -0.296198, 0.839912, + 0.247382, -0.512145, -0.326129, 0.755082], + [-0.156434, 0.987688, -0.463292, -0.267617, 0.844832, + 0.225081, -0.530827, -0.295521, 0.761728], + [-0.139173, 0.990268, -0.470946, -0.238709, 0.849251, + 0.202020, -0.547684, -0.264287, 0.767712], + [-0.121869, 0.992546, -0.477722, -0.209511, 0.853163, + 0.178279, -0.562672, -0.232494, 0.773023], + [-0.104528, 0.994522, -0.483611, -0.180057, 0.856563, + 0.153937, -0.575747, -0.200207, 0.777648], + [-0.087156, 0.996195, -0.488606, -0.150384, 0.859447, + 0.129078, -0.586872, -0.167494, 0.781579], + [-0.069756, 0.997564, -0.492701, -0.120527, 0.861811, + 0.103786, -0.596018, -0.134426, 0.784806], + [-0.052336, 0.998630, -0.495891, -0.090524, 0.863653, + 0.078146, -0.603158, -0.101071, 0.787324], + [-0.034899, 0.999391, -0.498173, -0.060411, 0.864971, + 0.052243, -0.608272, -0.067500, 0.789126], + [-0.017452, 0.999848, -0.499543, -0.030224, 0.865762, + 0.026165, -0.611347, -0.033786, 0.790208], + [0.000000, 1.000000, -0.500000, 0.000000, 0.866025, + -0.000000, -0.612372, 0.000000, 0.790569], + [0.017452, 0.999848, -0.499543, 0.030224, 0.865762, + -0.026165, -0.611347, 0.033786, 0.790208], + [0.034899, 0.999391, -0.498173, 0.060411, 0.864971, + -0.052243, -0.608272, 0.067500, 0.789126], + [0.052336, 0.998630, -0.495891, 0.090524, 0.863653, + -0.078146, -0.603158, 0.101071, 0.787324], + [0.069756, 0.997564, -0.492701, 0.120527, 0.861811, + -0.103786, -0.596018, 0.134426, 0.784806], + [0.087156, 0.996195, -0.488606, 0.150384, 0.859447, + -0.129078, -0.586872, 0.167494, 0.781579], + [0.104528, 0.994522, -0.483611, 0.180057, 0.856563, + -0.153937, -0.575747, 0.200207, 0.777648], + [0.121869, 0.992546, -0.477722, 0.209511, 0.853163, + -0.178279, -0.562672, 0.232494, 0.773023], + [0.139173, 0.990268, -0.470946, 0.238709, 0.849251, + -0.202020, -0.547684, 0.264287, 0.767712], + [0.156434, 0.987688, -0.463292, 0.267617, 0.844832, + -0.225081, -0.530827, 0.295521, 0.761728], + [0.173648, 0.984808, -0.454769, 0.296198, 0.839912, + -0.247382, -0.512145, 0.326129, 0.755082], + [0.190809, 0.981627, -0.445388, 0.324419, 0.834495, + -0.268846, -0.491693, 0.356047, 0.747790], + [0.207912, 0.978148, -0.435159, 0.352244, 0.828589, + -0.289399, -0.469527, 0.385215, 0.739866], + [0.224951, 0.974370, -0.424096, 0.379641, 0.822202, + -0.308969, -0.445709, 0.413572, 0.731327], + [0.241922, 0.970296, -0.412211, 0.406574, 0.815340, + -0.327486, -0.420306, 0.441061, 0.722191], + [0.258819, 0.965926, -0.399519, 0.433013, 0.808013, + -0.344885, -0.393389, 0.467627, 0.712478], + [0.275637, 0.961262, -0.386036, 0.458924, 0.800228, + -0.361102, -0.365034, 0.493216, 0.702207], + [0.292372, 0.956305, -0.371778, 0.484275, 0.791997, + -0.376077, -0.335319, 0.517778, 0.691399], + [0.309017, 0.951057, -0.356763, 0.509037, 0.783327, + -0.389754, -0.304329, 0.541266, 0.680078], + [0.325568, 0.945519, -0.341008, 0.533178, 0.774231, + -0.402081, -0.272150, 0.563635, 0.668267], + [0.342020, 0.939693, -0.324533, 0.556670, 0.764720, + -0.413008, -0.238872, 0.584843, 0.655990], + [0.358368, 0.933580, -0.307359, 0.579484, 0.754804, + -0.422491, -0.204589, 0.604851, 0.643273], + [0.374607, 0.927184, -0.289505, 0.601592, 0.744496, + -0.430488, -0.169397, 0.623624, 0.630141], + [0.390731, 0.920505, -0.270994, 0.622967, 0.733809, + -0.436964, -0.133395, 0.641130, 0.616621], + [0.406737, 0.913545, -0.251848, 0.643582, 0.722755, + -0.441884, -0.096684, 0.657339, 0.602741], + [0.422618, 0.906308, -0.232091, 0.663414, 0.711348, + -0.445222, -0.059368, 0.672226, 0.588528], + [0.438371, 0.898794, -0.211746, 0.682437, 0.699602, + -0.446953, -0.021550, 0.685769, 0.574011], + [0.453990, 0.891007, -0.190839, 0.700629, 0.687531, + -0.447059, 0.016662, 0.697950, 0.559220], + [0.469472, 0.882948, -0.169395, 0.717968, 0.675150, + -0.445524, 0.055160, 0.708753, 0.544183], + [0.484810, 0.874620, -0.147439, 0.734431, 0.662474, + -0.442340, 0.093837, 0.718167, 0.528929], + [0.500000, 0.866025, -0.125000, 0.750000, 0.649519, + -0.437500, 0.132583, 0.726184, 0.513490], + [0.515038, 0.857167, -0.102104, 0.764655, 0.636300, + -0.431004, 0.171288, 0.732801, 0.497894], + [0.529919, 0.848048, -0.078778, 0.778378, 0.622833, + -0.422856, 0.209843, 0.738017, 0.482171], + [0.544639, 0.838671, -0.055052, 0.791154, 0.609135, + -0.413066, 0.248140, 0.741835, 0.466352], + [0.559193, 0.829038, -0.030955, 0.802965, 0.595222, + -0.401645, 0.286069, 0.744262, 0.450467], + [0.573576, 0.819152, -0.006515, 0.813798, 0.581112, + -0.388612, 0.323524, 0.745308, 0.434544], + [0.587785, 0.809017, 0.018237, 0.823639, 0.566821, + -0.373991, 0.360397, 0.744989, 0.418613], + [0.601815, 0.798636, 0.043272, 0.832477, 0.552367, + -0.357807, 0.396584, 0.743320, 0.402704], + [0.615661, 0.788011, 0.068559, 0.840301, 0.537768, + -0.340093, 0.431982, 0.740324, 0.386845], + [0.629320, 0.777146, 0.094066, 0.847101, 0.523041, + -0.320884, 0.466490, 0.736025, 0.371063], + [0.642788, 0.766044, 0.119764, 0.852869, 0.508205, + -0.300221, 0.500009, 0.730451, 0.355387], + [0.656059, 0.754710, 0.145620, 0.857597, 0.493276, + -0.278147, 0.532443, 0.723633, 0.339844], + [0.669131, 0.743145, 0.171604, 0.861281, 0.478275, + -0.254712, 0.563700, 0.715605, 0.324459], + [0.681998, 0.731354, 0.197683, 0.863916, 0.463218, + -0.229967, 0.593688, 0.706405, 0.309259], + [0.694658, 0.719340, 0.223825, 0.865498, 0.448125, + -0.203969, 0.622322, 0.696073, 0.294267], + [0.707107, 0.707107, 0.250000, 0.866025, 0.433013, + -0.176777, 0.649519, 0.684653, 0.279508], + [0.719340, 0.694658, 0.276175, 0.865498, 0.417901, + -0.148454, 0.675199, 0.672190, 0.265005], + [0.731354, 0.681998, 0.302317, 0.863916, 0.402807, + -0.119068, 0.699288, 0.658734, 0.250778], + [0.743145, 0.669131, 0.328396, 0.861281, 0.387751, + -0.088686, 0.721714, 0.644334, 0.236850], + [0.754710, 0.656059, 0.354380, 0.857597, 0.372749, + -0.057383, 0.742412, 0.629044, 0.223238], + [0.766044, 0.642788, 0.380236, 0.852869, 0.357821, + -0.025233, 0.761319, 0.612921, 0.209963], + [0.777146, 0.629320, 0.405934, 0.847101, 0.342984, + 0.007686, 0.778379, 0.596021, 0.197040], + [0.788011, 0.615661, 0.431441, 0.840301, 0.328257, + 0.041294, 0.793541, 0.578405, 0.184487], + [0.798636, 0.601815, 0.456728, 0.832477, 0.313658, + 0.075508, 0.806757, 0.560132, 0.172317], + [0.809017, 0.587785, 0.481763, 0.823639, 0.299204, + 0.110246, 0.817987, 0.541266, 0.160545], + [0.819152, 0.573576, 0.506515, 0.813798, 0.284914, + 0.145420, 0.827194, 0.521871, 0.149181], + [0.829038, 0.559193, 0.530955, 0.802965, 0.270803, + 0.180944, 0.834347, 0.502011, 0.138237], + [0.838671, 0.544639, 0.555052, 0.791154, 0.256891, + 0.216730, 0.839422, 0.481753, 0.127722], + [0.848048, 0.529919, 0.578778, 0.778378, 0.243192, + 0.252688, 0.842399, 0.461164, 0.117644], + [0.857167, 0.515038, 0.602104, 0.764655, 0.229726, + 0.288728, 0.843265, 0.440311, 0.108009], + [0.866025, 0.500000, 0.625000, 0.750000, 0.216506, + 0.324760, 0.842012, 0.419263, 0.098821], + [0.874620, 0.484810, 0.647439, 0.734431, 0.203551, + 0.360692, 0.838638, 0.398086, 0.090085], + [0.882948, 0.469472, 0.669395, 0.717968, 0.190875, + 0.396436, 0.833145, 0.376851, 0.081803], + [0.891007, 0.453990, 0.690839, 0.700629, 0.178494, + 0.431899, 0.825544, 0.355623, 0.073974], + [0.898794, 0.438371, 0.711746, 0.682437, 0.166423, + 0.466993, 0.815850, 0.334472, 0.066599], + [0.906308, 0.422618, 0.732091, 0.663414, 0.154678, + 0.501627, 0.804083, 0.313464, 0.059674], + [0.913545, 0.406737, 0.751848, 0.643582, 0.143271, + 0.535715, 0.790270, 0.292666, 0.053196], + [0.920505, 0.390731, 0.770994, 0.622967, 0.132217, + 0.569169, 0.774442, 0.272143, 0.047160], + [0.927184, 0.374607, 0.789505, 0.601592, 0.121529, + 0.601904, 0.756637, 0.251960, 0.041559], + [0.933580, 0.358368, 0.807359, 0.579484, 0.111222, + 0.633837, 0.736898, 0.232180, 0.036385], + [0.939693, 0.342020, 0.824533, 0.556670, 0.101306, + 0.664885, 0.715274, 0.212865, 0.031630], + [0.945519, 0.325568, 0.841008, 0.533178, 0.091794, + 0.694969, 0.691816, 0.194075, 0.027281], + [0.951057, 0.309017, 0.856763, 0.509037, 0.082698, + 0.724012, 0.666583, 0.175868, 0.023329], + [0.956305, 0.292372, 0.871778, 0.484275, 0.074029, + 0.751940, 0.639639, 0.158301, 0.019758], + [0.961262, 0.275637, 0.886036, 0.458924, 0.065797, + 0.778680, 0.611050, 0.141427, 0.016556], + [0.965926, 0.258819, 0.899519, 0.433013, 0.058013, + 0.804164, 0.580889, 0.125300, 0.013707], + [0.970296, 0.241922, 0.912211, 0.406574, 0.050685, + 0.828326, 0.549233, 0.109969, 0.011193], + [0.974370, 0.224951, 0.924096, 0.379641, 0.043823, + 0.851105, 0.516162, 0.095481, 0.008999], + [0.978148, 0.207912, 0.935159, 0.352244, 0.037436, + 0.872441, 0.481759, 0.081880, 0.007105], + [0.981627, 0.190809, 0.945388, 0.324419, 0.031530, + 0.892279, 0.446114, 0.069209, 0.005492], + [0.984808, 0.173648, 0.954769, 0.296198, 0.026114, + 0.910569, 0.409317, 0.057505, 0.004140], + [0.987688, 0.156434, 0.963292, 0.267617, 0.021193, + 0.927262, 0.371463, 0.046806, 0.003026], + [0.990268, 0.139173, 0.970946, 0.238709, 0.016774, + 0.942316, 0.332649, 0.037143, 0.002131], + [0.992546, 0.121869, 0.977722, 0.209511, 0.012862, + 0.955693, 0.292976, 0.028547, 0.001431], + [0.994522, 0.104528, 0.983611, 0.180057, 0.009462, + 0.967356, 0.252544, 0.021043, 0.000903], + [0.996195, 0.087156, 0.988606, 0.150384, 0.006578, + 0.977277, 0.211460, 0.014654, 0.000523], + [0.997564, 0.069756, 0.992701, 0.120527, 0.004214, + 0.985429, 0.169828, 0.009400, 0.000268], + [0.998630, 0.052336, 0.995891, 0.090524, 0.002372, + 0.991791, 0.127757, 0.005297, 0.000113], + [0.999391, 0.034899, 0.998173, 0.060411, 0.001055, + 0.996348, 0.085356, 0.002357, 0.000034], + [0.999848, 0.017452, 0.999543, 0.030224, 0.000264, + 0.999086, 0.042733, 0.000590, 0.000004], + [1.000000, -0.000000, 1.000000, -0.000000, 0.000000, + 1.000000, -0.000000, 0.000000, -0.000000], + ], +]; +/** @type {Number} */ +const SPHERICAL_HARMONICS_AZIMUTH_RESOLUTION = SPHERICAL_HARMONICS[0].length; +/** @type {Number} */ +const SPHERICAL_HARMONICS_ELEVATION_RESOLUTION = SPHERICAL_HARMONICS[1].length; +/** + * The maximum allowed ambisonic order. + * @type {Number} + */ +const SPHERICAL_HARMONICS_MAX_ORDER = SPHERICAL_HARMONICS[0][0].length / 2; +/** + * Pre-computed per-band weighting coefficients for producing energy-preserving + * Max-Re sources. + */ +const MAX_RE_WEIGHTS = [ + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.000000, 1.000000, 1.000000, 1.000000], + [1.003236, 1.002156, 0.999152, 0.990038], + [1.032370, 1.021194, 0.990433, 0.898572], + [1.062694, 1.040231, 0.979161, 0.799806], + [1.093999, 1.058954, 0.964976, 0.693603], + [1.126003, 1.077006, 0.947526, 0.579890], + [1.158345, 1.093982, 0.926474, 0.458690], + [1.190590, 1.109437, 0.901512, 0.330158], + [1.222228, 1.122890, 0.872370, 0.194621], + [1.252684, 1.133837, 0.838839, 0.052614], + [1.281987, 1.142358, 0.801199, 0.000000], + [1.312073, 1.150207, 0.760839, 0.000000], + [1.343011, 1.157424, 0.717799, 0.000000], + [1.374649, 1.163859, 0.671999, 0.000000], + [1.406809, 1.169354, 0.623371, 0.000000], + [1.439286, 1.173739, 0.571868, 0.000000], + [1.471846, 1.176837, 0.517465, 0.000000], + [1.504226, 1.178465, 0.460174, 0.000000], + [1.536133, 1.178438, 0.400043, 0.000000], + [1.567253, 1.176573, 0.337165, 0.000000], + [1.597247, 1.172695, 0.271688, 0.000000], + [1.625766, 1.166645, 0.203815, 0.000000], + [1.652455, 1.158285, 0.133806, 0.000000], + [1.676966, 1.147506, 0.061983, 0.000000], + [1.699006, 1.134261, 0.000000, 0.000000], + [1.720224, 1.119789, 0.000000, 0.000000], + [1.741631, 1.104810, 0.000000, 0.000000], + [1.763183, 1.089330, 0.000000, 0.000000], + [1.784837, 1.073356, 0.000000, 0.000000], + [1.806548, 1.056898, 0.000000, 0.000000], + [1.828269, 1.039968, 0.000000, 0.000000], + [1.849952, 1.022580, 0.000000, 0.000000], + [1.871552, 1.004752, 0.000000, 0.000000], + [1.893018, 0.986504, 0.000000, 0.000000], + [1.914305, 0.967857, 0.000000, 0.000000], + [1.935366, 0.948837, 0.000000, 0.000000], + [1.956154, 0.929471, 0.000000, 0.000000], + [1.976625, 0.909790, 0.000000, 0.000000], + [1.996736, 0.889823, 0.000000, 0.000000], + [2.016448, 0.869607, 0.000000, 0.000000], + [2.035721, 0.849175, 0.000000, 0.000000], + [2.054522, 0.828565, 0.000000, 0.000000], + [2.072818, 0.807816, 0.000000, 0.000000], + [2.090581, 0.786964, 0.000000, 0.000000], + [2.107785, 0.766051, 0.000000, 0.000000], + [2.124411, 0.745115, 0.000000, 0.000000], + [2.140439, 0.724196, 0.000000, 0.000000], + [2.155856, 0.703332, 0.000000, 0.000000], + [2.170653, 0.682561, 0.000000, 0.000000], + [2.184823, 0.661921, 0.000000, 0.000000], + [2.198364, 0.641445, 0.000000, 0.000000], + [2.211275, 0.621169, 0.000000, 0.000000], + [2.223562, 0.601125, 0.000000, 0.000000], + [2.235230, 0.581341, 0.000000, 0.000000], + [2.246289, 0.561847, 0.000000, 0.000000], + [2.256751, 0.542667, 0.000000, 0.000000], + [2.266631, 0.523826, 0.000000, 0.000000], + [2.275943, 0.505344, 0.000000, 0.000000], + [2.284707, 0.487239, 0.000000, 0.000000], + [2.292939, 0.469528, 0.000000, 0.000000], + [2.300661, 0.452225, 0.000000, 0.000000], + [2.307892, 0.435342, 0.000000, 0.000000], + [2.314654, 0.418888, 0.000000, 0.000000], + [2.320969, 0.402870, 0.000000, 0.000000], + [2.326858, 0.387294, 0.000000, 0.000000], + [2.332343, 0.372164, 0.000000, 0.000000], + [2.337445, 0.357481, 0.000000, 0.000000], + [2.342186, 0.343246, 0.000000, 0.000000], + [2.346585, 0.329458, 0.000000, 0.000000], + [2.350664, 0.316113, 0.000000, 0.000000], + [2.354442, 0.303208, 0.000000, 0.000000], + [2.357937, 0.290738, 0.000000, 0.000000], + [2.361168, 0.278698, 0.000000, 0.000000], + [2.364152, 0.267080, 0.000000, 0.000000], + [2.366906, 0.255878, 0.000000, 0.000000], + [2.369446, 0.245082, 0.000000, 0.000000], + [2.371786, 0.234685, 0.000000, 0.000000], + [2.373940, 0.224677, 0.000000, 0.000000], + [2.375923, 0.215048, 0.000000, 0.000000], + [2.377745, 0.205790, 0.000000, 0.000000], + [2.379421, 0.196891, 0.000000, 0.000000], + [2.380959, 0.188342, 0.000000, 0.000000], + [2.382372, 0.180132, 0.000000, 0.000000], + [2.383667, 0.172251, 0.000000, 0.000000], + [2.384856, 0.164689, 0.000000, 0.000000], + [2.385945, 0.157435, 0.000000, 0.000000], + [2.386943, 0.150479, 0.000000, 0.000000], + [2.387857, 0.143811, 0.000000, 0.000000], + [2.388694, 0.137421, 0.000000, 0.000000], + [2.389460, 0.131299, 0.000000, 0.000000], + [2.390160, 0.125435, 0.000000, 0.000000], + [2.390801, 0.119820, 0.000000, 0.000000], + [2.391386, 0.114445, 0.000000, 0.000000], + [2.391921, 0.109300, 0.000000, 0.000000], + [2.392410, 0.104376, 0.000000, 0.000000], + [2.392857, 0.099666, 0.000000, 0.000000], + [2.393265, 0.095160, 0.000000, 0.000000], + [2.393637, 0.090851, 0.000000, 0.000000], + [2.393977, 0.086731, 0.000000, 0.000000], + [2.394288, 0.082791, 0.000000, 0.000000], + [2.394571, 0.079025, 0.000000, 0.000000], + [2.394829, 0.075426, 0.000000, 0.000000], + [2.395064, 0.071986, 0.000000, 0.000000], + [2.395279, 0.068699, 0.000000, 0.000000], + [2.395475, 0.065558, 0.000000, 0.000000], + [2.395653, 0.062558, 0.000000, 0.000000], + [2.395816, 0.059693, 0.000000, 0.000000], + [2.395964, 0.056955, 0.000000, 0.000000], + [2.396099, 0.054341, 0.000000, 0.000000], + [2.396222, 0.051845, 0.000000, 0.000000], + [2.396334, 0.049462, 0.000000, 0.000000], + [2.396436, 0.047186, 0.000000, 0.000000], + [2.396529, 0.045013, 0.000000, 0.000000], + [2.396613, 0.042939, 0.000000, 0.000000], + [2.396691, 0.040959, 0.000000, 0.000000], + [2.396761, 0.039069, 0.000000, 0.000000], + [2.396825, 0.037266, 0.000000, 0.000000], + [2.396883, 0.035544, 0.000000, 0.000000], + [2.396936, 0.033901, 0.000000, 0.000000], + [2.396984, 0.032334, 0.000000, 0.000000], + [2.397028, 0.030838, 0.000000, 0.000000], + [2.397068, 0.029410, 0.000000, 0.000000], + [2.397104, 0.028048, 0.000000, 0.000000], + [2.397137, 0.026749, 0.000000, 0.000000], + [2.397167, 0.025509, 0.000000, 0.000000], + [2.397194, 0.024326, 0.000000, 0.000000], + [2.397219, 0.023198, 0.000000, 0.000000], + [2.397242, 0.022122, 0.000000, 0.000000], + [2.397262, 0.021095, 0.000000, 0.000000], + [2.397281, 0.020116, 0.000000, 0.000000], + [2.397298, 0.019181, 0.000000, 0.000000], + [2.397314, 0.018290, 0.000000, 0.000000], + [2.397328, 0.017441, 0.000000, 0.000000], + [2.397341, 0.016630, 0.000000, 0.000000], + [2.397352, 0.015857, 0.000000, 0.000000], + [2.397363, 0.015119, 0.000000, 0.000000], + [2.397372, 0.014416, 0.000000, 0.000000], + [2.397381, 0.013745, 0.000000, 0.000000], + [2.397389, 0.013106, 0.000000, 0.000000], + [2.397396, 0.012496, 0.000000, 0.000000], + [2.397403, 0.011914, 0.000000, 0.000000], + [2.397409, 0.011360, 0.000000, 0.000000], + [2.397414, 0.010831, 0.000000, 0.000000], + [2.397419, 0.010326, 0.000000, 0.000000], + [2.397424, 0.009845, 0.000000, 0.000000], + [2.397428, 0.009387, 0.000000, 0.000000], + [2.397432, 0.008949, 0.000000, 0.000000], + [2.397435, 0.008532, 0.000000, 0.000000], + [2.397438, 0.008135, 0.000000, 0.000000], + [2.397441, 0.007755, 0.000000, 0.000000], + [2.397443, 0.007394, 0.000000, 0.000000], + [2.397446, 0.007049, 0.000000, 0.000000], + [2.397448, 0.006721, 0.000000, 0.000000], + [2.397450, 0.006407, 0.000000, 0.000000], + [2.397451, 0.006108, 0.000000, 0.000000], + [2.397453, 0.005824, 0.000000, 0.000000], + [2.397454, 0.005552, 0.000000, 0.000000], + [2.397456, 0.005293, 0.000000, 0.000000], + [2.397457, 0.005046, 0.000000, 0.000000], + [2.397458, 0.004811, 0.000000, 0.000000], + [2.397459, 0.004586, 0.000000, 0.000000], + [2.397460, 0.004372, 0.000000, 0.000000], + [2.397461, 0.004168, 0.000000, 0.000000], + [2.397461, 0.003974, 0.000000, 0.000000], + [2.397462, 0.003788, 0.000000, 0.000000], + [2.397463, 0.003611, 0.000000, 0.000000], + [2.397463, 0.003443, 0.000000, 0.000000], + [2.397464, 0.003282, 0.000000, 0.000000], + [2.397464, 0.003129, 0.000000, 0.000000], + [2.397465, 0.002983, 0.000000, 0.000000], + [2.397465, 0.002844, 0.000000, 0.000000], + [2.397465, 0.002711, 0.000000, 0.000000], + [2.397466, 0.002584, 0.000000, 0.000000], + [2.397466, 0.002464, 0.000000, 0.000000], + [2.397466, 0.002349, 0.000000, 0.000000], + [2.397466, 0.002239, 0.000000, 0.000000], + [2.397467, 0.002135, 0.000000, 0.000000], + [2.397467, 0.002035, 0.000000, 0.000000], + [2.397467, 0.001940, 0.000000, 0.000000], + [2.397467, 0.001849, 0.000000, 0.000000], + [2.397467, 0.001763, 0.000000, 0.000000], + [2.397467, 0.001681, 0.000000, 0.000000], + [2.397468, 0.001602, 0.000000, 0.000000], + [2.397468, 0.001527, 0.000000, 0.000000], + [2.397468, 0.001456, 0.000000, 0.000000], + [2.397468, 0.001388, 0.000000, 0.000000], + [2.397468, 0.001323, 0.000000, 0.000000], + [2.397468, 0.001261, 0.000000, 0.000000], + [2.397468, 0.001202, 0.000000, 0.000000], + [2.397468, 0.001146, 0.000000, 0.000000], + [2.397468, 0.001093, 0.000000, 0.000000], + [2.397468, 0.001042, 0.000000, 0.000000], + [2.397468, 0.000993, 0.000000, 0.000000], + [2.397468, 0.000947, 0.000000, 0.000000], + [2.397468, 0.000902, 0.000000, 0.000000], + [2.397468, 0.000860, 0.000000, 0.000000], + [2.397468, 0.000820, 0.000000, 0.000000], + [2.397469, 0.000782, 0.000000, 0.000000], + [2.397469, 0.000745, 0.000000, 0.000000], + [2.397469, 0.000710, 0.000000, 0.000000], + [2.397469, 0.000677, 0.000000, 0.000000], + [2.397469, 0.000646, 0.000000, 0.000000], + [2.397469, 0.000616, 0.000000, 0.000000], + [2.397469, 0.000587, 0.000000, 0.000000], + [2.397469, 0.000559, 0.000000, 0.000000], + [2.397469, 0.000533, 0.000000, 0.000000], + [2.397469, 0.000508, 0.000000, 0.000000], + [2.397469, 0.000485, 0.000000, 0.000000], + [2.397469, 0.000462, 0.000000, 0.000000], + [2.397469, 0.000440, 0.000000, 0.000000], + [2.397469, 0.000420, 0.000000, 0.000000], + [2.397469, 0.000400, 0.000000, 0.000000], + [2.397469, 0.000381, 0.000000, 0.000000], + [2.397469, 0.000364, 0.000000, 0.000000], + [2.397469, 0.000347, 0.000000, 0.000000], + [2.397469, 0.000330, 0.000000, 0.000000], + [2.397469, 0.000315, 0.000000, 0.000000], + [2.397469, 0.000300, 0.000000, 0.000000], + [2.397469, 0.000286, 0.000000, 0.000000], + [2.397469, 0.000273, 0.000000, 0.000000], + [2.397469, 0.000260, 0.000000, 0.000000], + [2.397469, 0.000248, 0.000000, 0.000000], + [2.397469, 0.000236, 0.000000, 0.000000], + [2.397469, 0.000225, 0.000000, 0.000000], + [2.397469, 0.000215, 0.000000, 0.000000], + [2.397469, 0.000205, 0.000000, 0.000000], + [2.397469, 0.000195, 0.000000, 0.000000], + [2.397469, 0.000186, 0.000000, 0.000000], + [2.397469, 0.000177, 0.000000, 0.000000], + [2.397469, 0.000169, 0.000000, 0.000000], + [2.397469, 0.000161, 0.000000, 0.000000], + [2.397469, 0.000154, 0.000000, 0.000000], + [2.397469, 0.000147, 0.000000, 0.000000], + [2.397469, 0.000140, 0.000000, 0.000000], + [2.397469, 0.000133, 0.000000, 0.000000], + [2.397469, 0.000127, 0.000000, 0.000000], + [2.397469, 0.000121, 0.000000, 0.000000], + [2.397469, 0.000115, 0.000000, 0.000000], + [2.397469, 0.000110, 0.000000, 0.000000], + [2.397469, 0.000105, 0.000000, 0.000000], + [2.397469, 0.000100, 0.000000, 0.000000], + [2.397469, 0.000095, 0.000000, 0.000000], + [2.397469, 0.000091, 0.000000, 0.000000], + [2.397469, 0.000087, 0.000000, 0.000000], + [2.397469, 0.000083, 0.000000, 0.000000], + [2.397469, 0.000079, 0.000000, 0.000000], + [2.397469, 0.000075, 0.000000, 0.000000], + [2.397469, 0.000071, 0.000000, 0.000000], + [2.397469, 0.000068, 0.000000, 0.000000], + [2.397469, 0.000065, 0.000000, 0.000000], + [2.397469, 0.000062, 0.000000, 0.000000], + [2.397469, 0.000059, 0.000000, 0.000000], + [2.397469, 0.000056, 0.000000, 0.000000], + [2.397469, 0.000054, 0.000000, 0.000000], + [2.397469, 0.000051, 0.000000, 0.000000], + [2.397469, 0.000049, 0.000000, 0.000000], + [2.397469, 0.000046, 0.000000, 0.000000], + [2.397469, 0.000044, 0.000000, 0.000000], + [2.397469, 0.000042, 0.000000, 0.000000], + [2.397469, 0.000040, 0.000000, 0.000000], + [2.397469, 0.000038, 0.000000, 0.000000], + [2.397469, 0.000037, 0.000000, 0.000000], + [2.397469, 0.000035, 0.000000, 0.000000], + [2.397469, 0.000033, 0.000000, 0.000000], + [2.397469, 0.000032, 0.000000, 0.000000], + [2.397469, 0.000030, 0.000000, 0.000000], + [2.397469, 0.000029, 0.000000, 0.000000], + [2.397469, 0.000027, 0.000000, 0.000000], + [2.397469, 0.000026, 0.000000, 0.000000], + [2.397469, 0.000025, 0.000000, 0.000000], + [2.397469, 0.000024, 0.000000, 0.000000], + [2.397469, 0.000023, 0.000000, 0.000000], + [2.397469, 0.000022, 0.000000, 0.000000], + [2.397469, 0.000021, 0.000000, 0.000000], + [2.397469, 0.000020, 0.000000, 0.000000], + [2.397469, 0.000019, 0.000000, 0.000000], + [2.397469, 0.000018, 0.000000, 0.000000], + [2.397469, 0.000017, 0.000000, 0.000000], + [2.397469, 0.000016, 0.000000, 0.000000], + [2.397469, 0.000015, 0.000000, 0.000000], + [2.397469, 0.000015, 0.000000, 0.000000], + [2.397469, 0.000014, 0.000000, 0.000000], + [2.397469, 0.000013, 0.000000, 0.000000], + [2.397469, 0.000013, 0.000000, 0.000000], + [2.397469, 0.000012, 0.000000, 0.000000], + [2.397469, 0.000012, 0.000000, 0.000000], + [2.397469, 0.000011, 0.000000, 0.000000], + [2.397469, 0.000011, 0.000000, 0.000000], + [2.397469, 0.000010, 0.000000, 0.000000], + [2.397469, 0.000010, 0.000000, 0.000000], + [2.397469, 0.000009, 0.000000, 0.000000], + [2.397469, 0.000009, 0.000000, 0.000000], + [2.397469, 0.000008, 0.000000, 0.000000], + [2.397469, 0.000008, 0.000000, 0.000000], + [2.397469, 0.000008, 0.000000, 0.000000], + [2.397469, 0.000007, 0.000000, 0.000000], + [2.397469, 0.000007, 0.000000, 0.000000], + [2.397469, 0.000007, 0.000000, 0.000000], + [2.397469, 0.000006, 0.000000, 0.000000], + [2.397469, 0.000006, 0.000000, 0.000000], + [2.397469, 0.000006, 0.000000, 0.000000], + [2.397469, 0.000005, 0.000000, 0.000000], + [2.397469, 0.000005, 0.000000, 0.000000], + [2.397469, 0.000005, 0.000000, 0.000000], + [2.397469, 0.000005, 0.000000, 0.000000], + [2.397469, 0.000004, 0.000000, 0.000000], + [2.397469, 0.000004, 0.000000, 0.000000], + [2.397469, 0.000004, 0.000000, 0.000000], + [2.397469, 0.000004, 0.000000, 0.000000], + [2.397469, 0.000004, 0.000000, 0.000000], + [2.397469, 0.000004, 0.000000, 0.000000], + [2.397469, 0.000003, 0.000000, 0.000000], + [2.397469, 0.000003, 0.000000, 0.000000], + [2.397469, 0.000003, 0.000000, 0.000000], + [2.397469, 0.000003, 0.000000, 0.000000], + [2.397469, 0.000003, 0.000000, 0.000000], + [2.397469, 0.000003, 0.000000, 0.000000], + [2.397469, 0.000003, 0.000000, 0.000000], + [2.397469, 0.000002, 0.000000, 0.000000], + [2.397469, 0.000002, 0.000000, 0.000000], + [2.397469, 0.000002, 0.000000, 0.000000], + [2.397469, 0.000002, 0.000000, 0.000000], + [2.397469, 0.000002, 0.000000, 0.000000], + [2.397469, 0.000002, 0.000000, 0.000000], + [2.397469, 0.000002, 0.000000, 0.000000], + [2.397469, 0.000002, 0.000000, 0.000000], + [2.397469, 0.000002, 0.000000, 0.000000], + [2.397469, 0.000002, 0.000000, 0.000000], + [2.397469, 0.000001, 0.000000, 0.000000], + [2.397469, 0.000001, 0.000000, 0.000000], + [2.397469, 0.000001, 0.000000, 0.000000], +]; +/** @type {Number} */ +const MAX_RE_WEIGHTS_RESOLUTION = MAX_RE_WEIGHTS.length; +export default { + SPHERICAL_HARMONICS, + SPHERICAL_HARMONICS_AZIMUTH_RESOLUTION, + SPHERICAL_HARMONICS_ELEVATION_RESOLUTION, + SPHERICAL_HARMONICS_MAX_ORDER, + MAX_RE_WEIGHTS, + MAX_RE_WEIGHTS_RESOLUTION +}; diff --git a/src/framework/resonator/vendor/resonance-es6/utils.d.ts b/src/framework/resonator/vendor/resonance-es6/utils.d.ts new file mode 100644 index 0000000..48d24d2 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/utils.d.ts @@ -0,0 +1,98 @@ +export default Utils; +/** + * @class Utils + * @description A set of defaults, constants and utility functions. + */ +declare class Utils { + /** + * Properties describing the geometry of a room. + * @typedef {Object} Utils~RoomDimensions + * @property {Number} width (in meters). + * @property {Number} height (in meters). + * @property {Number} depth (in meters). + */ + /** + * Properties describing the wall materials (from + * {@linkcode Utils.ROOM_MATERIAL_COEFFICIENTS ROOM_MATERIAL_COEFFICIENTS}) + * of a room. + * @typedef {Object} Utils~RoomMaterials + * @property {String} left Left-wall material name. + * @property {String} right Right-wall material name. + * @property {String} front Front-wall material name. + * @property {String} back Back-wall material name. + * @property {String} up Up-wall material name. + * @property {String} down Down-wall material name. + */ + /** + * ResonanceAudio library logging function. + * @type {Function} + * @param {any} Message to be printed out. + * @private + */ + private static log; +} +declare namespace Utils { + const DEFAULT_SOURCE_GAIN: number; + const LISTENER_MAX_OUTSIDE_ROOM_DISTANCE: number; + const SOURCE_MAX_OUTSIDE_ROOM_DISTANCE: number; + const DEFAULT_SOURCE_DISTANCE: number; + const DEFAULT_POSITION: Float32Array; + const DEFAULT_FORWARD: Float32Array; + const DEFAULT_UP: Float32Array; + const DEFAULT_RIGHT: Float32Array; + const DEFAULT_SPEED_OF_SOUND: number; + const ATTENUATION_ROLLOFFS: any[]; + const DEFAULT_ATTENUATION_ROLLOFF: string; + const DEFAULT_MIN_DISTANCE: number; + const DEFAULT_MAX_DISTANCE: number; + const DEFAULT_DIRECTIVITY_ALPHA: number; + const DEFAULT_DIRECTIVITY_SHARPNESS: number; + const DEFAULT_AZIMUTH: number; + const DEFAULT_ELEVATION: number; + const DEFAULT_AMBISONIC_ORDER: number; + const DEFAULT_SOURCE_WIDTH: number; + const DEFAULT_REFLECTION_MAX_DURATION: number; + const DEFAULT_REFLECTION_CUTOFF_FREQUENCY: number; + const DEFAULT_REFLECTION_COEFFICIENTS: any; + const DEFAULT_REFLECTION_MIN_DISTANCE: number; + const DEFAULT_ROOM_DIMENSIONS: any; + const DEFAULT_REFLECTION_MULTIPLIER: number; + const DEFAULT_REVERB_BANDWIDTH: number; + const DEFAULT_REVERB_DURATION_MULTIPLIER: number; + const DEFAULT_REVERB_PREDELAY: number; + const DEFAULT_REVERB_TAIL_ONSET: number; + const DEFAULT_REVERB_GAIN: number; + const DEFAULT_REVERB_MAX_DURATION: number; + const DEFAULT_REVERB_FREQUENCY_BANDS: any[]; + const NUMBER_REVERB_FREQUENCY_BANDS: number; + const DEFAULT_REVERB_DURATIONS: Float32Array; + const ROOM_MATERIAL_COEFFICIENTS: any; + const DEFAULT_ROOM_MATERIALS: any; + const NUMBER_REFLECTION_AVERAGING_BANDS: number; + const ROOM_STARTING_AVERAGING_BAND: number; + const ROOM_MIN_VOLUME: number; + const ROOM_AIR_ABSORPTION_COEFFICIENTS: Float32Array; + const ROOM_EYRING_CORRECTION_COEFFICIENT: number; + const TWO_PI: number; + const TWENTY_FOUR_LOG10: number; + const LOG1000: number; + const LOG2_DIV2: number; + const DEGREES_TO_RADIANS: number; + const RADIANS_TO_DEGREES: number; + const EPSILON_FLOAT: number; + /** + * Normalize a 3-d vector. + * @param {Float32Array} v 3-element vector. + * @return {Float32Array} 3-element vector. + * @private + */ + function normalizeVector(v: Float32Array): Float32Array; + /** + * Cross-product between two 3-d vectors. + * @param {Float32Array} a 3-element vector. + * @param {Float32Array} b 3-element vector. + * @return {Float32Array} + * @private + */ + function crossProduct(a: Float32Array, b: Float32Array): Float32Array; +} diff --git a/src/framework/resonator/vendor/resonance-es6/utils.js b/src/framework/resonator/vendor/resonance-es6/utils.js new file mode 100644 index 0000000..5299f0c --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/utils.js @@ -0,0 +1,379 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ResonanceAudio library common utilities, mathematical constants, + * and default values. + * @author Andrew Allen + */ +'use strict'; +/** + * @class Utils + * @description A set of defaults, constants and utility functions. + */ +class Utils { + /** + * Properties describing the geometry of a room. + * @typedef {Object} Utils~RoomDimensions + * @property {Number} width (in meters). + * @property {Number} height (in meters). + * @property {Number} depth (in meters). + */ + /** + * Properties describing the wall materials (from + * {@linkcode Utils.ROOM_MATERIAL_COEFFICIENTS ROOM_MATERIAL_COEFFICIENTS}) + * of a room. + * @typedef {Object} Utils~RoomMaterials + * @property {String} left Left-wall material name. + * @property {String} right Right-wall material name. + * @property {String} front Front-wall material name. + * @property {String} back Back-wall material name. + * @property {String} up Up-wall material name. + * @property {String} down Down-wall material name. + */ + /** + * ResonanceAudio library logging function. + * @type {Function} + * @param {any} Message to be printed out. + * @private + */ + static log() { + window.console.log.apply(window.console, [ + '%c[ResonanceAudio]%c ' + + Array.prototype.slice.call(arguments).join(' ') + ' %c(@' + + performance.now().toFixed(2) + 'ms)', + 'background: #BBDEFB; color: #FF5722; font-weight: 700', + 'font-weight: 400', + 'color: #AAA', + ]); + } +} +/** + * Default input gain (linear). + * @type {Number} + */ +Utils.DEFAULT_SOURCE_GAIN = 1; +/** + * Maximum outside-the-room distance to attenuate far-field listener by. + * @type {Number} + */ +Utils.LISTENER_MAX_OUTSIDE_ROOM_DISTANCE = 1; +/** + * Maximum outside-the-room distance to attenuate far-field sources by. + * @type {Number} + */ +Utils.SOURCE_MAX_OUTSIDE_ROOM_DISTANCE = 1; +/** + * Default distance from listener when setting angle. + * @type {Number} + */ +Utils.DEFAULT_SOURCE_DISTANCE = 1; +/** @type {Float32Array} */ +Utils.DEFAULT_POSITION = [0, 0, 0]; +/** @type {Float32Array} */ +Utils.DEFAULT_FORWARD = [0, 0, -1]; +/** @type {Float32Array} */ +Utils.DEFAULT_UP = [0, 1, 0]; +/** @type {Float32Array} */ +Utils.DEFAULT_RIGHT = [1, 0, 0]; +/** + * @type {Number} + */ +Utils.DEFAULT_SPEED_OF_SOUND = 343; +/** Rolloff models (e.g. 'logarithmic', 'linear', or 'none'). + * @type {Array} + */ +Utils.ATTENUATION_ROLLOFFS = ['logarithmic', 'linear', 'none']; +/** Default rolloff model ('logarithmic'). + * @type {string} + */ +Utils.DEFAULT_ATTENUATION_ROLLOFF = 'logarithmic'; +/** @type {Number} */ +Utils.DEFAULT_MIN_DISTANCE = 1; +/** @type {Number} */ +Utils.DEFAULT_MAX_DISTANCE = 1000; +/** + * The default alpha (i.e. microphone pattern). + * @type {Number} + */ +Utils.DEFAULT_DIRECTIVITY_ALPHA = 0; +/** + * The default pattern sharpness (i.e. pattern exponent). + * @type {Number} + */ +Utils.DEFAULT_DIRECTIVITY_SHARPNESS = 1; +/** + * Default azimuth (in degrees). Suitable range is 0 to 360. + * @type {Number} + */ +Utils.DEFAULT_AZIMUTH = 0; +/** + * Default elevation (in degres). + * Suitable range is from -90 (below) to 90 (above). + * @type {Number} + */ +Utils.DEFAULT_ELEVATION = 0; +/** + * The default ambisonic order. + * @type {Number} + */ +Utils.DEFAULT_AMBISONIC_ORDER = 1; +/** + * The default source width. + * @type {Number} + */ +Utils.DEFAULT_SOURCE_WIDTH = 0; +/** + * The maximum delay (in seconds) of a single wall reflection. + * @type {Number} + */ +Utils.DEFAULT_REFLECTION_MAX_DURATION = 0.5; +/** + * The -12dB cutoff frequency (in Hertz) for the lowpass filter applied to + * all reflections. + * @type {Number} + */ +Utils.DEFAULT_REFLECTION_CUTOFF_FREQUENCY = 6400; // Uses -12dB cutoff. +/** + * The default reflection coefficients (where 0 = no reflection, 1 = perfect + * reflection, -1 = mirrored reflection (180-degrees out of phase)). + * @type {Object} + */ +Utils.DEFAULT_REFLECTION_COEFFICIENTS = { + left: 0, right: 0, front: 0, back: 0, down: 0, up: 0, +}; +/** + * The minimum distance we consider the listener to be to any given wall. + * @type {Number} + */ +Utils.DEFAULT_REFLECTION_MIN_DISTANCE = 1; +/** + * Default room dimensions (in meters). + * @type {Object} + */ +Utils.DEFAULT_ROOM_DIMENSIONS = { + width: 0, height: 0, depth: 0, +}; +/** + * The multiplier to apply to distances from the listener to each wall. + * @type {Number} + */ +Utils.DEFAULT_REFLECTION_MULTIPLIER = 1; +/** The default bandwidth (in octaves) of the center frequencies. + * @type {Number} + */ +Utils.DEFAULT_REVERB_BANDWIDTH = 1; +/** The default multiplier applied when computing tail lengths. + * @type {Number} + */ +Utils.DEFAULT_REVERB_DURATION_MULTIPLIER = 1; +/** + * The late reflections pre-delay (in milliseconds). + * @type {Number} + */ +Utils.DEFAULT_REVERB_PREDELAY = 1.5; +/** + * The length of the beginning of the impulse response to apply a + * half-Hann window to. + * @type {Number} + */ +Utils.DEFAULT_REVERB_TAIL_ONSET = 3.8; +/** + * The default gain (linear). + * @type {Number} + */ +Utils.DEFAULT_REVERB_GAIN = 0.01; +/** + * The maximum impulse response length (in seconds). + * @type {Number} + */ +Utils.DEFAULT_REVERB_MAX_DURATION = 3; +/** + * Center frequencies of the multiband late reflections. + * Nine bands are computed by: 31.25 * 2^(0:8). + * @type {Array} + */ +Utils.DEFAULT_REVERB_FREQUENCY_BANDS = [ + 31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, +]; +/** + * The number of frequency bands. + */ +Utils.NUMBER_REVERB_FREQUENCY_BANDS = + Utils.DEFAULT_REVERB_FREQUENCY_BANDS.length; +/** + * The default multiband RT60 durations (in seconds). + * @type {Float32Array} + */ +Utils.DEFAULT_REVERB_DURATIONS = + new Float32Array(Utils.NUMBER_REVERB_FREQUENCY_BANDS); +/** + * Pre-defined frequency-dependent absorption coefficients for listed materials. + * Currently supported materials are: + *
    + *
  • 'transparent'
  • + *
  • 'acoustic-ceiling-tiles'
  • + *
  • 'brick-bare'
  • + *
  • 'brick-painted'
  • + *
  • 'concrete-block-coarse'
  • + *
  • 'concrete-block-painted'
  • + *
  • 'curtain-heavy'
  • + *
  • 'fiber-glass-insulation'
  • + *
  • 'glass-thin'
  • + *
  • 'glass-thick'
  • + *
  • 'grass'
  • + *
  • 'linoleum-on-concrete'
  • + *
  • 'marble'
  • + *
  • 'metal'
  • + *
  • 'parquet-on-concrete'
  • + *
  • 'plaster-smooth'
  • + *
  • 'plywood-panel'
  • + *
  • 'polished-concrete-or-tile'
  • + *
  • 'sheetrock'
  • + *
  • 'water-or-ice-surface'
  • + *
  • 'wood-ceiling'
  • + *
  • 'wood-panel'
  • + *
  • 'uniform'
  • + *
+ * @type {Object} + */ +Utils.ROOM_MATERIAL_COEFFICIENTS = { + 'transparent': [1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000], + 'acoustic-ceiling-tiles': [0.672, 0.675, 0.700, 0.660, 0.720, 0.920, 0.880, 0.750, 1.000], + 'brick-bare': [0.030, 0.030, 0.030, 0.030, 0.030, 0.040, 0.050, 0.070, 0.140], + 'brick-painted': [0.006, 0.007, 0.010, 0.010, 0.020, 0.020, 0.020, 0.030, 0.060], + 'concrete-block-coarse': [0.360, 0.360, 0.360, 0.440, 0.310, 0.290, 0.390, 0.250, 0.500], + 'concrete-block-painted': [0.092, 0.090, 0.100, 0.050, 0.060, 0.070, 0.090, 0.080, 0.160], + 'curtain-heavy': [0.073, 0.106, 0.140, 0.350, 0.550, 0.720, 0.700, 0.650, 1.000], + 'fiber-glass-insulation': [0.193, 0.220, 0.220, 0.820, 0.990, 0.990, 0.990, 0.990, 1.000], + 'glass-thin': [0.180, 0.169, 0.180, 0.060, 0.040, 0.030, 0.020, 0.020, 0.040], + 'glass-thick': [0.350, 0.350, 0.350, 0.250, 0.180, 0.120, 0.070, 0.040, 0.080], + 'grass': [0.050, 0.050, 0.150, 0.250, 0.400, 0.550, 0.600, 0.600, 0.600], + 'linoleum-on-concrete': [0.020, 0.020, 0.020, 0.030, 0.030, 0.030, 0.030, 0.020, 0.040], + 'marble': [0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.020, 0.020, 0.040], + 'metal': [0.030, 0.035, 0.040, 0.040, 0.050, 0.050, 0.050, 0.070, 0.090], + 'parquet-on-concrete': [0.028, 0.030, 0.040, 0.040, 0.070, 0.060, 0.060, 0.070, 0.140], + 'plaster-rough': [0.017, 0.018, 0.020, 0.030, 0.040, 0.050, 0.040, 0.030, 0.060], + 'plaster-smooth': [0.011, 0.012, 0.013, 0.015, 0.020, 0.030, 0.040, 0.050, 0.100], + 'plywood-panel': [0.400, 0.340, 0.280, 0.220, 0.170, 0.090, 0.100, 0.110, 0.220], + 'polished-concrete-or-tile': [0.008, 0.008, 0.010, 0.010, 0.015, 0.020, 0.020, 0.020, 0.040], + 'sheet-rock': [0.290, 0.279, 0.290, 0.100, 0.050, 0.040, 0.070, 0.090, 0.180], + 'water-or-ice-surface': [0.006, 0.006, 0.008, 0.008, 0.013, 0.015, 0.020, 0.025, 0.050], + 'wood-ceiling': [0.150, 0.147, 0.150, 0.110, 0.100, 0.070, 0.060, 0.070, 0.140], + 'wood-panel': [0.280, 0.280, 0.280, 0.220, 0.170, 0.090, 0.100, 0.110, 0.220], + 'uniform': [0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500], +}; +/** + * Default materials that use strings from + * {@linkcode Utils.MATERIAL_COEFFICIENTS MATERIAL_COEFFICIENTS} + * @type {Object} + */ +Utils.DEFAULT_ROOM_MATERIALS = { + left: 'transparent', right: 'transparent', front: 'transparent', + back: 'transparent', down: 'transparent', up: 'transparent', +}; +/** + * The number of bands to average over when computing reflection coefficients. + * @type {Number} + */ +Utils.NUMBER_REFLECTION_AVERAGING_BANDS = 3; +/** + * The starting band to average over when computing reflection coefficients. + * @type {Number} + */ +Utils.ROOM_STARTING_AVERAGING_BAND = 4; +/** + * The minimum threshold for room volume. + * Room model is disabled if volume is below this value. + * @type {Number} */ +Utils.ROOM_MIN_VOLUME = 1e-4; +/** + * Air absorption coefficients per frequency band. + * @type {Float32Array} + */ +Utils.ROOM_AIR_ABSORPTION_COEFFICIENTS = + [0.0006, 0.0006, 0.0007, 0.0008, 0.0010, 0.0015, 0.0026, 0.0060, 0.0207]; +/** + * A scalar correction value to ensure Sabine and Eyring produce the same RT60 + * value at the cross-over threshold. + * @type {Number} + */ +Utils.ROOM_EYRING_CORRECTION_COEFFICIENT = 1.38; +/** + * @type {Number} + * @private + */ +Utils.TWO_PI = 6.28318530717959; +/** + * @type {Number} + * @private + */ +Utils.TWENTY_FOUR_LOG10 = 55.2620422318571; +/** + * @type {Number} + * @private + */ +Utils.LOG1000 = 6.90775527898214; +/** + * @type {Number} + * @private + */ +Utils.LOG2_DIV2 = 0.346573590279973; +/** + * @type {Number} + * @private + */ +Utils.DEGREES_TO_RADIANS = 0.017453292519943; +/** + * @type {Number} + * @private + */ +Utils.RADIANS_TO_DEGREES = 57.295779513082323; +/** + * @type {Number} + * @private + */ +Utils.EPSILON_FLOAT = 1e-8; +/** + * Normalize a 3-d vector. + * @param {Float32Array} v 3-element vector. + * @return {Float32Array} 3-element vector. + * @private + */ +Utils.normalizeVector = v => { + let n = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + if (n > Utils.EPSILON_FLOAT) { + n = 1 / n; + v[0] *= n; + v[1] *= n; + v[2] *= n; + } + return v; +}; +/** + * Cross-product between two 3-d vectors. + * @param {Float32Array} a 3-element vector. + * @param {Float32Array} b 3-element vector. + * @return {Float32Array} + * @private + */ +Utils.crossProduct = (a, b) => { + return [ + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], + ]; +}; +export default Utils; diff --git a/src/framework/resonator/vendor/resonance-es6/version.d.ts b/src/framework/resonator/vendor/resonance-es6/version.d.ts new file mode 100644 index 0000000..ac3a06d --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/version.d.ts @@ -0,0 +1,2 @@ +declare var _default: "1.0.0"; +export default _default; diff --git a/src/framework/resonator/vendor/resonance-es6/version.js b/src/framework/resonator/vendor/resonance-es6/version.js new file mode 100644 index 0000000..6719930 --- /dev/null +++ b/src/framework/resonator/vendor/resonance-es6/version.js @@ -0,0 +1,24 @@ +/** + * Copyright 2017 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ResonanceAudio version. + * @author Andrew Allen + */ +'use strict'; +/** + * ResonanceAudio library version + * @type {String} + */ +export default '1.0.0'; diff --git a/src/framework/resonator/vendor/tsm/constants.d.ts b/src/framework/resonator/vendor/tsm/constants.d.ts new file mode 100644 index 0000000..4ceaa99 --- /dev/null +++ b/src/framework/resonator/vendor/tsm/constants.d.ts @@ -0,0 +1 @@ +export declare const epsilon = 0.00001; diff --git a/src/framework/resonator/vendor/tsm/constants.js b/src/framework/resonator/vendor/tsm/constants.js new file mode 100644 index 0000000..347e812 --- /dev/null +++ b/src/framework/resonator/vendor/tsm/constants.js @@ -0,0 +1 @@ +export const epsilon = 0.00001; diff --git a/src/framework/resonator/vendor/tsm/mat2.d.ts b/src/framework/resonator/vendor/tsm/mat2.d.ts new file mode 100644 index 0000000..ab03acb --- /dev/null +++ b/src/framework/resonator/vendor/tsm/mat2.d.ts @@ -0,0 +1,23 @@ +import vec2 from './vec2'; +export default class mat2 { + constructor(values?: number[]); + private values; + static readonly identity: mat2; + at(index: number): number; + init(values: number[]): mat2; + reset(): void; + copy(dest?: mat2): mat2; + all(): number[]; + row(index: number): number[]; + col(index: number): number[]; + equals(matrix: mat2, threshold?: number): boolean; + determinant(): number; + setIdentity(): mat2; + transpose(): mat2; + inverse(): mat2; + multiply(matrix: mat2): mat2; + rotate(angle: number): mat2; + multiplyVec2(vector: vec2, result: vec2): vec2; + scale(vector: vec2): mat2; + static product(m1: mat2, m2: mat2, result: mat2): mat2; +} diff --git a/src/framework/resonator/vendor/tsm/mat2.js b/src/framework/resonator/vendor/tsm/mat2.js new file mode 100644 index 0000000..e67db94 --- /dev/null +++ b/src/framework/resonator/vendor/tsm/mat2.js @@ -0,0 +1,161 @@ +import vec2 from './vec2'; +import { epsilon } from './constants'; +export default class mat2 { + constructor(values) { + this.values = new Float32Array(4); + if (values !== undefined) { + this.init(values); + } + } + at(index) { + return this.values[index]; + } + init(values) { + for (let i = 0; i < 4; i++) { + this.values[i] = values[i]; + } + return this; + } + reset() { + for (let i = 0; i < 4; i++) { + this.values[i] = 0; + } + } + copy(dest) { + if (!dest) { + dest = new mat2(); + } + for (let i = 0; i < 4; i++) { + dest.values[i] = this.values[i]; + } + return dest; + } + all() { + const data = []; + for (let i = 0; i < 4; i++) { + data[i] = this.values[i]; + } + return data; + } + row(index) { + return [this.values[index * 2 + 0], this.values[index * 2 + 1]]; + } + col(index) { + return [this.values[index], this.values[index + 2]]; + } + equals(matrix, threshold = epsilon) { + for (let i = 0; i < 4; i++) { + if (Math.abs(this.values[i] - matrix.at(i)) > threshold) { + return false; + } + } + return true; + } + determinant() { + return this.values[0] * this.values[3] - this.values[2] * this.values[1]; + } + setIdentity() { + this.values[0] = 1; + this.values[1] = 0; + this.values[2] = 0; + this.values[3] = 1; + return this; + } + transpose() { + const temp = this.values[1]; + this.values[1] = this.values[2]; + this.values[2] = temp; + return this; + } + inverse() { + let det = this.determinant(); + if (!det) { + return null; + } + det = 1.0 / det; + const a11 = this.values[0]; + this.values[0] = det * this.values[3]; + this.values[1] = det * -this.values[1]; + this.values[2] = det * -this.values[2]; + this.values[3] = det * a11; + return this; + } + multiply(matrix) { + const a11 = this.values[0]; + const a12 = this.values[1]; + const a21 = this.values[2]; + const a22 = this.values[3]; + this.values[0] = a11 * matrix.at(0) + a12 * matrix.at(2); + this.values[1] = a11 * matrix.at(1) + a12 * matrix.at(3); + this.values[2] = a21 * matrix.at(0) + a22 * matrix.at(2); + this.values[3] = a21 * matrix.at(1) + a22 * matrix.at(3); + return this; + } + rotate(angle) { + const a11 = this.values[0]; + const a12 = this.values[1]; + const a21 = this.values[2]; + const a22 = this.values[3]; + const sin = Math.sin(angle); + const cos = Math.cos(angle); + this.values[0] = a11 * cos + a12 * sin; + this.values[1] = a11 * -sin + a12 * cos; + this.values[2] = a21 * cos + a22 * sin; + this.values[3] = a21 * -sin + a22 * cos; + return this; + } + multiplyVec2(vector, result) { + const x = vector.x; + const y = vector.y; + if (result) { + result.xy = [ + x * this.values[0] + y * this.values[1], + x * this.values[2] + y * this.values[3] + ]; + return result; + } + else { + return new vec2([ + x * this.values[0] + y * this.values[1], + x * this.values[2] + y * this.values[3] + ]); + } + } + scale(vector) { + const a11 = this.values[0]; + const a12 = this.values[1]; + const a21 = this.values[2]; + const a22 = this.values[3]; + const x = vector.x; + const y = vector.y; + this.values[0] = a11 * x; + this.values[1] = a12 * y; + this.values[2] = a21 * x; + this.values[3] = a22 * y; + return this; + } + static product(m1, m2, result) { + const a11 = m1.at(0); + const a12 = m1.at(1); + const a21 = m1.at(2); + const a22 = m1.at(3); + if (result) { + result.init([ + a11 * m2.at(0) + a12 * m2.at(2), + a11 * m2.at(1) + a12 * m2.at(3), + a21 * m2.at(0) + a22 * m2.at(2), + a21 * m2.at(1) + a22 * m2.at(3) + ]); + return result; + } + else { + return new mat2([ + a11 * m2.at(0) + a12 * m2.at(2), + a11 * m2.at(1) + a12 * m2.at(3), + a21 * m2.at(0) + a22 * m2.at(2), + a21 * m2.at(1) + a22 * m2.at(3) + ]); + } + } +} +mat2.identity = new mat2().setIdentity(); diff --git a/src/framework/resonator/vendor/tsm/mat3.d.ts b/src/framework/resonator/vendor/tsm/mat3.d.ts new file mode 100644 index 0000000..03bf323 --- /dev/null +++ b/src/framework/resonator/vendor/tsm/mat3.d.ts @@ -0,0 +1,28 @@ +import mat4 from './mat4'; +import quat from './quat'; +import vec2 from './vec2'; +import vec3 from './vec3'; +export default class mat3 { + constructor(values?: number[]); + private values; + static readonly identity: mat3; + at(index: number): number; + init(values: number[]): mat3; + reset(): void; + copy(dest?: mat3): mat3; + all(): number[]; + row(index: number): number[]; + col(index: number): number[]; + equals(matrix: mat3, threshold?: number): boolean; + determinant(): number; + setIdentity(): mat3; + transpose(): mat3; + inverse(): mat3; + multiply(matrix: mat3): mat3; + multiplyVec2(vector: vec2, result: vec2): vec2; + multiplyVec3(vector: vec3, result: vec3): vec3; + toMat4(result: mat4): mat4; + toQuat(): quat; + rotate(angle: number, axis: vec3): mat3; + static product(m1: mat3, m2: mat3, result: mat3): mat3; +} diff --git a/src/framework/resonator/vendor/tsm/mat3.js b/src/framework/resonator/vendor/tsm/mat3.js new file mode 100644 index 0000000..76e31b5 --- /dev/null +++ b/src/framework/resonator/vendor/tsm/mat3.js @@ -0,0 +1,392 @@ +import mat4 from './mat4'; +import quat from './quat'; +import vec2 from './vec2'; +import vec3 from './vec3'; +import { epsilon } from './constants'; +export default class mat3 { + constructor(values) { + this.values = new Float32Array(9); + if (values !== undefined) { + this.init(values); + } + } + at(index) { + return this.values[index]; + } + init(values) { + for (let i = 0; i < 9; i++) { + this.values[i] = values[i]; + } + return this; + } + reset() { + for (let i = 0; i < 9; i++) { + this.values[i] = 0; + } + } + copy(dest) { + if (!dest) { + dest = new mat3(); + } + for (let i = 0; i < 9; i++) { + dest.values[i] = this.values[i]; + } + return dest; + } + all() { + const data = []; + for (let i = 0; i < 9; i++) { + data[i] = this.values[i]; + } + return data; + } + row(index) { + return [ + this.values[index * 3 + 0], + this.values[index * 3 + 1], + this.values[index * 3 + 2] + ]; + } + col(index) { + return [this.values[index], this.values[index + 3], this.values[index + 6]]; + } + equals(matrix, threshold = epsilon) { + for (let i = 0; i < 9; i++) { + if (Math.abs(this.values[i] - matrix.at(i)) > threshold) { + return false; + } + } + return true; + } + determinant() { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a10 = this.values[3]; + const a11 = this.values[4]; + const a12 = this.values[5]; + const a20 = this.values[6]; + const a21 = this.values[7]; + const a22 = this.values[8]; + const det01 = a22 * a11 - a12 * a21; + const det11 = -a22 * a10 + a12 * a20; + const det21 = a21 * a10 - a11 * a20; + return a00 * det01 + a01 * det11 + a02 * det21; + } + setIdentity() { + this.values[0] = 1; + this.values[1] = 0; + this.values[2] = 0; + this.values[3] = 0; + this.values[4] = 1; + this.values[5] = 0; + this.values[6] = 0; + this.values[7] = 0; + this.values[8] = 1; + return this; + } + transpose() { + const temp01 = this.values[1]; + const temp02 = this.values[2]; + const temp12 = this.values[5]; + this.values[1] = this.values[3]; + this.values[2] = this.values[6]; + this.values[3] = temp01; + this.values[5] = this.values[7]; + this.values[6] = temp02; + this.values[7] = temp12; + return this; + } + inverse() { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a10 = this.values[3]; + const a11 = this.values[4]; + const a12 = this.values[5]; + const a20 = this.values[6]; + const a21 = this.values[7]; + const a22 = this.values[8]; + const det01 = a22 * a11 - a12 * a21; + const det11 = -a22 * a10 + a12 * a20; + const det21 = a21 * a10 - a11 * a20; + let det = a00 * det01 + a01 * det11 + a02 * det21; + if (!det) { + return null; + } + det = 1.0 / det; + this.values[0] = det01 * det; + this.values[1] = (-a22 * a01 + a02 * a21) * det; + this.values[2] = (a12 * a01 - a02 * a11) * det; + this.values[3] = det11 * det; + this.values[4] = (a22 * a00 - a02 * a20) * det; + this.values[5] = (-a12 * a00 + a02 * a10) * det; + this.values[6] = det21 * det; + this.values[7] = (-a21 * a00 + a01 * a20) * det; + this.values[8] = (a11 * a00 - a01 * a10) * det; + return this; + } + multiply(matrix) { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a10 = this.values[3]; + const a11 = this.values[4]; + const a12 = this.values[5]; + const a20 = this.values[6]; + const a21 = this.values[7]; + const a22 = this.values[8]; + const b00 = matrix.at(0); + const b01 = matrix.at(1); + const b02 = matrix.at(2); + const b10 = matrix.at(3); + const b11 = matrix.at(4); + const b12 = matrix.at(5); + const b20 = matrix.at(6); + const b21 = matrix.at(7); + const b22 = matrix.at(8); + this.values[0] = b00 * a00 + b01 * a10 + b02 * a20; + this.values[1] = b00 * a01 + b01 * a11 + b02 * a21; + this.values[2] = b00 * a02 + b01 * a12 + b02 * a22; + this.values[3] = b10 * a00 + b11 * a10 + b12 * a20; + this.values[4] = b10 * a01 + b11 * a11 + b12 * a21; + this.values[5] = b10 * a02 + b11 * a12 + b12 * a22; + this.values[6] = b20 * a00 + b21 * a10 + b22 * a20; + this.values[7] = b20 * a01 + b21 * a11 + b22 * a21; + this.values[8] = b20 * a02 + b21 * a12 + b22 * a22; + return this; + } + multiplyVec2(vector, result) { + const x = vector.x; + const y = vector.y; + if (result) { + result.xy = [ + x * this.values[0] + y * this.values[3] + this.values[6], + x * this.values[1] + y * this.values[4] + this.values[7] + ]; + return result; + } + else { + return new vec2([ + x * this.values[0] + y * this.values[3] + this.values[6], + x * this.values[1] + y * this.values[4] + this.values[7] + ]); + } + } + multiplyVec3(vector, result) { + const x = vector.x; + const y = vector.y; + const z = vector.z; + if (result) { + result.xyz = [ + x * this.values[0] + y * this.values[3] + z * this.values[6], + x * this.values[1] + y * this.values[4] + z * this.values[7], + x * this.values[2] + y * this.values[5] + z * this.values[8] + ]; + return result; + } + else { + return new vec3([ + x * this.values[0] + y * this.values[3] + z * this.values[6], + x * this.values[1] + y * this.values[4] + z * this.values[7], + x * this.values[2] + y * this.values[5] + z * this.values[8] + ]); + } + } + toMat4(result) { + if (result) { + result.init([ + this.values[0], + this.values[1], + this.values[2], + 0, + this.values[3], + this.values[4], + this.values[5], + 0, + this.values[6], + this.values[7], + this.values[8], + 0, + 0, + 0, + 0, + 1 + ]); + return result; + } + else { + return new mat4([ + this.values[0], + this.values[1], + this.values[2], + 0, + this.values[3], + this.values[4], + this.values[5], + 0, + this.values[6], + this.values[7], + this.values[8], + 0, + 0, + 0, + 0, + 1 + ]); + } + } + toQuat() { + const m00 = this.values[0]; + const m01 = this.values[1]; + const m02 = this.values[2]; + const m10 = this.values[3]; + const m11 = this.values[4]; + const m12 = this.values[5]; + const m20 = this.values[6]; + const m21 = this.values[7]; + const m22 = this.values[8]; + const fourXSquaredMinus1 = m00 - m11 - m22; + const fourYSquaredMinus1 = m11 - m00 - m22; + const fourZSquaredMinus1 = m22 - m00 - m11; + const fourWSquaredMinus1 = m00 + m11 + m22; + let biggestIndex = 0; + let fourBiggestSquaredMinus1 = fourWSquaredMinus1; + if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) { + fourBiggestSquaredMinus1 = fourXSquaredMinus1; + biggestIndex = 1; + } + if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) { + fourBiggestSquaredMinus1 = fourYSquaredMinus1; + biggestIndex = 2; + } + if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) { + fourBiggestSquaredMinus1 = fourZSquaredMinus1; + biggestIndex = 3; + } + const biggestVal = Math.sqrt(fourBiggestSquaredMinus1 + 1) * 0.5; + const mult = 0.25 / biggestVal; + const result = new quat(); + switch (biggestIndex) { + case 0: + result.w = biggestVal; + result.x = (m12 - m21) * mult; + result.y = (m20 - m02) * mult; + result.z = (m01 - m10) * mult; + break; + case 1: + result.w = (m12 - m21) * mult; + result.x = biggestVal; + result.y = (m01 + m10) * mult; + result.z = (m20 + m02) * mult; + break; + case 2: + result.w = (m20 - m02) * mult; + result.x = (m01 + m10) * mult; + result.y = biggestVal; + result.z = (m12 + m21) * mult; + break; + case 3: + result.w = (m01 - m10) * mult; + result.x = (m20 + m02) * mult; + result.y = (m12 + m21) * mult; + result.z = biggestVal; + break; + } + return result; + } + rotate(angle, axis) { + let x = axis.x; + let y = axis.y; + let z = axis.z; + let length = Math.sqrt(x * x + y * y + z * z); + if (!length) { + return null; + } + if (length !== 1) { + length = 1 / length; + x *= length; + y *= length; + z *= length; + } + const s = Math.sin(angle); + const c = Math.cos(angle); + const t = 1.0 - c; + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const b00 = x * x * t + c; + const b01 = y * x * t + z * s; + const b02 = z * x * t - y * s; + const b10 = x * y * t - z * s; + const b11 = y * y * t + c; + const b12 = z * y * t + x * s; + const b20 = x * z * t + y * s; + const b21 = y * z * t - x * s; + const b22 = z * z * t + c; + this.values[0] = a00 * b00 + a10 * b01 + a20 * b02; + this.values[1] = a01 * b00 + a11 * b01 + a21 * b02; + this.values[2] = a02 * b00 + a12 * b01 + a22 * b02; + this.values[3] = a00 * b10 + a10 * b11 + a20 * b12; + this.values[4] = a01 * b10 + a11 * b11 + a21 * b12; + this.values[5] = a02 * b10 + a12 * b11 + a22 * b12; + this.values[6] = a00 * b20 + a10 * b21 + a20 * b22; + this.values[7] = a01 * b20 + a11 * b21 + a21 * b22; + this.values[8] = a02 * b20 + a12 * b21 + a22 * b22; + return this; + } + static product(m1, m2, result) { + const a00 = m1.at(0); + const a01 = m1.at(1); + const a02 = m1.at(2); + const a10 = m1.at(3); + const a11 = m1.at(4); + const a12 = m1.at(5); + const a20 = m1.at(6); + const a21 = m1.at(7); + const a22 = m1.at(8); + const b00 = m2.at(0); + const b01 = m2.at(1); + const b02 = m2.at(2); + const b10 = m2.at(3); + const b11 = m2.at(4); + const b12 = m2.at(5); + const b20 = m2.at(6); + const b21 = m2.at(7); + const b22 = m2.at(8); + if (result) { + result.init([ + b00 * a00 + b01 * a10 + b02 * a20, + b00 * a01 + b01 * a11 + b02 * a21, + b00 * a02 + b01 * a12 + b02 * a22, + b10 * a00 + b11 * a10 + b12 * a20, + b10 * a01 + b11 * a11 + b12 * a21, + b10 * a02 + b11 * a12 + b12 * a22, + b20 * a00 + b21 * a10 + b22 * a20, + b20 * a01 + b21 * a11 + b22 * a21, + b20 * a02 + b21 * a12 + b22 * a22 + ]); + return result; + } + else { + return new mat3([ + b00 * a00 + b01 * a10 + b02 * a20, + b00 * a01 + b01 * a11 + b02 * a21, + b00 * a02 + b01 * a12 + b02 * a22, + b10 * a00 + b11 * a10 + b12 * a20, + b10 * a01 + b11 * a11 + b12 * a21, + b10 * a02 + b11 * a12 + b12 * a22, + b20 * a00 + b21 * a10 + b22 * a20, + b20 * a01 + b21 * a11 + b22 * a21, + b20 * a02 + b21 * a12 + b22 * a22 + ]); + } + } +} +mat3.identity = new mat3().setIdentity(); diff --git a/src/framework/resonator/vendor/tsm/mat4.d.ts b/src/framework/resonator/vendor/tsm/mat4.d.ts new file mode 100644 index 0000000..799d9a3 --- /dev/null +++ b/src/framework/resonator/vendor/tsm/mat4.d.ts @@ -0,0 +1,33 @@ +import mat3 from './mat3'; +import vec3 from './vec3'; +import vec4 from './vec4'; +export default class mat4 { + constructor(values?: number[]); + private values; + static readonly identity: mat4; + at(index: number): number; + init(values: number[]): mat4; + reset(): void; + copy(dest?: mat4): mat4; + all(): number[]; + row(index: number): number[]; + col(index: number): number[]; + equals(matrix: mat4, threshold?: number): boolean; + determinant(): number; + setIdentity(): mat4; + transpose(): mat4; + inverse(): mat4; + multiply(matrix: mat4): mat4; + multiplyVec3(vector: vec3): vec3; + multiplyVec4(vector: vec4, dest?: vec4): vec4; + toMat3(): mat3; + toInverseMat3(): mat3; + translate(vector: vec3): mat4; + scale(vector: vec3): mat4; + rotate(angle: number, axis: vec3): mat4; + static frustum(left: number, right: number, bottom: number, top: number, near: number, far: number): mat4; + static perspective(fov: number, aspect: number, near: number, far: number): mat4; + static orthographic(left: number, right: number, bottom: number, top: number, near: number, far: number): mat4; + static lookAt(position: vec3, target: vec3, up?: vec3): mat4; + static product(m1: mat4, m2: mat4, result: mat4): mat4; +} diff --git a/src/framework/resonator/vendor/tsm/mat4.js b/src/framework/resonator/vendor/tsm/mat4.js new file mode 100644 index 0000000..1447f4e --- /dev/null +++ b/src/framework/resonator/vendor/tsm/mat4.js @@ -0,0 +1,579 @@ +import mat3 from './mat3'; +import vec3 from './vec3'; +import vec4 from './vec4'; +import { epsilon } from './constants'; +export default class mat4 { + constructor(values) { + this.values = new Float32Array(16); + if (values !== undefined) { + this.init(values); + } + } + at(index) { + return this.values[index]; + } + init(values) { + for (let i = 0; i < 16; i++) { + this.values[i] = values[i]; + } + return this; + } + reset() { + for (let i = 0; i < 16; i++) { + this.values[i] = 0; + } + } + copy(dest) { + if (!dest) { + dest = new mat4(); + } + for (let i = 0; i < 16; i++) { + dest.values[i] = this.values[i]; + } + return dest; + } + all() { + const data = []; + for (let i = 0; i < 16; i++) { + data[i] = this.values[i]; + } + return data; + } + row(index) { + return [ + this.values[index * 4 + 0], + this.values[index * 4 + 1], + this.values[index * 4 + 2], + this.values[index * 4 + 3] + ]; + } + col(index) { + return [ + this.values[index], + this.values[index + 4], + this.values[index + 8], + this.values[index + 12] + ]; + } + equals(matrix, threshold = epsilon) { + for (let i = 0; i < 16; i++) { + if (Math.abs(this.values[i] - matrix.at(i)) > threshold) { + return false; + } + } + return true; + } + determinant() { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a03 = this.values[3]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a13 = this.values[7]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const a23 = this.values[11]; + const a30 = this.values[12]; + const a31 = this.values[13]; + const a32 = this.values[14]; + const a33 = this.values[15]; + const det00 = a00 * a11 - a01 * a10; + const det01 = a00 * a12 - a02 * a10; + const det02 = a00 * a13 - a03 * a10; + const det03 = a01 * a12 - a02 * a11; + const det04 = a01 * a13 - a03 * a11; + const det05 = a02 * a13 - a03 * a12; + const det06 = a20 * a31 - a21 * a30; + const det07 = a20 * a32 - a22 * a30; + const det08 = a20 * a33 - a23 * a30; + const det09 = a21 * a32 - a22 * a31; + const det10 = a21 * a33 - a23 * a31; + const det11 = a22 * a33 - a23 * a32; + return (det00 * det11 - + det01 * det10 + + det02 * det09 + + det03 * det08 - + det04 * det07 + + det05 * det06); + } + setIdentity() { + this.values[0] = 1; + this.values[1] = 0; + this.values[2] = 0; + this.values[3] = 0; + this.values[4] = 0; + this.values[5] = 1; + this.values[6] = 0; + this.values[7] = 0; + this.values[8] = 0; + this.values[9] = 0; + this.values[10] = 1; + this.values[11] = 0; + this.values[12] = 0; + this.values[13] = 0; + this.values[14] = 0; + this.values[15] = 1; + return this; + } + transpose() { + const temp01 = this.values[1]; + const temp02 = this.values[2]; + const temp03 = this.values[3]; + const temp12 = this.values[6]; + const temp13 = this.values[7]; + const temp23 = this.values[11]; + this.values[1] = this.values[4]; + this.values[2] = this.values[8]; + this.values[3] = this.values[12]; + this.values[4] = temp01; + this.values[6] = this.values[9]; + this.values[7] = this.values[13]; + this.values[8] = temp02; + this.values[9] = temp12; + this.values[11] = this.values[14]; + this.values[12] = temp03; + this.values[13] = temp13; + this.values[14] = temp23; + return this; + } + inverse() { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a03 = this.values[3]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a13 = this.values[7]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const a23 = this.values[11]; + const a30 = this.values[12]; + const a31 = this.values[13]; + const a32 = this.values[14]; + const a33 = this.values[15]; + const det00 = a00 * a11 - a01 * a10; + const det01 = a00 * a12 - a02 * a10; + const det02 = a00 * a13 - a03 * a10; + const det03 = a01 * a12 - a02 * a11; + const det04 = a01 * a13 - a03 * a11; + const det05 = a02 * a13 - a03 * a12; + const det06 = a20 * a31 - a21 * a30; + const det07 = a20 * a32 - a22 * a30; + const det08 = a20 * a33 - a23 * a30; + const det09 = a21 * a32 - a22 * a31; + const det10 = a21 * a33 - a23 * a31; + const det11 = a22 * a33 - a23 * a32; + let det = det00 * det11 - + det01 * det10 + + det02 * det09 + + det03 * det08 - + det04 * det07 + + det05 * det06; + if (!det) { + return null; + } + det = 1.0 / det; + this.values[0] = (a11 * det11 - a12 * det10 + a13 * det09) * det; + this.values[1] = (-a01 * det11 + a02 * det10 - a03 * det09) * det; + this.values[2] = (a31 * det05 - a32 * det04 + a33 * det03) * det; + this.values[3] = (-a21 * det05 + a22 * det04 - a23 * det03) * det; + this.values[4] = (-a10 * det11 + a12 * det08 - a13 * det07) * det; + this.values[5] = (a00 * det11 - a02 * det08 + a03 * det07) * det; + this.values[6] = (-a30 * det05 + a32 * det02 - a33 * det01) * det; + this.values[7] = (a20 * det05 - a22 * det02 + a23 * det01) * det; + this.values[8] = (a10 * det10 - a11 * det08 + a13 * det06) * det; + this.values[9] = (-a00 * det10 + a01 * det08 - a03 * det06) * det; + this.values[10] = (a30 * det04 - a31 * det02 + a33 * det00) * det; + this.values[11] = (-a20 * det04 + a21 * det02 - a23 * det00) * det; + this.values[12] = (-a10 * det09 + a11 * det07 - a12 * det06) * det; + this.values[13] = (a00 * det09 - a01 * det07 + a02 * det06) * det; + this.values[14] = (-a30 * det03 + a31 * det01 - a32 * det00) * det; + this.values[15] = (a20 * det03 - a21 * det01 + a22 * det00) * det; + return this; + } + multiply(matrix) { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a03 = this.values[3]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a13 = this.values[7]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const a23 = this.values[11]; + const a30 = this.values[12]; + const a31 = this.values[13]; + const a32 = this.values[14]; + const a33 = this.values[15]; + let b0 = matrix.at(0); + let b1 = matrix.at(1); + let b2 = matrix.at(2); + let b3 = matrix.at(3); + this.values[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + this.values[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + this.values[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + this.values[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = matrix.at(4); + b1 = matrix.at(5); + b2 = matrix.at(6); + b3 = matrix.at(7); + this.values[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + this.values[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + this.values[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + this.values[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = matrix.at(8); + b1 = matrix.at(9); + b2 = matrix.at(10); + b3 = matrix.at(11); + this.values[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + this.values[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + this.values[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + this.values[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = matrix.at(12); + b1 = matrix.at(13); + b2 = matrix.at(14); + b3 = matrix.at(15); + this.values[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + this.values[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + this.values[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + this.values[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + return this; + } + multiplyVec3(vector) { + const x = vector.x; + const y = vector.y; + const z = vector.z; + return new vec3([ + this.values[0] * x + + this.values[4] * y + + this.values[8] * z + + this.values[12], + this.values[1] * x + + this.values[5] * y + + this.values[9] * z + + this.values[13], + this.values[2] * x + + this.values[6] * y + + this.values[10] * z + + this.values[14] + ]); + } + multiplyVec4(vector, dest) { + if (!dest) { + dest = new vec4(); + } + const x = vector.x; + const y = vector.y; + const z = vector.z; + const w = vector.w; + dest.x = + this.values[0] * x + + this.values[4] * y + + this.values[8] * z + + this.values[12] * w; + dest.y = + this.values[1] * x + + this.values[5] * y + + this.values[9] * z + + this.values[13] * w; + dest.z = + this.values[2] * x + + this.values[6] * y + + this.values[10] * z + + this.values[14] * w; + dest.w = + this.values[3] * x + + this.values[7] * y + + this.values[11] * z + + this.values[15] * w; + return dest; + } + toMat3() { + return new mat3([ + this.values[0], + this.values[1], + this.values[2], + this.values[4], + this.values[5], + this.values[6], + this.values[8], + this.values[9], + this.values[10] + ]); + } + toInverseMat3() { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const det01 = a22 * a11 - a12 * a21; + const det11 = -a22 * a10 + a12 * a20; + const det21 = a21 * a10 - a11 * a20; + let det = a00 * det01 + a01 * det11 + a02 * det21; + if (!det) { + return null; + } + det = 1.0 / det; + return new mat3([ + det01 * det, + (-a22 * a01 + a02 * a21) * det, + (a12 * a01 - a02 * a11) * det, + det11 * det, + (a22 * a00 - a02 * a20) * det, + (-a12 * a00 + a02 * a10) * det, + det21 * det, + (-a21 * a00 + a01 * a20) * det, + (a11 * a00 - a01 * a10) * det + ]); + } + translate(vector) { + const x = vector.x; + const y = vector.y; + const z = vector.z; + this.values[12] += + this.values[0] * x + this.values[4] * y + this.values[8] * z; + this.values[13] += + this.values[1] * x + this.values[5] * y + this.values[9] * z; + this.values[14] += + this.values[2] * x + this.values[6] * y + this.values[10] * z; + this.values[15] += + this.values[3] * x + this.values[7] * y + this.values[11] * z; + return this; + } + scale(vector) { + const x = vector.x; + const y = vector.y; + const z = vector.z; + this.values[0] *= x; + this.values[1] *= x; + this.values[2] *= x; + this.values[3] *= x; + this.values[4] *= y; + this.values[5] *= y; + this.values[6] *= y; + this.values[7] *= y; + this.values[8] *= z; + this.values[9] *= z; + this.values[10] *= z; + this.values[11] *= z; + return this; + } + rotate(angle, axis) { + let x = axis.x; + let y = axis.y; + let z = axis.z; + let length = Math.sqrt(x * x + y * y + z * z); + if (!length) { + return null; + } + if (length !== 1) { + length = 1 / length; + x *= length; + y *= length; + z *= length; + } + const s = Math.sin(angle); + const c = Math.cos(angle); + const t = 1.0 - c; + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a03 = this.values[3]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a13 = this.values[7]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const a23 = this.values[11]; + const b00 = x * x * t + c; + const b01 = y * x * t + z * s; + const b02 = z * x * t - y * s; + const b10 = x * y * t - z * s; + const b11 = y * y * t + c; + const b12 = z * y * t + x * s; + const b20 = x * z * t + y * s; + const b21 = y * z * t - x * s; + const b22 = z * z * t + c; + this.values[0] = a00 * b00 + a10 * b01 + a20 * b02; + this.values[1] = a01 * b00 + a11 * b01 + a21 * b02; + this.values[2] = a02 * b00 + a12 * b01 + a22 * b02; + this.values[3] = a03 * b00 + a13 * b01 + a23 * b02; + this.values[4] = a00 * b10 + a10 * b11 + a20 * b12; + this.values[5] = a01 * b10 + a11 * b11 + a21 * b12; + this.values[6] = a02 * b10 + a12 * b11 + a22 * b12; + this.values[7] = a03 * b10 + a13 * b11 + a23 * b12; + this.values[8] = a00 * b20 + a10 * b21 + a20 * b22; + this.values[9] = a01 * b20 + a11 * b21 + a21 * b22; + this.values[10] = a02 * b20 + a12 * b21 + a22 * b22; + this.values[11] = a03 * b20 + a13 * b21 + a23 * b22; + return this; + } + static frustum(left, right, bottom, top, near, far) { + const rl = right - left; + const tb = top - bottom; + const fn = far - near; + return new mat4([ + (near * 2) / rl, + 0, + 0, + 0, + 0, + (near * 2) / tb, + 0, + 0, + (right + left) / rl, + (top + bottom) / tb, + -(far + near) / fn, + -1, + 0, + 0, + -(far * near * 2) / fn, + 0 + ]); + } + static perspective(fov, aspect, near, far) { + const top = near * Math.tan((fov * Math.PI) / 360.0); + const right = top * aspect; + return mat4.frustum(-right, right, -top, top, near, far); + } + static orthographic(left, right, bottom, top, near, far) { + const rl = right - left; + const tb = top - bottom; + const fn = far - near; + return new mat4([ + 2 / rl, + 0, + 0, + 0, + 0, + 2 / tb, + 0, + 0, + 0, + 0, + -2 / fn, + 0, + -(left + right) / rl, + -(top + bottom) / tb, + -(far + near) / fn, + 1 + ]); + } + static lookAt(position, target, up = vec3.up) { + if (position.equals(target)) { + return this.identity; + } + const z = vec3.difference(position, target).normalize(); + const x = vec3.cross(up, z).normalize(); + const y = vec3.cross(z, x).normalize(); + return new mat4([ + x.x, + y.x, + z.x, + 0, + x.y, + y.y, + z.y, + 0, + x.z, + y.z, + z.z, + 0, + -vec3.dot(x, position), + -vec3.dot(y, position), + -vec3.dot(z, position), + 1 + ]); + } + static product(m1, m2, result) { + const a00 = m1.at(0); + const a01 = m1.at(1); + const a02 = m1.at(2); + const a03 = m1.at(3); + const a10 = m1.at(4); + const a11 = m1.at(5); + const a12 = m1.at(6); + const a13 = m1.at(7); + const a20 = m1.at(8); + const a21 = m1.at(9); + const a22 = m1.at(10); + const a23 = m1.at(11); + const a30 = m1.at(12); + const a31 = m1.at(13); + const a32 = m1.at(14); + const a33 = m1.at(15); + const b00 = m2.at(0); + const b01 = m2.at(1); + const b02 = m2.at(2); + const b03 = m2.at(3); + const b10 = m2.at(4); + const b11 = m2.at(5); + const b12 = m2.at(6); + const b13 = m2.at(7); + const b20 = m2.at(8); + const b21 = m2.at(9); + const b22 = m2.at(10); + const b23 = m2.at(11); + const b30 = m2.at(12); + const b31 = m2.at(13); + const b32 = m2.at(14); + const b33 = m2.at(15); + if (result) { + result.init([ + b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30, + b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31, + b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32, + b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33, + b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30, + b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31, + b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32, + b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33, + b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30, + b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31, + b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32, + b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33, + b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30, + b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31, + b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32, + b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33 + ]); + return result; + } + else { + return new mat4([ + b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30, + b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31, + b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32, + b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33, + b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30, + b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31, + b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32, + b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33, + b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30, + b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31, + b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32, + b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33, + b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30, + b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31, + b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32, + b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33 + ]); + } + } +} +mat4.identity = new mat4().setIdentity(); diff --git a/src/framework/resonator/vendor/tsm/quat.d.ts b/src/framework/resonator/vendor/tsm/quat.d.ts new file mode 100644 index 0000000..1195bfd --- /dev/null +++ b/src/framework/resonator/vendor/tsm/quat.d.ts @@ -0,0 +1,47 @@ +import mat3 from './mat3'; +import mat4 from './mat4'; +import vec3 from './vec3'; +export default class quat { + get x(): number; + get y(): number; + get z(): number; + get w(): number; + get xy(): [number, number]; + get xyz(): [number, number, number]; + get xyzw(): [number, number, number, number]; + set x(value: number); + set y(value: number); + set z(value: number); + set w(value: number); + set xy(values: [number, number]); + set xyz(values: [number, number, number]); + set xyzw(values: [number, number, number, number]); + constructor(values?: [number, number, number, number]); + private values; + static readonly identity: quat; + at(index: number): number; + reset(): void; + copy(dest?: quat): quat; + roll(): number; + pitch(): number; + yaw(): number; + equals(vector: quat, threshold?: number): boolean; + setIdentity(): quat; + calculateW(): quat; + inverse(): quat; + conjugate(): quat; + length(): number; + normalize(dest?: quat): quat; + add(other: quat): quat; + multiply(other: quat): quat; + multiplyVec3(vector: vec3, dest?: vec3): vec3; + toMat3(dest?: mat3): mat3; + toMat4(dest?: mat4): mat4; + static dot(q1: quat, q2: quat): number; + static sum(q1: quat, q2: quat, dest?: quat): quat; + static product(q1: quat, q2: quat, dest?: quat): quat; + static cross(q1: quat, q2: quat, dest?: quat): quat; + static shortMix(q1: quat, q2: quat, time: number, dest?: quat): quat; + static mix(q1: quat, q2: quat, time: number, dest?: quat): quat; + static fromAxisAngle(axis: vec3, angle: number, dest?: quat): quat; +} diff --git a/src/framework/resonator/vendor/tsm/quat.js b/src/framework/resonator/vendor/tsm/quat.js new file mode 100644 index 0000000..54c43ba --- /dev/null +++ b/src/framework/resonator/vendor/tsm/quat.js @@ -0,0 +1,404 @@ +import mat3 from './mat3'; +import mat4 from './mat4'; +import vec3 from './vec3'; +import { epsilon } from './constants'; +export default class quat { + constructor(values) { + this.values = new Float32Array(4); + if (values !== undefined) { + this.xyzw = values; + } + } + get x() { + return this.values[0]; + } + get y() { + return this.values[1]; + } + get z() { + return this.values[2]; + } + get w() { + return this.values[3]; + } + get xy() { + return [this.values[0], this.values[1]]; + } + get xyz() { + return [this.values[0], this.values[1], this.values[2]]; + } + get xyzw() { + return [this.values[0], this.values[1], this.values[2], this.values[3]]; + } + set x(value) { + this.values[0] = value; + } + set y(value) { + this.values[1] = value; + } + set z(value) { + this.values[2] = value; + } + set w(value) { + this.values[3] = value; + } + set xy(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + set xyz(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + } + set xyzw(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + this.values[3] = values[3]; + } + at(index) { + return this.values[index]; + } + reset() { + for (let i = 0; i < 4; i++) { + this.values[i] = 0; + } + } + copy(dest) { + if (!dest) { + dest = new quat(); + } + for (let i = 0; i < 4; i++) { + dest.values[i] = this.values[i]; + } + return dest; + } + roll() { + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + return Math.atan2(2.0 * (x * y + w * z), w * w + x * x - y * y - z * z); + } + pitch() { + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + return Math.atan2(2.0 * (y * z + w * x), w * w - x * x - y * y + z * z); + } + yaw() { + return Math.asin(2.0 * (this.x * this.z - this.w * this.y)); + } + equals(vector, threshold = epsilon) { + for (let i = 0; i < 4; i++) { + if (Math.abs(this.values[i] - vector.at(i)) > threshold) { + return false; + } + } + return true; + } + setIdentity() { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 1; + return this; + } + calculateW() { + const x = this.x; + const y = this.y; + const z = this.z; + this.w = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return this; + } + inverse() { + const dot = quat.dot(this, this); + if (!dot) { + this.xyzw = [0, 0, 0, 0]; + return this; + } + const invDot = dot ? 1.0 / dot : 0; + this.x *= -invDot; + this.y *= -invDot; + this.z *= -invDot; + this.w *= invDot; + return this; + } + conjugate() { + this.values[0] *= -1; + this.values[1] *= -1; + this.values[2] *= -1; + return this; + } + length() { + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + return Math.sqrt(x * x + y * y + z * z + w * w); + } + normalize(dest) { + if (!dest) { + dest = this; + } + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + let length = Math.sqrt(x * x + y * y + z * z + w * w); + if (!length) { + dest.x = 0; + dest.y = 0; + dest.z = 0; + dest.w = 0; + return dest; + } + length = 1 / length; + dest.x = x * length; + dest.y = y * length; + dest.z = z * length; + dest.w = w * length; + return dest; + } + add(other) { + for (let i = 0; i < 4; i++) { + this.values[i] += other.at(i); + } + return this; + } + multiply(other) { + const q1x = this.values[0]; + const q1y = this.values[1]; + const q1z = this.values[2]; + const q1w = this.values[3]; + const q2x = other.x; + const q2y = other.y; + const q2z = other.z; + const q2w = other.w; + this.x = q1x * q2w + q1w * q2x + q1y * q2z - q1z * q2y; + this.y = q1y * q2w + q1w * q2y + q1z * q2x - q1x * q2z; + this.z = q1z * q2w + q1w * q2z + q1x * q2y - q1y * q2x; + this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z; + return this; + } + multiplyVec3(vector, dest) { + if (!dest) { + dest = new vec3(); + } + const x = vector.x; + const y = vector.y; + const z = vector.z; + const qx = this.x; + const qy = this.y; + const qz = this.z; + const qw = this.w; + const ix = qw * x + qy * z - qz * y; + const iy = qw * y + qz * x - qx * z; + const iz = qw * z + qx * y - qy * x; + const iw = -qx * x - qy * y - qz * z; + dest.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; + dest.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; + dest.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return dest; + } + toMat3(dest) { + if (!dest) { + dest = new mat3(); + } + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + const x2 = x + x; + const y2 = y + y; + const z2 = z + z; + const xx = x * x2; + const xy = x * y2; + const xz = x * z2; + const yy = y * y2; + const yz = y * z2; + const zz = z * z2; + const wx = w * x2; + const wy = w * y2; + const wz = w * z2; + dest.init([ + 1 - (yy + zz), + xy + wz, + xz - wy, + xy - wz, + 1 - (xx + zz), + yz + wx, + xz + wy, + yz - wx, + 1 - (xx + yy) + ]); + return dest; + } + toMat4(dest) { + if (!dest) { + dest = new mat4(); + } + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + const x2 = x + x; + const y2 = y + y; + const z2 = z + z; + const xx = x * x2; + const xy = x * y2; + const xz = x * z2; + const yy = y * y2; + const yz = y * z2; + const zz = z * z2; + const wx = w * x2; + const wy = w * y2; + const wz = w * z2; + dest.init([ + 1 - (yy + zz), + xy + wz, + xz - wy, + 0, + xy - wz, + 1 - (xx + zz), + yz + wx, + 0, + xz + wy, + yz - wx, + 1 - (xx + yy), + 0, + 0, + 0, + 0, + 1 + ]); + return dest; + } + static dot(q1, q2) { + return q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; + } + static sum(q1, q2, dest) { + if (!dest) { + dest = new quat(); + } + dest.x = q1.x + q2.x; + dest.y = q1.y + q2.y; + dest.z = q1.z + q2.z; + dest.w = q1.w + q2.w; + return dest; + } + static product(q1, q2, dest) { + if (!dest) { + dest = new quat(); + } + const q1x = q1.x; + const q1y = q1.y; + const q1z = q1.z; + const q1w = q1.w; + const q2x = q2.x; + const q2y = q2.y; + const q2z = q2.z; + const q2w = q2.w; + dest.x = q1x * q2w + q1w * q2x + q1y * q2z - q1z * q2y; + dest.y = q1y * q2w + q1w * q2y + q1z * q2x - q1x * q2z; + dest.z = q1z * q2w + q1w * q2z + q1x * q2y - q1y * q2x; + dest.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z; + return dest; + } + static cross(q1, q2, dest) { + if (!dest) { + dest = new quat(); + } + const q1x = q1.x; + const q1y = q1.y; + const q1z = q1.z; + const q1w = q1.w; + const q2x = q2.x; + const q2y = q2.y; + const q2z = q2.z; + const q2w = q2.w; + dest.x = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x; + dest.y = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z; + dest.z = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y; + dest.w = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z; + return dest; + } + static shortMix(q1, q2, time, dest) { + if (!dest) { + dest = new quat(); + } + if (time <= 0.0) { + dest.xyzw = q1.xyzw; + return dest; + } + else if (time >= 1.0) { + dest.xyzw = q2.xyzw; + return dest; + } + let cos = quat.dot(q1, q2); + const q2a = q2.copy(); + if (cos < 0.0) { + q2a.inverse(); + cos = -cos; + } + let k0; + let k1; + if (cos > 0.9999) { + k0 = 1 - time; + k1 = 0 + time; + } + else { + const sin = Math.sqrt(1 - cos * cos); + const angle = Math.atan2(sin, cos); + const oneOverSin = 1 / sin; + k0 = Math.sin((1 - time) * angle) * oneOverSin; + k1 = Math.sin((0 + time) * angle) * oneOverSin; + } + dest.x = k0 * q1.x + k1 * q2a.x; + dest.y = k0 * q1.y + k1 * q2a.y; + dest.z = k0 * q1.z + k1 * q2a.z; + dest.w = k0 * q1.w + k1 * q2a.w; + return dest; + } + static mix(q1, q2, time, dest) { + if (!dest) { + dest = new quat(); + } + const cosHalfTheta = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; + if (Math.abs(cosHalfTheta) >= 1.0) { + dest.xyzw = q1.xyzw; + return dest; + } + const halfTheta = Math.acos(cosHalfTheta); + const sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); + if (Math.abs(sinHalfTheta) < 0.001) { + dest.x = q1.x * 0.5 + q2.x * 0.5; + dest.y = q1.y * 0.5 + q2.y * 0.5; + dest.z = q1.z * 0.5 + q2.z * 0.5; + dest.w = q1.w * 0.5 + q2.w * 0.5; + return dest; + } + const ratioA = Math.sin((1 - time) * halfTheta) / sinHalfTheta; + const ratioB = Math.sin(time * halfTheta) / sinHalfTheta; + dest.x = q1.x * ratioA + q2.x * ratioB; + dest.y = q1.y * ratioA + q2.y * ratioB; + dest.z = q1.z * ratioA + q2.z * ratioB; + dest.w = q1.w * ratioA + q2.w * ratioB; + return dest; + } + static fromAxisAngle(axis, angle, dest) { + if (!dest) { + dest = new quat(); + } + angle *= 0.5; + const sin = Math.sin(angle); + dest.x = axis.x * sin; + dest.y = axis.y * sin; + dest.z = axis.z * sin; + dest.w = Math.cos(angle); + return dest; + } +} +quat.identity = new quat().setIdentity(); diff --git a/src/framework/resonator/vendor/tsm/tsm.d.ts b/src/framework/resonator/vendor/tsm/tsm.d.ts new file mode 100644 index 0000000..4fcfb6d --- /dev/null +++ b/src/framework/resonator/vendor/tsm/tsm.d.ts @@ -0,0 +1,17 @@ +import mat2 from './mat2'; +import mat3 from './mat3'; +import mat4 from './mat4'; +import quat from './quat'; +import vec2 from './vec2'; +import vec3 from './vec3'; +import vec4 from './vec4'; +declare const _default: { + vec2: typeof vec2; + vec3: typeof vec3; + vec4: typeof vec4; + mat2: typeof mat2; + mat3: typeof mat3; + mat4: typeof mat4; + quat: typeof quat; +}; +export default _default; diff --git a/src/framework/resonator/vendor/tsm/tsm.js b/src/framework/resonator/vendor/tsm/tsm.js new file mode 100644 index 0000000..def6504 --- /dev/null +++ b/src/framework/resonator/vendor/tsm/tsm.js @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012, 2018 Matthias Ferch + * + * Project homepage: https://github.com/matthiasferch/tsm + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ +import mat2 from './mat2'; +import mat3 from './mat3'; +import mat4 from './mat4'; +import quat from './quat'; +import vec2 from './vec2'; +import vec3 from './vec3'; +import vec4 from './vec4'; +export default { + vec2, + vec3, + vec4, + mat2, + mat3, + mat4, + quat +}; diff --git a/src/framework/resonator/vendor/tsm/vec2.d.ts b/src/framework/resonator/vendor/tsm/vec2.d.ts new file mode 100644 index 0000000..34a2e50 --- /dev/null +++ b/src/framework/resonator/vendor/tsm/vec2.d.ts @@ -0,0 +1,40 @@ +import mat2 from './mat2'; +import mat3 from './mat3'; +import vec3 from './vec3'; +export default class vec2 { + get x(): number; + get y(): number; + get xy(): [number, number]; + set x(value: number); + set y(value: number); + set xy(values: [number, number]); + constructor(values?: [number, number]); + private values; + static readonly zero: vec2; + static readonly one: vec2; + at(index: number): number; + reset(): void; + copy(dest?: vec2): vec2; + negate(dest?: vec2): vec2; + equals(vector: vec2, threshold?: number): boolean; + length(): number; + squaredLength(): number; + add(vector: vec2): vec2; + subtract(vector: vec2): vec2; + multiply(vector: vec2): vec2; + divide(vector: vec2): vec2; + scale(value: number, dest?: vec2): vec2; + normalize(dest?: vec2): vec2; + multiplyMat2(matrix: mat2, dest?: vec2): vec2; + multiplyMat3(matrix: mat3, dest?: vec2): vec2; + static cross(vector: vec2, vector2: vec2, dest?: vec3): vec3; + static dot(vector: vec2, vector2: vec2): number; + static distance(vector: vec2, vector2: vec2): number; + static squaredDistance(vector: vec2, vector2: vec2): number; + static direction(vector: vec2, vector2: vec2, dest?: vec2): vec2; + static mix(vector: vec2, vector2: vec2, time: number, dest?: vec2): vec2; + static sum(vector: vec2, vector2: vec2, dest?: vec2): vec2; + static difference(vector: vec2, vector2: vec2, dest?: vec2): vec2; + static product(vector: vec2, vector2: vec2, dest?: vec2): vec2; + static quotient(vector: vec2, vector2: vec2, dest?: vec2): vec2; +} diff --git a/src/framework/resonator/vendor/tsm/vec2.js b/src/framework/resonator/vendor/tsm/vec2.js new file mode 100644 index 0000000..598afdc --- /dev/null +++ b/src/framework/resonator/vendor/tsm/vec2.js @@ -0,0 +1,215 @@ +import vec3 from './vec3'; +import { epsilon } from './constants'; +export default class vec2 { + constructor(values) { + this.values = new Float32Array(2); + if (values !== undefined) { + this.xy = values; + } + } + get x() { + return this.values[0]; + } + get y() { + return this.values[1]; + } + get xy() { + return [this.values[0], this.values[1]]; + } + set x(value) { + this.values[0] = value; + } + set y(value) { + this.values[1] = value; + } + set xy(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + at(index) { + return this.values[index]; + } + reset() { + this.x = 0; + this.y = 0; + } + copy(dest) { + if (!dest) { + dest = new vec2(); + } + dest.x = this.x; + dest.y = this.y; + return dest; + } + negate(dest) { + if (!dest) { + dest = this; + } + dest.x = -this.x; + dest.y = -this.y; + return dest; + } + equals(vector, threshold = epsilon) { + if (Math.abs(this.x - vector.x) > threshold) { + return false; + } + if (Math.abs(this.y - vector.y) > threshold) { + return false; + } + return true; + } + length() { + return Math.sqrt(this.squaredLength()); + } + squaredLength() { + const x = this.x; + const y = this.y; + return x * x + y * y; + } + add(vector) { + this.x += vector.x; + this.y += vector.y; + return this; + } + subtract(vector) { + this.x -= vector.x; + this.y -= vector.y; + return this; + } + multiply(vector) { + this.x *= vector.x; + this.y *= vector.y; + return this; + } + divide(vector) { + this.x /= vector.x; + this.y /= vector.y; + return this; + } + scale(value, dest) { + if (!dest) { + dest = this; + } + dest.x *= value; + dest.y *= value; + return dest; + } + normalize(dest) { + if (!dest) { + dest = this; + } + let length = this.length(); + if (length === 1) { + return this; + } + if (length === 0) { + dest.x = 0; + dest.y = 0; + return dest; + } + length = 1.0 / length; + dest.x *= length; + dest.y *= length; + return dest; + } + multiplyMat2(matrix, dest) { + if (!dest) { + dest = this; + } + return matrix.multiplyVec2(this, dest); + } + multiplyMat3(matrix, dest) { + if (!dest) { + dest = this; + } + return matrix.multiplyVec2(this, dest); + } + static cross(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + const x = vector.x; + const y = vector.y; + const x2 = vector2.x; + const y2 = vector2.y; + const z = x * y2 - y * x2; + dest.x = 0; + dest.y = 0; + dest.z = z; + return dest; + } + static dot(vector, vector2) { + return vector.x * vector2.x + vector.y * vector2.y; + } + static distance(vector, vector2) { + return Math.sqrt(this.squaredDistance(vector, vector2)); + } + static squaredDistance(vector, vector2) { + const x = vector2.x - vector.x; + const y = vector2.y - vector.y; + return x * x + y * y; + } + static direction(vector, vector2, dest) { + if (!dest) { + dest = new vec2(); + } + const x = vector.x - vector2.x; + const y = vector.y - vector2.y; + let length = Math.sqrt(x * x + y * y); + if (length === 0) { + dest.x = 0; + dest.y = 0; + return dest; + } + length = 1 / length; + dest.x = x * length; + dest.y = y * length; + return dest; + } + static mix(vector, vector2, time, dest) { + if (!dest) { + dest = new vec2(); + } + const x = vector.x; + const y = vector.y; + const x2 = vector2.x; + const y2 = vector2.y; + dest.x = x + time * (x2 - x); + dest.y = y + time * (y2 - y); + return dest; + } + static sum(vector, vector2, dest) { + if (!dest) { + dest = new vec2(); + } + dest.x = vector.x + vector2.x; + dest.y = vector.y + vector2.y; + return dest; + } + static difference(vector, vector2, dest) { + if (!dest) { + dest = new vec2(); + } + dest.x = vector.x - vector2.x; + dest.y = vector.y - vector2.y; + return dest; + } + static product(vector, vector2, dest) { + if (!dest) { + dest = new vec2(); + } + dest.x = vector.x * vector2.x; + dest.y = vector.y * vector2.y; + return dest; + } + static quotient(vector, vector2, dest) { + if (!dest) { + dest = new vec2(); + } + dest.x = vector.x / vector2.x; + dest.y = vector.y / vector2.y; + return dest; + } +} +vec2.zero = new vec2([0, 0]); +vec2.one = new vec2([1, 1]); diff --git a/src/framework/resonator/vendor/tsm/vec3.d.ts b/src/framework/resonator/vendor/tsm/vec3.d.ts new file mode 100644 index 0000000..900889a --- /dev/null +++ b/src/framework/resonator/vendor/tsm/vec3.d.ts @@ -0,0 +1,47 @@ +import mat3 from './mat3'; +import quat from './quat'; +export default class vec3 { + get x(): number; + get y(): number; + get z(): number; + get xy(): [number, number]; + get xyz(): [number, number, number]; + set x(value: number); + set y(value: number); + set z(value: number); + set xy(values: [number, number]); + set xyz(values: [number, number, number]); + constructor(values?: [number, number, number]); + private values; + static readonly zero: vec3; + static readonly one: vec3; + static readonly up: vec3; + static readonly right: vec3; + static readonly forward: vec3; + at(index: number): number; + reset(): void; + copy(dest?: vec3): vec3; + negate(dest?: vec3): vec3; + equals(vector: vec3, threshold?: number): boolean; + length(): number; + squaredLength(): number; + add(vector: vec3): vec3; + subtract(vector: vec3): vec3; + multiply(vector: vec3): vec3; + divide(vector: vec3): vec3; + scale(value: number, dest?: vec3): vec3; + normalize(dest?: vec3): vec3; + multiplyByMat3(matrix: mat3, dest?: vec3): vec3; + multiplyByQuat(quaternion: quat, dest?: vec3): vec3; + toQuat(dest?: quat): quat; + static cross(vector: vec3, vector2: vec3, dest?: vec3): vec3; + static dot(vector: vec3, vector2: vec3): number; + static distance(vector: vec3, vector2: vec3): number; + static squaredDistance(vector: vec3, vector2: vec3): number; + static direction(vector: vec3, vector2: vec3, dest?: vec3): vec3; + static mix(vector: vec3, vector2: vec3, time: number, dest?: vec3): vec3; + static sum(vector: vec3, vector2: vec3, dest?: vec3): vec3; + static difference(vector: vec3, vector2: vec3, dest?: vec3): vec3; + static product(vector: vec3, vector2: vec3, dest?: vec3): vec3; + static quotient(vector: vec3, vector2: vec3, dest?: vec3): vec3; +} diff --git a/src/framework/resonator/vendor/tsm/vec3.js b/src/framework/resonator/vendor/tsm/vec3.js new file mode 100644 index 0000000..26b6b21 --- /dev/null +++ b/src/framework/resonator/vendor/tsm/vec3.js @@ -0,0 +1,279 @@ +import quat from './quat'; +import { epsilon } from './constants'; +export default class vec3 { + constructor(values) { + this.values = new Float32Array(3); + if (values !== undefined) { + this.xyz = values; + } + } + get x() { + return this.values[0]; + } + get y() { + return this.values[1]; + } + get z() { + return this.values[2]; + } + get xy() { + return [this.values[0], this.values[1]]; + } + get xyz() { + return [this.values[0], this.values[1], this.values[2]]; + } + set x(value) { + this.values[0] = value; + } + set y(value) { + this.values[1] = value; + } + set z(value) { + this.values[2] = value; + } + set xy(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + set xyz(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + } + at(index) { + return this.values[index]; + } + reset() { + this.x = 0; + this.y = 0; + this.z = 0; + } + copy(dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = this.x; + dest.y = this.y; + dest.z = this.z; + return dest; + } + negate(dest) { + if (!dest) { + dest = this; + } + dest.x = -this.x; + dest.y = -this.y; + dest.z = -this.z; + return dest; + } + equals(vector, threshold = epsilon) { + if (Math.abs(this.x - vector.x) > threshold) { + return false; + } + if (Math.abs(this.y - vector.y) > threshold) { + return false; + } + if (Math.abs(this.z - vector.z) > threshold) { + return false; + } + return true; + } + length() { + return Math.sqrt(this.squaredLength()); + } + squaredLength() { + const x = this.x; + const y = this.y; + const z = this.z; + return x * x + y * y + z * z; + } + add(vector) { + this.x += vector.x; + this.y += vector.y; + this.z += vector.z; + return this; + } + subtract(vector) { + this.x -= vector.x; + this.y -= vector.y; + this.z -= vector.z; + return this; + } + multiply(vector) { + this.x *= vector.x; + this.y *= vector.y; + this.z *= vector.z; + return this; + } + divide(vector) { + this.x /= vector.x; + this.y /= vector.y; + this.z /= vector.z; + return this; + } + scale(value, dest) { + if (!dest) { + dest = this; + } + dest.x *= value; + dest.y *= value; + dest.z *= value; + return dest; + } + normalize(dest) { + if (!dest) { + dest = this; + } + let length = this.length(); + if (length === 1) { + return this; + } + if (length === 0) { + dest.x = 0; + dest.y = 0; + dest.z = 0; + return dest; + } + length = 1.0 / length; + dest.x *= length; + dest.y *= length; + dest.z *= length; + return dest; + } + multiplyByMat3(matrix, dest) { + if (!dest) { + dest = this; + } + return matrix.multiplyVec3(this, dest); + } + multiplyByQuat(quaternion, dest) { + if (!dest) { + dest = this; + } + return quaternion.multiplyVec3(this, dest); + } + toQuat(dest) { + if (!dest) { + dest = new quat(); + } + const c = new vec3(); + const s = new vec3(); + c.x = Math.cos(this.x * 0.5); + s.x = Math.sin(this.x * 0.5); + c.y = Math.cos(this.y * 0.5); + s.y = Math.sin(this.y * 0.5); + c.z = Math.cos(this.z * 0.5); + s.z = Math.sin(this.z * 0.5); + dest.x = s.x * c.y * c.z - c.x * s.y * s.z; + dest.y = c.x * s.y * c.z + s.x * c.y * s.z; + dest.z = c.x * c.y * s.z - s.x * s.y * c.z; + dest.w = c.x * c.y * c.z + s.x * s.y * s.z; + return dest; + } + static cross(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + const x = vector.x; + const y = vector.y; + const z = vector.z; + const x2 = vector2.x; + const y2 = vector2.y; + const z2 = vector2.z; + dest.x = y * z2 - z * y2; + dest.y = z * x2 - x * z2; + dest.z = x * y2 - y * x2; + return dest; + } + static dot(vector, vector2) { + const x = vector.x; + const y = vector.y; + const z = vector.z; + const x2 = vector2.x; + const y2 = vector2.y; + const z2 = vector2.z; + return x * x2 + y * y2 + z * z2; + } + static distance(vector, vector2) { + const x = vector2.x - vector.x; + const y = vector2.y - vector.y; + const z = vector2.z - vector.z; + return Math.sqrt(this.squaredDistance(vector, vector2)); + } + static squaredDistance(vector, vector2) { + const x = vector2.x - vector.x; + const y = vector2.y - vector.y; + const z = vector2.z - vector.z; + return x * x + y * y + z * z; + } + static direction(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + const x = vector.x - vector2.x; + const y = vector.y - vector2.y; + const z = vector.z - vector2.z; + let length = Math.sqrt(x * x + y * y + z * z); + if (length === 0) { + dest.x = 0; + dest.y = 0; + dest.z = 0; + return dest; + } + length = 1 / length; + dest.x = x * length; + dest.y = y * length; + dest.z = z * length; + return dest; + } + static mix(vector, vector2, time, dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = vector.x + time * (vector2.x - vector.x); + dest.y = vector.y + time * (vector2.y - vector.y); + dest.z = vector.z + time * (vector2.z - vector.z); + return dest; + } + static sum(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = vector.x + vector2.x; + dest.y = vector.y + vector2.y; + dest.z = vector.z + vector2.z; + return dest; + } + static difference(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = vector.x - vector2.x; + dest.y = vector.y - vector2.y; + dest.z = vector.z - vector2.z; + return dest; + } + static product(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = vector.x * vector2.x; + dest.y = vector.y * vector2.y; + dest.z = vector.z * vector2.z; + return dest; + } + static quotient(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = vector.x / vector2.x; + dest.y = vector.y / vector2.y; + dest.z = vector.z / vector2.z; + return dest; + } +} +vec3.zero = new vec3([0, 0, 0]); +vec3.one = new vec3([1, 1, 1]); +vec3.up = new vec3([0, 1, 0]); +vec3.right = new vec3([1, 0, 0]); +vec3.forward = new vec3([0, 0, 1]); diff --git a/src/framework/resonator/vendor/tsm/vec4.d.ts b/src/framework/resonator/vendor/tsm/vec4.d.ts new file mode 100644 index 0000000..35366a8 --- /dev/null +++ b/src/framework/resonator/vendor/tsm/vec4.d.ts @@ -0,0 +1,54 @@ +import mat4 from './mat4'; +export default class vec4 { + get x(): number; + get y(): number; + get z(): number; + get w(): number; + get xy(): [number, number]; + get xyz(): [number, number, number]; + get xyzw(): [number, number, number, number]; + set x(value: number); + set y(value: number); + set z(value: number); + set w(value: number); + set xy(values: [number, number]); + set xyz(values: [number, number, number]); + set xyzw(values: [number, number, number, number]); + get r(): number; + get g(): number; + get b(): number; + get a(): number; + get rg(): [number, number]; + get rgb(): [number, number, number]; + get rgba(): [number, number, number, number]; + set r(value: number); + set g(value: number); + set b(value: number); + set a(value: number); + set rg(values: [number, number]); + set rgb(values: [number, number, number]); + set rgba(values: [number, number, number, number]); + constructor(values?: [number, number, number, number]); + private values; + static readonly zero: vec4; + static readonly one: vec4; + at(index: number): number; + reset(): void; + copy(dest?: vec4): vec4; + negate(dest?: vec4): vec4; + equals(vector: vec4, threshold?: number): boolean; + length(): number; + squaredLength(): number; + add(vector: vec4): vec4; + subtract(vector: vec4): vec4; + multiply(vector: vec4): vec4; + divide(vector: vec4): vec4; + scale(value: number, dest?: vec4): vec4; + normalize(dest?: vec4): vec4; + multiplyMat4(matrix: mat4, dest?: vec4): vec4; + static mix(vector: vec4, vector2: vec4, time: number, dest?: vec4): vec4; + static sum(vector: vec4, vector2: vec4, dest?: vec4): vec4; + static difference(vector: vec4, vector2: vec4, dest?: vec4): vec4; + static product(vector: vec4, vector2: vec4, dest?: vec4): vec4; + static quotient(vector: vec4, vector2: vec4, dest?: vec4): vec4; +} diff --git a/src/framework/resonator/vendor/tsm/vec4.js b/src/framework/resonator/vendor/tsm/vec4.js new file mode 100644 index 0000000..fd0337d --- /dev/null +++ b/src/framework/resonator/vendor/tsm/vec4.js @@ -0,0 +1,277 @@ +import { epsilon } from './constants'; +export default class vec4 { + constructor(values) { + this.values = new Float32Array(4); + if (values !== undefined) { + this.xyzw = values; + } + } + get x() { + return this.values[0]; + } + get y() { + return this.values[1]; + } + get z() { + return this.values[2]; + } + get w() { + return this.values[3]; + } + get xy() { + return [this.values[0], this.values[1]]; + } + get xyz() { + return [this.values[0], this.values[1], this.values[2]]; + } + get xyzw() { + return [this.values[0], this.values[1], this.values[2], this.values[3]]; + } + set x(value) { + this.values[0] = value; + } + set y(value) { + this.values[1] = value; + } + set z(value) { + this.values[2] = value; + } + set w(value) { + this.values[3] = value; + } + set xy(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + set xyz(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + } + set xyzw(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + this.values[3] = values[3]; + } + get r() { + return this.values[0]; + } + get g() { + return this.values[1]; + } + get b() { + return this.values[2]; + } + get a() { + return this.values[3]; + } + get rg() { + return [this.values[0], this.values[1]]; + } + get rgb() { + return [this.values[0], this.values[1], this.values[2]]; + } + get rgba() { + return [this.values[0], this.values[1], this.values[2], this.values[3]]; + } + set r(value) { + this.values[0] = value; + } + set g(value) { + this.values[1] = value; + } + set b(value) { + this.values[2] = value; + } + set a(value) { + this.values[3] = value; + } + set rg(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + set rgb(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + } + set rgba(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + this.values[3] = values[3]; + } + at(index) { + return this.values[index]; + } + reset() { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + } + copy(dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = this.x; + dest.y = this.y; + dest.z = this.z; + dest.w = this.w; + return dest; + } + negate(dest) { + if (!dest) { + dest = this; + } + dest.x = -this.x; + dest.y = -this.y; + dest.z = -this.z; + dest.w = -this.w; + return dest; + } + equals(vector, threshold = epsilon) { + if (Math.abs(this.x - vector.x) > threshold) { + return false; + } + if (Math.abs(this.y - vector.y) > threshold) { + return false; + } + if (Math.abs(this.z - vector.z) > threshold) { + return false; + } + if (Math.abs(this.w - vector.w) > threshold) { + return false; + } + return true; + } + length() { + return Math.sqrt(this.squaredLength()); + } + squaredLength() { + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + return x * x + y * y + z * z + w * w; + } + add(vector) { + this.x += vector.x; + this.y += vector.y; + this.z += vector.z; + this.w += vector.w; + return this; + } + subtract(vector) { + this.x -= vector.x; + this.y -= vector.y; + this.z -= vector.z; + this.w -= vector.w; + return this; + } + multiply(vector) { + this.x *= vector.x; + this.y *= vector.y; + this.z *= vector.z; + this.w *= vector.w; + return this; + } + divide(vector) { + this.x /= vector.x; + this.y /= vector.y; + this.z /= vector.z; + this.w /= vector.w; + return this; + } + scale(value, dest) { + if (!dest) { + dest = this; + } + dest.x *= value; + dest.y *= value; + dest.z *= value; + dest.w *= value; + return dest; + } + normalize(dest) { + if (!dest) { + dest = this; + } + let length = this.length(); + if (length === 1) { + return this; + } + if (length === 0) { + dest.x *= 0; + dest.y *= 0; + dest.z *= 0; + dest.w *= 0; + return dest; + } + length = 1.0 / length; + dest.x *= length; + dest.y *= length; + dest.z *= length; + dest.w *= length; + return dest; + } + multiplyMat4(matrix, dest) { + if (!dest) { + dest = this; + } + return matrix.multiplyVec4(this, dest); + } + static mix(vector, vector2, time, dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = vector.x + time * (vector2.x - vector.x); + dest.y = vector.y + time * (vector2.y - vector.y); + dest.z = vector.z + time * (vector2.z - vector.z); + dest.w = vector.w + time * (vector2.w - vector.w); + return dest; + } + static sum(vector, vector2, dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = vector.x + vector2.x; + dest.y = vector.y + vector2.y; + dest.z = vector.z + vector2.z; + dest.w = vector.w + vector2.w; + return dest; + } + static difference(vector, vector2, dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = vector.x - vector2.x; + dest.y = vector.y - vector2.y; + dest.z = vector.z - vector2.z; + dest.w = vector.w - vector2.w; + return dest; + } + static product(vector, vector2, dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = vector.x * vector2.x; + dest.y = vector.y * vector2.y; + dest.z = vector.z * vector2.z; + dest.w = vector.w * vector2.w; + return dest; + } + static quotient(vector, vector2, dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = vector.x / vector2.x; + dest.y = vector.y / vector2.y; + dest.z = vector.z / vector2.z; + dest.w = vector.w / vector2.w; + return dest; + } +} +vec4.zero = new vec4([0, 0, 0, 1]); +vec4.one = new vec4([1, 1, 1, 1]); diff --git a/src/framework/scene/index.d.ts b/src/framework/scene/index.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/scene/index.js b/src/framework/scene/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/scene/manager.d.ts b/src/framework/scene/manager.d.ts new file mode 100644 index 0000000..e49ae08 --- /dev/null +++ b/src/framework/scene/manager.d.ts @@ -0,0 +1,12 @@ +import { Scene } from './scene'; +export declare class SceneManager { + scenes: Map; + currentScene: Scene; + defaultScene: Scene; + constructor(); + init(): void; + addScene(scene: Scene): void; + removeScene(scene: Scene): void; + switchTo(scene: Scene): void; + setDefaultScene(scene: Scene): void; +} diff --git a/src/framework/scene/manager.js b/src/framework/scene/manager.js new file mode 100644 index 0000000..4158fac --- /dev/null +++ b/src/framework/scene/manager.js @@ -0,0 +1,33 @@ +export class SceneManager { + constructor() { + this.scenes = new Map(); + } + init() { + if (this.defaultScene) { + this.switchTo(this.defaultScene); + } + } + addScene(scene) { + this.scenes.set(scene.id, scene); + } + removeScene(scene) { + if (scene === this.currentScene) + this.currentScene.onDeactivate(); + this.scenes.delete(scene.id); + } + switchTo(scene) { + if (scene === this.currentScene) + return; + let data; + if (this.currentScene) { + this.currentScene.onDeactivate(); + data = this.currentScene.data; + } + this.currentScene = this.scenes.get(scene.id); + this.currentScene.onSwitch(data); + this.currentScene.onActivate(this); + } + setDefaultScene(scene) { + this.defaultScene = scene; + } +} diff --git a/src/framework/scene/scene.d.ts b/src/framework/scene/scene.d.ts new file mode 100644 index 0000000..b52e59f --- /dev/null +++ b/src/framework/scene/scene.d.ts @@ -0,0 +1,10 @@ +import { SceneManager } from './manager'; +export interface Scene { + id: string; + data: any; + onActivate(manager: SceneManager): any; + onDeactivate(): any; + onSwitch(data: any): any; + update(dt: number): any; + updateDraw(): any; +} diff --git a/src/framework/scene/scene.js b/src/framework/scene/scene.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/scene/scene.js @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/scheduler/index.d.ts b/src/framework/scheduler/index.d.ts new file mode 100644 index 0000000..ddbd2ae --- /dev/null +++ b/src/framework/scheduler/index.d.ts @@ -0,0 +1,12 @@ +import { EventBus } from '../event-bus'; +import { RAFTimer } from './raf'; +import { Timer } from './timer'; +export declare class Scheduler extends EventBus { + logicPerSecond: number; + logicTimer: Timer; + drawTimer: RAFTimer; + constructor(logicPerSecond: number); + init(): void; + start(): void; + stop(): void; +} diff --git a/src/framework/scheduler/index.js b/src/framework/scheduler/index.js new file mode 100644 index 0000000..cf7b243 --- /dev/null +++ b/src/framework/scheduler/index.js @@ -0,0 +1,37 @@ +import { EventBus } from '../event-bus'; +import { RAFTimer } from './raf'; +import { Timer } from './timer'; +export class Scheduler extends EventBus { + constructor(logicPerSecond) { + super(); + this.logicPerSecond = logicPerSecond; + this.init(); + } + init() { + const interval = 1000 / this.logicPerSecond; + this.logicTimer = new Timer(interval, { + id: 0, + func: (dt) => { + this.emit('preupdate.logic'); + this.emit('update.logic', dt); + this.emit('postupdate.logic'); + } + }); + this.drawTimer = new RAFTimer({ + id: 1, + func: (dt) => { + this.emit('preupdate.draw'); + this.emit('update.draw', dt); + this.emit('postupdate.draw'); + } + }); + } + start() { + this.logicTimer.start(); + this.drawTimer.start(); + } + stop() { + this.logicTimer.stop(); + this.drawTimer.stop(); + } +} diff --git a/src/framework/scheduler/node.d.ts b/src/framework/scheduler/node.d.ts new file mode 100644 index 0000000..d715c57 --- /dev/null +++ b/src/framework/scheduler/node.d.ts @@ -0,0 +1,4 @@ +export interface SchedulerNode { + id: number; + func: Function; +} diff --git a/src/framework/scheduler/node.js b/src/framework/scheduler/node.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/scheduler/node.js @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/scheduler/raf.d.ts b/src/framework/scheduler/raf.d.ts new file mode 100644 index 0000000..98f9dd1 --- /dev/null +++ b/src/framework/scheduler/raf.d.ts @@ -0,0 +1,10 @@ +import { SchedulerNode } from './node'; +export declare class RAFTimer { + isStarted: boolean; + node: SchedulerNode; + constructor(node: SchedulerNode); + start(): void; + stop(): void; + schedule(): void; + handleResolve(): void; +} diff --git a/src/framework/scheduler/raf.js b/src/framework/scheduler/raf.js new file mode 100644 index 0000000..d7e5e67 --- /dev/null +++ b/src/framework/scheduler/raf.js @@ -0,0 +1,24 @@ +export class RAFTimer { + constructor(node) { + this.isStarted = false; + this.node = node; + } + start() { + this.isStarted = true; + this.schedule(); + } + stop() { + this.isStarted = false; + } + schedule() { + window.requestAnimationFrame(this.handleResolve.bind(this)); + } + handleResolve() { + if (this.node) { + this.node.func(1); + if (this.isStarted) { + this.schedule(); + } + } + } +} diff --git a/src/framework/scheduler/scheduler-node.d.ts b/src/framework/scheduler/scheduler-node.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/scheduler/scheduler-node.js b/src/framework/scheduler/scheduler-node.js new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/scheduler/timer.d.ts b/src/framework/scheduler/timer.d.ts new file mode 100644 index 0000000..ffca662 --- /dev/null +++ b/src/framework/scheduler/timer.d.ts @@ -0,0 +1,14 @@ +import { SchedulerNode } from './node'; +export declare class Timer { + time: number; + lastTime: number; + fluctuation: number; + node: SchedulerNode; + isStarted: boolean; + intervalID: number; + constructor(time: number, node: SchedulerNode); + start(): void; + stop(): void; + schedule(): void; + handleResolve(): void; +} diff --git a/src/framework/scheduler/timer.js b/src/framework/scheduler/timer.js new file mode 100644 index 0000000..2c73863 --- /dev/null +++ b/src/framework/scheduler/timer.js @@ -0,0 +1,39 @@ +export class Timer { + constructor(time, node) { + this.time = time; + this.node = node; + this.isStarted = false; + } + start() { + this.isStarted = true; + this.schedule(); + } + stop() { + if (this.isStarted) { + if (this.intervalID) { + clearTimeout(this.intervalID); + this.intervalID = null; + this.isStarted = false; + } + } + } + schedule() { + let toWait = this.time; + if (this.lastTime) { + const fluc = Date.now() - this.lastTime; + this.fluctuation = fluc; + toWait -= fluc; + } + this.lastTime = Date.now(); + this.intervalID = setTimeout(this.handleResolve.bind(this), toWait); + } + handleResolve() { + this.lastTime = Date.now(); + if (this.node) { + this.node.func(this.time / this.lastTime); + } + if (this.isStarted) { + this.schedule(); + } + } +} diff --git a/src/framework/tsm/constants.d.ts b/src/framework/tsm/constants.d.ts new file mode 100644 index 0000000..4ceaa99 --- /dev/null +++ b/src/framework/tsm/constants.d.ts @@ -0,0 +1 @@ +export declare const epsilon = 0.00001; diff --git a/src/framework/tsm/constants.js b/src/framework/tsm/constants.js new file mode 100644 index 0000000..347e812 --- /dev/null +++ b/src/framework/tsm/constants.js @@ -0,0 +1 @@ +export const epsilon = 0.00001; diff --git a/src/framework/tsm/mat2.d.ts b/src/framework/tsm/mat2.d.ts new file mode 100644 index 0000000..ab03acb --- /dev/null +++ b/src/framework/tsm/mat2.d.ts @@ -0,0 +1,23 @@ +import vec2 from './vec2'; +export default class mat2 { + constructor(values?: number[]); + private values; + static readonly identity: mat2; + at(index: number): number; + init(values: number[]): mat2; + reset(): void; + copy(dest?: mat2): mat2; + all(): number[]; + row(index: number): number[]; + col(index: number): number[]; + equals(matrix: mat2, threshold?: number): boolean; + determinant(): number; + setIdentity(): mat2; + transpose(): mat2; + inverse(): mat2; + multiply(matrix: mat2): mat2; + rotate(angle: number): mat2; + multiplyVec2(vector: vec2, result: vec2): vec2; + scale(vector: vec2): mat2; + static product(m1: mat2, m2: mat2, result: mat2): mat2; +} diff --git a/src/framework/tsm/mat2.js b/src/framework/tsm/mat2.js new file mode 100644 index 0000000..e67db94 --- /dev/null +++ b/src/framework/tsm/mat2.js @@ -0,0 +1,161 @@ +import vec2 from './vec2'; +import { epsilon } from './constants'; +export default class mat2 { + constructor(values) { + this.values = new Float32Array(4); + if (values !== undefined) { + this.init(values); + } + } + at(index) { + return this.values[index]; + } + init(values) { + for (let i = 0; i < 4; i++) { + this.values[i] = values[i]; + } + return this; + } + reset() { + for (let i = 0; i < 4; i++) { + this.values[i] = 0; + } + } + copy(dest) { + if (!dest) { + dest = new mat2(); + } + for (let i = 0; i < 4; i++) { + dest.values[i] = this.values[i]; + } + return dest; + } + all() { + const data = []; + for (let i = 0; i < 4; i++) { + data[i] = this.values[i]; + } + return data; + } + row(index) { + return [this.values[index * 2 + 0], this.values[index * 2 + 1]]; + } + col(index) { + return [this.values[index], this.values[index + 2]]; + } + equals(matrix, threshold = epsilon) { + for (let i = 0; i < 4; i++) { + if (Math.abs(this.values[i] - matrix.at(i)) > threshold) { + return false; + } + } + return true; + } + determinant() { + return this.values[0] * this.values[3] - this.values[2] * this.values[1]; + } + setIdentity() { + this.values[0] = 1; + this.values[1] = 0; + this.values[2] = 0; + this.values[3] = 1; + return this; + } + transpose() { + const temp = this.values[1]; + this.values[1] = this.values[2]; + this.values[2] = temp; + return this; + } + inverse() { + let det = this.determinant(); + if (!det) { + return null; + } + det = 1.0 / det; + const a11 = this.values[0]; + this.values[0] = det * this.values[3]; + this.values[1] = det * -this.values[1]; + this.values[2] = det * -this.values[2]; + this.values[3] = det * a11; + return this; + } + multiply(matrix) { + const a11 = this.values[0]; + const a12 = this.values[1]; + const a21 = this.values[2]; + const a22 = this.values[3]; + this.values[0] = a11 * matrix.at(0) + a12 * matrix.at(2); + this.values[1] = a11 * matrix.at(1) + a12 * matrix.at(3); + this.values[2] = a21 * matrix.at(0) + a22 * matrix.at(2); + this.values[3] = a21 * matrix.at(1) + a22 * matrix.at(3); + return this; + } + rotate(angle) { + const a11 = this.values[0]; + const a12 = this.values[1]; + const a21 = this.values[2]; + const a22 = this.values[3]; + const sin = Math.sin(angle); + const cos = Math.cos(angle); + this.values[0] = a11 * cos + a12 * sin; + this.values[1] = a11 * -sin + a12 * cos; + this.values[2] = a21 * cos + a22 * sin; + this.values[3] = a21 * -sin + a22 * cos; + return this; + } + multiplyVec2(vector, result) { + const x = vector.x; + const y = vector.y; + if (result) { + result.xy = [ + x * this.values[0] + y * this.values[1], + x * this.values[2] + y * this.values[3] + ]; + return result; + } + else { + return new vec2([ + x * this.values[0] + y * this.values[1], + x * this.values[2] + y * this.values[3] + ]); + } + } + scale(vector) { + const a11 = this.values[0]; + const a12 = this.values[1]; + const a21 = this.values[2]; + const a22 = this.values[3]; + const x = vector.x; + const y = vector.y; + this.values[0] = a11 * x; + this.values[1] = a12 * y; + this.values[2] = a21 * x; + this.values[3] = a22 * y; + return this; + } + static product(m1, m2, result) { + const a11 = m1.at(0); + const a12 = m1.at(1); + const a21 = m1.at(2); + const a22 = m1.at(3); + if (result) { + result.init([ + a11 * m2.at(0) + a12 * m2.at(2), + a11 * m2.at(1) + a12 * m2.at(3), + a21 * m2.at(0) + a22 * m2.at(2), + a21 * m2.at(1) + a22 * m2.at(3) + ]); + return result; + } + else { + return new mat2([ + a11 * m2.at(0) + a12 * m2.at(2), + a11 * m2.at(1) + a12 * m2.at(3), + a21 * m2.at(0) + a22 * m2.at(2), + a21 * m2.at(1) + a22 * m2.at(3) + ]); + } + } +} +mat2.identity = new mat2().setIdentity(); diff --git a/src/framework/tsm/mat3.d.ts b/src/framework/tsm/mat3.d.ts new file mode 100644 index 0000000..03bf323 --- /dev/null +++ b/src/framework/tsm/mat3.d.ts @@ -0,0 +1,28 @@ +import mat4 from './mat4'; +import quat from './quat'; +import vec2 from './vec2'; +import vec3 from './vec3'; +export default class mat3 { + constructor(values?: number[]); + private values; + static readonly identity: mat3; + at(index: number): number; + init(values: number[]): mat3; + reset(): void; + copy(dest?: mat3): mat3; + all(): number[]; + row(index: number): number[]; + col(index: number): number[]; + equals(matrix: mat3, threshold?: number): boolean; + determinant(): number; + setIdentity(): mat3; + transpose(): mat3; + inverse(): mat3; + multiply(matrix: mat3): mat3; + multiplyVec2(vector: vec2, result: vec2): vec2; + multiplyVec3(vector: vec3, result: vec3): vec3; + toMat4(result: mat4): mat4; + toQuat(): quat; + rotate(angle: number, axis: vec3): mat3; + static product(m1: mat3, m2: mat3, result: mat3): mat3; +} diff --git a/src/framework/tsm/mat3.js b/src/framework/tsm/mat3.js new file mode 100644 index 0000000..76e31b5 --- /dev/null +++ b/src/framework/tsm/mat3.js @@ -0,0 +1,392 @@ +import mat4 from './mat4'; +import quat from './quat'; +import vec2 from './vec2'; +import vec3 from './vec3'; +import { epsilon } from './constants'; +export default class mat3 { + constructor(values) { + this.values = new Float32Array(9); + if (values !== undefined) { + this.init(values); + } + } + at(index) { + return this.values[index]; + } + init(values) { + for (let i = 0; i < 9; i++) { + this.values[i] = values[i]; + } + return this; + } + reset() { + for (let i = 0; i < 9; i++) { + this.values[i] = 0; + } + } + copy(dest) { + if (!dest) { + dest = new mat3(); + } + for (let i = 0; i < 9; i++) { + dest.values[i] = this.values[i]; + } + return dest; + } + all() { + const data = []; + for (let i = 0; i < 9; i++) { + data[i] = this.values[i]; + } + return data; + } + row(index) { + return [ + this.values[index * 3 + 0], + this.values[index * 3 + 1], + this.values[index * 3 + 2] + ]; + } + col(index) { + return [this.values[index], this.values[index + 3], this.values[index + 6]]; + } + equals(matrix, threshold = epsilon) { + for (let i = 0; i < 9; i++) { + if (Math.abs(this.values[i] - matrix.at(i)) > threshold) { + return false; + } + } + return true; + } + determinant() { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a10 = this.values[3]; + const a11 = this.values[4]; + const a12 = this.values[5]; + const a20 = this.values[6]; + const a21 = this.values[7]; + const a22 = this.values[8]; + const det01 = a22 * a11 - a12 * a21; + const det11 = -a22 * a10 + a12 * a20; + const det21 = a21 * a10 - a11 * a20; + return a00 * det01 + a01 * det11 + a02 * det21; + } + setIdentity() { + this.values[0] = 1; + this.values[1] = 0; + this.values[2] = 0; + this.values[3] = 0; + this.values[4] = 1; + this.values[5] = 0; + this.values[6] = 0; + this.values[7] = 0; + this.values[8] = 1; + return this; + } + transpose() { + const temp01 = this.values[1]; + const temp02 = this.values[2]; + const temp12 = this.values[5]; + this.values[1] = this.values[3]; + this.values[2] = this.values[6]; + this.values[3] = temp01; + this.values[5] = this.values[7]; + this.values[6] = temp02; + this.values[7] = temp12; + return this; + } + inverse() { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a10 = this.values[3]; + const a11 = this.values[4]; + const a12 = this.values[5]; + const a20 = this.values[6]; + const a21 = this.values[7]; + const a22 = this.values[8]; + const det01 = a22 * a11 - a12 * a21; + const det11 = -a22 * a10 + a12 * a20; + const det21 = a21 * a10 - a11 * a20; + let det = a00 * det01 + a01 * det11 + a02 * det21; + if (!det) { + return null; + } + det = 1.0 / det; + this.values[0] = det01 * det; + this.values[1] = (-a22 * a01 + a02 * a21) * det; + this.values[2] = (a12 * a01 - a02 * a11) * det; + this.values[3] = det11 * det; + this.values[4] = (a22 * a00 - a02 * a20) * det; + this.values[5] = (-a12 * a00 + a02 * a10) * det; + this.values[6] = det21 * det; + this.values[7] = (-a21 * a00 + a01 * a20) * det; + this.values[8] = (a11 * a00 - a01 * a10) * det; + return this; + } + multiply(matrix) { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a10 = this.values[3]; + const a11 = this.values[4]; + const a12 = this.values[5]; + const a20 = this.values[6]; + const a21 = this.values[7]; + const a22 = this.values[8]; + const b00 = matrix.at(0); + const b01 = matrix.at(1); + const b02 = matrix.at(2); + const b10 = matrix.at(3); + const b11 = matrix.at(4); + const b12 = matrix.at(5); + const b20 = matrix.at(6); + const b21 = matrix.at(7); + const b22 = matrix.at(8); + this.values[0] = b00 * a00 + b01 * a10 + b02 * a20; + this.values[1] = b00 * a01 + b01 * a11 + b02 * a21; + this.values[2] = b00 * a02 + b01 * a12 + b02 * a22; + this.values[3] = b10 * a00 + b11 * a10 + b12 * a20; + this.values[4] = b10 * a01 + b11 * a11 + b12 * a21; + this.values[5] = b10 * a02 + b11 * a12 + b12 * a22; + this.values[6] = b20 * a00 + b21 * a10 + b22 * a20; + this.values[7] = b20 * a01 + b21 * a11 + b22 * a21; + this.values[8] = b20 * a02 + b21 * a12 + b22 * a22; + return this; + } + multiplyVec2(vector, result) { + const x = vector.x; + const y = vector.y; + if (result) { + result.xy = [ + x * this.values[0] + y * this.values[3] + this.values[6], + x * this.values[1] + y * this.values[4] + this.values[7] + ]; + return result; + } + else { + return new vec2([ + x * this.values[0] + y * this.values[3] + this.values[6], + x * this.values[1] + y * this.values[4] + this.values[7] + ]); + } + } + multiplyVec3(vector, result) { + const x = vector.x; + const y = vector.y; + const z = vector.z; + if (result) { + result.xyz = [ + x * this.values[0] + y * this.values[3] + z * this.values[6], + x * this.values[1] + y * this.values[4] + z * this.values[7], + x * this.values[2] + y * this.values[5] + z * this.values[8] + ]; + return result; + } + else { + return new vec3([ + x * this.values[0] + y * this.values[3] + z * this.values[6], + x * this.values[1] + y * this.values[4] + z * this.values[7], + x * this.values[2] + y * this.values[5] + z * this.values[8] + ]); + } + } + toMat4(result) { + if (result) { + result.init([ + this.values[0], + this.values[1], + this.values[2], + 0, + this.values[3], + this.values[4], + this.values[5], + 0, + this.values[6], + this.values[7], + this.values[8], + 0, + 0, + 0, + 0, + 1 + ]); + return result; + } + else { + return new mat4([ + this.values[0], + this.values[1], + this.values[2], + 0, + this.values[3], + this.values[4], + this.values[5], + 0, + this.values[6], + this.values[7], + this.values[8], + 0, + 0, + 0, + 0, + 1 + ]); + } + } + toQuat() { + const m00 = this.values[0]; + const m01 = this.values[1]; + const m02 = this.values[2]; + const m10 = this.values[3]; + const m11 = this.values[4]; + const m12 = this.values[5]; + const m20 = this.values[6]; + const m21 = this.values[7]; + const m22 = this.values[8]; + const fourXSquaredMinus1 = m00 - m11 - m22; + const fourYSquaredMinus1 = m11 - m00 - m22; + const fourZSquaredMinus1 = m22 - m00 - m11; + const fourWSquaredMinus1 = m00 + m11 + m22; + let biggestIndex = 0; + let fourBiggestSquaredMinus1 = fourWSquaredMinus1; + if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) { + fourBiggestSquaredMinus1 = fourXSquaredMinus1; + biggestIndex = 1; + } + if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) { + fourBiggestSquaredMinus1 = fourYSquaredMinus1; + biggestIndex = 2; + } + if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) { + fourBiggestSquaredMinus1 = fourZSquaredMinus1; + biggestIndex = 3; + } + const biggestVal = Math.sqrt(fourBiggestSquaredMinus1 + 1) * 0.5; + const mult = 0.25 / biggestVal; + const result = new quat(); + switch (biggestIndex) { + case 0: + result.w = biggestVal; + result.x = (m12 - m21) * mult; + result.y = (m20 - m02) * mult; + result.z = (m01 - m10) * mult; + break; + case 1: + result.w = (m12 - m21) * mult; + result.x = biggestVal; + result.y = (m01 + m10) * mult; + result.z = (m20 + m02) * mult; + break; + case 2: + result.w = (m20 - m02) * mult; + result.x = (m01 + m10) * mult; + result.y = biggestVal; + result.z = (m12 + m21) * mult; + break; + case 3: + result.w = (m01 - m10) * mult; + result.x = (m20 + m02) * mult; + result.y = (m12 + m21) * mult; + result.z = biggestVal; + break; + } + return result; + } + rotate(angle, axis) { + let x = axis.x; + let y = axis.y; + let z = axis.z; + let length = Math.sqrt(x * x + y * y + z * z); + if (!length) { + return null; + } + if (length !== 1) { + length = 1 / length; + x *= length; + y *= length; + z *= length; + } + const s = Math.sin(angle); + const c = Math.cos(angle); + const t = 1.0 - c; + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const b00 = x * x * t + c; + const b01 = y * x * t + z * s; + const b02 = z * x * t - y * s; + const b10 = x * y * t - z * s; + const b11 = y * y * t + c; + const b12 = z * y * t + x * s; + const b20 = x * z * t + y * s; + const b21 = y * z * t - x * s; + const b22 = z * z * t + c; + this.values[0] = a00 * b00 + a10 * b01 + a20 * b02; + this.values[1] = a01 * b00 + a11 * b01 + a21 * b02; + this.values[2] = a02 * b00 + a12 * b01 + a22 * b02; + this.values[3] = a00 * b10 + a10 * b11 + a20 * b12; + this.values[4] = a01 * b10 + a11 * b11 + a21 * b12; + this.values[5] = a02 * b10 + a12 * b11 + a22 * b12; + this.values[6] = a00 * b20 + a10 * b21 + a20 * b22; + this.values[7] = a01 * b20 + a11 * b21 + a21 * b22; + this.values[8] = a02 * b20 + a12 * b21 + a22 * b22; + return this; + } + static product(m1, m2, result) { + const a00 = m1.at(0); + const a01 = m1.at(1); + const a02 = m1.at(2); + const a10 = m1.at(3); + const a11 = m1.at(4); + const a12 = m1.at(5); + const a20 = m1.at(6); + const a21 = m1.at(7); + const a22 = m1.at(8); + const b00 = m2.at(0); + const b01 = m2.at(1); + const b02 = m2.at(2); + const b10 = m2.at(3); + const b11 = m2.at(4); + const b12 = m2.at(5); + const b20 = m2.at(6); + const b21 = m2.at(7); + const b22 = m2.at(8); + if (result) { + result.init([ + b00 * a00 + b01 * a10 + b02 * a20, + b00 * a01 + b01 * a11 + b02 * a21, + b00 * a02 + b01 * a12 + b02 * a22, + b10 * a00 + b11 * a10 + b12 * a20, + b10 * a01 + b11 * a11 + b12 * a21, + b10 * a02 + b11 * a12 + b12 * a22, + b20 * a00 + b21 * a10 + b22 * a20, + b20 * a01 + b21 * a11 + b22 * a21, + b20 * a02 + b21 * a12 + b22 * a22 + ]); + return result; + } + else { + return new mat3([ + b00 * a00 + b01 * a10 + b02 * a20, + b00 * a01 + b01 * a11 + b02 * a21, + b00 * a02 + b01 * a12 + b02 * a22, + b10 * a00 + b11 * a10 + b12 * a20, + b10 * a01 + b11 * a11 + b12 * a21, + b10 * a02 + b11 * a12 + b12 * a22, + b20 * a00 + b21 * a10 + b22 * a20, + b20 * a01 + b21 * a11 + b22 * a21, + b20 * a02 + b21 * a12 + b22 * a22 + ]); + } + } +} +mat3.identity = new mat3().setIdentity(); diff --git a/src/framework/tsm/mat4.d.ts b/src/framework/tsm/mat4.d.ts new file mode 100644 index 0000000..799d9a3 --- /dev/null +++ b/src/framework/tsm/mat4.d.ts @@ -0,0 +1,33 @@ +import mat3 from './mat3'; +import vec3 from './vec3'; +import vec4 from './vec4'; +export default class mat4 { + constructor(values?: number[]); + private values; + static readonly identity: mat4; + at(index: number): number; + init(values: number[]): mat4; + reset(): void; + copy(dest?: mat4): mat4; + all(): number[]; + row(index: number): number[]; + col(index: number): number[]; + equals(matrix: mat4, threshold?: number): boolean; + determinant(): number; + setIdentity(): mat4; + transpose(): mat4; + inverse(): mat4; + multiply(matrix: mat4): mat4; + multiplyVec3(vector: vec3): vec3; + multiplyVec4(vector: vec4, dest?: vec4): vec4; + toMat3(): mat3; + toInverseMat3(): mat3; + translate(vector: vec3): mat4; + scale(vector: vec3): mat4; + rotate(angle: number, axis: vec3): mat4; + static frustum(left: number, right: number, bottom: number, top: number, near: number, far: number): mat4; + static perspective(fov: number, aspect: number, near: number, far: number): mat4; + static orthographic(left: number, right: number, bottom: number, top: number, near: number, far: number): mat4; + static lookAt(position: vec3, target: vec3, up?: vec3): mat4; + static product(m1: mat4, m2: mat4, result: mat4): mat4; +} diff --git a/src/framework/tsm/mat4.js b/src/framework/tsm/mat4.js new file mode 100644 index 0000000..1447f4e --- /dev/null +++ b/src/framework/tsm/mat4.js @@ -0,0 +1,579 @@ +import mat3 from './mat3'; +import vec3 from './vec3'; +import vec4 from './vec4'; +import { epsilon } from './constants'; +export default class mat4 { + constructor(values) { + this.values = new Float32Array(16); + if (values !== undefined) { + this.init(values); + } + } + at(index) { + return this.values[index]; + } + init(values) { + for (let i = 0; i < 16; i++) { + this.values[i] = values[i]; + } + return this; + } + reset() { + for (let i = 0; i < 16; i++) { + this.values[i] = 0; + } + } + copy(dest) { + if (!dest) { + dest = new mat4(); + } + for (let i = 0; i < 16; i++) { + dest.values[i] = this.values[i]; + } + return dest; + } + all() { + const data = []; + for (let i = 0; i < 16; i++) { + data[i] = this.values[i]; + } + return data; + } + row(index) { + return [ + this.values[index * 4 + 0], + this.values[index * 4 + 1], + this.values[index * 4 + 2], + this.values[index * 4 + 3] + ]; + } + col(index) { + return [ + this.values[index], + this.values[index + 4], + this.values[index + 8], + this.values[index + 12] + ]; + } + equals(matrix, threshold = epsilon) { + for (let i = 0; i < 16; i++) { + if (Math.abs(this.values[i] - matrix.at(i)) > threshold) { + return false; + } + } + return true; + } + determinant() { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a03 = this.values[3]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a13 = this.values[7]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const a23 = this.values[11]; + const a30 = this.values[12]; + const a31 = this.values[13]; + const a32 = this.values[14]; + const a33 = this.values[15]; + const det00 = a00 * a11 - a01 * a10; + const det01 = a00 * a12 - a02 * a10; + const det02 = a00 * a13 - a03 * a10; + const det03 = a01 * a12 - a02 * a11; + const det04 = a01 * a13 - a03 * a11; + const det05 = a02 * a13 - a03 * a12; + const det06 = a20 * a31 - a21 * a30; + const det07 = a20 * a32 - a22 * a30; + const det08 = a20 * a33 - a23 * a30; + const det09 = a21 * a32 - a22 * a31; + const det10 = a21 * a33 - a23 * a31; + const det11 = a22 * a33 - a23 * a32; + return (det00 * det11 - + det01 * det10 + + det02 * det09 + + det03 * det08 - + det04 * det07 + + det05 * det06); + } + setIdentity() { + this.values[0] = 1; + this.values[1] = 0; + this.values[2] = 0; + this.values[3] = 0; + this.values[4] = 0; + this.values[5] = 1; + this.values[6] = 0; + this.values[7] = 0; + this.values[8] = 0; + this.values[9] = 0; + this.values[10] = 1; + this.values[11] = 0; + this.values[12] = 0; + this.values[13] = 0; + this.values[14] = 0; + this.values[15] = 1; + return this; + } + transpose() { + const temp01 = this.values[1]; + const temp02 = this.values[2]; + const temp03 = this.values[3]; + const temp12 = this.values[6]; + const temp13 = this.values[7]; + const temp23 = this.values[11]; + this.values[1] = this.values[4]; + this.values[2] = this.values[8]; + this.values[3] = this.values[12]; + this.values[4] = temp01; + this.values[6] = this.values[9]; + this.values[7] = this.values[13]; + this.values[8] = temp02; + this.values[9] = temp12; + this.values[11] = this.values[14]; + this.values[12] = temp03; + this.values[13] = temp13; + this.values[14] = temp23; + return this; + } + inverse() { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a03 = this.values[3]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a13 = this.values[7]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const a23 = this.values[11]; + const a30 = this.values[12]; + const a31 = this.values[13]; + const a32 = this.values[14]; + const a33 = this.values[15]; + const det00 = a00 * a11 - a01 * a10; + const det01 = a00 * a12 - a02 * a10; + const det02 = a00 * a13 - a03 * a10; + const det03 = a01 * a12 - a02 * a11; + const det04 = a01 * a13 - a03 * a11; + const det05 = a02 * a13 - a03 * a12; + const det06 = a20 * a31 - a21 * a30; + const det07 = a20 * a32 - a22 * a30; + const det08 = a20 * a33 - a23 * a30; + const det09 = a21 * a32 - a22 * a31; + const det10 = a21 * a33 - a23 * a31; + const det11 = a22 * a33 - a23 * a32; + let det = det00 * det11 - + det01 * det10 + + det02 * det09 + + det03 * det08 - + det04 * det07 + + det05 * det06; + if (!det) { + return null; + } + det = 1.0 / det; + this.values[0] = (a11 * det11 - a12 * det10 + a13 * det09) * det; + this.values[1] = (-a01 * det11 + a02 * det10 - a03 * det09) * det; + this.values[2] = (a31 * det05 - a32 * det04 + a33 * det03) * det; + this.values[3] = (-a21 * det05 + a22 * det04 - a23 * det03) * det; + this.values[4] = (-a10 * det11 + a12 * det08 - a13 * det07) * det; + this.values[5] = (a00 * det11 - a02 * det08 + a03 * det07) * det; + this.values[6] = (-a30 * det05 + a32 * det02 - a33 * det01) * det; + this.values[7] = (a20 * det05 - a22 * det02 + a23 * det01) * det; + this.values[8] = (a10 * det10 - a11 * det08 + a13 * det06) * det; + this.values[9] = (-a00 * det10 + a01 * det08 - a03 * det06) * det; + this.values[10] = (a30 * det04 - a31 * det02 + a33 * det00) * det; + this.values[11] = (-a20 * det04 + a21 * det02 - a23 * det00) * det; + this.values[12] = (-a10 * det09 + a11 * det07 - a12 * det06) * det; + this.values[13] = (a00 * det09 - a01 * det07 + a02 * det06) * det; + this.values[14] = (-a30 * det03 + a31 * det01 - a32 * det00) * det; + this.values[15] = (a20 * det03 - a21 * det01 + a22 * det00) * det; + return this; + } + multiply(matrix) { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a03 = this.values[3]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a13 = this.values[7]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const a23 = this.values[11]; + const a30 = this.values[12]; + const a31 = this.values[13]; + const a32 = this.values[14]; + const a33 = this.values[15]; + let b0 = matrix.at(0); + let b1 = matrix.at(1); + let b2 = matrix.at(2); + let b3 = matrix.at(3); + this.values[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + this.values[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + this.values[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + this.values[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = matrix.at(4); + b1 = matrix.at(5); + b2 = matrix.at(6); + b3 = matrix.at(7); + this.values[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + this.values[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + this.values[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + this.values[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = matrix.at(8); + b1 = matrix.at(9); + b2 = matrix.at(10); + b3 = matrix.at(11); + this.values[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + this.values[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + this.values[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + this.values[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = matrix.at(12); + b1 = matrix.at(13); + b2 = matrix.at(14); + b3 = matrix.at(15); + this.values[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + this.values[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + this.values[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + this.values[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + return this; + } + multiplyVec3(vector) { + const x = vector.x; + const y = vector.y; + const z = vector.z; + return new vec3([ + this.values[0] * x + + this.values[4] * y + + this.values[8] * z + + this.values[12], + this.values[1] * x + + this.values[5] * y + + this.values[9] * z + + this.values[13], + this.values[2] * x + + this.values[6] * y + + this.values[10] * z + + this.values[14] + ]); + } + multiplyVec4(vector, dest) { + if (!dest) { + dest = new vec4(); + } + const x = vector.x; + const y = vector.y; + const z = vector.z; + const w = vector.w; + dest.x = + this.values[0] * x + + this.values[4] * y + + this.values[8] * z + + this.values[12] * w; + dest.y = + this.values[1] * x + + this.values[5] * y + + this.values[9] * z + + this.values[13] * w; + dest.z = + this.values[2] * x + + this.values[6] * y + + this.values[10] * z + + this.values[14] * w; + dest.w = + this.values[3] * x + + this.values[7] * y + + this.values[11] * z + + this.values[15] * w; + return dest; + } + toMat3() { + return new mat3([ + this.values[0], + this.values[1], + this.values[2], + this.values[4], + this.values[5], + this.values[6], + this.values[8], + this.values[9], + this.values[10] + ]); + } + toInverseMat3() { + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const det01 = a22 * a11 - a12 * a21; + const det11 = -a22 * a10 + a12 * a20; + const det21 = a21 * a10 - a11 * a20; + let det = a00 * det01 + a01 * det11 + a02 * det21; + if (!det) { + return null; + } + det = 1.0 / det; + return new mat3([ + det01 * det, + (-a22 * a01 + a02 * a21) * det, + (a12 * a01 - a02 * a11) * det, + det11 * det, + (a22 * a00 - a02 * a20) * det, + (-a12 * a00 + a02 * a10) * det, + det21 * det, + (-a21 * a00 + a01 * a20) * det, + (a11 * a00 - a01 * a10) * det + ]); + } + translate(vector) { + const x = vector.x; + const y = vector.y; + const z = vector.z; + this.values[12] += + this.values[0] * x + this.values[4] * y + this.values[8] * z; + this.values[13] += + this.values[1] * x + this.values[5] * y + this.values[9] * z; + this.values[14] += + this.values[2] * x + this.values[6] * y + this.values[10] * z; + this.values[15] += + this.values[3] * x + this.values[7] * y + this.values[11] * z; + return this; + } + scale(vector) { + const x = vector.x; + const y = vector.y; + const z = vector.z; + this.values[0] *= x; + this.values[1] *= x; + this.values[2] *= x; + this.values[3] *= x; + this.values[4] *= y; + this.values[5] *= y; + this.values[6] *= y; + this.values[7] *= y; + this.values[8] *= z; + this.values[9] *= z; + this.values[10] *= z; + this.values[11] *= z; + return this; + } + rotate(angle, axis) { + let x = axis.x; + let y = axis.y; + let z = axis.z; + let length = Math.sqrt(x * x + y * y + z * z); + if (!length) { + return null; + } + if (length !== 1) { + length = 1 / length; + x *= length; + y *= length; + z *= length; + } + const s = Math.sin(angle); + const c = Math.cos(angle); + const t = 1.0 - c; + const a00 = this.values[0]; + const a01 = this.values[1]; + const a02 = this.values[2]; + const a03 = this.values[3]; + const a10 = this.values[4]; + const a11 = this.values[5]; + const a12 = this.values[6]; + const a13 = this.values[7]; + const a20 = this.values[8]; + const a21 = this.values[9]; + const a22 = this.values[10]; + const a23 = this.values[11]; + const b00 = x * x * t + c; + const b01 = y * x * t + z * s; + const b02 = z * x * t - y * s; + const b10 = x * y * t - z * s; + const b11 = y * y * t + c; + const b12 = z * y * t + x * s; + const b20 = x * z * t + y * s; + const b21 = y * z * t - x * s; + const b22 = z * z * t + c; + this.values[0] = a00 * b00 + a10 * b01 + a20 * b02; + this.values[1] = a01 * b00 + a11 * b01 + a21 * b02; + this.values[2] = a02 * b00 + a12 * b01 + a22 * b02; + this.values[3] = a03 * b00 + a13 * b01 + a23 * b02; + this.values[4] = a00 * b10 + a10 * b11 + a20 * b12; + this.values[5] = a01 * b10 + a11 * b11 + a21 * b12; + this.values[6] = a02 * b10 + a12 * b11 + a22 * b12; + this.values[7] = a03 * b10 + a13 * b11 + a23 * b12; + this.values[8] = a00 * b20 + a10 * b21 + a20 * b22; + this.values[9] = a01 * b20 + a11 * b21 + a21 * b22; + this.values[10] = a02 * b20 + a12 * b21 + a22 * b22; + this.values[11] = a03 * b20 + a13 * b21 + a23 * b22; + return this; + } + static frustum(left, right, bottom, top, near, far) { + const rl = right - left; + const tb = top - bottom; + const fn = far - near; + return new mat4([ + (near * 2) / rl, + 0, + 0, + 0, + 0, + (near * 2) / tb, + 0, + 0, + (right + left) / rl, + (top + bottom) / tb, + -(far + near) / fn, + -1, + 0, + 0, + -(far * near * 2) / fn, + 0 + ]); + } + static perspective(fov, aspect, near, far) { + const top = near * Math.tan((fov * Math.PI) / 360.0); + const right = top * aspect; + return mat4.frustum(-right, right, -top, top, near, far); + } + static orthographic(left, right, bottom, top, near, far) { + const rl = right - left; + const tb = top - bottom; + const fn = far - near; + return new mat4([ + 2 / rl, + 0, + 0, + 0, + 0, + 2 / tb, + 0, + 0, + 0, + 0, + -2 / fn, + 0, + -(left + right) / rl, + -(top + bottom) / tb, + -(far + near) / fn, + 1 + ]); + } + static lookAt(position, target, up = vec3.up) { + if (position.equals(target)) { + return this.identity; + } + const z = vec3.difference(position, target).normalize(); + const x = vec3.cross(up, z).normalize(); + const y = vec3.cross(z, x).normalize(); + return new mat4([ + x.x, + y.x, + z.x, + 0, + x.y, + y.y, + z.y, + 0, + x.z, + y.z, + z.z, + 0, + -vec3.dot(x, position), + -vec3.dot(y, position), + -vec3.dot(z, position), + 1 + ]); + } + static product(m1, m2, result) { + const a00 = m1.at(0); + const a01 = m1.at(1); + const a02 = m1.at(2); + const a03 = m1.at(3); + const a10 = m1.at(4); + const a11 = m1.at(5); + const a12 = m1.at(6); + const a13 = m1.at(7); + const a20 = m1.at(8); + const a21 = m1.at(9); + const a22 = m1.at(10); + const a23 = m1.at(11); + const a30 = m1.at(12); + const a31 = m1.at(13); + const a32 = m1.at(14); + const a33 = m1.at(15); + const b00 = m2.at(0); + const b01 = m2.at(1); + const b02 = m2.at(2); + const b03 = m2.at(3); + const b10 = m2.at(4); + const b11 = m2.at(5); + const b12 = m2.at(6); + const b13 = m2.at(7); + const b20 = m2.at(8); + const b21 = m2.at(9); + const b22 = m2.at(10); + const b23 = m2.at(11); + const b30 = m2.at(12); + const b31 = m2.at(13); + const b32 = m2.at(14); + const b33 = m2.at(15); + if (result) { + result.init([ + b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30, + b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31, + b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32, + b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33, + b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30, + b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31, + b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32, + b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33, + b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30, + b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31, + b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32, + b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33, + b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30, + b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31, + b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32, + b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33 + ]); + return result; + } + else { + return new mat4([ + b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30, + b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31, + b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32, + b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33, + b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30, + b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31, + b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32, + b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33, + b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30, + b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31, + b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32, + b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33, + b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30, + b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31, + b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32, + b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33 + ]); + } + } +} +mat4.identity = new mat4().setIdentity(); diff --git a/src/framework/tsm/quat.d.ts b/src/framework/tsm/quat.d.ts new file mode 100644 index 0000000..1195bfd --- /dev/null +++ b/src/framework/tsm/quat.d.ts @@ -0,0 +1,47 @@ +import mat3 from './mat3'; +import mat4 from './mat4'; +import vec3 from './vec3'; +export default class quat { + get x(): number; + get y(): number; + get z(): number; + get w(): number; + get xy(): [number, number]; + get xyz(): [number, number, number]; + get xyzw(): [number, number, number, number]; + set x(value: number); + set y(value: number); + set z(value: number); + set w(value: number); + set xy(values: [number, number]); + set xyz(values: [number, number, number]); + set xyzw(values: [number, number, number, number]); + constructor(values?: [number, number, number, number]); + private values; + static readonly identity: quat; + at(index: number): number; + reset(): void; + copy(dest?: quat): quat; + roll(): number; + pitch(): number; + yaw(): number; + equals(vector: quat, threshold?: number): boolean; + setIdentity(): quat; + calculateW(): quat; + inverse(): quat; + conjugate(): quat; + length(): number; + normalize(dest?: quat): quat; + add(other: quat): quat; + multiply(other: quat): quat; + multiplyVec3(vector: vec3, dest?: vec3): vec3; + toMat3(dest?: mat3): mat3; + toMat4(dest?: mat4): mat4; + static dot(q1: quat, q2: quat): number; + static sum(q1: quat, q2: quat, dest?: quat): quat; + static product(q1: quat, q2: quat, dest?: quat): quat; + static cross(q1: quat, q2: quat, dest?: quat): quat; + static shortMix(q1: quat, q2: quat, time: number, dest?: quat): quat; + static mix(q1: quat, q2: quat, time: number, dest?: quat): quat; + static fromAxisAngle(axis: vec3, angle: number, dest?: quat): quat; +} diff --git a/src/framework/tsm/quat.js b/src/framework/tsm/quat.js new file mode 100644 index 0000000..54c43ba --- /dev/null +++ b/src/framework/tsm/quat.js @@ -0,0 +1,404 @@ +import mat3 from './mat3'; +import mat4 from './mat4'; +import vec3 from './vec3'; +import { epsilon } from './constants'; +export default class quat { + constructor(values) { + this.values = new Float32Array(4); + if (values !== undefined) { + this.xyzw = values; + } + } + get x() { + return this.values[0]; + } + get y() { + return this.values[1]; + } + get z() { + return this.values[2]; + } + get w() { + return this.values[3]; + } + get xy() { + return [this.values[0], this.values[1]]; + } + get xyz() { + return [this.values[0], this.values[1], this.values[2]]; + } + get xyzw() { + return [this.values[0], this.values[1], this.values[2], this.values[3]]; + } + set x(value) { + this.values[0] = value; + } + set y(value) { + this.values[1] = value; + } + set z(value) { + this.values[2] = value; + } + set w(value) { + this.values[3] = value; + } + set xy(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + set xyz(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + } + set xyzw(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + this.values[3] = values[3]; + } + at(index) { + return this.values[index]; + } + reset() { + for (let i = 0; i < 4; i++) { + this.values[i] = 0; + } + } + copy(dest) { + if (!dest) { + dest = new quat(); + } + for (let i = 0; i < 4; i++) { + dest.values[i] = this.values[i]; + } + return dest; + } + roll() { + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + return Math.atan2(2.0 * (x * y + w * z), w * w + x * x - y * y - z * z); + } + pitch() { + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + return Math.atan2(2.0 * (y * z + w * x), w * w - x * x - y * y + z * z); + } + yaw() { + return Math.asin(2.0 * (this.x * this.z - this.w * this.y)); + } + equals(vector, threshold = epsilon) { + for (let i = 0; i < 4; i++) { + if (Math.abs(this.values[i] - vector.at(i)) > threshold) { + return false; + } + } + return true; + } + setIdentity() { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 1; + return this; + } + calculateW() { + const x = this.x; + const y = this.y; + const z = this.z; + this.w = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return this; + } + inverse() { + const dot = quat.dot(this, this); + if (!dot) { + this.xyzw = [0, 0, 0, 0]; + return this; + } + const invDot = dot ? 1.0 / dot : 0; + this.x *= -invDot; + this.y *= -invDot; + this.z *= -invDot; + this.w *= invDot; + return this; + } + conjugate() { + this.values[0] *= -1; + this.values[1] *= -1; + this.values[2] *= -1; + return this; + } + length() { + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + return Math.sqrt(x * x + y * y + z * z + w * w); + } + normalize(dest) { + if (!dest) { + dest = this; + } + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + let length = Math.sqrt(x * x + y * y + z * z + w * w); + if (!length) { + dest.x = 0; + dest.y = 0; + dest.z = 0; + dest.w = 0; + return dest; + } + length = 1 / length; + dest.x = x * length; + dest.y = y * length; + dest.z = z * length; + dest.w = w * length; + return dest; + } + add(other) { + for (let i = 0; i < 4; i++) { + this.values[i] += other.at(i); + } + return this; + } + multiply(other) { + const q1x = this.values[0]; + const q1y = this.values[1]; + const q1z = this.values[2]; + const q1w = this.values[3]; + const q2x = other.x; + const q2y = other.y; + const q2z = other.z; + const q2w = other.w; + this.x = q1x * q2w + q1w * q2x + q1y * q2z - q1z * q2y; + this.y = q1y * q2w + q1w * q2y + q1z * q2x - q1x * q2z; + this.z = q1z * q2w + q1w * q2z + q1x * q2y - q1y * q2x; + this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z; + return this; + } + multiplyVec3(vector, dest) { + if (!dest) { + dest = new vec3(); + } + const x = vector.x; + const y = vector.y; + const z = vector.z; + const qx = this.x; + const qy = this.y; + const qz = this.z; + const qw = this.w; + const ix = qw * x + qy * z - qz * y; + const iy = qw * y + qz * x - qx * z; + const iz = qw * z + qx * y - qy * x; + const iw = -qx * x - qy * y - qz * z; + dest.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; + dest.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; + dest.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return dest; + } + toMat3(dest) { + if (!dest) { + dest = new mat3(); + } + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + const x2 = x + x; + const y2 = y + y; + const z2 = z + z; + const xx = x * x2; + const xy = x * y2; + const xz = x * z2; + const yy = y * y2; + const yz = y * z2; + const zz = z * z2; + const wx = w * x2; + const wy = w * y2; + const wz = w * z2; + dest.init([ + 1 - (yy + zz), + xy + wz, + xz - wy, + xy - wz, + 1 - (xx + zz), + yz + wx, + xz + wy, + yz - wx, + 1 - (xx + yy) + ]); + return dest; + } + toMat4(dest) { + if (!dest) { + dest = new mat4(); + } + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + const x2 = x + x; + const y2 = y + y; + const z2 = z + z; + const xx = x * x2; + const xy = x * y2; + const xz = x * z2; + const yy = y * y2; + const yz = y * z2; + const zz = z * z2; + const wx = w * x2; + const wy = w * y2; + const wz = w * z2; + dest.init([ + 1 - (yy + zz), + xy + wz, + xz - wy, + 0, + xy - wz, + 1 - (xx + zz), + yz + wx, + 0, + xz + wy, + yz - wx, + 1 - (xx + yy), + 0, + 0, + 0, + 0, + 1 + ]); + return dest; + } + static dot(q1, q2) { + return q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; + } + static sum(q1, q2, dest) { + if (!dest) { + dest = new quat(); + } + dest.x = q1.x + q2.x; + dest.y = q1.y + q2.y; + dest.z = q1.z + q2.z; + dest.w = q1.w + q2.w; + return dest; + } + static product(q1, q2, dest) { + if (!dest) { + dest = new quat(); + } + const q1x = q1.x; + const q1y = q1.y; + const q1z = q1.z; + const q1w = q1.w; + const q2x = q2.x; + const q2y = q2.y; + const q2z = q2.z; + const q2w = q2.w; + dest.x = q1x * q2w + q1w * q2x + q1y * q2z - q1z * q2y; + dest.y = q1y * q2w + q1w * q2y + q1z * q2x - q1x * q2z; + dest.z = q1z * q2w + q1w * q2z + q1x * q2y - q1y * q2x; + dest.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z; + return dest; + } + static cross(q1, q2, dest) { + if (!dest) { + dest = new quat(); + } + const q1x = q1.x; + const q1y = q1.y; + const q1z = q1.z; + const q1w = q1.w; + const q2x = q2.x; + const q2y = q2.y; + const q2z = q2.z; + const q2w = q2.w; + dest.x = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x; + dest.y = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z; + dest.z = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y; + dest.w = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z; + return dest; + } + static shortMix(q1, q2, time, dest) { + if (!dest) { + dest = new quat(); + } + if (time <= 0.0) { + dest.xyzw = q1.xyzw; + return dest; + } + else if (time >= 1.0) { + dest.xyzw = q2.xyzw; + return dest; + } + let cos = quat.dot(q1, q2); + const q2a = q2.copy(); + if (cos < 0.0) { + q2a.inverse(); + cos = -cos; + } + let k0; + let k1; + if (cos > 0.9999) { + k0 = 1 - time; + k1 = 0 + time; + } + else { + const sin = Math.sqrt(1 - cos * cos); + const angle = Math.atan2(sin, cos); + const oneOverSin = 1 / sin; + k0 = Math.sin((1 - time) * angle) * oneOverSin; + k1 = Math.sin((0 + time) * angle) * oneOverSin; + } + dest.x = k0 * q1.x + k1 * q2a.x; + dest.y = k0 * q1.y + k1 * q2a.y; + dest.z = k0 * q1.z + k1 * q2a.z; + dest.w = k0 * q1.w + k1 * q2a.w; + return dest; + } + static mix(q1, q2, time, dest) { + if (!dest) { + dest = new quat(); + } + const cosHalfTheta = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; + if (Math.abs(cosHalfTheta) >= 1.0) { + dest.xyzw = q1.xyzw; + return dest; + } + const halfTheta = Math.acos(cosHalfTheta); + const sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); + if (Math.abs(sinHalfTheta) < 0.001) { + dest.x = q1.x * 0.5 + q2.x * 0.5; + dest.y = q1.y * 0.5 + q2.y * 0.5; + dest.z = q1.z * 0.5 + q2.z * 0.5; + dest.w = q1.w * 0.5 + q2.w * 0.5; + return dest; + } + const ratioA = Math.sin((1 - time) * halfTheta) / sinHalfTheta; + const ratioB = Math.sin(time * halfTheta) / sinHalfTheta; + dest.x = q1.x * ratioA + q2.x * ratioB; + dest.y = q1.y * ratioA + q2.y * ratioB; + dest.z = q1.z * ratioA + q2.z * ratioB; + dest.w = q1.w * ratioA + q2.w * ratioB; + return dest; + } + static fromAxisAngle(axis, angle, dest) { + if (!dest) { + dest = new quat(); + } + angle *= 0.5; + const sin = Math.sin(angle); + dest.x = axis.x * sin; + dest.y = axis.y * sin; + dest.z = axis.z * sin; + dest.w = Math.cos(angle); + return dest; + } +} +quat.identity = new quat().setIdentity(); diff --git a/src/framework/tsm/tsm.d.ts b/src/framework/tsm/tsm.d.ts new file mode 100644 index 0000000..4fcfb6d --- /dev/null +++ b/src/framework/tsm/tsm.d.ts @@ -0,0 +1,17 @@ +import mat2 from './mat2'; +import mat3 from './mat3'; +import mat4 from './mat4'; +import quat from './quat'; +import vec2 from './vec2'; +import vec3 from './vec3'; +import vec4 from './vec4'; +declare const _default: { + vec2: typeof vec2; + vec3: typeof vec3; + vec4: typeof vec4; + mat2: typeof mat2; + mat3: typeof mat3; + mat4: typeof mat4; + quat: typeof quat; +}; +export default _default; diff --git a/src/framework/tsm/tsm.js b/src/framework/tsm/tsm.js new file mode 100644 index 0000000..def6504 --- /dev/null +++ b/src/framework/tsm/tsm.js @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012, 2018 Matthias Ferch + * + * Project homepage: https://github.com/matthiasferch/tsm + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ +import mat2 from './mat2'; +import mat3 from './mat3'; +import mat4 from './mat4'; +import quat from './quat'; +import vec2 from './vec2'; +import vec3 from './vec3'; +import vec4 from './vec4'; +export default { + vec2, + vec3, + vec4, + mat2, + mat3, + mat4, + quat +}; diff --git a/src/framework/tsm/vec2.d.ts b/src/framework/tsm/vec2.d.ts new file mode 100644 index 0000000..34a2e50 --- /dev/null +++ b/src/framework/tsm/vec2.d.ts @@ -0,0 +1,40 @@ +import mat2 from './mat2'; +import mat3 from './mat3'; +import vec3 from './vec3'; +export default class vec2 { + get x(): number; + get y(): number; + get xy(): [number, number]; + set x(value: number); + set y(value: number); + set xy(values: [number, number]); + constructor(values?: [number, number]); + private values; + static readonly zero: vec2; + static readonly one: vec2; + at(index: number): number; + reset(): void; + copy(dest?: vec2): vec2; + negate(dest?: vec2): vec2; + equals(vector: vec2, threshold?: number): boolean; + length(): number; + squaredLength(): number; + add(vector: vec2): vec2; + subtract(vector: vec2): vec2; + multiply(vector: vec2): vec2; + divide(vector: vec2): vec2; + scale(value: number, dest?: vec2): vec2; + normalize(dest?: vec2): vec2; + multiplyMat2(matrix: mat2, dest?: vec2): vec2; + multiplyMat3(matrix: mat3, dest?: vec2): vec2; + static cross(vector: vec2, vector2: vec2, dest?: vec3): vec3; + static dot(vector: vec2, vector2: vec2): number; + static distance(vector: vec2, vector2: vec2): number; + static squaredDistance(vector: vec2, vector2: vec2): number; + static direction(vector: vec2, vector2: vec2, dest?: vec2): vec2; + static mix(vector: vec2, vector2: vec2, time: number, dest?: vec2): vec2; + static sum(vector: vec2, vector2: vec2, dest?: vec2): vec2; + static difference(vector: vec2, vector2: vec2, dest?: vec2): vec2; + static product(vector: vec2, vector2: vec2, dest?: vec2): vec2; + static quotient(vector: vec2, vector2: vec2, dest?: vec2): vec2; +} diff --git a/src/framework/tsm/vec2.js b/src/framework/tsm/vec2.js new file mode 100644 index 0000000..598afdc --- /dev/null +++ b/src/framework/tsm/vec2.js @@ -0,0 +1,215 @@ +import vec3 from './vec3'; +import { epsilon } from './constants'; +export default class vec2 { + constructor(values) { + this.values = new Float32Array(2); + if (values !== undefined) { + this.xy = values; + } + } + get x() { + return this.values[0]; + } + get y() { + return this.values[1]; + } + get xy() { + return [this.values[0], this.values[1]]; + } + set x(value) { + this.values[0] = value; + } + set y(value) { + this.values[1] = value; + } + set xy(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + at(index) { + return this.values[index]; + } + reset() { + this.x = 0; + this.y = 0; + } + copy(dest) { + if (!dest) { + dest = new vec2(); + } + dest.x = this.x; + dest.y = this.y; + return dest; + } + negate(dest) { + if (!dest) { + dest = this; + } + dest.x = -this.x; + dest.y = -this.y; + return dest; + } + equals(vector, threshold = epsilon) { + if (Math.abs(this.x - vector.x) > threshold) { + return false; + } + if (Math.abs(this.y - vector.y) > threshold) { + return false; + } + return true; + } + length() { + return Math.sqrt(this.squaredLength()); + } + squaredLength() { + const x = this.x; + const y = this.y; + return x * x + y * y; + } + add(vector) { + this.x += vector.x; + this.y += vector.y; + return this; + } + subtract(vector) { + this.x -= vector.x; + this.y -= vector.y; + return this; + } + multiply(vector) { + this.x *= vector.x; + this.y *= vector.y; + return this; + } + divide(vector) { + this.x /= vector.x; + this.y /= vector.y; + return this; + } + scale(value, dest) { + if (!dest) { + dest = this; + } + dest.x *= value; + dest.y *= value; + return dest; + } + normalize(dest) { + if (!dest) { + dest = this; + } + let length = this.length(); + if (length === 1) { + return this; + } + if (length === 0) { + dest.x = 0; + dest.y = 0; + return dest; + } + length = 1.0 / length; + dest.x *= length; + dest.y *= length; + return dest; + } + multiplyMat2(matrix, dest) { + if (!dest) { + dest = this; + } + return matrix.multiplyVec2(this, dest); + } + multiplyMat3(matrix, dest) { + if (!dest) { + dest = this; + } + return matrix.multiplyVec2(this, dest); + } + static cross(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + const x = vector.x; + const y = vector.y; + const x2 = vector2.x; + const y2 = vector2.y; + const z = x * y2 - y * x2; + dest.x = 0; + dest.y = 0; + dest.z = z; + return dest; + } + static dot(vector, vector2) { + return vector.x * vector2.x + vector.y * vector2.y; + } + static distance(vector, vector2) { + return Math.sqrt(this.squaredDistance(vector, vector2)); + } + static squaredDistance(vector, vector2) { + const x = vector2.x - vector.x; + const y = vector2.y - vector.y; + return x * x + y * y; + } + static direction(vector, vector2, dest) { + if (!dest) { + dest = new vec2(); + } + const x = vector.x - vector2.x; + const y = vector.y - vector2.y; + let length = Math.sqrt(x * x + y * y); + if (length === 0) { + dest.x = 0; + dest.y = 0; + return dest; + } + length = 1 / length; + dest.x = x * length; + dest.y = y * length; + return dest; + } + static mix(vector, vector2, time, dest) { + if (!dest) { + dest = new vec2(); + } + const x = vector.x; + const y = vector.y; + const x2 = vector2.x; + const y2 = vector2.y; + dest.x = x + time * (x2 - x); + dest.y = y + time * (y2 - y); + return dest; + } + static sum(vector, vector2, dest) { + if (!dest) { + dest = new vec2(); + } + dest.x = vector.x + vector2.x; + dest.y = vector.y + vector2.y; + return dest; + } + static difference(vector, vector2, dest) { + if (!dest) { + dest = new vec2(); + } + dest.x = vector.x - vector2.x; + dest.y = vector.y - vector2.y; + return dest; + } + static product(vector, vector2, dest) { + if (!dest) { + dest = new vec2(); + } + dest.x = vector.x * vector2.x; + dest.y = vector.y * vector2.y; + return dest; + } + static quotient(vector, vector2, dest) { + if (!dest) { + dest = new vec2(); + } + dest.x = vector.x / vector2.x; + dest.y = vector.y / vector2.y; + return dest; + } +} +vec2.zero = new vec2([0, 0]); +vec2.one = new vec2([1, 1]); diff --git a/src/framework/tsm/vec3.d.ts b/src/framework/tsm/vec3.d.ts new file mode 100644 index 0000000..900889a --- /dev/null +++ b/src/framework/tsm/vec3.d.ts @@ -0,0 +1,47 @@ +import mat3 from './mat3'; +import quat from './quat'; +export default class vec3 { + get x(): number; + get y(): number; + get z(): number; + get xy(): [number, number]; + get xyz(): [number, number, number]; + set x(value: number); + set y(value: number); + set z(value: number); + set xy(values: [number, number]); + set xyz(values: [number, number, number]); + constructor(values?: [number, number, number]); + private values; + static readonly zero: vec3; + static readonly one: vec3; + static readonly up: vec3; + static readonly right: vec3; + static readonly forward: vec3; + at(index: number): number; + reset(): void; + copy(dest?: vec3): vec3; + negate(dest?: vec3): vec3; + equals(vector: vec3, threshold?: number): boolean; + length(): number; + squaredLength(): number; + add(vector: vec3): vec3; + subtract(vector: vec3): vec3; + multiply(vector: vec3): vec3; + divide(vector: vec3): vec3; + scale(value: number, dest?: vec3): vec3; + normalize(dest?: vec3): vec3; + multiplyByMat3(matrix: mat3, dest?: vec3): vec3; + multiplyByQuat(quaternion: quat, dest?: vec3): vec3; + toQuat(dest?: quat): quat; + static cross(vector: vec3, vector2: vec3, dest?: vec3): vec3; + static dot(vector: vec3, vector2: vec3): number; + static distance(vector: vec3, vector2: vec3): number; + static squaredDistance(vector: vec3, vector2: vec3): number; + static direction(vector: vec3, vector2: vec3, dest?: vec3): vec3; + static mix(vector: vec3, vector2: vec3, time: number, dest?: vec3): vec3; + static sum(vector: vec3, vector2: vec3, dest?: vec3): vec3; + static difference(vector: vec3, vector2: vec3, dest?: vec3): vec3; + static product(vector: vec3, vector2: vec3, dest?: vec3): vec3; + static quotient(vector: vec3, vector2: vec3, dest?: vec3): vec3; +} diff --git a/src/framework/tsm/vec3.js b/src/framework/tsm/vec3.js new file mode 100644 index 0000000..26b6b21 --- /dev/null +++ b/src/framework/tsm/vec3.js @@ -0,0 +1,279 @@ +import quat from './quat'; +import { epsilon } from './constants'; +export default class vec3 { + constructor(values) { + this.values = new Float32Array(3); + if (values !== undefined) { + this.xyz = values; + } + } + get x() { + return this.values[0]; + } + get y() { + return this.values[1]; + } + get z() { + return this.values[2]; + } + get xy() { + return [this.values[0], this.values[1]]; + } + get xyz() { + return [this.values[0], this.values[1], this.values[2]]; + } + set x(value) { + this.values[0] = value; + } + set y(value) { + this.values[1] = value; + } + set z(value) { + this.values[2] = value; + } + set xy(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + set xyz(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + } + at(index) { + return this.values[index]; + } + reset() { + this.x = 0; + this.y = 0; + this.z = 0; + } + copy(dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = this.x; + dest.y = this.y; + dest.z = this.z; + return dest; + } + negate(dest) { + if (!dest) { + dest = this; + } + dest.x = -this.x; + dest.y = -this.y; + dest.z = -this.z; + return dest; + } + equals(vector, threshold = epsilon) { + if (Math.abs(this.x - vector.x) > threshold) { + return false; + } + if (Math.abs(this.y - vector.y) > threshold) { + return false; + } + if (Math.abs(this.z - vector.z) > threshold) { + return false; + } + return true; + } + length() { + return Math.sqrt(this.squaredLength()); + } + squaredLength() { + const x = this.x; + const y = this.y; + const z = this.z; + return x * x + y * y + z * z; + } + add(vector) { + this.x += vector.x; + this.y += vector.y; + this.z += vector.z; + return this; + } + subtract(vector) { + this.x -= vector.x; + this.y -= vector.y; + this.z -= vector.z; + return this; + } + multiply(vector) { + this.x *= vector.x; + this.y *= vector.y; + this.z *= vector.z; + return this; + } + divide(vector) { + this.x /= vector.x; + this.y /= vector.y; + this.z /= vector.z; + return this; + } + scale(value, dest) { + if (!dest) { + dest = this; + } + dest.x *= value; + dest.y *= value; + dest.z *= value; + return dest; + } + normalize(dest) { + if (!dest) { + dest = this; + } + let length = this.length(); + if (length === 1) { + return this; + } + if (length === 0) { + dest.x = 0; + dest.y = 0; + dest.z = 0; + return dest; + } + length = 1.0 / length; + dest.x *= length; + dest.y *= length; + dest.z *= length; + return dest; + } + multiplyByMat3(matrix, dest) { + if (!dest) { + dest = this; + } + return matrix.multiplyVec3(this, dest); + } + multiplyByQuat(quaternion, dest) { + if (!dest) { + dest = this; + } + return quaternion.multiplyVec3(this, dest); + } + toQuat(dest) { + if (!dest) { + dest = new quat(); + } + const c = new vec3(); + const s = new vec3(); + c.x = Math.cos(this.x * 0.5); + s.x = Math.sin(this.x * 0.5); + c.y = Math.cos(this.y * 0.5); + s.y = Math.sin(this.y * 0.5); + c.z = Math.cos(this.z * 0.5); + s.z = Math.sin(this.z * 0.5); + dest.x = s.x * c.y * c.z - c.x * s.y * s.z; + dest.y = c.x * s.y * c.z + s.x * c.y * s.z; + dest.z = c.x * c.y * s.z - s.x * s.y * c.z; + dest.w = c.x * c.y * c.z + s.x * s.y * s.z; + return dest; + } + static cross(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + const x = vector.x; + const y = vector.y; + const z = vector.z; + const x2 = vector2.x; + const y2 = vector2.y; + const z2 = vector2.z; + dest.x = y * z2 - z * y2; + dest.y = z * x2 - x * z2; + dest.z = x * y2 - y * x2; + return dest; + } + static dot(vector, vector2) { + const x = vector.x; + const y = vector.y; + const z = vector.z; + const x2 = vector2.x; + const y2 = vector2.y; + const z2 = vector2.z; + return x * x2 + y * y2 + z * z2; + } + static distance(vector, vector2) { + const x = vector2.x - vector.x; + const y = vector2.y - vector.y; + const z = vector2.z - vector.z; + return Math.sqrt(this.squaredDistance(vector, vector2)); + } + static squaredDistance(vector, vector2) { + const x = vector2.x - vector.x; + const y = vector2.y - vector.y; + const z = vector2.z - vector.z; + return x * x + y * y + z * z; + } + static direction(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + const x = vector.x - vector2.x; + const y = vector.y - vector2.y; + const z = vector.z - vector2.z; + let length = Math.sqrt(x * x + y * y + z * z); + if (length === 0) { + dest.x = 0; + dest.y = 0; + dest.z = 0; + return dest; + } + length = 1 / length; + dest.x = x * length; + dest.y = y * length; + dest.z = z * length; + return dest; + } + static mix(vector, vector2, time, dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = vector.x + time * (vector2.x - vector.x); + dest.y = vector.y + time * (vector2.y - vector.y); + dest.z = vector.z + time * (vector2.z - vector.z); + return dest; + } + static sum(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = vector.x + vector2.x; + dest.y = vector.y + vector2.y; + dest.z = vector.z + vector2.z; + return dest; + } + static difference(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = vector.x - vector2.x; + dest.y = vector.y - vector2.y; + dest.z = vector.z - vector2.z; + return dest; + } + static product(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = vector.x * vector2.x; + dest.y = vector.y * vector2.y; + dest.z = vector.z * vector2.z; + return dest; + } + static quotient(vector, vector2, dest) { + if (!dest) { + dest = new vec3(); + } + dest.x = vector.x / vector2.x; + dest.y = vector.y / vector2.y; + dest.z = vector.z / vector2.z; + return dest; + } +} +vec3.zero = new vec3([0, 0, 0]); +vec3.one = new vec3([1, 1, 1]); +vec3.up = new vec3([0, 1, 0]); +vec3.right = new vec3([1, 0, 0]); +vec3.forward = new vec3([0, 0, 1]); diff --git a/src/framework/tsm/vec4.d.ts b/src/framework/tsm/vec4.d.ts new file mode 100644 index 0000000..35366a8 --- /dev/null +++ b/src/framework/tsm/vec4.d.ts @@ -0,0 +1,54 @@ +import mat4 from './mat4'; +export default class vec4 { + get x(): number; + get y(): number; + get z(): number; + get w(): number; + get xy(): [number, number]; + get xyz(): [number, number, number]; + get xyzw(): [number, number, number, number]; + set x(value: number); + set y(value: number); + set z(value: number); + set w(value: number); + set xy(values: [number, number]); + set xyz(values: [number, number, number]); + set xyzw(values: [number, number, number, number]); + get r(): number; + get g(): number; + get b(): number; + get a(): number; + get rg(): [number, number]; + get rgb(): [number, number, number]; + get rgba(): [number, number, number, number]; + set r(value: number); + set g(value: number); + set b(value: number); + set a(value: number); + set rg(values: [number, number]); + set rgb(values: [number, number, number]); + set rgba(values: [number, number, number, number]); + constructor(values?: [number, number, number, number]); + private values; + static readonly zero: vec4; + static readonly one: vec4; + at(index: number): number; + reset(): void; + copy(dest?: vec4): vec4; + negate(dest?: vec4): vec4; + equals(vector: vec4, threshold?: number): boolean; + length(): number; + squaredLength(): number; + add(vector: vec4): vec4; + subtract(vector: vec4): vec4; + multiply(vector: vec4): vec4; + divide(vector: vec4): vec4; + scale(value: number, dest?: vec4): vec4; + normalize(dest?: vec4): vec4; + multiplyMat4(matrix: mat4, dest?: vec4): vec4; + static mix(vector: vec4, vector2: vec4, time: number, dest?: vec4): vec4; + static sum(vector: vec4, vector2: vec4, dest?: vec4): vec4; + static difference(vector: vec4, vector2: vec4, dest?: vec4): vec4; + static product(vector: vec4, vector2: vec4, dest?: vec4): vec4; + static quotient(vector: vec4, vector2: vec4, dest?: vec4): vec4; +} diff --git a/src/framework/tsm/vec4.js b/src/framework/tsm/vec4.js new file mode 100644 index 0000000..fd0337d --- /dev/null +++ b/src/framework/tsm/vec4.js @@ -0,0 +1,277 @@ +import { epsilon } from './constants'; +export default class vec4 { + constructor(values) { + this.values = new Float32Array(4); + if (values !== undefined) { + this.xyzw = values; + } + } + get x() { + return this.values[0]; + } + get y() { + return this.values[1]; + } + get z() { + return this.values[2]; + } + get w() { + return this.values[3]; + } + get xy() { + return [this.values[0], this.values[1]]; + } + get xyz() { + return [this.values[0], this.values[1], this.values[2]]; + } + get xyzw() { + return [this.values[0], this.values[1], this.values[2], this.values[3]]; + } + set x(value) { + this.values[0] = value; + } + set y(value) { + this.values[1] = value; + } + set z(value) { + this.values[2] = value; + } + set w(value) { + this.values[3] = value; + } + set xy(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + set xyz(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + } + set xyzw(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + this.values[3] = values[3]; + } + get r() { + return this.values[0]; + } + get g() { + return this.values[1]; + } + get b() { + return this.values[2]; + } + get a() { + return this.values[3]; + } + get rg() { + return [this.values[0], this.values[1]]; + } + get rgb() { + return [this.values[0], this.values[1], this.values[2]]; + } + get rgba() { + return [this.values[0], this.values[1], this.values[2], this.values[3]]; + } + set r(value) { + this.values[0] = value; + } + set g(value) { + this.values[1] = value; + } + set b(value) { + this.values[2] = value; + } + set a(value) { + this.values[3] = value; + } + set rg(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + set rgb(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + } + set rgba(values) { + this.values[0] = values[0]; + this.values[1] = values[1]; + this.values[2] = values[2]; + this.values[3] = values[3]; + } + at(index) { + return this.values[index]; + } + reset() { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + } + copy(dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = this.x; + dest.y = this.y; + dest.z = this.z; + dest.w = this.w; + return dest; + } + negate(dest) { + if (!dest) { + dest = this; + } + dest.x = -this.x; + dest.y = -this.y; + dest.z = -this.z; + dest.w = -this.w; + return dest; + } + equals(vector, threshold = epsilon) { + if (Math.abs(this.x - vector.x) > threshold) { + return false; + } + if (Math.abs(this.y - vector.y) > threshold) { + return false; + } + if (Math.abs(this.z - vector.z) > threshold) { + return false; + } + if (Math.abs(this.w - vector.w) > threshold) { + return false; + } + return true; + } + length() { + return Math.sqrt(this.squaredLength()); + } + squaredLength() { + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + return x * x + y * y + z * z + w * w; + } + add(vector) { + this.x += vector.x; + this.y += vector.y; + this.z += vector.z; + this.w += vector.w; + return this; + } + subtract(vector) { + this.x -= vector.x; + this.y -= vector.y; + this.z -= vector.z; + this.w -= vector.w; + return this; + } + multiply(vector) { + this.x *= vector.x; + this.y *= vector.y; + this.z *= vector.z; + this.w *= vector.w; + return this; + } + divide(vector) { + this.x /= vector.x; + this.y /= vector.y; + this.z /= vector.z; + this.w /= vector.w; + return this; + } + scale(value, dest) { + if (!dest) { + dest = this; + } + dest.x *= value; + dest.y *= value; + dest.z *= value; + dest.w *= value; + return dest; + } + normalize(dest) { + if (!dest) { + dest = this; + } + let length = this.length(); + if (length === 1) { + return this; + } + if (length === 0) { + dest.x *= 0; + dest.y *= 0; + dest.z *= 0; + dest.w *= 0; + return dest; + } + length = 1.0 / length; + dest.x *= length; + dest.y *= length; + dest.z *= length; + dest.w *= length; + return dest; + } + multiplyMat4(matrix, dest) { + if (!dest) { + dest = this; + } + return matrix.multiplyVec4(this, dest); + } + static mix(vector, vector2, time, dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = vector.x + time * (vector2.x - vector.x); + dest.y = vector.y + time * (vector2.y - vector.y); + dest.z = vector.z + time * (vector2.z - vector.z); + dest.w = vector.w + time * (vector2.w - vector.w); + return dest; + } + static sum(vector, vector2, dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = vector.x + vector2.x; + dest.y = vector.y + vector2.y; + dest.z = vector.z + vector2.z; + dest.w = vector.w + vector2.w; + return dest; + } + static difference(vector, vector2, dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = vector.x - vector2.x; + dest.y = vector.y - vector2.y; + dest.z = vector.z - vector2.z; + dest.w = vector.w - vector2.w; + return dest; + } + static product(vector, vector2, dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = vector.x * vector2.x; + dest.y = vector.y * vector2.y; + dest.z = vector.z * vector2.z; + dest.w = vector.w * vector2.w; + return dest; + } + static quotient(vector, vector2, dest) { + if (!dest) { + dest = new vec4(); + } + dest.x = vector.x / vector2.x; + dest.y = vector.y / vector2.y; + dest.z = vector.z / vector2.z; + dest.w = vector.w / vector2.w; + return dest; + } +} +vec4.zero = new vec4([0, 0, 0, 1]); +vec4.one = new vec4([1, 1, 1, 1]); diff --git a/src/framework/tts/index.d.ts b/src/framework/tts/index.d.ts new file mode 100644 index 0000000..d3ca110 --- /dev/null +++ b/src/framework/tts/index.d.ts @@ -0,0 +1,7 @@ +import { BaseOutput } from './outputs/base-output'; +export declare class TTS { + private output; + constructor(output?: BaseOutput); + speak(text: string): void; + stop(): void; +} diff --git a/src/framework/tts/index.js b/src/framework/tts/index.js new file mode 100644 index 0000000..85edb61 --- /dev/null +++ b/src/framework/tts/index.js @@ -0,0 +1,12 @@ +import { createOutput } from './output-factory'; +export class TTS { + constructor(output = createOutput()) { + this.output = output; + } + speak(text) { + this.output.speak(text); + } + stop() { + this.output.stop(); + } +} diff --git a/src/framework/tts/output-factory.d.ts b/src/framework/tts/output-factory.d.ts new file mode 100644 index 0000000..e034ee0 --- /dev/null +++ b/src/framework/tts/output-factory.d.ts @@ -0,0 +1,5 @@ +import { BaseOutput } from './outputs/base-output'; +import { AriaOutput } from './outputs/aria'; +import { WebTTSOutput } from './outputs/webtts'; +export declare function createOutput(key?: string): any; +export { WebTTSOutput, AriaOutput, BaseOutput }; diff --git a/src/framework/tts/output-factory.js b/src/framework/tts/output-factory.js new file mode 100644 index 0000000..6ef84f2 --- /dev/null +++ b/src/framework/tts/output-factory.js @@ -0,0 +1,17 @@ +import { BaseOutput } from './outputs/base-output'; +import { AriaOutput } from './outputs/aria'; +import { WebTTSOutput } from './outputs/webtts'; +export function createOutput(key = 'aria') { + switch (key) { + case 'aria': + return AriaOutput; + break; + case 'webtts': + return WebTTSOutput; + break; + default: + return AriaOutput; + break; + } +} +export { WebTTSOutput, AriaOutput, BaseOutput }; diff --git a/src/framework/tts/outputs/aria.d.ts b/src/framework/tts/outputs/aria.d.ts new file mode 100644 index 0000000..5efbf8a --- /dev/null +++ b/src/framework/tts/outputs/aria.d.ts @@ -0,0 +1,11 @@ +import { BaseOutput } from './base-output'; +export declare class AriaOutput extends BaseOutput { + private container; + private speechDisplay; + private timeout; + constructor(options?: any); + private init; + speak(text: string): void; + stop(): void; + clearDisplay(): void; +} diff --git a/src/framework/tts/outputs/aria.js b/src/framework/tts/outputs/aria.js new file mode 100644 index 0000000..f3d5ec9 --- /dev/null +++ b/src/framework/tts/outputs/aria.js @@ -0,0 +1,32 @@ +import { BaseOutput } from './base-output'; +export class AriaOutput extends BaseOutput { + constructor(options = {}) { + super(); + this.timeout = 100; + this.timeout = options.timeout || 100; + this.init(); + } + init() { + this.container = document.createElement('div'); + this.container.setAttribute('aria-live', 'polite'); + this.speechDisplay = document.createElement('div'); + this.speechDisplay.setAttribute('aria-live', 'polite'); + this.container.append(this.speechDisplay); + document.body.appendChild(this.container); + document.body.insertBefore(this.container, document.body.firstChild); + } + speak(text) { + this.clearDisplay(); + const node = document.createTextNode(text); + const para = document.createElement('p'); + para.appendChild(node); + this.speechDisplay.appendChild(para); + setTimeout(this.clearDisplay.bind(this), this.timeout); + } + stop() { + this.clearDisplay(); + } + clearDisplay() { + this.speechDisplay.innerHTML = ''; + } +} diff --git a/src/framework/tts/outputs/base-output.d.ts b/src/framework/tts/outputs/base-output.d.ts new file mode 100644 index 0000000..a89f559 --- /dev/null +++ b/src/framework/tts/outputs/base-output.d.ts @@ -0,0 +1,5 @@ +export declare class BaseOutput { + speak(text: string): void; + stop(): void; + setOptions(options: any): void; +} diff --git a/src/framework/tts/outputs/base-output.js b/src/framework/tts/outputs/base-output.js new file mode 100644 index 0000000..762054b --- /dev/null +++ b/src/framework/tts/outputs/base-output.js @@ -0,0 +1,11 @@ +export class BaseOutput { + speak(text) { + return; + } + stop() { + return; + } + setOptions(options) { + return; + } +} diff --git a/src/framework/tts/outputs/webtts.d.ts b/src/framework/tts/outputs/webtts.d.ts new file mode 100644 index 0000000..8922e09 --- /dev/null +++ b/src/framework/tts/outputs/webtts.d.ts @@ -0,0 +1,3 @@ +import { BaseOutput } from './base-output'; +export declare class WebTTSOutput extends BaseOutput { +} diff --git a/src/framework/tts/outputs/webtts.js b/src/framework/tts/outputs/webtts.js new file mode 100644 index 0000000..dd4baad --- /dev/null +++ b/src/framework/tts/outputs/webtts.js @@ -0,0 +1,3 @@ +import { BaseOutput } from './base-output'; +export class WebTTSOutput extends BaseOutput { +} diff --git a/src/framework/ui/index.d.ts b/src/framework/ui/index.d.ts new file mode 100644 index 0000000..08963e3 --- /dev/null +++ b/src/framework/ui/index.d.ts @@ -0,0 +1 @@ +export * from './menu/index'; diff --git a/src/framework/ui/index.js b/src/framework/ui/index.js new file mode 100644 index 0000000..8d4cb7f --- /dev/null +++ b/src/framework/ui/index.js @@ -0,0 +1,2 @@ +export * from './menu/index'; +// export * as Text from './text'; diff --git a/src/framework/ui/menu/index.d.ts b/src/framework/ui/menu/index.d.ts new file mode 100644 index 0000000..aab85a9 --- /dev/null +++ b/src/framework/ui/menu/index.d.ts @@ -0,0 +1,39 @@ +import { BaseItem } from './items/base-item'; +import { SoundSet } from './interfaces/sound-set'; +import * as EventEmitter from 'eventemitter3'; +export declare class Menu extends EventEmitter { + private title; + private menuItems; + private soundSet; + private defaultAction; + private cancelAction; + private titleContainer; + private currentItem; + private currentIndex; + private container; + private element; + private DOMNodes; + private soundManager; + private keyboardManager; + constructor(title?: string, menuItems?: BaseItem[], soundSet?: SoundSet, defaultAction?: string, cancelAction?: string); + private init; + addItem(item: BaseItem): this; + setTitle(title: string): this; + setSoundSet(soundSet: SoundSet): this; + setDefaultAction(id: string): this; + setCancelAction(id: string): this; + run(element: HTMLElement): Promise; + close(): void; + private appendToContainer; + private handleItemUpdate; + private onItemFocus; + focusNext(): void; + focusPrevious(): void; + private focusCurrentIndex; + getCurrentFocus(): BaseItem; + getContainer(): HTMLElement; + clickDefaultAction(): void; + clickCancelAction(): void; + private compile; +} +export * from './items'; diff --git a/src/framework/ui/menu/index.js b/src/framework/ui/menu/index.js new file mode 100644 index 0000000..bb53ddf --- /dev/null +++ b/src/framework/ui/menu/index.js @@ -0,0 +1,145 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import * as EventEmitter from 'eventemitter3'; +import { SoundManager } from './sound-manager'; +import { KeyboardManager } from './keyboard-manager'; +export class Menu extends EventEmitter { + constructor(title = 'Menu', menuItems = [], soundSet = null, defaultAction = null, cancelAction = null) { + super(); + this.title = title; + this.menuItems = menuItems; + this.soundSet = soundSet; + this.defaultAction = defaultAction; + this.cancelAction = cancelAction; + this.currentIndex = 0; + this.DOMNodes = []; + this.currentIndex = 0; + this.currentItem = null; + this.soundManager = new SoundManager(soundSet); + this.keyboardManager = new KeyboardManager(this); + this.init(); + } + init() { + this.menuItems[this.currentIndex] && + this.menuItems[this.currentIndex].focus(); + this.emit('init'); + } + addItem(item) { + this.menuItems.push(item); + this.emit('item.add', item); + return this; + } + setTitle(title) { + this.title = title; + return this; + } + setSoundSet(soundSet) { + this.soundSet = soundSet; + this.soundManager.setSoundSet(this.soundSet); + return this; + } + setDefaultAction(id) { + this.defaultAction = id; + return this; + } + setCancelAction(id) { + this.cancelAction = id; + return this; + } + run(element) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve, reject) => { + this.element = element; + this.container = document.createElement('div'); + this.titleContainer = document.createElement('h1'); + this.titleContainer.textContent = this.title; + this.container.appendChild(this.titleContainer); + this.menuItems.forEach((item) => { + this.appendToContainer(item.getDOMNode()); + item.on('update', this.handleItemUpdate.bind(this)); + item.on('focus', this.onItemFocus.bind(this)); + item.on('choose', (event) => { + const menuMap = this.compile(); + this.soundManager.handleSound('choose'); + this.emit('choose', menuMap); + resolve(menuMap); + }); + }); + element.appendChild(this.container); + this.soundManager.handleSound('open'); + this.keyboardManager.init(); + // push some data onto the history stack so that we can use the browser's back button to exit out of the menu. + history.pushState({ menu: true }, null, null); + }); + }); + } + close() { + this.container.remove(); + this.soundManager.handleSound('close'); + this.keyboardManager.release(); + this.DOMNodes.forEach((item) => { + this.container.removeChild(item); + }); + this.emit('close'); + } + appendToContainer(node) { + this.container.appendChild(node); + this.DOMNodes.push(node); + } + handleItemUpdate(value) { + this.soundManager.handleSound(value.type, value.value); + this.emit('update', this.compile()); + } + onItemFocus(id) { + this.soundManager.handleSound('focus'); + this.currentIndex = this.menuItems.indexOf(this.menuItems.find((item) => item.getID() == id)); + this.emit('focus', this.menuItems[this.currentIndex]); + } + focusNext() { + if (this.currentIndex < this.menuItems.length - 1) { + this.currentIndex++; + } + this.focusCurrentIndex(); + } + focusPrevious() { + if (this.currentIndex > 0) { + this.currentIndex--; + } + this.focusCurrentIndex(); + } + focusCurrentIndex() { + this.menuItems[this.currentIndex].focus(); + } + getCurrentFocus() { + return this.menuItems[this.currentIndex]; + } + getContainer() { + return this.container; + } + clickDefaultAction() { + if (!this.defaultAction) + return; + const item = this.menuItems.find((item) => item.getID() === this.defaultAction); + item.click(); + } + clickCancelAction() { + if (!this.cancelAction) + return; + const node = this.menuItems.find((item) => item.getID() === this.cancelAction); + node.click(); + } + compile() { + const menuMap = new Map(); + this.menuItems.forEach((item) => menuMap.set(item.getID(), item.getContents())); + menuMap.set('selected', this.menuItems[this.currentIndex].getID()); + return menuMap; + } +} +export * from './items'; diff --git a/src/framework/ui/menu/interfaces/playable-sound.d.ts b/src/framework/ui/menu/interfaces/playable-sound.d.ts new file mode 100644 index 0000000..900dad8 --- /dev/null +++ b/src/framework/ui/menu/interfaces/playable-sound.d.ts @@ -0,0 +1,3 @@ +export interface IPlayableSound { + play(): any; +} diff --git a/src/framework/ui/menu/interfaces/playable-sound.js b/src/framework/ui/menu/interfaces/playable-sound.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/ui/menu/interfaces/playable-sound.js @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/ui/menu/interfaces/sound-set.d.ts b/src/framework/ui/menu/interfaces/sound-set.d.ts new file mode 100644 index 0000000..8297040 --- /dev/null +++ b/src/framework/ui/menu/interfaces/sound-set.d.ts @@ -0,0 +1,17 @@ +import { IPlayableSound } from './playable-sound'; +export interface SoundSet { + open?: IPlayableSound; + close?: IPlayableSound; + boundary?: IPlayableSound; + choose?: IPlayableSound; + move?: IPlayableSound; + scroller?: IPlayableSound; + sliderLeft?: IPlayableSound; + sliderRight?: IPlayableSound; + wrap?: IPlayableSound; + char?: IPlayableSound; + delete?: IPlayableSound; + enter?: IPlayableSound; + checked?: IPlayableSound; + unchecked?: IPlayableSound; +} diff --git a/src/framework/ui/menu/interfaces/sound-set.js b/src/framework/ui/menu/interfaces/sound-set.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/ui/menu/interfaces/sound-set.js @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/ui/menu/items/base-item.d.ts b/src/framework/ui/menu/items/base-item.d.ts new file mode 100644 index 0000000..6e8bd57 --- /dev/null +++ b/src/framework/ui/menu/items/base-item.d.ts @@ -0,0 +1,13 @@ +import * as EventEmitter from 'eventemitter3'; +export declare class BaseItem extends EventEmitter { + protected id: string; + protected title: string; + protected container: HTMLElement; + constructor(id: string, title: string); + getDOMNode(): HTMLElement; + getContents(): void; + protected onFocus(event: Event): void; + focus(): void; + click(): void; + getID(): string; +} diff --git a/src/framework/ui/menu/items/base-item.js b/src/framework/ui/menu/items/base-item.js new file mode 100644 index 0000000..c9c64a7 --- /dev/null +++ b/src/framework/ui/menu/items/base-item.js @@ -0,0 +1,29 @@ +import * as EventEmitter from 'eventemitter3'; +export class BaseItem extends EventEmitter { + constructor(id, title) { + super(); + this.id = id; + this.title = title; + } + getDOMNode() { + let node = document.createTextNode(this.title); + let element = document.createElement('div'); + element.appendChild(node); + return element; + } + getContents() { + return; + } + onFocus(event) { + this.emit('focus', this.id); + } + focus() { + this.container && this.container.focus(); + } + click() { + return; + } + getID() { + return this.id; + } +} diff --git a/src/framework/ui/menu/items/checkbox-item.d.ts b/src/framework/ui/menu/items/checkbox-item.d.ts new file mode 100644 index 0000000..faa398d --- /dev/null +++ b/src/framework/ui/menu/items/checkbox-item.d.ts @@ -0,0 +1,10 @@ +import { BaseItem } from './base-item'; +export declare class CheckboxItem extends BaseItem { + private checkboxElement; + private label; + constructor(id: string, title: string); + getDOMNode(): HTMLElement; + getContents(): boolean; + private onChange; + focus(): void; +} diff --git a/src/framework/ui/menu/items/checkbox-item.js b/src/framework/ui/menu/items/checkbox-item.js new file mode 100644 index 0000000..93d539d --- /dev/null +++ b/src/framework/ui/menu/items/checkbox-item.js @@ -0,0 +1,32 @@ +import { BaseItem } from './base-item'; +export class CheckboxItem extends BaseItem { + constructor(id, title) { + super(id, title); + } + getDOMNode() { + this.container = document.createElement('div'); + this.label = document.createElement('label'); + this.label.setAttribute('for', `chkbx_${this.id}`); + this.label.textContent = this.title; + this.checkboxElement = document.createElement('input'); + this.checkboxElement.setAttribute('type', 'checkbox'); + this.checkboxElement.setAttribute('id', `chkbx_${this.id}`); + this.checkboxElement.addEventListener('focus', this.onFocus.bind(this)); + this.checkboxElement.addEventListener('change', this.onChange.bind(this)); + this.container.appendChild(this.label); + this.container.appendChild(this.checkboxElement); + return this.container; + } + getContents() { + return this.checkboxElement.checked; + } + onChange(event) { + this.emit('update', { + type: 'checkbox', + value: this.checkboxElement.checked + }); + } + focus() { + this.checkboxElement.focus(); + } +} diff --git a/src/framework/ui/menu/items/edit-item.d.ts b/src/framework/ui/menu/items/edit-item.d.ts new file mode 100644 index 0000000..2aa90b1 --- /dev/null +++ b/src/framework/ui/menu/items/edit-item.d.ts @@ -0,0 +1,13 @@ +import { BaseItem } from './base-item'; +export declare class EditItem extends BaseItem { + private initialText; + private isPassword; + private contents; + private label; + private editField; + constructor(id: string, title: string, initialText: string, isPassword?: boolean); + getDOMNode(): HTMLElement; + getContents(): string; + private onChange; + focus(): void; +} diff --git a/src/framework/ui/menu/items/edit-item.js b/src/framework/ui/menu/items/edit-item.js new file mode 100644 index 0000000..2954189 --- /dev/null +++ b/src/framework/ui/menu/items/edit-item.js @@ -0,0 +1,42 @@ +import { BaseItem } from './base-item'; +export class EditItem extends BaseItem { + constructor(id, title, initialText, isPassword = false) { + super(id, title); + this.initialText = initialText; + this.isPassword = isPassword; + this.contents = initialText; + } + getDOMNode() { + const node = document.createElement('div'); + const label = document.createElement('label'); + label.setAttribute('for', `edit_${this.id}`); + label.textContent = this.title; + const editField = document.createElement('input'); + editField.id = `edit_${this.id}`; + editField.value = this.contents; + editField.addEventListener('keydown', this.onChange.bind(this)); + editField.addEventListener('focus', this.onFocus.bind(this)); + if (this.isPassword) { + editField.type = 'password'; + } + node.appendChild(label); + node.appendChild(editField); + node.addEventListener('focus', this.onFocus.bind(this)); + this.editField = editField; + this.label = label; + this.container = node; + return node; + } + getContents() { + return this.editField.value; + } + onChange(event) { + this.emit('update', { + type: 'edit', + value: this.editField.value + }); + } + focus() { + this.editField && this.editField.focus(); + } +} diff --git a/src/framework/ui/menu/items/index.d.ts b/src/framework/ui/menu/items/index.d.ts new file mode 100644 index 0000000..ef7e6d9 --- /dev/null +++ b/src/framework/ui/menu/items/index.d.ts @@ -0,0 +1,6 @@ +export { BaseItem } from './base-item'; +export { EditItem } from './edit-item'; +export { MenuItem } from './menu-item'; +export { SelectorItem } from './selector-item'; +export { SliderItem } from './slider-item'; +export { CheckboxItem } from './checkbox-item'; diff --git a/src/framework/ui/menu/items/index.js b/src/framework/ui/menu/items/index.js new file mode 100644 index 0000000..ef7e6d9 --- /dev/null +++ b/src/framework/ui/menu/items/index.js @@ -0,0 +1,6 @@ +export { BaseItem } from './base-item'; +export { EditItem } from './edit-item'; +export { MenuItem } from './menu-item'; +export { SelectorItem } from './selector-item'; +export { SliderItem } from './slider-item'; +export { CheckboxItem } from './checkbox-item'; diff --git a/src/framework/ui/menu/items/menu-item.d.ts b/src/framework/ui/menu/items/menu-item.d.ts new file mode 100644 index 0000000..d917d70 --- /dev/null +++ b/src/framework/ui/menu/items/menu-item.d.ts @@ -0,0 +1,10 @@ +import { BaseItem } from './base-item'; +export declare class MenuItem extends BaseItem { + private button; + constructor(id: string, title: string); + getDOMNode(): HTMLElement; + getContents(): string; + private handleClick; + focus(): void; + click(): void; +} diff --git a/src/framework/ui/menu/items/menu-item.js b/src/framework/ui/menu/items/menu-item.js new file mode 100644 index 0000000..2463340 --- /dev/null +++ b/src/framework/ui/menu/items/menu-item.js @@ -0,0 +1,29 @@ +import { BaseItem } from './base-item'; +export class MenuItem extends BaseItem { + constructor(id, title) { + super(id, title); + } + getDOMNode() { + const container = document.createElement('div'); + const button = document.createElement('button'); + button.textContent = this.title; + button.addEventListener('click', this.handleClick.bind(this)); + button.addEventListener('focus', this.onFocus.bind(this)); + container.appendChild(button); + this.container = container; + this.button = button; + return container; + } + getContents() { + return this.id; + } + handleClick(event) { + this.emit('choose', this.id); + } + focus() { + this.button && this.button.focus(); + } + click() { + this.button.click(); + } +} diff --git a/src/framework/ui/menu/items/selector-item.d.ts b/src/framework/ui/menu/items/selector-item.d.ts new file mode 100644 index 0000000..6c455fb --- /dev/null +++ b/src/framework/ui/menu/items/selector-item.d.ts @@ -0,0 +1,21 @@ +import { BaseItem } from './base-item'; +export declare class SelectorItem extends BaseItem { + private items; + private listContainer; + private fieldSet; + private label; + private entries; + private currentValue; + constructor(id: string, title: string, items: SelectorEntry[]); + getDOMNode(): HTMLElement; + private buildEntries; + private onItemFocus; + getContents(): any; + private onSelectItem; + private onChangeItem; + focus(): void; +} +export interface SelectorEntry { + id: string; + title: string; +} diff --git a/src/framework/ui/menu/items/selector-item.js b/src/framework/ui/menu/items/selector-item.js new file mode 100644 index 0000000..ce7c1fb --- /dev/null +++ b/src/framework/ui/menu/items/selector-item.js @@ -0,0 +1,62 @@ +import { BaseItem } from './base-item'; +export class SelectorItem extends BaseItem { + constructor(id, title, items) { + super(id, title); + this.items = items; + this.entries = []; + } + getDOMNode() { + this.container = document.createElement('div'); + this.listContainer = document.createElement('ul'); + this.label = document.createElement('legend'); + this.fieldSet = document.createElement('fieldset'); + this.fieldSet.setAttribute('class', 'radiogroup'); + this.fieldSet.id = `fs_selector_${this.id}`; + const name = document.createTextNode(this.title); + this.label.appendChild(name); + this.fieldSet.appendChild(this.label); + this.buildEntries(); + this.container.appendChild(this.fieldSet); + this.container.addEventListener('focus', this.onFocus.bind(this)); + return this.container; + } + buildEntries() { + this.items.forEach((item, index) => { + const node = document.createElement('input'); + node.type = 'radio'; + node.id = `${this.id}_${item.id}`; + node.name = this.id; + node.value = item.id || `${index}`; + node.addEventListener('focus', this.onItemFocus.bind(this)); + node.addEventListener('select', this.onSelectItem.bind(this)); + node.addEventListener('change', this.onChangeItem.bind(this)); + this.entries.push(node); + const label = document.createElement('label'); + label.setAttribute('for', `${this.id}_${item.id}`); + label.textContent = item.title; + this.fieldSet.append(node); + this.fieldSet.append(label); + }); + } + onItemFocus(event) { + console.log(`Item focused: `, event); + this.emit('focus', this.id); + } + getContents() { + return this.currentValue; + } + onSelectItem(event) { } + onChangeItem(event) { + const node = document.querySelector(`input[name = "${this.id}"]:checked`); + this.currentValue = this.items.find((item) => `${this.id}_${item.id}` === node.id); + this.emit('update', { + type: 'selector', + value: this.currentValue + }); + } + focus() { + const node = document.querySelector(`input[name = "${this.id}"]:checked`) || + this.entries[0]; + node.focus(); + } +} diff --git a/src/framework/ui/menu/items/slider-item.d.ts b/src/framework/ui/menu/items/slider-item.d.ts new file mode 100644 index 0000000..505aa3d --- /dev/null +++ b/src/framework/ui/menu/items/slider-item.d.ts @@ -0,0 +1,15 @@ +import { BaseItem } from './base-item'; +export declare class SliderItem extends BaseItem { + private min; + private max; + private step; + private defaultValue; + private slider; + private label; + private currentValue; + constructor(id: string, title: string, min: number, max: number, step: number, defaultValue?: number); + getDOMNode(): HTMLElement; + getContents(): string; + private onChange; + focus(): void; +} diff --git a/src/framework/ui/menu/items/slider-item.js b/src/framework/ui/menu/items/slider-item.js new file mode 100644 index 0000000..117b39d --- /dev/null +++ b/src/framework/ui/menu/items/slider-item.js @@ -0,0 +1,42 @@ +import { BaseItem } from './base-item'; +export class SliderItem extends BaseItem { + constructor(id, title, min, max, step, defaultValue = null) { + super(id, title); + this.min = min; + this.max = max; + this.step = step; + this.defaultValue = defaultValue; + } + getDOMNode() { + this.container = document.createElement('div'); + this.label = document.createElement('label'); + this.label.textContent = this.title; + this.label.setAttribute('for', `slider_${this.id}`); + this.slider = document.createElement('input'); + this.slider.id = `slider_${this.id}`; + this.slider.type = 'range'; + this.slider.setAttribute('min', this.min.toString()); + this.slider.setAttribute('max', this.max.toString()); + this.slider.setAttribute('step', this.step.toString()); + if (this.defaultValue) + this.slider.value = this.defaultValue.toString(); + this.slider.addEventListener('change', this.onChange.bind(this)); + this.slider.addEventListener('focus', this.onFocus.bind(this)); + this.container.appendChild(this.label); + this.container.appendChild(this.slider); + this.container.addEventListener('focus', this.onFocus.bind(this)); + return this.container; + } + getContents() { + return this.slider.value; + } + onChange(event) { + this.emit('update', { + type: 'slider', + value: this.slider.value + }); + } + focus() { + this.slider && this.slider.focus(); + } +} diff --git a/src/framework/ui/menu/keyboard-manager.d.ts b/src/framework/ui/menu/keyboard-manager.d.ts new file mode 100644 index 0000000..daf8e8c --- /dev/null +++ b/src/framework/ui/menu/keyboard-manager.d.ts @@ -0,0 +1,8 @@ +import { Menu } from '.'; +export declare class KeyboardManager { + private menu; + constructor(menu: Menu); + init(): void; + private handler; + release(): void; +} diff --git a/src/framework/ui/menu/keyboard-manager.js b/src/framework/ui/menu/keyboard-manager.js new file mode 100644 index 0000000..048dd0e --- /dev/null +++ b/src/framework/ui/menu/keyboard-manager.js @@ -0,0 +1,40 @@ +export class KeyboardManager { + constructor(menu) { + this.menu = menu; + } + init() { + this.menu + .getContainer() + .addEventListener('keydown', this.handler.bind(this)); + // This trick let's us detect the press of the back or forward buttons to exit out of the menu. + window.onpopstate = () => this.menu.clickCancelAction(); + } + handler(event) { + switch (event.key) { + case 'ArrowDown': + event.preventDefault(); + this.menu.focusNext(); + break; + case 'ArrowUp': + event.preventDefault(); + this.menu.focusPrevious(); + break; + case 'Enter': + event.preventDefault(); + this.menu.clickDefaultAction(); + break; + case 'Escape': + event.preventDefault(); + this.menu.clickCancelAction(); + break; + default: + break; + } + } + release() { + this.menu + .getContainer() + .removeEventListener('keydown', this.handler.bind(this)); + window.onpopstate = null; + } +} diff --git a/src/framework/ui/menu/menu.d.ts b/src/framework/ui/menu/menu.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/ui/menu/menu.js b/src/framework/ui/menu/menu.js new file mode 100644 index 0000000..e69de29 diff --git a/src/framework/ui/menu/sound-manager.d.ts b/src/framework/ui/menu/sound-manager.d.ts new file mode 100644 index 0000000..bbb7e84 --- /dev/null +++ b/src/framework/ui/menu/sound-manager.d.ts @@ -0,0 +1,16 @@ +import { SoundSet } from './interfaces/sound-set'; +export declare class SoundManager { + private soundSet; + private data; + constructor(soundSet?: SoundSet); + setSoundSet(soundSet: SoundSet): void; + handleSound(type: string, data?: any): void; + private handleEditSound; + private handleSelectorSound; + private handleSliderSound; + private handleFocusSound; + private handleOpenSound; + private handleCloseSound; + private handleChooseSound; + private handleCheckboxSound; +} diff --git a/src/framework/ui/menu/sound-manager.js b/src/framework/ui/menu/sound-manager.js new file mode 100644 index 0000000..77f125f --- /dev/null +++ b/src/framework/ui/menu/sound-manager.js @@ -0,0 +1,84 @@ +export class SoundManager { + constructor(soundSet = null) { + this.soundSet = null; + this.data = new Map(); + this.soundSet = soundSet; + } + setSoundSet(soundSet) { + this.soundSet = soundSet; + } + handleSound(type, data = null) { + switch (type) { + case 'edit': + this.handleEditSound(data); + break; + case 'slider': + this.handleSliderSound(data); + break; + case 'selector': + this.handleSelectorSound(data); + break; + case 'checkbox': + this.handleCheckboxSound(data); + break; + case 'focus': + this.handleFocusSound(); + break; + case 'choose': + this.handleChooseSound(); + break; + case 'open': + this.handleOpenSound(); + break; + case 'close': + this.handleCloseSound(); + break; + default: + return; + break; + } + } + handleEditSound(data) { + const prevData = this.data.get('edit') || ''; + if (data.length <= prevData.length) { + this.soundSet.delete && this.soundSet.delete.play(); + } + else { + this.soundSet.char && this.soundSet.char.play(); + } + this.data.set('edit', data); + } + handleSelectorSound(data) { + this.soundSet.scroller && this.soundSet.scroller.play(); + } + handleSliderSound(data) { + const prevData = this.data.get('slider'); + if (data < prevData) { + this.soundSet.sliderLeft && this.soundSet.sliderLeft.play(); + } + else { + this.soundSet.sliderRight && this.soundSet.sliderRight.play(); + } + this.data.set('slider', data); + } + handleFocusSound() { + this.soundSet.move && this.soundSet.move.play(); + } + handleOpenSound() { + this.soundSet.open && this.soundSet.open.play(); + } + handleCloseSound() { + this.soundSet.close && this.soundSet.close.play(); + } + handleChooseSound() { + this.soundSet.choose && this.soundSet.choose.play(); + } + handleCheckboxSound(data) { + if (data === true) { + this.soundSet.checked && this.soundSet.checked.play(); + } + else { + this.soundSet.unchecked && this.soundSet.unchecked.play(); + } + } +} diff --git a/src/framework/ui/text/index.d.ts b/src/framework/ui/text/index.d.ts new file mode 100644 index 0000000..89a5db8 --- /dev/null +++ b/src/framework/ui/text/index.d.ts @@ -0,0 +1,28 @@ +import * as EventEmitter from 'eventemitter3'; +import { SoundSet } from '../menu/interfaces/sound-set'; +import { Line } from './line'; +export declare class ScrollingText extends EventEmitter { + private text; + private delimiter; + private soundSet; + private appearingCharacters; + private characterAppearSpeed; + private currentLineIndex; + private currentLine; + private lines; + private wrapper; + private container; + private soundManager; + private keyboardManager; + constructor(text?: string, delimiter?: string, soundSet?: SoundSet, appearingCharacters?: boolean, characterAppearSpeed?: number); + setText(text: string): this; + setSoundSet(soundSet: SoundSet): this; + setDelimiter(delimiter: string): this; + setAppearingCharacters(appearing: boolean): this; + setAppearingCharacterSpeed(speed: number): this; + init(): void; + run(element: HTMLElement): Promise; + displayLine(index: number): Promise; + getContainer(): HTMLElement; + getCurrentLine(): Line; +} diff --git a/src/framework/ui/text/index.js b/src/framework/ui/text/index.js new file mode 100644 index 0000000..48dec58 --- /dev/null +++ b/src/framework/ui/text/index.js @@ -0,0 +1,102 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import * as EventEmitter from 'eventemitter3'; +import { Line } from './line'; +import { SoundManager } from './sound-manager'; +import { KeyboardManager } from './keyboard-manager'; +export class ScrollingText extends EventEmitter { + constructor(text = null, delimiter = '\n', soundSet = null, appearingCharacters = false, characterAppearSpeed = 0) { + super(); + this.text = text; + this.delimiter = delimiter; + this.soundSet = soundSet; + this.appearingCharacters = appearingCharacters; + this.characterAppearSpeed = characterAppearSpeed; + this.lines = []; + this.soundManager = new SoundManager(this, this.soundSet); + this.keyboardManager = new KeyboardManager(this); + this.init(); + } + setText(text) { + this.text = text; + this.init(); + return this; + } + setSoundSet(soundSet) { + this.soundSet = soundSet; + this.init(); + this.soundManager.setSoundSet(this.soundSet); + return this; + } + setDelimiter(delimiter) { + this.delimiter = delimiter; + this.init(); + return this; + } + setAppearingCharacters(appearing) { + this.appearingCharacters = appearing; + this.init(); + return this; + } + setAppearingCharacterSpeed(speed) { + this.characterAppearSpeed = speed; + this.init(); + return this; + } + init() { + const split = this.text.split(this.delimiter); + this.lines = split.map((line) => new Line(line)); + } + run(element) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { + this.wrapper = document.createElement('div'); + this.wrapper.setAttribute('aria-role', 'polite'); + this.container = document.createElement('div'); + this.wrapper.appendChild(this.container); + element.appendChild(this.wrapper); + this.soundManager.init(); + this.keyboardManager.init(); + this.emit('open'); + let index = 0; + this.currentLineIndex = 0; + while (index < this.lines.length) { + this.currentLineIndex = index; + yield this.displayLine(index); + index++; + } + this.emit('close'); + this.keyboardManager.release(); + this.container.remove(); + resolve(); + })); + }); + } + displayLine(index) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve, reject) => { + this.container.innerHTML = ''; + this.container.appendChild(this.lines[index].getDOMNode()); + this.lines[index].display(this.container, this.appearingCharacters, this.characterAppearSpeed); + this.lines[index].on('character.appear', (event) => this.emit('character.appear', event)); + this.lines[index].on('advance', () => { + this.emit('advance'); + resolve(); + }); + }); + }); + } + getContainer() { + return this.wrapper; + } + getCurrentLine() { + return this.lines[this.currentLineIndex]; + } +} diff --git a/src/framework/ui/text/interfaces/playable-sound.d.ts b/src/framework/ui/text/interfaces/playable-sound.d.ts new file mode 100644 index 0000000..900dad8 --- /dev/null +++ b/src/framework/ui/text/interfaces/playable-sound.d.ts @@ -0,0 +1,3 @@ +export interface IPlayableSound { + play(): any; +} diff --git a/src/framework/ui/text/interfaces/playable-sound.js b/src/framework/ui/text/interfaces/playable-sound.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/ui/text/interfaces/playable-sound.js @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/ui/text/interfaces/sound-set.d.ts b/src/framework/ui/text/interfaces/sound-set.d.ts new file mode 100644 index 0000000..c1c4a45 --- /dev/null +++ b/src/framework/ui/text/interfaces/sound-set.d.ts @@ -0,0 +1,7 @@ +import { IPlayableSound } from './playable-sound'; +export interface SoundSet { + open?: IPlayableSound; + close?: IPlayableSound; + scroll?: IPlayableSound; + characterAppear?: IPlayableSound; +} diff --git a/src/framework/ui/text/interfaces/sound-set.js b/src/framework/ui/text/interfaces/sound-set.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/ui/text/interfaces/sound-set.js @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/ui/text/keyboard-manager.d.ts b/src/framework/ui/text/keyboard-manager.d.ts new file mode 100644 index 0000000..759f133 --- /dev/null +++ b/src/framework/ui/text/keyboard-manager.d.ts @@ -0,0 +1,8 @@ +import { ScrollingText } from '.'; +export declare class KeyboardManager { + private scrollingText; + constructor(scrollingText: ScrollingText); + init(): void; + release(): void; + private handler; +} diff --git a/src/framework/ui/text/keyboard-manager.js b/src/framework/ui/text/keyboard-manager.js new file mode 100644 index 0000000..6fa2127 --- /dev/null +++ b/src/framework/ui/text/keyboard-manager.js @@ -0,0 +1,25 @@ +export class KeyboardManager { + constructor(scrollingText) { + this.scrollingText = scrollingText; + } + init() { + this.scrollingText + .getContainer() + .addEventListener('keydown', (event) => this.handler(event)); + } + release() { + this.scrollingText + .getContainer() + .removeEventListener('keydown', (event) => this.handler(event)); + } + handler(event) { + switch (event.key) { + case 'Enter': + event.preventDefault(); + this.scrollingText.getCurrentLine().getAdvanceButton().click(); + break; + default: + break; + } + } +} diff --git a/src/framework/ui/text/line.d.ts b/src/framework/ui/text/line.d.ts new file mode 100644 index 0000000..abb6e20 --- /dev/null +++ b/src/framework/ui/text/line.d.ts @@ -0,0 +1,13 @@ +import * as EventEmitter from 'eventemitter3'; +export declare class Line extends EventEmitter { + private text; + private container; + private textField; + private advanceButton; + private active; + constructor(text: string); + getDOMNode(): HTMLElement; + display(element: HTMLElement, appearingCharacters?: boolean, appearingCharacterSpeed?: number): void; + private fillText; + getAdvanceButton(): HTMLElement; +} diff --git a/src/framework/ui/text/line.js b/src/framework/ui/text/line.js new file mode 100644 index 0000000..fceff12 --- /dev/null +++ b/src/framework/ui/text/line.js @@ -0,0 +1,45 @@ +import * as EventEmitter from 'eventemitter3'; +export class Line extends EventEmitter { + constructor(text) { + super(); + this.text = text; + this.active = false; + } + getDOMNode() { + this.container = document.createElement('div'); + this.container.setAttribute('aria-role', 'polite'); + this.textField = document.createElement('div'); + this.container.appendChild(this.textField); + this.advanceButton = document.createElement('button'); + this.advanceButton.textContent = 'Advance'; + this.advanceButton.addEventListener('click', (event) => { + this.emit('advance'); + this.active = false; + }); + this.container.appendChild(this.advanceButton); + return this.container; + } + display(element, appearingCharacters = false, appearingCharacterSpeed = 0) { + this.active = true; + this.textField.focus(); + if (!appearingCharacters) { + this.textField.textContent = this.text; + } + else { + this.fillText(0, appearingCharacterSpeed); + } + } + fillText(index, speed) { + if (!this.active) + return; + if (index > this.text.length) { + return; + } + this.textField.textContent += this.text.charAt(index); + this.emit('character.appear', this.textField.textContent); + setTimeout(() => this.fillText((index += 1), speed), speed); + } + getAdvanceButton() { + return this.advanceButton; + } +} diff --git a/src/framework/ui/text/sound-manager.d.ts b/src/framework/ui/text/sound-manager.d.ts new file mode 100644 index 0000000..1348404 --- /dev/null +++ b/src/framework/ui/text/sound-manager.d.ts @@ -0,0 +1,13 @@ +import { SoundSet } from './interfaces/sound-set'; +import { ScrollingText } from '.'; +export declare class SoundManager { + private instance; + private soundSet; + constructor(instance: ScrollingText, soundSet: SoundSet); + setSoundSet(soundSet: SoundSet): void; + init(): void; + private handleOpen; + private handleCharacterAppear; + private handleAdvance; + private handleClose; +} diff --git a/src/framework/ui/text/sound-manager.js b/src/framework/ui/text/sound-manager.js new file mode 100644 index 0000000..842d74e --- /dev/null +++ b/src/framework/ui/text/sound-manager.js @@ -0,0 +1,27 @@ +export class SoundManager { + constructor(instance, soundSet) { + this.instance = instance; + this.soundSet = soundSet; + } + setSoundSet(soundSet) { + this.soundSet = soundSet; + } + init() { + this.instance.on('character.appear', this.handleCharacterAppear.bind(this)); + this.instance.on('open', this.handleOpen.bind(this)); + this.instance.on('close', this.handleClose.bind(this)); + this.instance.on('advance', this.handleAdvance.bind(this)); + } + handleOpen() { + this.soundSet.open && this.soundSet.open.play(); + } + handleCharacterAppear() { + this.soundSet.characterAppear && this.soundSet.characterAppear.play(); + } + handleAdvance() { + this.soundSet.scroll && this.soundSet.scroll.play(); + } + handleClose() { + this.soundSet.close && this.soundSet.close.play(); + } +} diff --git a/src/framework/world/component.d.ts b/src/framework/world/component.d.ts new file mode 100644 index 0000000..0de71c4 --- /dev/null +++ b/src/framework/world/component.d.ts @@ -0,0 +1,5 @@ +export declare class Component { + id: number; + properties: T; + constructor(props: T); +} diff --git a/src/framework/world/component.js b/src/framework/world/component.js new file mode 100644 index 0000000..1c31dba --- /dev/null +++ b/src/framework/world/component.js @@ -0,0 +1,5 @@ +export class Component { + constructor(props) { + this.properties = props; + } +} diff --git a/src/framework/world/ecs-world.d.ts b/src/framework/world/ecs-world.d.ts new file mode 100644 index 0000000..cfdf43d --- /dev/null +++ b/src/framework/world/ecs-world.d.ts @@ -0,0 +1,24 @@ +import { World } from '../ecs/index'; +import { Game } from '../game'; +import { Component } from '../ecs/component'; +import { System } from '../ecs/system'; +import { BaseEntity, Entity } from '../ecs/entity'; +import { Query } from '../ecs/query'; +import { World as IWorld } from '.'; +export declare class ECSWorld implements IWorld { + instance: Game; + id: string; + world: World; + running: boolean; + constructor(instance: Game); + update(): void; + updateDraw(): boolean; + createEntity(components: Array): BaseEntity; + createComponent(props: any): Component; + createSystem(systemExecutor: Function): void; + addSystem(system: System): void; + addEntity(entity: BaseEntity): void; + removeEntity(entity: BaseEntity): void; + createQuery(include: Array, exclude: Array): Query; + extendEntity(entity: Entity, components: Array): BaseEntity; +} diff --git a/src/framework/world/ecs-world.js b/src/framework/world/ecs-world.js new file mode 100644 index 0000000..f24b82d --- /dev/null +++ b/src/framework/world/ecs-world.js @@ -0,0 +1,40 @@ +import { World } from '../ecs/index'; +export class ECSWorld { + constructor(instance) { + this.instance = instance; + this.running = true; + this.id = 'ECSScene'; + this.world = new World(); + } + update() { + if (this.running) + this.world.run(); + } + updateDraw() { + return true; + } + createEntity(components) { + return this.world.createEntity(components); + } + createComponent(props) { + return this.world.createComponent(props); + } + createSystem(systemExecutor) { + return this.world.createSystem(systemExecutor); + } + addSystem(system) { + return this.world.addSystem(system); + } + addEntity(entity) { + this.world.addEntity(entity); + } + removeEntity(entity) { + return this.world.removeEntity(entity); + } + createQuery(include, exclude) { + return this.world.createQuery(include, exclude); + } + extendEntity(entity, components) { + return this.world.extendEntity(entity, components); + } +} diff --git a/src/framework/world/entity.d.ts b/src/framework/world/entity.d.ts new file mode 100644 index 0000000..c852bdc --- /dev/null +++ b/src/framework/world/entity.d.ts @@ -0,0 +1,6 @@ +import { Component } from "./component"; +export declare class Entity { + id: number; + components: Array>; + constructor(); +} diff --git a/src/framework/world/entity.js b/src/framework/world/entity.js new file mode 100644 index 0000000..81cb671 --- /dev/null +++ b/src/framework/world/entity.js @@ -0,0 +1,5 @@ +export class Entity { + constructor() { + this.components = new Array(); + } +} diff --git a/src/framework/world/event-bus.d.ts b/src/framework/world/event-bus.d.ts new file mode 100644 index 0000000..24b52fb --- /dev/null +++ b/src/framework/world/event-bus.d.ts @@ -0,0 +1,11 @@ +export declare class EventBus { + private events; + constructor(); + emit(id: string, data: any): void; + subscribe(id: string, subscriber: Function): void; +} +export declare class EventItem { + id: string; + subscribers: Function[]; + constructor(id: string); +} diff --git a/src/framework/world/event-bus.js b/src/framework/world/event-bus.js new file mode 100644 index 0000000..11ee12b --- /dev/null +++ b/src/framework/world/event-bus.js @@ -0,0 +1,30 @@ +export class EventBus { + constructor() { + this.events = new Map(); + } + emit(id, data) { + let ev = this.events.get(id); + if (!ev) { + let ev = new EventItem(id); + this.events.set(id, ev); + return; + } + ev.subscribers.forEach((subscriber) => { + subscriber(data); + }); + } + subscribe(id, subscriber) { + let ev = this.events.get(id); + if (!ev) { + ev = new EventItem(id); + this.events.set(id, ev); + } + ev.subscribers.push(subscriber); + } +} +export class EventItem { + constructor(id) { + this.id = id; + this.subscribers = []; + } +} diff --git a/src/framework/world/index.d.ts b/src/framework/world/index.d.ts new file mode 100644 index 0000000..27b60d3 --- /dev/null +++ b/src/framework/world/index.d.ts @@ -0,0 +1,3 @@ +export interface World { + update(dt: number): any; +} diff --git a/src/framework/world/index.js b/src/framework/world/index.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/framework/world/index.js @@ -0,0 +1 @@ +export {}; diff --git a/src/framework/world/query.d.ts b/src/framework/world/query.d.ts new file mode 100644 index 0000000..f375754 --- /dev/null +++ b/src/framework/world/query.d.ts @@ -0,0 +1,13 @@ +import { World } from "."; +import { Component } from "./component"; +import { Entity } from "./entity"; +export declare class Query { + include: Array>; + exclude: Array>; + private results; + isDirty: boolean; + includeComponentIds: number[]; + excludeComponentIds: number[]; + constructor(include: Array>, exclude: Array>); + execute(world: World): Array; +} diff --git a/src/framework/world/query.js b/src/framework/world/query.js new file mode 100644 index 0000000..c3099d0 --- /dev/null +++ b/src/framework/world/query.js @@ -0,0 +1,27 @@ +export class Query { + constructor(include, exclude) { + this.include = include; + this.exclude = exclude; + this.isDirty = true; + this.results = new Array(); + this.includeComponentIds = include.map((component) => component.id); + this.excludeComponentIds = exclude.map((component) => component.id); + } + execute(world) { + if (!this.isDirty && this.results) { + return this.results; + } + let filtered; + const entities = world.entities.filter(entity => { + let ids = entity.components.map(component => component.id); + let includes = ids.map(id => this.includeComponentIds.includes(id)).includes(true); + let excludes = ids.map(id => this.excludeComponentIds.includes(id)).includes(true); + return includes && !excludes; + }); + if (entities.length > 0) { + this.isDirty = false; + this.results = entities; + } + return entities; + } +} diff --git a/src/framework/world/system.d.ts b/src/framework/world/system.d.ts new file mode 100644 index 0000000..0645f89 --- /dev/null +++ b/src/framework/world/system.d.ts @@ -0,0 +1,6 @@ +import { World } from "."; +export declare class System { + executor: Function; + constructor(executor: Function); + execute(world: World): void; +} diff --git a/src/framework/world/system.js b/src/framework/world/system.js new file mode 100644 index 0000000..f6feb3a --- /dev/null +++ b/src/framework/world/system.js @@ -0,0 +1,8 @@ +export class System { + constructor(executor) { } + execute(world) { + if (this.executor) { + this.executor(world); + } + } +} diff --git a/src/game/commands/meow.js b/src/game/commands/meow.js new file mode 100644 index 0000000..9016ad9 --- /dev/null +++ b/src/game/commands/meow.js @@ -0,0 +1,3 @@ +export default async function MeowCommand(args, context) { + context.print(`You meow.`); +} \ No newline at end of file diff --git a/src/game/index.html b/src/game/index.html index 250bc78..714d9e3 100644 --- a/src/game/index.html +++ b/src/game/index.html @@ -4,8 +4,7 @@

Assassin bug

-
+
- \ No newline at end of file diff --git a/src/game/index.js b/src/game/index.js index e1558bf..6a3dd9c 100644 --- a/src/game/index.js +++ b/src/game/index.js @@ -1 +1,14 @@ -document.getElementById('output-area').appendChild(document.createTextNode("Hi I'm javascript and I approve this message")); \ No newline at end of file +import Game from '../engine'; +import Rooms from './rooms'; +import Items from './items'; +import MeowCommand from './commands/meow'; + +const game = new Game(); + +game.init({ + rooms: Rooms, + commands: [ + [["meow", "mew"], MeowCommand] + ], + items: Items +}); \ No newline at end of file diff --git a/src/game/items/index.js b/src/game/items/index.js new file mode 100644 index 0000000..589b06a --- /dev/null +++ b/src/game/items/index.js @@ -0,0 +1,5 @@ +import Stone from './stone'; + +export default [ + Stone +] \ No newline at end of file diff --git a/src/game/items/stone.js b/src/game/items/stone.js new file mode 100644 index 0000000..88bf067 --- /dev/null +++ b/src/game/items/stone.js @@ -0,0 +1,15 @@ +import ItemBuilder from "../../engine/builders/item"; + +export default new ItemBuilder() +.withID("stone") +.withName("A dull stone") +.withDescription("There is nothing remarkable about this rough, bland stone.") +.isTakeable(true) +.isUsable(true) +.withTakeCallback(async function(context) { + context.output.say("It feels heavy in your hands."); +}) +.withUseCallback(async function(context) { + context.output.say("You can't really figure out what to do with this yet."); +}) +.create(); \ No newline at end of file diff --git a/src/game/rooms/index.js b/src/game/rooms/index.js new file mode 100644 index 0000000..0bf0bcd --- /dev/null +++ b/src/game/rooms/index.js @@ -0,0 +1,7 @@ +import Start from './start'; +import Tunnel1 from './tunnel1'; + +export default [ + Start, + Tunnel1 +]; \ No newline at end of file diff --git a/src/game/rooms/start.js b/src/game/rooms/start.js new file mode 100644 index 0000000..1e5b17e --- /dev/null +++ b/src/game/rooms/start.js @@ -0,0 +1,18 @@ +import RoomBuilder from '../../engine/builders/room'; + +export default new RoomBuilder() +.withID("start") +.withTitle("The starting room") +.withFirstDescription("You set foot in your very first room") +.withDescription("The first room. Nothing special about it.") +.withExit("north", "tunnel_1") +.withEnterCallback(async function(context) { + const { output, wait } = context; + output.say("You slowly wake up"); + await wait(5000); + output.say("It's strange. You never used to be able to be conscious about the fact that you were waking up."); + await wait(5000); + output.say("Yet here we are."); +}) +.withItem("stone") +.create(); \ No newline at end of file diff --git a/src/game/rooms/tunnel1.js b/src/game/rooms/tunnel1.js new file mode 100644 index 0000000..98f46dd --- /dev/null +++ b/src/game/rooms/tunnel1.js @@ -0,0 +1,9 @@ +import RoomBuilder from '../../engine/builders/room'; + +export default new RoomBuilder() +.withID("tunnel_1") +.withTitle("A long dark tunnel") +.withFirstDescription("You first step foot in this dark loomy tunnel.") +.withDescription("The walls are wet. Everything is wet. Ugh. Why do you even.") +.withExit("south", "start") +.create(); \ No newline at end of file diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..1a2322c --- /dev/null +++ b/test/index.js @@ -0,0 +1,17 @@ +const room = { + name: "A room", + onEnter() { + console.log("I entered"); + return "meow"; + }, + onExit() { + console.log("I exited"); + } +} + +async function start() { + await room.onEnter(); + await room.onExit(); +} + +start(); \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 246e962..0759ccb 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,7 @@ const path = require('path'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const HTMLWebpackPlugin = require('html-webpack-plugin'); +const TerserWebpackPlugin = require('terser-webpack-plugin'); const Package = require('./package'); module.exports = { @@ -9,6 +10,9 @@ module.exports = { performance: { hints: false }, + optimization: { + minimizer: [new TerserWebpackPlugin()] + }, "plugins": [ new HTMLWebpackPlugin({ title: Package.name,