diff --git a/Custom/ControllerComponents.js b/Custom/ControllerComponents.js index a6d6cc3..01beb1f 100644 --- a/Custom/ControllerComponents.js +++ b/Custom/ControllerComponents.js @@ -11,18 +11,8 @@ init: function () { this.el.addEventListener('thumbstickmoved', this.logThumbstick); }, logThumbstick: function (evt) { - if (evt.detail.y > 0.95) { - - } - if (evt.detail.y < -0.95) { - - } - if (evt.detail.x < -0.95) { - - } - if (evt.detail.x > 0.95) { - - } + thumbstickDetailL.x = evt.detail.x + thumbstickDetailL.y = evt.detail.y } }); @@ -32,19 +22,8 @@ init: function () { this.el.addEventListener('thumbstickmoved', this.logThumbstick); }, logThumbstick: function (evt) { - if (evt.detail.y > 0.95) { - - } - if (evt.detail.y < -0.95) { - - } - if (evt.detail.x < -0.95) { - - } - if (evt.detail.x > 0.95) { - - - } + thumbstickDetailR.x = evt.detail.x + thumbstickDetailR.y = evt.detail.y } }); @@ -54,18 +33,8 @@ AFRAME.registerComponent('trackpad-left',{ this.el.addEventListener('trackpadmoved', this.logTrackpad); }, logTrackpad: function (evt) { - if (evt.detail.y > 0.2) { - - } - if (evt.detail.y < -0.2) { - - } - if (evt.detail.x < -0.2) { - - } - if (evt.detail.x > 0.2) { - - } + trackpadDetailL.x = evt.detail.x; + trackpadDetailL.y = evt.detail.y; } }); @@ -76,18 +45,9 @@ AFRAME.registerComponent('trackpad-left',{ this.el.addEventListener('trackpadmoved', this.logTrackpad); }, logTrackpad: function (evt) { - if (evt.detail.y > 0.2) { - - } - if (evt.detail.y < -0.2) { - - } - if (evt.detail.x < -0.2) { - - } - if (evt.detail.x > 0.2) { - - } + console.log(JSON.stringify(evt.detail)); + trackpadDetailR.x = evt.detail.x; + trackpadDetailR.y = evt.detail.y; } }); @@ -97,32 +57,55 @@ init: function () { var el = this.el; el.addEventListener('gripdown', function (evt) { - displayNext(true); + buttonsDownR['grip'] = true; + if(thumbstickDetailR.y <= -.75 || trackpadDetailR.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailR.y >= .75 || trackpadDetailR.y >= .75){ + displayNext(true); + } + + }); + + el.addEventListener('gripup', function (evt) { + buttonsDownR['grip'] = false; }); - /*el.addEventListener('gripup', function (evt) { - - });*/ el.addEventListener('triggerdown', function (evt) { - displayNext(true); + buttonsDownR['trigger'] = true; + if(thumbstickDetailR.y <= -.75 || trackpadDetailR.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailR.y >= .75 || trackpadDetailR.y >= .75){ + displayNext(true); + } + }); + + el.addEventListener('triggerup', function (evt) { + buttonsDownR['grip'] = false; }); - /*el.addEventListener('triggerup', function (evt) { - - });*/ el.addEventListener('abuttondown', function (evt) { - displayNext(true); + buttonsDownR['abutton'] = true; + if(thumbstickDetailR.y <= -.75 || trackpadDetailR.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailR.y >= .75 || trackpadDetailR.y >= .75){ + displayNext(true); + } + }); + el.addEventListener('abuttonup', function (evt) { + buttonsDownR['abutton'] = false; }); - /*el.addEventListener('abuttonup', function (evt) { - - });*/ el.addEventListener('bbuttondown', function (evt) { - displayNext(true); + buttonsDownR['bbutton'] = true; + if(thumbstickDetailR.y <= -.75 || trackpadDetailR.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailR.y >= .75 || trackpadDetailR.y >= .75){ + displayNext(true); + } + }); + el.addEventListener('bbuttonup', function (evt) { + buttonsDownR['bbutton'] = false; }); - /*el.addEventListener('bbuttonup', function (evt) { - - });*/ /*el.addEventListener('menudown', function (evt) { menuRightPressed = true; @@ -130,27 +113,39 @@ init: function () { });*/ el.addEventListener('thumbstickdown', function (evt) { - displayNext(true); + buttonsDownR['thumbstick'] = true; + if(trackpadDetailR.y >= .75 || thumbstickDetailR.y >= .75 ){ + stopAll() + } else if(trackpadDetailR.x >= .75 || thumbstickDetailR.x >= .75 ){ + if(startAllButton.disabled){ + pauseAll(); + } else { + startAll(); + } + } + }); + el.addEventListener('thumbstickup', function (evt) { + buttonsDownR['thumbstick'] = false; }); - /*el.addEventListener('thumbstickup', function (evt) { - - });*/ el.addEventListener('trackpaddown', function (evt) { - displayNext(true); - }); -/* - el.addEventListener('trackpadup', function (evt) { - + console.log("Trackpad detail: "+JSON.stringify(trackpadDetailR)) + console.log("Thumbstick detail: "+JSON.stringify(thumbstickDetailR)) + buttonsDownR['trackpad'] = true; + if(trackpadDetailR.y >= .75 || thumbstickDetailR.y >= .75 ){ + stopAll() + } else if(trackpadDetailR.x >= .75 || thumbstickDetailR.x >= .75 ){ + if(startAllButton.disabled){ + pauseAll(); + } else { + startAll(); + } + } }); - el.addEventListener('trackpadtouchstart', function (evt) { - + el.addEventListener('trackpadup', function (evt) { + buttonsDownR['trackpad'] = false; }); - - el.addEventListener('trackpadtouchend', function (evt) { - - });*/ } }); @@ -159,63 +154,107 @@ init: function () { var el = this.el; el.addEventListener('gripdown', function (evt) { - displayNext(false); + buttonsDownL['grip'] = true; + if(thumbstickDetailL.y <= -.75 || trackpadDetailL.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailL.y >= .75 || trackpadDetailL.y >= .75){ + displayNext(true); + } }); - /* + el.addEventListener('gripup', function (evt) { - - });*/ + buttonsDownL['grip'] = false; + }); el.addEventListener('triggerdown', function (evt) { - displayNext(false); + buttonsDownL['trigger'] = true; + if(thumbstickDetailL.y <= -.75 || trackpadDetailL.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailL.y >= .75 || trackpadDetailL.y >= .75){ + displayNext(true); + } }); - /* + el.addEventListener('triggerup', function (evt) { - - });*/ + buttonsDownL['grip'] = false; + }); el.addEventListener('xbuttondown', function (evt) { - displayNext(false); + buttonsDownL['xbutton'] = true; + if(thumbstickDetailL.y <= -.75 || trackpadDetailL.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailL.y >= .75 || trackpadDetailL.y >= .75){ + displayNext(true); + } }); - /*el.addEventListener('xbuttonup', function (evt) { - - });*/ + el.addEventListener('xbuttonup', function (evt) { + buttonsDownL['xbutton'] = false; + if(thumbstickDetailL.y <= -.75 || trackpadDetailL.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailL.y >= .75 || trackpadDetailL.y >= .75){ + displayNext(true); + } + }); el.addEventListener('ybuttondown', function (evt) { - displayNext(false); + buttonsDownL['ybutton'] = true; }); - /*el.addEventListener('ybuttonup', function (evt) { - - });*/ - - /*el.addEventListener('menudown', function (evt) { - menuLeftPressed = true; - leftMenu.setAttribute("value", "Left Menu: Yes"); leftMenu.setAttribute("color","green") - });*/ + el.addEventListener('ybuttonup', function (evt) { + buttonsDownL['ybutton'] = false; + }); el.addEventListener('thumbstickdown', function (evt) { - displayNext(false); + buttonsDownL['thumbstick'] = true + if(trackpadDetailL.y >= .75 || thumbstickDetailL.y >= .75){ + stopAll() + } else if(trackpadDetailL.x >= .75 || thumbstickDetailL.x >= .75){ + if(startAllButton.disabled){ + pauseAll(); + } else { + startAll(); + } + } + }); + el.addEventListener('thumbstickup', function (evt) { + buttonsDownL['thumbstick'] = false; }); - /*el.addEventListener('thumbstickup', function (evt) { - - });*/ el.addEventListener('trackpaddown', function (evt) { - displayNext(false); + buttonsDownL['trackpad'] = true + if(trackpadDetailL.y >= .75 || thumbstickDetailL.y >= .75){ + stopAll() + } else if(trackpadDetailL.x >= .75 || thumbstickDetailL.x >= .75){ + if(startAllButton.disabled){ + pauseAll(); + } else { + startAll(); + } + } }); - /*el.addEventListener('trackpadup', function (evt) { - + el.addEventListener('trackpadup', function (evt) { + buttonsDownL['trackpad'] = false; }); - el.addEventListener('trackpadtouchstart', function (evt) { +} +}); - }); +thumbstickDetailL = {x: 0, y: 0} +trackpadDetailL = {x: 0, y: 0} +buttonsDownL = {trigger: false, grip: false, trackpad: false, thumbstick: false, abutton: false, bbutton: false} - el.addEventListener('trackpadtouchend', function (evt) { +thumbstickDetailR = {x: 0, y: 0} +trackpadDetailR = {x: 0, y: 0} +buttonsDownR = {trigger: false, grip: false, trackpad: false, thumbstick: false, xbutton: false, ybutton: false} - });*/ -} -}); \ No newline at end of file + + +// trigger/grip controls pattern selection + // trigger while holding up- go to previous pattern + // trigger while holding down- go to next pattern + +// thumbstick/trackpad controls animations + // thumbstick while holding right- start/pause + // thumbstick while holding down- stop animation \ No newline at end of file diff --git a/Custom/ControllerInputMapping.js b/Custom/ControllerInputMapping.js index 2270d20..44da552 100644 --- a/Custom/ControllerInputMapping.js +++ b/Custom/ControllerInputMapping.js @@ -5,43 +5,174 @@ */ -import data from '../Compatibility/controller_profiles.json' assert { type: "json" }; +//import data from '../Compatibility/controller_profiles.json' assert { type: "json" }; const windows = data[0]['windows'] const oc_touch = data[0]['oc_touch'] const oc_go = data[0]['oc_go'] const vive = data[0]['vive'] const vive_focus = data[0]['vive_focus'] +const magic = data[0]['magic'] // contains all possible buttons and axes const generic = data[0]['generic'] +let isRight, isLeft, scheme; +function findControls(){ + isRight = !(conRight.getAttribute("position").x == 0 && conRight.getAttribute("position").y == 0 && conRight.getAttribute("position").z == 0); + isLeft = !(conLeft.getAttribute("position").x == 0 && conLeft.getAttribute("position").y == 0 && conLeft.getAttribute("position").z == 0); + if(isRight){ + + if(conRight.components['tracked-controls'].attrValue.hasOwnProperty("id") && conRight.components['tracked-controls'].attrValue.id != ""){ + scheme = conRight.components['tracked-controls-webxr'].attrValue.id + clearInterval(controlsInterval) + if(scheme.includes("touch")){ + conLeft.removeAttribute("oculus-touch-controls") + conRight.removeAttribute("oculus-touch-controls") + } else if(scheme.includes("go")){ + conLeft.removeAttribute("oculus-go-controls") + conRight.removeAttribute("oculus-go-controls") + } else if(scheme.includes("focus")){ + conLeft.removeAttribute("vive-focus-controls") + conRight.removeAttribute("vive-focus-controls") + } else if(scheme.includes("vive")){ + conLeft.removeAttribute("vive-controls") + conRight.removeAttribute("vive-controls") + } else if(scheme.includes("windows")){ + conLeft.removeAttribute("windows-motion-controls") + conRight.removeAttribute("windows-motion-controls") + } else if(scheme.includes("magicleap")) { + conLeft.removeAttribute("magicleap-controls") + conRight.removeAttribute("magicleap-controls") + } + } else if(conRight.components['tracked-controls'].attrValue.hasOwnProperty("idPrefix") && conRight.components['tracked-controls'].attrValue.idPrefix != ""){ + scheme = conRight.getAttribute("tracked-controls").idPrefix; + clearInterval(controlsInterval) + if(scheme.includes("touch")){ + conLeft.removeAttribute("oculus-touch-controls") + conRight.removeAttribute("oculus-touch-controls") + } else if(scheme.includes("go")){ + conLeft.removeAttribute("oculus-go-controls") + conRight.removeAttribute("oculus-go-controls") + } else if(scheme.includes("focus")){ + conLeft.removeAttribute("vive-focus-controls") + conRight.removeAttribute("vive-focus-controls") + } else if(scheme.includes("vive")){ + conLeft.removeAttribute("vive-controls") + conRight.removeAttribute("vive-controls") + } else if(scheme.includes("windows")){ + conLeft.removeAttribute("windows-motion-controls") + conRight.removeAttribute("windows-motion-controls") + } else if(scheme.includes("magicleap")) { + conLeft.removeAttribute("magicleap-controls") + conRight.removeAttribute("magicleap-controls") + } + } else { + scheme = null + } + + } else if(isLeft) { + if(conLeft.components['tracked-controls'].attrValue.hasOwnProperty("id") && conLeft.components['tracked-controls'].attrValue.id != ""){ + scheme = conLeft.components['tracked-controls-webxr'].attrValue.id + clearInterval(controlsInterval) + if(scheme.includes("touch")){ + conLeft.removeAttribute("oculus-touch-controls") + conRight.removeAttribute("oculus-touch-controls") + } else if(scheme.includes("go")){ + conLeft.removeAttribute("oculus-go-controls") + conRight.removeAttribute("oculus-go-controls") + } else if(scheme.includes("focus")){ + conLeft.removeAttribute("vive-focus-controls") + conRight.removeAttribute("vive-focus-controls") + } else if(scheme.includes("vive")){ + conLeft.removeAttribute("vive-controls") + conRight.removeAttribute("vive-controls") + } else if(scheme.includes("windows")){ + conLeft.removeAttribute("windows-motion-controls") + conRight.removeAttribute("windows-motion-controls") + } else if(scheme.includes("magicleap")) { + conLeft.removeAttribute("magicleap-controls") + conRight.removeAttribute("magicleap-controls") + } + } else if(conLeft.components['tracked-controls'].attrValue.hasOwnProperty("idPrefix") && conLeft.components['tracked-controls'].attrValue.idPrefix != ""){ + scheme = conLeft.getAttribute("tracked-controls").idPrefix; + clearInterval(controlsInterval) + if(scheme.includes("touch")){ + conLeft.removeAttribute("oculus-touch-controls") + conRight.removeAttribute("oculus-touch-controls") + } else if(scheme.includes("go")){ + conLeft.removeAttribute("oculus-go-controls") + conRight.removeAttribute("oculus-go-controls") + } else if(scheme.includes("focus")){ + conLeft.removeAttribute("vive-focus-controls") + conRight.removeAttribute("vive-focus-controls") + } else if(scheme.includes("vive")){ + conLeft.removeAttribute("vive-controls") + conRight.removeAttribute("vive-controls") + } else if(scheme.includes("windows")){ + conLeft.removeAttribute("windows-motion-controls") + conRight.removeAttribute("windows-motion-controls") + } else if(scheme.includes("magicleap")) { + conLeft.removeAttribute("magicleap-controls") + conRight.removeAttribute("magicleap-controls") + } + + } else { + scheme = null + } + } + +} + + + + + + + + + + + + + conLeft.addEventListener('buttondown', function (evt) { + console.log("left") + console.log(JSON.stringify(evt.detail.id)) if(scheme == 'windows-mixed-reality'){ conLeft.dispatchEvent(new CustomEvent(windows['left'][evt.detail.id]+'down', {detail: true})) } else if(scheme == 'oculus-touch' || scheme == 'oculus-touch-v2' || scheme == 'oculus-touch-v3' || scheme == 'meta-quest-touch-pro') { conLeft.dispatchEvent(new CustomEvent(oc_touch['left'][evt.detail.id]+'down', {detail: true})) - } else if (scheme = 'oculus-go'){ + } else if (scheme == 'oculus-go'){ conLeft.dispatchEvent(new CustomEvent(oc_go['left'][evt.detail.id]+'down', {detail: true})) } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus') { conLeft.dispatchEvent(new CustomEvent(vive['left'][evt.detail.id]+'down', {detail: true})) } else if(scheme == 'htc-vive-focus') { conLeft.dispatchEvent(new CustomEvent(vive_focus['left'][evt.detail.id]+'down', {detail: true})) + } else if(scheme == 'magicleap-one') { + conLeft.dispatchEvent(new CustomEvent(magic['left'][evt.detail.id]+'down', {detail: true})) } else { conLeft.dispatchEvent(new CustomEvent(generic['left'][evt.detail.id]+'down', {detail: true})) } }); conRight.addEventListener('buttondown', function (evt) { + console.log("right") + console.log(JSON.stringify(evt.detail.id)) + console.log(scheme) if(scheme == 'windows-mixed-reality'){ conRight.dispatchEvent(new CustomEvent(windows['right'][evt.detail.id]+'down', {detail: true})) } else if(scheme == 'oculus-touch' || scheme == 'oculus-touch-v2' || scheme == 'oculus-touch-v3' || scheme == 'meta-quest-touch-pro') { conRight.dispatchEvent(new CustomEvent(oc_touch['right'][evt.detail.id]+'down', {detail: true})) - } else if (scheme = 'oculus-go'){ + } else if (scheme == 'oculus-go'){ conRight.dispatchEvent(new CustomEvent(oc_go['right'][evt.detail.id]+'down', {detail: true})) } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus') { conRight.dispatchEvent(new CustomEvent(vive['right'][evt.detail.id]+'down', {detail: true})) } else if(scheme == 'htc-vive-focus') { conRight.dispatchEvent(new CustomEvent(vive_focus['right'][evt.detail.id]+'down', {detail: true})) + } else if(scheme == 'magicleap-one') { + console.log(magic['right'][evt.detail.id]+'down') + conRight.dispatchEvent(new CustomEvent(magic['right'][evt.detail.id]+'down', {detail: true})) } else { + console.log(JSON.stringify(generic['right'][evt.detail.id]+'down')) conRight.dispatchEvent(new CustomEvent(generic['right'][evt.detail.id]+'down', {detail: true})) } }); @@ -51,12 +182,14 @@ conLeft.addEventListener('buttonup', function (evt) { conLeft.dispatchEvent(new CustomEvent(windows['left'][evt.detail.id]+'up', {detail: true})) } else if(scheme == 'oculus-touch' || scheme == 'oculus-touch-v2' || scheme == 'oculus-touch-v3' || scheme == 'meta-quest-touch-pro') { conLeft.dispatchEvent(new CustomEvent(oc_touch['left'][evt.detail.id]+'up', {detail: true})) - } else if (scheme = 'oculus-go'){ + } else if (scheme == 'oculus-go'){ conLeft.dispatchEvent(new CustomEvent(oc_go['left'][evt.detail.id]+'up', {detail: true})) } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus') { conLeft.dispatchEvent(new CustomEvent(vive['left'][evt.detail.id]+'up', {detail: true})) } else if(scheme == 'htc-vive-focus') { conLeft.dispatchEvent(new CustomEvent(vive_focus['left'][evt.detail.id]+'up', {detail: true})) + } else if(scheme == 'magicleap-one') { + conLeft.dispatchEvent(new CustomEvent(magic['left'][evt.detail.id]+'up', {detail: true})) } else { conLeft.dispatchEvent(new CustomEvent(generic['left'][evt.detail.id]+'up', {detail: true})) } @@ -67,12 +200,14 @@ conRight.addEventListener('buttonup', function (evt) { conRight.dispatchEvent(new CustomEvent(windows['right'][evt.detail.id]+'up', {detail: true})) } else if(scheme == 'oculus-touch' || scheme == 'oculus-touch-v2' || scheme == 'oculus-touch-v3' || scheme == 'meta-quest-touch-pro') { conRight.dispatchEvent(new CustomEvent(oc_touch['right'][evt.detail.id]+'up', {detail: true})) - } else if (scheme = 'oculus-go'){ + } else if (scheme == 'oculus-go'){ conRight.dispatchEvent(new CustomEvent(oc_go['right'][evt.detail.id]+'up', {detail: true})) } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus') { conRight.dispatchEvent(new CustomEvent(vive['right'][evt.detail.id]+'up', {detail: true})) } else if(scheme == 'htc-vive-focus') { conRight.dispatchEvent(new CustomEvent(vive_focus['right'][evt.detail.id]+'up', {detail: true})) + } else if(scheme == 'magicleap-one') { + conRight.dispatchEvent(new CustomEvent(magic['right'][evt.detail.id]+'up', {detail: true})) } else { conRight.dispatchEvent(new CustomEvent(generic['right'][evt.detail.id]+'up', {detail: true})) } @@ -102,14 +237,14 @@ conLeft.addEventListener('axismove', function (evt) { } }) conLeft.dispatchEvent(thumbstickmove) - } else if (scheme = 'oculus-go'){ - let touchpadmove = new CustomEvent('touchpadmoved', { + } else if (scheme == 'oculus-go'){ + let trackpadmove = new CustomEvent('trackpadmoved', { detail: { x: evt.detail.axis[0], y: evt.detail.axis[1] } }) - conLeft.dispatchEvent(touchpadmove) + conLeft.dispatchEvent(trackpadmove) } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus' || scheme == 'htc-vive-focus') { let trackpadmove = new CustomEvent('trackpadmoved', { detail: { @@ -118,14 +253,22 @@ conLeft.addEventListener('axismove', function (evt) { } }) conLeft.dispatchEvent(trackpadmove) + } else if(scheme == "magicleap-one") { + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conLeft.dispatchEvent(trackpadmove) } else { - let touchpadmove = new CustomEvent('touchpadmoved', { + let trackpadmove = new CustomEvent('trackpadmoved', { detail: { x: evt.detail.axis[0], y: evt.detail.axis[1] } }) - conLeft.dispatchEvent(touchpadmove) + conLeft.dispatchEvent(trackpadmove) let thumbstickmove = new CustomEvent('thumbstickmoved', { detail: { x: evt.detail.axis[2], @@ -160,14 +303,14 @@ conRight.addEventListener('axismove', function (evt) { } }) conRight.dispatchEvent(thumbstickmove) - } else if (scheme = 'oculus-go'){ - let touchpadmove = new CustomEvent('touchpadmoved', { + } else if (scheme == 'oculus-go'){ + let trackpadmove = new CustomEvent('trackpadmoved', { detail: { x: evt.detail.axis[0], y: evt.detail.axis[1] } }) - conRight.dispatchEvent(touchpadmove) + conRight.dispatchEvent(trackpadmove) } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus' || scheme == 'htc-vive-focus') { let trackpadmove = new CustomEvent('trackpadmoved', { detail: { @@ -176,14 +319,22 @@ conRight.addEventListener('axismove', function (evt) { } }) conRight.dispatchEvent(trackpadmove) + } else if(scheme == "magicleap-one") { + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conRight.dispatchEvent(trackpadmove) } else { - let touchpadmove = new CustomEvent('touchpadmoved', { + let trackpadmove = new CustomEvent('trackpadmoved', { detail: { x: evt.detail.axis[0], y: evt.detail.axis[1] } }) - conRight.dispatchEvent(touchpadmove) + conRight.dispatchEvent(trackpadmove) let thumbstickmove = new CustomEvent('thumbstickmoved', { detail: { x: evt.detail.axis[2], diff --git a/Custom/README.md b/Custom/README.md index cdec24c..627cbfc 100644 --- a/Custom/README.md +++ b/Custom/README.md @@ -1,168 +1,245 @@ -# Manual User Interface Tests # - -Begin by opening this link: https://didsr.github.io/WebXR-tools/Custom/ - -## Test 1- Adding and editing a package -This test will examine the package and pattern systems. - 1. Add a package with the name "Test 1" by pressing the *plus* icon next to **Packages** - 2. Add three patterns to this package by pressing the *plus* icon next to **Pattern List** - - Name the first pattern "Pattern 1" - - Name the second pattern "Pattern 2" - - Name the third pattern "Pattern 3" - - At this point, the tool should look like this: - ![image](../Images/test1_1.PNG) - - 3. Reorder the patterns so that "Pattern 3" is the first pattern, "Pattern 2" is the second pattern, and "Pattern 1" is the last pattern - - Do this by dragging "Pattern 3" and dropping it in the first position - - Drag "Pattern 2" and drop it in the second position - 4. Switch packages to the "default" package - 5. Multi-select the patterns "crosshair" and "ring_w2" by holding Ctrl while clicking on the patterns - 6. Copy these patterns by pressing Ctrl+C on windows or Cmd+C on Mac - 7. Switch packages to the "Test 1 Package" package - 8. Paste the copied patterns by pressing Ctrl+V or Cmd+V - - At this point, the tool should look like this: - ![image](../Images/test1_2.PNG) - -## Test 2- Adding and editing a pattern -This test will examine the pattern and entity systems. - 9. Rename the "crosshair" pattern to "Pattern 4" - - Select the "crosshair" pattern by clicking on it - - Open settings by pressing the *gear* icon at the top right of the tab - - Under **Pattern Settings** select the *Rename* button - - Enter "Pattern 4" in the textbox and press OK - 10. Delete the "ring_w2" pattern by pressing the *trash* icon next to **Pattern List** - - Select OK on the confirmation - 11. Close the settings by pressing the *X* icon at the top right of the tab - 11. Reorder the patterns so that "Pattern 4" is first, "Pattern 3" is second, "Pattern 2" is third, and "Pattern 1" is last - - At this point, the tool should look like this: - ![image](../Images/test1_3.PNG) - - 12. Select "Pattern 3" - 13. Switch the toggle at the top of the tab to be in *Edit* mode - 14. Add a circle, plane, and triangle to this pattern - - Select circle as the type of entity to add from the dropdown next to **Type of entity:** - - Press the *Add to Pattern* button to add it to the pattern - - Repeat for plane and triangle - - The entities will spawn at random places on the screen - 15. Switch the second toggle to *Edit Entity* mode - 16. Edit the Circle - - Click on the circle or select "circle0" from the **Entity** dropdown - - Move the circle so that it is in position (0, 0, 250) - - Change the color of the circle to white by selecting it from the color selector or entering #ffffff - - Increase the radius of the circle to 40m - - Decrease the border size to 15m - 17. Edit the Plane - - Click on the plane or select "plane0" from the **Entity** dropdown - - Move the plane so that it is in position (-20, 100, 250) - - Change the color of the plane to blue by selecting it from the color selector or entering #0000ff - - Increase the height of the plane to 100m - - Decrease the width of the plane to 20m - - If desired, add a texture from the drop down or upload an image file to use as a texture - - If an uploaded texture is added here, one step will change in Part 3 - - Adding a texture will rescale the entity to meet the aspect ratio of the image being used - 18. Edit the Triangle - - Click on the triangle or select "triangle0" from the **Entity** dropdown - - Move the triangle so that it is in position (20, -100, 250) - - Change the color of the triangle to green by selecting it from the color selector or entering #00ff00 - - Change vertex A to be at (0, 50) - - Change vertex B to be at (-50, -50) - - Change vertex C to be at (50, -50) - At this point the tool should look like this: - - ![image](../Images/test2_1.PNG) - - 19. Switch the first toggle to *Display* mode - 20. Select "Pattern 2" - 21. Switch the toggle at the top of the tab to be in *Edit* mode - 22. Add a gradient, checkerboard, and grille to this pattern - - Select gradient as the type of entity to add from the dropdown next to **Type of entity:** - - Press the *Add to Pattern* button to add it to the pattern - - Repeat for checkerboard and grille - - The entities will spawn at random places on the screen - 23. Switch the second toggle to *Edit Entity* mode - 24. Edit the Gradient - - Click on the gradient or select "gradient0" from the **Entity** dropdown - - Move the gradient so that it is in position (0, 150, 250) - - Change the primary color of the gradient to white by selecting it from the color selector or entering #ffffff - - Increase the bar height to 20m - - Decrease the bar width to 3m - - Increase the number of bars to 100 - - Change the secondary color to red by selecting it from the color selector or entering #ff0000 - 25. Edit the Checkerboard - - Click on the checkerboard or select "checkerboard0" from the **Entity** dropdown - - Move the plane so that it is in position (0, 0, 250) - - Change the primary color of the checkerboard to purple by selecting it from the color selector or entering #800080 - - Increase the number of columns to 20 - - Increase the number of rows to 19 - - Increase the size of the tiles to 10m - - Change the secondary color to white by selecting it from the color selector or entering #ffffff - 26. Edit the grille - - Click on the grille or select "grille0" from the **Entity** dropdown - - Move the gradient so that it is in position (0, -150, 250) - - Change the primary color of the grille to orange by selecting it from the color selector or entering #ffa500 - - Decrease the bar height to 20m - - Decrease the bar width to 3m - - Increase the number of bars to 100 - - Change the secondary color to white by selecting it from the color selector or entering #ffffff - At this point the tool should look like this: -![image](../Images/test2_2.png) - - 27. Switch the first toggle to *Display* mode - 28. Select "Pattern 1" - 29. Switch the toggle at the top of the tab to be in *Edit* mode - 30. Add a dot array, circular dot array, and bullseye to this pattern - - Select dot array as the type of entity to add from the dropdown next to **Type of entity:** - - Press the *Add to Pattern* button to add it to the pattern - - Repeat for circular dot array and bullseye - - The entities will spawn at random places on the screen - 31. Switch the second toggle to *Edit Entity* mode - 32. Edit the Dot Array - - Click on the dot array or select "dotarray0" from the **Entity** dropdown - - Move the dot array so that it is in position (-20, -100, 250) - - Change the color of the dot array to green by selecting it from the color selector or entering #00ff00 - - Increase the number of columns to 7 - - Increase the number of rows to 7 - - Increase the radius of the dots to 3m - - Decrease the spacing of the dots to 5m - - Toggle on the center dot being filled - 33. Edit the Circular Dot Array - - Click on the circular dot array or select "circularDotarray0" from the **Entity** dropdown - - Move the circular dot array so that it is in position (0, 0, 249) - - Change the color of the dot array to white by selecting it from the color selector or entering #ffffff - - Increase the number of dots to 15 - - Increase the number of rings to 6 - - Keep the radius of the dots at 2m - - Keep the spacing of the dots at 10m - - Keep the center dot hollow - 34. Edit the bullseye - - Click on the bullseye or select "bullseye0" from the **Entity** dropdown - - Move the bullseye so that it is in position (0, 0, 250) - - Change the primary color of the bullseye to blue by selecting it from the color selector or entering #0000ff - - Increase the number of rings to 6 - - Keep the ring pitch at 5m -At this point the tool should look like this: -![image](../Images/test2_3.PNG) - -## Test 3- Saving and uploading a package -This test will examine the sharing feature of the tool. - - 35. Switch the first toggle to *Display* mode - 36. Select the save package button next to **Packages:** - - Note: if an uploaded texture was used in Test 2, select cancel when prompted to include textures - - Uploaded textures cause generated files to be too large for the link generation service - 37. Make a note of the package id saved in the url after "?id=" ('?id=') - 38. Refresh the page - 39. Ensure that the package "Test 1" is loaded and the patterns are correct - 40. Upload the "Test 1 Comparison" package - - Open settings by hitting the *gear* icon at the top right of the tab - - Press the import button denoted by a cloud with a down arrow - - Paste this link into the textbox: https://didsr.github.io/WebXR-tools/Custom/?id=598zgfm3bqw9 - 41. Check that there is now a package called "Test 1 Comparison" with the same patterns as those created in these tests - 42. To interactively check for correct results, navigate between the two packages and ensure entity values are the same - 43. Close the tab completely - 44. Navigate back to the starting page of the tool (https://didsr.github.io/WebXR-tools/Custom/) - 45. Select "Test 1" and "Test 1 Comparison" from the **Recent Packages** dropdown and ensure that they are correct +# Pattern Generation Tool # +The Pattern Generation Tool provides a user interface for creating, editing, and displaying [WebXR](https://immersiveweb.dev/) scenes. +Scenes consist of 2 and 3 dimensional entities taken from [A-Frame](https://aframe.io/docs/1.4.0/core/entity.html). +These entities can be manipulated in a variety of different ways to create flexible patterns. +Scenes are grouped together in packages that can be downloaded and shared. +### Features +- Create packages of scenes that can be shared via a file or a link. +- Order of scenes within the package can be easily changed. +- Scenes are fully editable upon uploading or downloading. +- Scenes can be switched between using arrow keys or when in immersive mode, the buttons on the left and right controllers. +- Default package available containing commonly used patterns: + - Red: A solid red background + - Green: A solid green background + - Blue: A solid blue background + - White: A solid white background + - Grille: A black and white background + - Crosshair: A white crosshair + - Line: A white vertical line + - Dot Array: A matrix of dots + - Circular Dot Array: A dot array arranged in a circle + - Checkerboard_w: A checkerboard with a white center tile + - Checkerboard_b: A checkerboard with a black center tile + - Ring_w1: A bullseye with a high pitch + - Ring_w2: A bullseye with a medium pitch + - Ring_w5: A bullseye with a low pitch + + +# Directions For Use # +### Packages +1. [Create a Package](#createPackage) +2. Edit a Package +3. Display a Package +4. Save a Package +5. Share a Package + +### Patterns +1. Create a Pattern +2. Edit a Pattern +3. Display a Pattern +4. Save a Pattern +5. Share a Pattern + + +
+## Create a Package + +## Edit a Package + +## Display a Package + +## Save a Package + +## Share a Package + + + + + + + + + +## Displaying Patterns + + + +### Selecting a Pattern +To select a pattern, first ensure that the checkbox for the pattern under ***Pattern List*** has been checked. Then select the desired pattern from the ***Pattern Display*** dropdown. + +### Adding a Pattern +A pattern a can be added using the ***Add Pattern*** button. A name for the added pattern can be specified using textbox above the ***Add Pattern*** button. + +### Removing a Pattern +Using the ***Remove Pattern*** button, the currently displayed pattern can be removed. + +### Selecting Multiple Patterns +To select multiple patterns, check all of the desired checkboxes under ***Pattern List***. +**The order that the patterns are selected in will be the display order** +The current pattern can be rotated through using the up or down arrows on the keyboard or the buttons on the left and right controllers. +Any buttons on the left controller step back one pattern and any buttons on the right controller step forward one pattern. + +### Displaying the Desired Pattern +To display the pattern, hit the ***VR*** button in the bottom right hand corner. +[**Check the demos folder for some basic editable patterns**](./demos) + +### Saving and Uploading a Group of Patterns +Multiple patterns can be saved together by hitting the ***Save Selected Patterns*** button. This will save all currently selected patterns from the ***Pattern List*** as a JSON file. +To upload a group of patterns, press the ***Upload Patterns*** button and select the desired JSON file. This JSON file can contain one or more patterns and will add them all to the ***Pattern List***. + +## Editing Patterns + +### Uploading a Pattern +A JSON file containing a single pattern can be uploaded to the ***Upload a pattern*** input. If a file containing multiple patterns is uploaded, it will be rejected. + +### Uploading images +Image files are treated as entity textures. Here are the steps on how to properly upload these files. + +1. Add a plane to a scene. Planes are the only entities that can be textured. +2. Click the edit entity toggle and locate the input for textures labeled "Upload your textures here." Any number of files can be uploaded here. Images uploaded will be available as textures in the dropdown labeled "Texture". +3. Select the desired texture from the "Texture" dropdown. Selecting a texture will automatically resize the plane to the images aspect ratio. To retain the original image colors, set the plane color to be white. + +### Adding Entities +![plot](../Images/custom.PNG) +To add an entity, make sure the slider at the top left of the webpage is set to ***Edit Pattern*** then move the slider below it to the ***Add Entity***. + +Then, select the type of entity you would like to add using the ***Type of entity*** dropdown and hit the ***Add to Pattern*** button. + +Entities will spawn at a random position within the editor camera field of view with a random color. + +***Entities may appear skewed in the editor but this affect is corrected on the headset. Do not stack entities, it will result in [z-fighting].(https://en.wikipedia.org/wiki/Z-fighting)*** + +#### Types of Entities +- Circle +- Plane +- Triangle +- Gradient +- Checkerboard +- Grille + +### Editing Entities +![plot](../Images/editorExample.PNG) +**In order to access the editor one or more entities must be added to the pattern**. The editing bar can be hidden by pressing the ***Hide Settings*** button. + +Once entities have been added to the pattern, change the slider to ***Edit Entity***. + +To change the entity being edited, either click on the entity or select the desired entity from the dropdown labeled ***Current Entity***. +Entity ID's are automatically generated with the format **\\**. + +Examples: circle0, checkerboard4, plane20. + +The current entity can be removed by hitting the ***Remove from Pattern***. The entire pattern can be reset by hitting the ***Reset Pattern*** button. + +### Saving the Pattern +To save the current pattern, press the ***Save Pattern*** button located at the bottom of the editor tab. The pattern is saved as an editable JSON file. Uploaded textures are preserved without image compression. + +### Background Color +To change the background color, select a new color or enter the hexadecimal code of the desired color. + +### Universal Entity Settings +#### Position: (x deg, y world units) +To edit the position, locate the position text boxes under **Universal Entity Settings**. The left box refers to the x position and the y position. + +The position of an entity refers to where its center point is located. The editor interfaces the three axis in different manners. +Positional data is calculated based on a cylinder that encompasses the camera. The radius of this cylinder cannot be changed, however the cylinder has an infinite height. The x and z axes are responsible for horizontal positioning. The y axis controls the vertical position. + +![plot](../Images/cylinderRadius.PNG) + +- x-axis: + - This axis is in terms of **degrees** where 1 degree references 1 degree of clockwise rotation away from the fixed camera center on the cylinder. +- y-axis: + - This axis is in terms of **world units** where 1 world unit is equal to 1 meter in 3D space. +- z-axis: + - **The z-axis is NOT editable by users.** The location on the z-axis is automatically calculated based on the x-axis angle. + +The reason that the radius of the cylinder is not editable is due to the fact that all entities are 2-dimensional. Changing their depth would essentially have the same effect as making entities smaller or larger. + +#### Rotation: θ deg +To edit the rotation, locate the text box labeled ***Rotation*** under **Universal Entity Settings**. + +For this program, the rotation of an entity refers to rotation around the **z-axis only**. The axis of rotation is located at the center point of entity. The unit for rotation is **degrees** where 1 degree refers to one degree of rotation about the axis of rotation. + +#### Color: #HEXCODE +To edit the color, locate the color selector labeled ***Color*** under **Universal Entity Settings**. + +Select a color using the color slider or input the desired color in hexadecimal format. + +***For checkerboards and grilles, the alternate color will always be black*** + +### Entity Specific Settings +#### Circle +- Radius (m): + - The unit for radius is world units where 1 world unit is equal to 1 meter in 3D space. +- Border Size: + - The unit for border size is world units where 1 world unit is equal to 1 meter in 3D space. By default, the border size is set to be equal to the radius of the circle, meaning the entity is entirely filled in. + +#### Plane +- Texture: + - There are some textures built into the site. [They can be found here.](./textures) Textures can also be uploaded as JPGS or PNGS and added to the list of available textures in the pattern. + - Applying a texture will automatically scale the plane to fit the aspect ratio. +- Height (m): + - The unit for height is world units where 1 world unit is equal to 1 meter in 3D space. +- Width (m): + - The unit for width is world units where 1 world unit is equal to 1 meter in 3D space. +- Border Size: + - The unit for border size is world units where 1 world unit is equal to 1 meter in 3D space. By default, the border size is set to be equal to the radius to the smallest dimension present of the plane, meaning the entity is entirely filled in. + +#### Triangle +![plot](../Images/triangle.PNG) +- Vertex A (x m,y m): + - Position of vertex A in relation to the center point of the entity. The coordinates are in world units where 1 world unit is equal to 1 meter in 3D space. +- Vertex B (x m,y m): + - Position of vertex B in relation to the center point of the entity. The coordinates are in world units where 1 world unit is equal to 1 meter in 3D space. +- Vertex C (x m,y m): + - Position of vertex C in relation to the center point of the entity. The coordinates are in world units where 1 world unit is equal to 1 meter in 3D space. + +#### Gradient +- Individual Bar Height (m): + - The unit for height is world units where 1 world unit is equal to 1 meter in 3D space. +- Indivdual Bar Width (m): + - The unit for width is world units where 1 world unit is equal to 1 meter in 3D space. +- Number of Bars: + - The number of bars in the gradient, the default is 32. + - The color is divided by the number of bars to determine the color of each bar. + +#### Checkerboard +- Tile size (m): + - The unit for width and height of tiles is world units where 1 world unit is equal to 1 meter in 3D space. +- Number of Rows: + - The number of rows in the checkerboard, the default is 16. +- Number of Columns: + - The number of columns in the checkerboard, the default is 16. + +#### Grille +- Individual Bar Height (m): + - The unit for height is world units where 1 world unit is equal to 1 meter in 3D space. +- Indivdual Bar Width (m): + - The unit for width is world units where 1 world unit is equal to 1 meter in 3D space. +- Number of Bars: + - The number of bars in the grille, the default is 32. + +### Other Notes +If you would like to inspect the HTML or JavaScript data of the selected entity: +- Open the inspector with Ctrl + Shift + i +- Enter "selectedEntity" into the command prompt, this will bring up all associated HTML code +- Enter "selectedEntity.getAttribute("\") to see the associated JavaScript data for the desired component + - Ex: selectedEntity.getAttribute("position") to view position or selectedEntity.getAttribute("material") to view texture and color +- Enter "selectedEntity.setAttribute("\",\) to set the associated JavaScript data for the desired component + - Ex: selectedEntity.setAttribute("position",{x: 0, y: 0, z: -125}) to set position to 0,0 on the cylindrical plane + - Ex: selectedEntity.setAttribute("material",{color: "#0000FF", shader: "flat", src: ""}) to set the color to red and remove the texture, ***make sure to include shader: "flat" to remove unnecessary lighting effects*** + + +## JSON File Format +{ +  "scenes": +    { map of scenes }, +   "textures": { +     "uploadedTextureFormats": +            { map of image sizes for uploaded textures }, +     "textureValues": +            [ list of textures associated with patterns being saved], +   } +   "date": "datetime of save" +} + diff --git a/Custom/animation.js b/Custom/animation.js new file mode 100644 index 0000000..2e88bfd --- /dev/null +++ b/Custom/animation.js @@ -0,0 +1,112 @@ +function handleAnimationToggle(){ + if(animationButton.children[0].className.includes('fa-play')){ + animationButton.children[0].className = "fa-solid fa-pause"; + selectedEntity.emit('animLoopStart',null,false) + } else { + animationButton.children[0].className = "fa-solid fa-play"; + selectedEntity.emit('animLoopPause',null,false) + } + +} + + +function startAll(){ + let status = 1; + if(timerNum > 0){ + startTimer() + } + let i = 0; + while(i < els.length){ + let data = els[i].getAttribute('movement'); + if(data.startPoints.length > 0){ + data.status = status; + els[i].setAttribute('movement',data) + } + + i++; + } + + startAllButton.disabled = true + pauseAllButton.disabled = false + stopAllButton.disabled = false +} + +function pauseAll(){ + let status = 0; + if(timerNum > 0){ + clearInterval(time) + } + let i = 0; + while(i < els.length){ + let data = els[i].getAttribute('movement'); + if(data.startPoints.length > 0){ + data.status = status; + els[i].setAttribute('movement',data) + } + + i++; + } + + startAllButton.disabled = false + pauseAllButton.disabled = true + stopAllButton.disabled = false +} + +function stopAll(){ + // if no entities then do nothing + if(els.length <= 0){ + return + } + // set start all button icon to play + //handleAllButton.children[0].className = "fa-solid fa-play"; + // loop through each + let i = 0; + while(i < els.length){ + let data = els[i].getAttribute('movement'); + data.status = -1; + els[i].setAttribute('movement',data) + + i++; + } + // timer pause + if(timerNum > 0){ + clearInterval(time) + timeElapsed = 0; + let timer = document.getElementById('timer0') + let textVal = timer.getAttribute('text') + textVal.value = "00:00.00 " + timer.setAttribute('text',textVal) + } + + startAllButton.disabled = false + pauseAllButton.disabled = true + stopAllButton.disabled = true + + movementIcon.className = "fa-solid fa-play" +} + + +function handleMovementToggle(e){ + e.stopPropagation() + if(movementIcon.className == "fa-solid fa-play"){ + let data = selectedEntity.getAttribute('movement') + data.status = 1 + selectedEntity.setAttribute('movement',data) + movementIcon.className = "fa-solid fa-pause" + } else { + let data = selectedEntity.getAttribute('movement') + data.status = 0 + selectedEntity.setAttribute('movement',data) + movementIcon.className = "fa-solid fa-play" + + } +} + +function stopIndividual(e){ + e.stopPropagation() + let data = selectedEntity.getAttribute('movement') + data.status = -1 + selectedEntity.setAttribute('movement',data) + movementIcon.className = "fa-solid fa-play" + +} \ No newline at end of file diff --git a/Custom/animations/animationControllers.js b/Custom/animations/animationControllers.js new file mode 100644 index 0000000..ed58cfa --- /dev/null +++ b/Custom/animations/animationControllers.js @@ -0,0 +1,129 @@ +/* Contains code related to starting or stopping animations*/ + +// starts all animations in the scene +// called by start all button and controller input +function startAll(){ + // starts timer if present + let status = 1; + if(timerNum > 0){ + startTimer() + } + + // updates statuses of each element in scene + let i = 0; + while(i < els.length){ + let data = els[i].getAttribute('movement'); + if(data.startPoints.length > 0){ + data.status = status; + els[i].setAttribute('movement',data) + } + + i++; + } + + // disables and enables appropiate buttons + startAllButton.disabled = true + pauseAllButton.disabled = false + stopAllButton.disabled = false +} + +// pauses all animations in the scene +// called by pause all button and controller input +function pauseAll(){ + // pauses timer if present + let status = 0; + if(timerNum > 0){ + clearInterval(time) + } + // updates status of each entity + let i = 0; + while(i < els.length){ + let data = els[i].getAttribute('movement'); + if(data.startPoints.length > 0){ + data.status = status; + els[i].setAttribute('movement',data) + } + + i++; + } + + // disables and enables appropriate buttons + startAllButton.disabled = false + pauseAllButton.disabled = true + stopAllButton.disabled = false +} + +// stops all animations in the scene +// called by stop all button and controller input +function stopAll(){ + // if no entities then do nothing + if(els.length <= 0){ + return + } + + // stop and reset timer if present + if(timerNum > 0){ + clearInterval(time) + timeElapsed = 0; + let timer = document.getElementById('timer0') + let textVal = timer.getAttribute('text') + textVal.value = "00:00.00 " + timer.setAttribute('text',textVal) + } + + // update status of each element + let i = 0; + while(i < els.length){ + let data = els[i].getAttribute('movement'); + data.status = -1; + els[i].setAttribute('movement',data) + + i++; + } + + // disables and enables appropriate buttons + startAllButton.disabled = false + pauseAllButton.disabled = true + stopAllButton.disabled = true + + movementIcon.className = "fa-solid fa-play" +} + +// handles movement of a single entity +function handleMovementToggle(e){ + e.stopPropagation() // prevents collapsing of movement section on button press + // if not currently playing + if(movementIcon.className == "fa-solid fa-play"){ + // start playing + let data = selectedEntity.getAttribute('movement') + data.status = 1 + selectedEntity.setAttribute('movement',data) + movementIcon.className = "fa-solid fa-pause" + } else { + // pause movement + let data = selectedEntity.getAttribute('movement') + data.status = 0 + selectedEntity.setAttribute('movement',data) + movementIcon.className = "fa-solid fa-play" + + } +} + +// stops movement of single entity +function stopIndividual(e){ + e.stopPropagation() // prevents collapsing of movement section on button press + let data = selectedEntity.getAttribute('movement') + data.status = -1 + selectedEntity.setAttribute('movement',data) + movementIcon.className = "fa-solid fa-play" + +} + +// stops the movement of a single entity +function stopMovement(el){ + mov = el.getAttribute('movement') + clearInterval(mov.status) + mov.status = -1 + el.setAttribute('movement',mov) + movementIcon.className = "fa-solid fa-play"; +} diff --git a/Custom/animations/movementComponent.js b/Custom/animations/movementComponent.js new file mode 100644 index 0000000..3a3af03 --- /dev/null +++ b/Custom/animations/movementComponent.js @@ -0,0 +1,220 @@ +AFRAME.registerComponent('movement', { + schema: { + origin: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, + rotationOrigin: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, + startPoints: {type: 'array', default: []}, + endPoints: {type: 'array', default: []}, + initialVelocities: {type: 'array', default: []}, + accelerations: {type: 'array', default: []}, + status: {type: 'float', default: -1}, + types: {type: 'array', default: []}, + index: {type: 'float', default: 0}, + currentVelocity: {type: 'float', default: 0}, + timeElapsed: {type: 'float', default: 0}, + }, + + init: function () { + // Do something when component first attached. + + }, + + update: function (oldData) { + // Do something when component's data is updated. + // if status has been changed to stop + if(oldData.status != -1 && this.data.status == -1){ + // then we have been moved to the stopped status + // restore position to origin and rotation to rotationOrigin + this.el.setAttribute('position',this.data.origin) + this.el.setAttribute('rotation',this.data.rotationOrigin) + } + + }, + + remove: function () { + // Do something the component or its entity is detached. + }, + + tick: function (time, timeDelta) { + // Do something on every scene tick or frame. + // if playing + if(this.data.status == 1){ + // time elapsed below 0 indicates that no movement is underway + if(this.data.timeElapsed < 0){ // set time elapsed to 0 + this.data.timeElapsed = 0 + + } else { // update time elapsed + this.data.timeElapsed = this.data.timeElapsed + timeDelta + } + this.updatePosition() // call movement function + + } else if(this.data.status == -1){ // if stopped + this.data.timeElapsed = -1; // reset time + + } + + + + }, + + // does calculation of new position + updatePosition: function (time, timeDelta) { + let data = this.data + + let startPoint = data.startPoints[data.index] + + let endPoint = data.endPoints[data.index] + + // if in advanced mode + if(startPoint.x){ + // if pause + if(data.types[data.index] == 'Pause'){ + // if pause has reached duration + if(data.timeElapsed >= data.initialVelocities[data.index]){ + // move on to next movement + this.data.timeElapsed = 0 + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + } + + // if Discontinuous + } else if(data.types[data.index] == 'Discontinuous'){ + // if time has reached duration + if(data.timeElapsed >= data.initialVelocities[data.index]){ + // teleport then move to next animation + this.el.setAttribute('position',endPoint) + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + } + } else { + // if the current type is start-to-finish or rubberband or rebound + // these are handled the same + // get total distance between points and amount of that distance covered + xDelta = endPoint.x-startPoint.x + yDelta = endPoint.y-startPoint.y + zDelta = endPoint.z-startPoint.z + d = Math.sqrt((xDelta)*(xDelta) + (yDelta)*(yDelta) + (zDelta)*(zDelta)) + distanceCovered = (data.initialVelocities[data.index])*(data.timeElapsed/1000) + 0.5*(data.accelerations[data.index])*((data.timeElapsed/1000)**2) + amtCovered = (distanceCovered/d) + amtCovered = d ? (distanceCovered/d) : 0 + + if(amtCovered > 1){ // if amount covered has exceeded that of the animation + // move to next animation + this.data.timeElapsed = 0 + amtCovered = 0 + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + // recalculate total distance and amt covered with new points + startPoint = data.startPoints[data.index] + endPoint = data.endPoints[data.index] + xDelta = endPoint.x-startPoint.x + yDelta = endPoint.y-startPoint.y + zDelta = endPoint.z-startPoint.z + d = Math.sqrt((xDelta)*(xDelta) + (yDelta)*(yDelta) + (zDelta)*(zDelta)) + distanceCovered = (data.initialVelocities[data.index])*(data.timeElapsed/1000) + 0.5*(data.accelerations[data.index])*((data.timeElapsed/1000)**2) + amtCovered = (distanceCovered/d) + amtCovered = d ? (distanceCovered/d) : 0 + + + } + + // if new animation is not a pause + if(this.data.types[this.data.index] != 'Pause'){ + // set position + xDelta = endPoint.x-startPoint.x + yDelta = endPoint.y-startPoint.y + zDelta = endPoint.z-startPoint.z + res = {x: startPoint.x+(xDelta*amtCovered), y: startPoint.y+(yDelta*amtCovered), z: startPoint.z+(zDelta*amtCovered)} + this.el.setAttribute('position',{x: res.x, y: res.y, z: res.z}) + } + + + } + } else { // if not in advanced mode + // if pause + if(data.types[data.index] == 'Pause'){ + // if pause has reached duration + if(data.timeElapsed >= data.initialVelocities[data.index]){ + // move on to next movement + this.data.timeElapsed = 0 + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + } + // if Discontinuous + } else if(data.types[data.index] == 'Discontinuous'){ + // if time has reached duration + if(data.timeElapsed >= data.initialVelocities[data.index]){ + // teleport then move to next animation + this.el.setAttribute('position',{x: -endPoint.r*Math.sin((endPoint.theta*Math.PI)/180), y: endPoint.y, z: endPoint.r * Math.cos((endPoint.theta*Math.PI)/180)}) + this.el.setAttribute('rotation',{x: 0, y: -endPoint.theta, z: 0}) + this.data.timeElapsed = 0 + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + + } + } else { + // if the current type is start-to-finish or rubberband or rebound + // these are handled the same + // get total distance between points and amount of that distance covered + thetaDelta = Math.abs(endPoint.theta-startPoint.theta) + arcLen = Math.abs(thetaDelta*Math.PI/180)*Math.abs(endPoint.r); + yDelta = Math.abs(endPoint.y-startPoint.y) + d = Math.sqrt((arcLen)*(arcLen) + (yDelta)*(yDelta)) + + distanceCovered = (data.initialVelocities[data.index])*(data.timeElapsed/1000) + 0.5*(data.accelerations[data.index])*((data.timeElapsed/1000)**2) + + amtCovered = d ? (distanceCovered/d) : 0 + + if(amtCovered > 1){ // if amount covered has exceeded that of the animation + // move to next animation + this.data.timeElapsed = 0 + amtCovered = 0 + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + + // recalculate total distance and amt covered with new points + startPoint = data.startPoints[data.index] + endPoint = data.endPoints[data.index] + thetaDelta = Math.abs(endPoint.theta-startPoint.theta) + arcLen = Math.abs(thetaDelta*Math.PI/180)*Math.abs(endPoint.r); + yDelta = Math.abs(endPoint.y-startPoint.y) + d = Math.sqrt((arcLen)*(arcLen) + (yDelta)*(yDelta)) + distanceCovered = (data.initialVelocities[data.index])*(data.timeElapsed/1000) + 0.5*(data.accelerations[data.index])*((data.timeElapsed/1000)**2) + amtCovered = (distanceCovered/d) + + + } + + // if new animation is not a pause + if(this.data.types[this.data.index] != 'Pause'){ + // set new position + thetaDelta = endPoint.theta-startPoint.theta + yDelta = endPoint.y-startPoint.y + res = {theta: startPoint.theta+(thetaDelta*amtCovered), y: startPoint.y+(yDelta*amtCovered), r: startPoint.r} + + this.el.setAttribute('position',{x: -res.r*Math.sin((res.theta*Math.PI)/180), y: res.y, z: res.r * Math.cos((res.theta*Math.PI)/180)}) + this.el.setAttribute("rotation", {x: 0, y: -res.theta, z: 0}); // set rotation to be 0 + } + + } + } + + } +}); \ No newline at end of file diff --git a/Custom/changeListeners.js b/Custom/changeListeners.js index 14ea9bd..18ba555 100644 --- a/Custom/changeListeners.js +++ b/Custom/changeListeners.js @@ -31,6 +31,51 @@ $("#z").change(function() { editEntity(); }); +/* If the textbox for endX value is changed */ +$("#startX").change(function() { + editEntity(); + }); + +/* If the textbox for endY value is changed */ +$("#startY").change(function() { + editEntity(); + }); + +/* If the textbox for endZ value is changed */ +$("#startZ").change(function() { + editEntity(); + }); + +/* If the textbox for endX value is changed */ +$("#endX").change(function() { + editEntity(); + }); + +/* If the textbox for endY value is changed */ +$("#endY").change(function() { + editEntity(); + }); + +/* If the textbox for endZ value is changed */ +$("#endZ").change(function() { + editEntity(); + }); + + /* If the textbox for endX value is changed */ +$("#speedIn").change(function() { + editEntity(); + }); + +/* If the textbox for endY value is changed */ +$("#accelerationIn").change(function() { + editEntity(); + }); + +/* If the textbox for endZ value is changed */ +$("#movementTypeIn").change(function() { + editEntity(); + }); + /* If the textbox for color value is changed */ $('#color').minicolors({ control: 'hue', @@ -52,7 +97,7 @@ $('#color2').minicolors({ }, }); -/* If the textbox for x value is changed */ +/* If the textbox for texture value is changed */ $("#texture").change(function() { if(texture.value == "none"){ selectedEntity.setAttribute("material",{color: selectedEntity.getAttribute("material").color, shader: "flat", src: ""}); @@ -81,7 +126,10 @@ $("#texture").change(function() { height.value = selectedEntity.getAttribute("geometry").height; fill.value = selectedEntity.getAttribute("fill").val; fillIn.style.display = "none"; + updateJSON(); } + + }); @@ -218,6 +266,11 @@ $("#vax").change(function() { editEntity(); }); + /* If the textbox for vax value is changed */ +$("#size").change(function() { + editEntity(); + }); + /* If the textbox for vay value is changed */ $("#vay").change(function() { editEntity(); @@ -311,66 +364,9 @@ $("#ringPitchIn").change(function() { editEntity(); }); -/* sends entity back or forward one layer */ -/*function sendBack(isback){ - let tmp = null; - let selected = els.indexOf(selectedEntity); /* finds layer number of current entity */ - /* checks if back or forward */ - /*if(isback){*/ /* if back */ - /*if (selected != 0) {*/ /* if it is not on layer 0 */ - /* swap z values */ - /*tmp = els[selected].getAttribute("position").z; - els[selected].getAttribute("position").z = els[selected-1].getAttribute("position").z; - els[selected-1].getAttribute("position").z = tmp;*/ - - /* swap position in el arr (this preserves the layering order) */ - /*tmp = els[selected]; - els[selected] = els[selected-1]; - els[selected-1] = tmp; - }*/ - /*} else {*/ /* if forward */ - /*if (selected != els.length-1) {*/ /* if not the last layer */ - /* swap z values */ - /*tmp = els[selected].getAttribute("position").z; - els[selected].getAttribute("position").z = els[selected+1].getAttribute("position").z; - els[selected+1].getAttribute("position").z = tmp;*/ - - /* swap position in el arr (this preserves the layering order) */ - /*tmp = els[selected]; - els[selected] = els[selected+1]; - els[selected+1] = tmp; - } - } -}*/ - -/* Raycasting with orthographic camera */ -/*var raycaster = new THREE.Raycaster(); -raycaster.layers.set(0); -window.addEventListener("pointerup", function(e) { - var screenPos = new THREE.Vector2(); - - screenPos.x = (e.clientX / window.innerWidth) * 2 - 1; - screenPos.y = - (e.clientY / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(screenPos, scene.camera); - - var rays = raycaster.intersectObjects(pool, true); - let i = rays.length; - if(i > 0){ - currMin = rays[0].distance; - selected = rays[0].object.el; - while (i > 0) { - i--; - if(rays[i].distance < currMin){ - currMin = rays[i].distance; - selected = rays[i].object.el; - } - } - selected = document.getElementById(selected.id.split("-")[0]); - console.log(selected); - selectNew(selected); - } -});*/ +$("#text").change(function() { + editEntity(); +}); finished = false @@ -397,6 +393,10 @@ scene_display_input.addEventListener("change", function() { reader.onload = function() { fileContent = JSON.parse(reader.result); + if(!validateJSON(fileContent)){ + alert('Invalid package'); + return + } names[fileName] = "" for (const [name, value] of Object.entries(fileContent['scenes'])) { const re = /^[a-zA-Z0-9-_ ]+( \([0-9]+\))?$/ @@ -416,13 +416,6 @@ scene_display_input.addEventListener("change", function() { packageSelect.options.add(new Option(fileName,fileName)) scenes[fileName] = fileContent['scenes'] names[fileName] = {} - /*Object.keys(fileContent['scenes']).forEach( currName => { - if(names[fileName] == null){ - names[fileName][currName] = names[fileName][currName] + 1 - } else { - names[fileName][currName] = 1 - } - });*/ textures = fileContent['textures']['textureValues'] uploadedTextureFormats = fileContent['textures']['uploadedTextureFormats'] cb(); @@ -458,7 +451,6 @@ scene_display_input.addEventListener("change", function() { return false; } reader.readAsText(itm); - console.log(fileName) // call cb when finished @@ -472,8 +464,7 @@ scene_display_input.addEventListener("change", function() { }) // when it's done.... myLoop.then(()=>{ - //patternList.innerHTML = '' - console.log('then'); + packages[fileName] = '' let arr = Object.keys(scenes) let len = arr.length @@ -519,26 +510,7 @@ scene_display_input.addEventListener("change", function() { packageSelect.value = fileName changePackage() - /*i = 0; - while(i < len){ - var toggle_button = '

'+arr[i]+'

'; - $('#patternList').append(toggle_button) - //pattern.options.add(new Option(arr[i], arr[i])) - i++ - }*/ - - + }); /** @@ -567,7 +539,7 @@ function slowLoop(items, loopBody) { }); -keysPressed = {ctrl: false, x: false, c: false, v: false, i: false} +keysPressed = {ctrl: false, x: false, c: false, v: false, i: false, m: false, r: false} /* listens for key presses to change pattern */ document.addEventListener('keyup', (e) => { @@ -605,6 +577,10 @@ document.addEventListener('keyup', (e) => { keysPressed["v"] = false; } else if(e.code === "KeyI"){ keysPressed["i"] = false; + } else if(e.code === "KeyM"){ + keysPressed["m"] = false; + } else if(e.code === "KeyR"){ + keysPressed["r"] = false; } }); @@ -649,5 +625,60 @@ document.addEventListener('keydown', (e) => { // paste document.querySelector("#debug").style.display == 'block' ? document.querySelector("#debug").style.display = 'none' : document.querySelector("#debug").style.display = 'block' } + } else if(e.code === "KeyM"){ + keysPressed['m'] = true; + let i = 0; + while(i < entityCanvas.children.length){ + mov = entityCanvas.children[i].getAttribute('mov') + if(mov.status != 0){ + break; + } + i++; + } + if(i != entityCanvas.children.length){ + stopAllMovement() + } else { + let i = 0; + sum = 0; + while(i < entityCanvas.children.length){ + if((entityCanvas.children[i].getAttribute('position').x == entityCanvas.children[i].getAttribute('mov').startPoint.x && entityCanvas.children[i].getAttribute('position').y == entityCanvas.children[i].getAttribute('mov').startPoint.y && entityCanvas.children[i].getAttribute('position').z == entityCanvas.children[i].getAttribute('mov').startPoint.z) && (entityCanvas.children[i].getAttribute('rotation').x == entityCanvas.children[i].getAttribute('mov').startRotation.x && entityCanvas.children[i].getAttribute('rotation').y == entityCanvas.children[i].getAttribute('mov').startRotation.y && entityCanvas.children[i].getAttribute('rotation').z == entityCanvas.children[i].getAttribute('mov').startRotation.z)){ + sum++; + } + i++; + } + if(sum != i){ + let i = 0; + while(i < entityCanvas.children.length){ + mov = entityCanvas.children[i].getAttribute('mov') + entityCanvas.children[i].setAttribute('position',mov.startPoint) + entityCanvas.children[i].setAttribute('rotation',mov.startRotation) + i++; + } + } else { + startAllMovement() + } + } + } else if(movementKeyBinds[e.key]) { + if(document.activeElement == keyBind){ + return + } + for(const i of movementKeyBinds[e.key]){ + if(entityCanvas.children[i].getAttribute('mov').status != 0){ + stopMovement(entityCanvas.children[i]) + } else { + if((entityCanvas.children[i].getAttribute('position').x == entityCanvas.children[i].getAttribute('mov').startPoint.x && entityCanvas.children[i].getAttribute('position').y == entityCanvas.children[i].getAttribute('mov').startPoint.y && entityCanvas.children[i].getAttribute('position').z == entityCanvas.children[i].getAttribute('mov').startPoint.z) && (entityCanvas.children[i].getAttribute('rotation').x == entityCanvas.children[i].getAttribute('mov').startRotation.x && entityCanvas.children[i].getAttribute('rotation').y == entityCanvas.children[i].getAttribute('mov').startRotation.y && entityCanvas.children[i].getAttribute('rotation').z == entityCanvas.children[i].getAttribute('mov').startRotation.z)){ + toggleMovement(i) + } else { + mov = entityCanvas.children[i].getAttribute('mov') + entityCanvas.children[i].setAttribute('position',mov.startPoint) + entityCanvas.children[i].setAttribute('rotation',mov.startRotation) + } + + } + } } + + + }); + diff --git a/Custom/drawing.js b/Custom/drawing.js index 8ad463b..f1b4188 100644 --- a/Custom/drawing.js +++ b/Custom/drawing.js @@ -59,11 +59,24 @@ function addEntity(){ el.setAttribute("material",{shader: "flat", color: "#"+R+G+B}); el.setAttribute("arraySpacing", {val: 10}); el.setAttribute("toggleCenterDot", {val: false}); - console.log('toggleCenterDot') } else if ($("#entity :selected").text() == "bullseye"){ el.setAttribute("id","bullseye"+bullseyeNum++); drawBullseye(5,5,"#"+R+G+B,el); el.setAttribute("material",{shader: "flat", color: "#"+R+G+B}); + } else if ($("#entity :selected").text() == "text"){ + el.setAttribute("id","text"+textNum++); + el.setAttribute("text",{"value": "Default Text", "color": "#FFFFFF", width: 65, height: 65, align:"center", "wrapCount": 12}); + /*el.setAttribute("value","Default Text") + el.setAttribute("color","#FFFFFF") + el.setAttribute("height", .25*2000) + el.setAttribute("width", .125*2000)*/ + } else if ($("#entity :selected").text() == "timer"){ + if(timerNum > 0){ + alert("A timer already exists") + return + } + el.setAttribute("id","timer"+timerNum++); + el.setAttribute("text",{"value": "00:00.00 ", "color": "#FFFFFF", width: 65, height: 65, align:"center", "wrapCount": 9}); } /* Set default universal stats */ @@ -74,8 +87,10 @@ function addEntity(){ let Y = Math.random() * 30 - 15; // random y position el.setAttribute("position",{x: -250 * Math.sin((THETAX*Math.PI)/180), y: Y, z: -250 * Math.cos((THETAX*Math.PI)/180)}); // set position to random x, random y, distance = 250 el.setAttribute("rotation", {x: 0, y: THETAX, z: 0}); // set rotation to be 0 - el.setAttribute("angle",{x: THETAX, z: -250}); // set angular units to be random x and 250 - + el.setAttribute("angle",{x: THETAX, z: -250}); // set angular units to be random x and + //el.setAttribute("mov",{startPoint: {x: -250 * Math.sin((THETAX*Math.PI)/180), y: Y, z: -250 * Math.cos((THETAX*Math.PI)/180)}, endPoint: {theta: 0, y: 0, r: -250},speed: 10, acceleration: 1, status: 0, type: "None", startRotation: {x: 0, y: THETAX, z: 0}, keyBind: ''}) + el.setAttribute("movement",{'startPoints': [],'endPoints': [],'initialVelocities':[],'accelerations':[],'types':[],'origin': {x: -250 * Math.sin((THETAX*Math.PI)/180), y: Y, z: -250 * Math.cos((THETAX*Math.PI)/180)}, 'rotationOrigin': {x: 0, y: THETAX, z: 0}, 'status': -1, 'index': 0, 'currentVelocity': 0, 'timeElapsed': 0}) + entityCanvas.appendChild(el); /* add entity to scene */ /* adds option to dropdown */ @@ -310,24 +325,39 @@ function updateJSON(){ const jsonData = {}; jsonData["sky"] = {skyColor: sky.getAttribute("material").color}; els.forEach(element => { + mov = JSON.parse(JSON.stringify(element.components.movement.attrValue)) + mov.status = -1; if(element.id.includes("gradient") || element.id.includes("grille")){ - jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, numBars: element.children.length, color2: element.components.color2.attrValue, childGeometry: element.children[0].components.geometry.attrValue, position: element.components.position.attrValue, material: element.components.material.attrValue, rotation: element.components.rotation.attrValue}; + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, numBars: element.children.length, color2: element.components.color2.attrValue, childGeometry: element.children[0].components.geometry.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; } else if(element.id.includes("checkerboard")){ - jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, rows: element.children.length, cols: element.children[0].children.length, tileSize: element.children[0].children[0].components.geometry.attrValue.width, color2: element.components.color2.attrValue, position: element.components.position.attrValue, material: element.components.material.attrValue, rotation: element.components.rotation.attrValue}; + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, rows: element.children.length, cols: element.children[0].children.length, tileSize: element.children[0].children[0].components.geometry.attrValue.width, color2: element.components.color2.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; } else if(element.id.includes("plane")){ - jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, widthReal: (element.children.length == 0 ? element.components.geometry.attrValue.width : element.children[2].components.geometry.attrValue.width),fill: element.components.fill.attrValue, geometry: element.components.geometry.attrValue, position: element.components.position.attrValue, material: element.components.material.attrValue, rotation: element.components.rotation.attrValue}; + let mat = JSON.parse(JSON.stringify(element.components.material.attrValue)); + for(let i = 0; i < texture.options.length; i++){ + if("#"+texture.options[i].value == mat.src){ + mat.src = texture.options[i].text; + break; + } + } + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, widthReal: (element.children.length == 0 ? element.components.geometry.attrValue.width : element.children[2].components.geometry.attrValue.width),fill: element.components.fill.attrValue, geometry: element.components.geometry.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: mat, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; } else if(element.id.includes("circle")){ - jsonData[element.id]={advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, geometry: element.components.geometry.attrValue, fill: element.components.fill.attrValue, position: element.components.position.attrValue, material: element.components.material.attrValue, rotation: element.components.rotation.attrValue}; + jsonData[element.id]={advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, geometry: element.components.geometry.attrValue, fill: element.components.fill.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; } else if(element.id.includes("triangle")){ - jsonData[element.id]={advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, geometry: element.components.geometry.attrValue, position: element.components.position.attrValue, material: element.components.material.attrValue, rotation: element.components.rotation.attrValue}; + jsonData[element.id]={advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, geometry: element.components.geometry.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; } else if(element.id.includes("circularDotarray")){ - jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, toggleCenterDot: element.components.toggleCenterDot.attrValue, circles: element.children.length-1, dots: element.children[1].children.length, arraySpacing: element.components.arraySpacing.attrValue, circleSize: element.children[0].components.geometry.attrValue.radiusOuter, position: element.components.position.attrValue, material: element.components.material.attrValue, rotation: element.components.rotation.attrValue}; + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, toggleCenterDot: element.components.toggleCenterDot.attrValue, circles: element.children.length-1, dots: element.children[1].children.length, arraySpacing: element.components.arraySpacing.attrValue, circleSize: element.children[0].components.geometry.attrValue.radiusOuter, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; } else if(element.id.includes("dotarray")){ - jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, toggleCenterDot: element.components.toggleCenterDot.attrValue, rows: element.children.length, cols: element.children[0].children.length, circleSize: element.children[0].children[0].components.geometry.attrValue.radiusOuter, spacing: element.components.arraySpacing.attrValue, position: element.components.position.attrValue, material: element.components.material.attrValue, rotation: element.components.rotation.attrValue}; + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, toggleCenterDot: element.components.toggleCenterDot.attrValue, rows: element.children.length, cols: element.children[0].children.length, circleSize: element.children[0].children[0].components.geometry.attrValue.radiusOuter, spacing: element.components.arraySpacing.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; } else if(element.id.includes("bullseye")){ - jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, numRings: element.children.length-1, ringPitch: element.children[0].components.geometry.attrValue.radiusOuter*2, position: element.components.position.attrValue, material: element.components.material.attrValue, rotation: element.components.rotation.attrValue}; + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, numRings: element.children.length-1, ringPitch: element.children[0].components.geometry.attrValue.radiusOuter*2, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("text")){ + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, text: element.components.text.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("timer")){ + text = JSON.parse(JSON.stringify(element.components.text.attrValue)) + text.value = "00:00.000" + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, text: element.components.text.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; }}); - scenes[packageSelect.value][patternList.children[parseFloat(patternList.getAttribute('selectedIndex'))].textContent] = jsonData + scenes[packageSelect.value][patternList.children[parseFloat(patternList.getAttribute('selectedIndex'))].id] = jsonData } /* converts hex to RGB values */ diff --git a/Custom/editing.js b/Custom/editing.js index 8510ed4..b7823bb 100644 --- a/Custom/editing.js +++ b/Custom/editing.js @@ -9,7 +9,7 @@ function editEntity(){ /* checks for valid entries of universal changes */ if(isNaN(parseFloat($("#x").val())) || isNaN(parseFloat($("#y").val())) || isNaN(parseFloat($("#z").val()))){ alert("Please enter a valid position"); - return; + return false; } if(hexToRgb($("#color").val()) == null){ alert("Invalid color (check that the color was entered in hexadecimal format)"); @@ -23,26 +23,135 @@ function editEntity(){ return; } + let p1; + let p2; + let d; /* Checks whether the current entity is advanced or not */ + let animationComponent = selectedEntity.getAttribute('movement') if(selectedEntity.getAttribute('advanced').val){ /* Updates universal settings if advanced */ - selectedEntity.setAttribute("position",{x: parseFloat($("#x").val()), y: parseFloat($("#y").val()), z: -parseFloat($("#z").val())}); + d = (-Math.round(Math.sqrt((parseFloat($("#x").val()))*(parseFloat($("#x").val()))+(parseFloat($("#z").val()))*(parseFloat($("#z").val()))))) selectedEntity.setAttribute("angle",{x: ((Math.asin(parseFloat($("#x").val())/d))*180)/Math.PI, z: d}); selectedEntity.setAttribute("rotation",{x: parseFloat($("#rotationX").val()), y: parseFloat($("#rotationY").val()), z: parseFloat($("#rotationZ").val())}); + stopMovement(selectedEntity); + selectedEntity.setAttribute("position",{x: parseFloat($("#x").val()), y: parseFloat($("#y").val()), z: -parseFloat($("#z").val())}); + + p1 = {x: parseFloat($("#startX").val()), y: parseFloat($("#startY").val()), z: -parseFloat($("#startZ").val())} + p2 = {x: parseFloat($("#endX").val()), y: parseFloat($("#endY").val()), z: -parseFloat($("#endZ").val())} + + let xDelta = p2.x-p1.x + let yDelta = p2.y-p1.y + let zDelta = p2.z-p1.z + d = Math.sqrt((xDelta)*(xDelta) + (yDelta)*(yDelta) + (zDelta)*(zDelta)) + animationComponent.origin = {x: parseFloat($("#x").val()), y: parseFloat($("#y").val()), z: -parseFloat($("#z").val())} + animationComponent.rotationOrigin = {x: parseFloat($("#rotationX").val()), y: parseFloat($("#rotationY").val()), z: parseFloat($("#rotationZ").val())} + } else { /* Updates universal settings if not advanced */ - selectedEntity.setAttribute("position",{x: -parseFloat($("#z").val()) * Math.sin((-parseFloat($("#x").val())*Math.PI)/180), y: parseFloat($("#y").val()), z: -parseFloat($("#z").val()) * Math.cos((-parseFloat($("#x").val())*Math.PI)/180)}); + selectedEntity.setAttribute("angle",{x: -parseFloat($("#x").val()), z: -parseFloat($("#z").val())}) selectedEntity.setAttribute("rotation",{x: 0, y: selectedEntity.getAttribute('angle').x, z: parseFloat($("#rotationZ").val())}); + stopMovement(selectedEntity); + selectedEntity.setAttribute("position",{x: -parseFloat($("#z").val()) * Math.sin((-parseFloat($("#x").val())*Math.PI)/180), y: parseFloat($("#y").val()), z: -parseFloat($("#z").val()) * Math.cos((-parseFloat($("#x").val())*Math.PI)/180)}); + //if($('#animationTypeIn').val() == 'position'){ + p1 = {theta: parseFloat($("#startX").val()), y: parseFloat($("#startY").val()), r: -parseFloat($("#startZ").val())} + p2 = {theta: parseFloat($("#endX").val()), y: parseFloat($("#endY").val()), r: -parseFloat($("#endZ").val())} + let thetaDelta = (p2.theta-p1.theta) + let arcLen = Math.abs(thetaDelta*Math.PI/180)*Math.abs(p2.r); + let yDelta = Math.abs(p2.y-p1.y) + d = Math.sqrt((arcLen)*(arcLen) + (yDelta)*(yDelta)) + let animationComponent = selectedEntity.getAttribute('movement') + animationComponent.origin = {x: -parseFloat($("#z").val()) * Math.sin((-parseFloat($("#x").val())*Math.PI)/180), y: parseFloat($("#y").val()), z: -parseFloat($("#z").val()) * Math.cos((-parseFloat($("#x").val())*Math.PI)/180)} + animationComponent.rotationOrigin = {x: 0, y: selectedEntity.getAttribute('angle').x, z: parseFloat($("#rotationZ").val())} + + + //} + } - - /* Adds a texture if a texture input is selected */ - if($("#texture").val() == "none"){ + + /* Updates Movement Animation */ + + let selectedIndex = parseFloat(animationList.getAttribute('selectedIndex')) + let i = 0; + let counter = -1; + // have to take into account rebounds from rubberband + while(i < animationComponent.types.length){ + if(animationComponent.types[i] != 'Rebound'){ + counter++; + if(counter == selectedIndex){ + break + } + } + i++; + } + + if(i < animationComponent.types.length-1 && animationComponent.types[i+1] == 'Rebound'){ + animationComponent.startPoints.splice(i+1,1); + animationComponent.endPoints.splice(i+1,1); + animationComponent.initialVelocities.splice(i+1,1); + animationComponent.accelerations.splice(i+1,1); + animationComponent.types.splice(i+1,1); + } + if($('#movementTypeIn').val() == 'Pause'){ + animationComponent.types[i] = 'Pause' + animationComponent.initialVelocities[i] = parseFloat($("#startY").val()); + animationComponent.accelerations[i] = 0; + animationComponent.status = -1 + animationComponent.index = 0 + animationComponent.timeElapsed = 0 + animationComponent.currentVelocity = 0 + } else if($('#movementTypeIn').val() == 'Rubberband'){ + animationComponent.startPoints[i] = p1; + animationComponent.startPoints.splice(i+1,0,p2); + animationComponent.endPoints[i] = p2; + animationComponent.endPoints.splice(i+1,0,p1); + animationComponent.initialVelocities[i] = parseFloat($("#speed").val()); + animationComponent.initialVelocities.splice(i+1,0,Math.sqrt(parseFloat($("#speed").val())**2+2*parseFloat($("#acceleration").val())*d)); + animationComponent.accelerations[i] = parseFloat($("#acceleration").val()); + animationComponent.accelerations.splice(i+1,0,parseFloat($("#acceleration").val())); + animationComponent.types[i] = 'Rubberband' + animationComponent.types.splice(i+1,0,'Rebound'); + animationComponent.status = -1 + animationComponent.index = 0 + animationComponent.timeElapsed = 0 + animationComponent.currentVelocity = 0 + + + //selectedEntity.setAttribute("animation__loopStart",{'property': 'position','to': p1Conv, 'from':p2Conv,'dur': dur, 'loop': true, 'startEvents': 'animLoopStart', 'pauseEvents': 'animLoopPause','dir': 'alternate', 'easing': 'linear'}) + //selectedEntity.setAttribute("animation__loopEnd",{'property': 'position','to': p2Conv, 'from':p1Conv,'dur': dur, 'loop': false, 'startEvents': 'animLoopEnd', 'pauseEvents': 'animLoopPause'}) + } else if($('#movementTypeIn').val() != 'None'){ + + animationComponent.startPoints[i] = p1; + animationComponent.endPoints[i] = p2; + animationComponent.initialVelocities[i] = parseFloat($("#speed").val()); + animationComponent.accelerations[i] = parseFloat($("#acceleration").val()); + animationComponent.types[i] = ($('#movementTypeIn').val() == 'Start') ? 'Start' :'Discontinuous' + animationComponent.status = -1 + animationComponent.index = 0 + animationComponent.timeElapsed = 0 + animationComponent.currentVelocity = 0 + + //selectedEntity.setAttribute("animation__loopStart",{'property': 'position','to': p1Conv, 'from':p2Conv, 'loop': false, 'startEvents': 'animLoopStart', 'pauseEvents': 'animLoopPause', 'easing': 'linear'}) + } + selectedEntity.setAttribute('movement',animationComponent) + updateMovementSettings() + + if(animationList.childElementCount != 0){ + animationList.children[selectedIndex].innerText = $('#movementTypeIn').val() + } + + if(!selectedEntity.getAttribute('id').includes("plane")){ selectedEntity.setAttribute("material",{shader: "flat", src: "", color: $("#color").val()}); } else { - selectedEntity.setAttribute("material",{shader: "flat", src: "#"+$("#texture").val(), color: $("#color").val()}); - } + /* Adds a texture if a texture input is selected */ + if($("#texture").val() == "none"){ + selectedEntity.setAttribute("material",{shader: "flat", src: "", color: $("#color").val()}); + } else { + selectedEntity.setAttribute("material",{shader: "flat", src: "#"+texture.selectedOptions[0].value, color: $("#color").val()}); + } + } + if(selectedEntity.getAttribute("id").includes("circle")){ /* circle only changes */ /* check for valid inputs */ @@ -313,6 +422,36 @@ function editEntity(){ } // draw new boxes drawBullseye(parseFloat($("#ringPitchIn").val()),parseFloat($("#numRingsIn").val()),$("#color").val(),selectedEntity); - } + } else if (selectedEntity.getAttribute("id").includes("text")){ /* text only changes */ + + if(isNaN(parseFloat($("#size").val()))){ + alert("Please enter a valid size"); + return; + } + if(($("#text").val()) == ""){ + alert("Please enter a valid text option"); + return; + } + if(parseFloat($("#size").val()) < 0){ + alert("Please enter a valid size ( >= 0 )"); + return; + } + + selectedEntity.setAttribute('text', {value: $("#text").val(), width: parseFloat($("#size").val()), height: parseFloat($("#size").val()), color: $("#color").val(), wrapCount: $("#text").val().length}) + + } else if (selectedEntity.getAttribute("id").includes("timer")){ /* text only changes */ + + if(isNaN(parseFloat($("#size").val()))){ + alert("Please enter a valid size"); + return; + } + if(parseFloat($("#size").val()) < 0){ + alert("Please enter a valid size ( >= 0 )"); + return; + } + + selectedEntity.setAttribute('text', {value: selectedEntity.getAttribute('text').value, width: parseFloat($("#size").val()), height: parseFloat($("#size").val()), color: $("#color").val(), wrapCount: selectedEntity.getAttribute('text').value.length}) + + } updateJSON() // update the json file of current scene } \ No newline at end of file diff --git a/Custom/entities/bullseye.js b/Custom/entities/bullseye.js new file mode 100644 index 0000000..649ee24 --- /dev/null +++ b/Custom/entities/bullseye.js @@ -0,0 +1,70 @@ +/* Contains code related to bullseye entity */ + +// creates a bullseye entity +// called when entity is added to the scene +function createBullseye(){ + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","bullseye"+bullseyeNum++); + drawBullseye(5,5,"#"+R+G+B,el); + el.setAttribute("material",{shader: "flat", color: "#"+R+G+B}); + + return el; +} + +// draws a bullseye entity using aframe rings +// called whenever an entity needs to be drawn or redrawn +function drawBullseye(pitch,rings,color1,parent){ + let r = 1; + let middleDot = document.createElement("a-entity"); + middleDot.setAttribute("id",parent.id+"-center"); + middleDot.setAttribute("geometry",{primitive: "ring", radiusOuter: pitch/2, radiusInner: 0, segmentsTheta: 100}); + middleDot.setAttribute("position",{x: 0, y: 0, z: 0}); + middleDot.setAttribute("material",{shader: "flat", color: color1}); + parent.appendChild(middleDot); + while(r <= rings){ + let elChild = document.createElement("a-entity"); + elChild.setAttribute("id",parent.id+"-"+"ring-"+r); + elChild.setAttribute("geometry",{primitive: "ring", radiusOuter: (pitch*2)*r+(pitch/2), radiusInner: (pitch*2)*r-(pitch/2), segmentsTheta: 100}); + elChild.setAttribute("position",{x: 0, y: 0, z: 0}); + elChild.setAttribute("material",{shader: "flat", color: color1}); + parent.append(elChild); + r++; + } +} + +// edits a bullseye entity +// called on bullseye attribute change +function editBullseye(ent) { + // check for valid inputs + if(isNaN(parseFloat($("#numRingsIn").val()))){ + alert("Please enter a valid number of rings"); + return false; + } + if(isNaN(parseFloat($("#ringPitchIn").val()))){ + alert("Please enter a valid ring thickness"); + return false; + } + if(parseFloat($("#numRingsIn").val()) <= 0 || parseFloat($("#numRingsIn").val()) % 1 != 0){ + alert("Please enter a valid number of rings ( > 0 and a whole number)"); + return false; + } else if(parseFloat($("#ringPitchIn").val()) < 0){ + alert("Please enter a ring thickness ( >= 0 )"); + return false; + } + // remove rings + let i = ent.children.length-1; + while (i >= 0) { + ent.children[i].parentNode.removeChild(ent.children[i]); + i--; + } + // draw new rings + drawBullseye(parseFloat($("#ringPitchIn").val()),parseFloat($("#numRingsIn").val()),$("#color").val(),ent); + + return true; +} \ No newline at end of file diff --git a/Custom/entities/checkerboard.js b/Custom/entities/checkerboard.js new file mode 100644 index 0000000..2f5c546 --- /dev/null +++ b/Custom/entities/checkerboard.js @@ -0,0 +1,87 @@ +/* Contains code related to checkerboard entity */ + +// creates a checkerboard entity +// called when entity is added to the scene +function createCheckerboard() { + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","checkerboard"+checkerboardNum++); + drawCheckerboard(16,16,.02*250,"#"+R+G+B,"#000000",el); + el.setAttribute("material",{shader: "flat", color: "#"+R+G+B}); + el.setAttribute("color2",{val: "#000000"}) + + return el; +} + +/* draws checkerboard */ +function drawCheckerboard(rows,cols,size,color1,color2,parent){ + /* draws evenly spaced squares */ + var r = 0; + var isBlack = false; + while (r < rows){ + let elChildRow = document.createElement("a-entity"); + elChildRow.setAttribute("id",parent.id+"-"+"row"+r); + var c = 0; + while (c < cols){ + let elChildCol = document.createElement("a-entity"); + elChildCol.setAttribute("id",parent.id+"-"+"col"+c); + elChildCol.setAttribute("geometry",{primitive: "plane", width: size, height: size}); + elChildCol.setAttribute("position",{x: size*cols/2-(size*c)-(size/2), y: size*rows/2-(size*r)-(size/2), z: 0}); + if(isBlack){ + elChildCol.setAttribute("material",{shader: "flat", color: color2}); + } else { + elChildCol.setAttribute("material",{shader: "flat", color: color1}); + } + isBlack = !isBlack; + elChildRow.appendChild(elChildCol) + c++; + } + if (cols % 2 == 0) {isBlack = !isBlack}; + r++; + parent.appendChild(elChildRow); + } +} + +// edits a bullseye entity +// called on bullseye attribute change +function editCheckerboard(ent) { + // check for valid inputs + if(isNaN(parseFloat($("#rowsIn").val()))){ + alert("Please enter a valid number of rows"); + return false; + } + if(isNaN(parseFloat($("#colsIn").val()))){ + alert("Please enter a valid number of columns"); + return false; + } + if(isNaN(parseFloat($("#tileSizeIn").val()))){ + alert("Please enter a valid size"); + return false; + } + if(parseFloat($("#rowsIn").val()) <= 0 || parseFloat($("#rowsIn").val()) % 1 != 0){ + alert("Please enter a valid number of rows ( > 0 and a whole number)"); + return false; + } else if(parseFloat($("#colsIn").val()) <= 0 || parseFloat($("#colsIn").val()) % 1 != 0){ + alert("Please enter a valid number of cols ( > 0 and a whole number)"); + return false; + } else if(parseFloat($("#tileSizeIn").val()) < 0){ + alert("Please enter a tile size ( >= 0 )"); + return false; + } + ent.setAttribute('color2',{val: $("#color2").val()}) + // remove boxes + let i = ent.children.length-1; + while (i >= 0) { + ent.children[i].parentNode.removeChild(ent.children[i]); + i--; + } + // draw new boxes + drawCheckerboard(parseFloat($("#rowsIn").val()),parseFloat($("#colsIn").val()),parseFloat($("#tileSizeIn").val()),$("#color").val(),$("#color2").val(),ent); + + return true; +} \ No newline at end of file diff --git a/Custom/entities/circle.js b/Custom/entities/circle.js new file mode 100644 index 0000000..6649943 --- /dev/null +++ b/Custom/entities/circle.js @@ -0,0 +1,47 @@ +/* Contains code related to circle entity + + No draw function because it only consists of one entity +*/ + +// creates a circle entity +// called when entity is added to the scene +function createCircle() { + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","circle"+circleNum++); + el.setAttribute("geometry",{primitive: "ring", radiusOuter: 0.125*250, radiusInner: 0, segmentsTheta: 100}); + el.setAttribute("fill",{val: .125*250, isFull: true}); + el.setAttribute("material", {shader: "flat", color: "#"+R+G+B}); + + return el; +} + +// edits a circle entity +// called on circle attribute change +function editCircle(ent) { + /* check for valid inputs */ + if(isNaN(parseFloat($("#radius").val()))){ + alert("Please enter a valid radius"); + return false; + } else if(parseFloat($("#radius").val()) < 0){ + alert("Please enter a valid radius"); + return false; + } else if(parseFloat($("#fill").val()) <= 0){ + alert("Border too small (0 < border <= smallest dimension of entity)"); + return false; + } else if(parseFloat($("#radius").val()) < parseFloat($("#fill").val())){ + alert("Border too large, will change size of entity (0 < border <= radius)") + return false; + } + + // edit stats + ent.setAttribute("geometry",{primitive: "ring", radiusOuter: parseFloat($("#radius").val()), radiusInner: parseFloat($("#radius").val())-parseFloat($("#fill").val()), segmentsTheta: 100}); + ent.setAttribute("material",{shader: "flat", src: ent.getAttribute("material").src, color: $("#color").val()}); + + return true; +} \ No newline at end of file diff --git a/Custom/entities/circularDotArray.js b/Custom/entities/circularDotArray.js new file mode 100644 index 0000000..d06f684 --- /dev/null +++ b/Custom/entities/circularDotArray.js @@ -0,0 +1,104 @@ +/* Contains code related to circlular dot array entity */ + +// creates a circlular dot array entity +// called when entity is added to the scene +function createCircularDotArray() { + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","circularDotarray"+circularDotarrayNum++); + drawCircularDotArray(10,5,10,2,"#"+R+G+B,false,el); + el.setAttribute("material",{shader: "flat", color: "#"+R+G+B}); + el.setAttribute("arraySpacing", {val: 10}); + el.setAttribute("toggleCenterDot", {val: false}); + + return el; +} + +// draws a circular dot array entity using aframe rings +// called whenever an entity needs to be drawn or redrawn +function drawCircularDotArray(radius,circles,dots,size,color1,toggle,parent){ + let c = 1; + let middleDot = document.createElement("a-entity"); + middleDot.setAttribute("id",parent.id+"-center"); + if(toggle){ + middleDot.setAttribute("geometry",{primitive: "ring", radiusOuter: size, radiusInner: 0, segmentsTheta: 100}); + } else { + middleDot.setAttribute("geometry",{primitive: "ring", radiusOuter: size, radiusInner: size/3, segmentsTheta: 100}); + } + + middleDot.setAttribute("position",{x: 0, y: 0, z: 0}); + middleDot.setAttribute("material",{shader: "flat", color: color1}); + parent.appendChild(middleDot) + while(c <= circles){ + let elChildArr = document.createElement("a-entity"); + elChildArr.setAttribute("id",parent.id+"-"+"circle"+c); + var i = 1; + while(i <= dots){ + let elChild = document.createElement("a-entity"); + elChild.setAttribute("id",parent.id+"-"+"circle"+c+"-"+i); + let theta = i*(2*Math.PI)/dots; + x = (radius*c)*Math.cos(theta); + y = (radius*c)*Math.sin(theta); + elChild.setAttribute("geometry",{primitive: "ring", radiusOuter: size, radiusInner: 0, segmentsTheta: 100}); + elChild.setAttribute("position",{x: x, y: y, z: 0}); + elChild.setAttribute("material",{shader: "flat", color: color1}); + elChildArr.appendChild(elChild) + i++; + } + parent.append(elChildArr); + c++; + } + +} + +// edits a circular dot array entity +// called on circular dot array attribute change +function editCircularDotArray(ent) { + // check for valid inputs + if(isNaN(parseFloat($("#numDotsIn").val()))){ + alert("Please enter a valid number of dots"); + return false; + } + if(isNaN(parseFloat($("#numCirclesIn").val()))){ + alert("Please enter a valid number of circles"); + return false; + } + if(isNaN(parseFloat($("#circleSizeIn").val()))){ + alert("Please enter a valid size"); + return false; + } + if(isNaN(parseFloat($("#arraySpacingIn").val()))){ + alert("Please enter a valid array spacing"); + return false; + } + if(parseFloat($("#numDotsIn").val()) < 0 || parseFloat($("#numDotsIn").val()) % 1 != 0){ + alert("Please enter a valid number of dots ( >= 0 and a whole number)"); + return false; + } else if(parseFloat($("#numCirclesIn").val()) <= 0 || parseFloat($("#numCirclesIn").val()) % 1 != 0){ + alert("Please enter a valid number of circles ( > 0 and a whole number)"); + return false; + } else if(parseFloat($("#circleSizeIn").val()) < 0){ + alert("Please enter a circle size ( >= 0 )"); + return false; + } else if(parseFloat($("#arraySpacingIn").val()) < 0){ + alert("Please enter a valid array spacing ( >= 0 )"); + return false; + } + // remove boxes + let i = ent.children.length-1; + while (i >= 0) { + ent.children[i].parentNode.removeChild(ent.children[i]); + i--; + } + // draw new boxes + drawCircularDotArray(parseFloat($("#arraySpacingIn").val()),parseFloat($("#numCirclesIn").val()),parseFloat($("#numDotsIn").val()),parseFloat($("#circleSizeIn").val()),$("#color").val(),toggleCenterDotIn.checked,ent); + ent.setAttribute('arraySpacing',{val: parseFloat($("#arraySpacingIn").val())}); + ent.setAttribute('toggleCenterDot',{val: toggleCenterDotIn.checked}); + + return true; +} \ No newline at end of file diff --git a/Custom/entities/dotArray.js b/Custom/entities/dotArray.js new file mode 100644 index 0000000..0fe187d --- /dev/null +++ b/Custom/entities/dotArray.js @@ -0,0 +1,98 @@ +/* Contains code related to dot array entity */ + +// creates a dot array entity +// called when entity is added to the scene +function createDotArray() { + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","dotarray"+dotarrayNum++); + drawDotArray(5,5,2,10,"#"+R+G+B,false,el); + el.setAttribute("arraySpacing",{val: 10}); + el.setAttribute("toggleCenterDot", {val: false}); + el.setAttribute("material",{shader: "flat", color: "#"+R+G+B}); + + return el; +} + +/* draws dot array */ +function drawDotArray(rows,cols,size,spacing,color1,toggle,parent){ + /* draws evenly spaced squares */ + var r = 0; + while (r < rows){ + let elChildRow = document.createElement("a-entity"); + elChildRow.setAttribute("id",parent.id+"-"+"row"+r); + var c = 0; + while (c < cols){ + let elChildCol = document.createElement("a-entity"); + elChildCol.setAttribute("id",parent.id+"-"+"col"+c); + if((size+2*spacing)*cols/2-((size+2*spacing)*c)-((size+2*spacing)/2) == 0 && (size+2*spacing)*rows/2-((size+2*spacing)*r)-((size+2*spacing)/2) == 0){ + if(toggle){ + elChildCol.setAttribute("geometry",{primitive: "ring", radiusOuter: size, radiusInner: 0, segmentsTheta: 100}); + } else { + elChildCol.setAttribute("geometry",{primitive: "ring", radiusOuter: size, radiusInner: size/3, segmentsTheta: 100}); + } + } else { + elChildCol.setAttribute("geometry",{primitive: "ring", radiusOuter: size, radiusInner: 0, segmentsTheta: 100}); + } + elChildCol.setAttribute("position",{x: (size+2*spacing)*cols/2-((size+2*spacing)*c)-((size+2*spacing)/2), y: (size+2*spacing)*rows/2-((size+2*spacing)*r)-((size+2*spacing)/2), z: 0}); + elChildCol.setAttribute("material",{shader: "flat", color: color1}); + elChildRow.appendChild(elChildCol) + c++; + } + parent.appendChild(elChildRow); + r++; + } + +} + +// edits a dot array entity +// called on dot array attribute change +function editDotArray(ent) { + // check for valid inputs + if(isNaN(parseFloat($("#rowsIn").val()))){ + alert("Please enter a valid number of rows"); + return false; + } + if(isNaN(parseFloat($("#colsIn").val()))){ + alert("Please enter a valid number of columns"); + return false; + } + if(isNaN(parseFloat($("#circleSizeIn").val()))){ + alert("Please enter a valid size"); + return false; + } + if(isNaN(parseFloat($("#spacingIn").val()))){ + alert("Please enter a spacing"); + return false; + } + if(parseFloat($("#rowsIn").val()) <= 0 || parseFloat($("#rowsIn").val()) % 1 != 0){ + alert("Please enter a valid number of rows ( > 0 and a whole number)"); + return false; + } else if(parseFloat($("#colsIn").val()) <= 0 || parseFloat($("#colsIn").val()) % 1 != 0){ + alert("Please enter a valid number of cols ( > 0 and a whole number)"); + return false; + } else if(parseFloat($("#circleSizeIn").val()) < 0){ + alert("Please enter a circle size ( >= 0 )"); + return false; + } else if(parseFloat($("#spacingIn").val()) < 0){ + alert("Please enter a valid spacing ( >= 0 )"); + return false; + } + // remove dots + let i = ent.children.length-1; + while (i >= 0) { + ent.children[i].parentNode.removeChild(ent.children[i]); + i--; + } + // draw new dots + drawDotArray(parseFloat($("#rowsIn").val()),parseFloat($("#colsIn").val()),parseFloat($("#circleSizeIn").val()),parseFloat($("#spacingIn").val()),$("#color").val(),toggleCenterDotIn.checked,ent); + ent.setAttribute('arraySpacing',{val: parseFloat($("#spacingIn").val())}); + ent.setAttribute('toggleCenterDot',{val: toggleCenterDotIn.checked}); + + return true; +} \ No newline at end of file diff --git a/Custom/entities/drawing.js b/Custom/entities/drawing.js new file mode 100644 index 0000000..c5b979f --- /dev/null +++ b/Custom/entities/drawing.js @@ -0,0 +1,87 @@ +/* + Responsible for adding entities to the scene and updating the json object of the current scene. + Can add circle, plane, triangle, checkerboard, grille, or gradient. + +*/ + +/* adds entity to scene */ +function addEntity(){ + + let el; + + /* check desired type of entity */ + if($("#entity :selected").text() == "circle"){ /* if circle */ + // make circle + el = createCircle(); + + + } else if ($("#entity :selected").text() == "plane"){ /* if plane */ + // make plane + el = createPlane(); + + } else if ($("#entity :selected").text() == "triangle"){ /* if triangle */ + // make triangle + el = createTriangle(); + + } else if ($("#entity :selected").text() == "gradient"){ + // make gradient + el = createGradient(); + + } else if ($("#entity :selected").text() == "checkerboard"){ + // make checkerboard + el = createCheckerboard(); + + } else if ($("#entity :selected").text() == "grille"){ + // make grille + el = createGrille(); + + } else if ($("#entity :selected").text() == "dot array"){ + // make dot array + el = createDotArray(); + + } else if ($("#entity :selected").text() == "circlular dot array"){ + // make circular dot array + el = createCircularDotArray(); + + } else if ($("#entity :selected").text() == "bullseye"){ + // make bullseye + el = createBullseye(); + + } else if ($("#entity :selected").text() == "text"){ + // make text + el = createText(); + + } else if ($("#entity :selected").text() == "timer"){ + // make timer + if(timerNum > 0){ + alert("A timer already exists") + return + } + el = createTimer(); + } + /* Set default universal stats */ + + numAdded++; // number of elements added + el.setAttribute("click-checker",""); // listener for click + el.setAttribute("advanced",{val: false}); // advanced mode indicator + let THETAX = Math.random() * 110 -55; // random x position + let Y = Math.random() * 30 - 15; // random y position + el.setAttribute("position",{x: -250 * Math.sin((THETAX*Math.PI)/180), y: Y, z: -250 * Math.cos((THETAX*Math.PI)/180)}); // set position to random x, random y, distance = 250 + el.setAttribute("rotation", {x: 0, y: THETAX, z: 0}); // set rotation to be 0 + el.setAttribute("angle",{x: THETAX, z: -250}); // set angular units to be random x and + //el.setAttribute("mov",{startPoint: {x: -250 * Math.sin((THETAX*Math.PI)/180), y: Y, z: -250 * Math.cos((THETAX*Math.PI)/180)}, endPoint: {theta: 0, y: 0, r: -250},speed: 10, acceleration: 1, status: 0, type: "None", startRotation: {x: 0, y: THETAX, z: 0}, keyBind: ''}) + el.setAttribute("movement",{'startPoints': [],'endPoints': [],'initialVelocities':[],'accelerations':[],'types':[],'origin': {x: -250 * Math.sin((THETAX*Math.PI)/180), y: Y, z: -250 * Math.cos((THETAX*Math.PI)/180)}, 'rotationOrigin': {x: 0, y: THETAX, z: 0}, 'status': -1, 'index': 0, 'currentVelocity': 0, 'timeElapsed': 0}) + + entityCanvas.appendChild(el); /* add entity to scene */ + + /* adds option to dropdown */ + var option = document.createElement("option"); + option.text = el.getAttribute("id"); + entitySelector.add(option); + + /* add entity to list of created entities */ + els.push(el); + pool.push(el.object3D); + + updateJSON(); // update JSON of current scene +} \ No newline at end of file diff --git a/Custom/entities/editing.js b/Custom/entities/editing.js new file mode 100644 index 0000000..d3288b7 --- /dev/null +++ b/Custom/entities/editing.js @@ -0,0 +1,204 @@ +/* + Responsible for editing currently selected entity + Whenever an appropriate change is queued, editEntity function is called and adjusts current entity based on changes + +*/ + +/* edits selected entity */ +function editEntity(){ + /* checks for valid entries of universal changes */ + if(isNaN(parseFloat($("#x").val())) || isNaN(parseFloat($("#y").val())) || isNaN(parseFloat($("#z").val()))){ + alert("Please enter a valid position"); + return false; + } + if(hexToRgb($("#color").val()) == null){ + alert("Invalid color (check that the color was entered in hexadecimal format)"); + return; + } + if(val && ((isNaN(parseFloat($("#rotationZ").val())) || isNaN(parseFloat($("#rotationY").val())) || isNaN(parseFloat($("#rotationX").val()))))){ + alert("Please enter a valid rotation"); + return; + } else if(!val && isNaN(parseFloat($("#rotationZ").val()))) { + alert("Please enter a valid rotation"); + return; + } + + let p1; + let p2; + let d; + /* Checks whether the current entity is advanced or not */ + let animationComponent = selectedEntity.getAttribute('movement') + if(selectedEntity.getAttribute('advanced').val){ + /* Updates universal settings if advanced */ + + d = (-Math.round(Math.sqrt((parseFloat($("#x").val()))*(parseFloat($("#x").val()))+(parseFloat($("#z").val()))*(parseFloat($("#z").val()))))) + selectedEntity.setAttribute("angle",{x: ((Math.asin(parseFloat($("#x").val())/d))*180)/Math.PI, z: d}); + selectedEntity.setAttribute("rotation",{x: parseFloat($("#rotationX").val()), y: parseFloat($("#rotationY").val()), z: parseFloat($("#rotationZ").val())}); + stopMovement(selectedEntity); + selectedEntity.setAttribute("position",{x: parseFloat($("#x").val()), y: parseFloat($("#y").val()), z: -parseFloat($("#z").val())}); + + p1 = {x: parseFloat($("#startX").val()), y: parseFloat($("#startY").val()), z: -parseFloat($("#startZ").val())} + p2 = {x: parseFloat($("#endX").val()), y: parseFloat($("#endY").val()), z: -parseFloat($("#endZ").val())} + + let xDelta = p2.x-p1.x + let yDelta = p2.y-p1.y + let zDelta = p2.z-p1.z + d = Math.sqrt((xDelta)*(xDelta) + (yDelta)*(yDelta) + (zDelta)*(zDelta)) + animationComponent.origin = {x: parseFloat($("#x").val()), y: parseFloat($("#y").val()), z: -parseFloat($("#z").val())} + animationComponent.rotationOrigin = {x: parseFloat($("#rotationX").val()), y: parseFloat($("#rotationY").val()), z: parseFloat($("#rotationZ").val())} + + } else { + /* Updates universal settings if not advanced */ + + selectedEntity.setAttribute("angle",{x: -parseFloat($("#x").val()), z: -parseFloat($("#z").val())}) + selectedEntity.setAttribute("rotation",{x: 0, y: selectedEntity.getAttribute('angle').x, z: parseFloat($("#rotationZ").val())}); + stopMovement(selectedEntity); + selectedEntity.setAttribute("position",{x: -parseFloat($("#z").val()) * Math.sin((-parseFloat($("#x").val())*Math.PI)/180), y: parseFloat($("#y").val()), z: -parseFloat($("#z").val()) * Math.cos((-parseFloat($("#x").val())*Math.PI)/180)}); + + p1 = {theta: parseFloat($("#startX").val()), y: parseFloat($("#startY").val()), r: -parseFloat($("#startZ").val())} + p2 = {theta: parseFloat($("#endX").val()), y: parseFloat($("#endY").val()), r: -parseFloat($("#endZ").val())} + let thetaDelta = (p2.theta-p1.theta) + let arcLen = Math.abs(thetaDelta*Math.PI/180)*Math.abs(p2.r); + let yDelta = Math.abs(p2.y-p1.y) + d = Math.sqrt((arcLen)*(arcLen) + (yDelta)*(yDelta)) + let animationComponent = selectedEntity.getAttribute('movement') + animationComponent.origin = {x: -parseFloat($("#z").val()) * Math.sin((-parseFloat($("#x").val())*Math.PI)/180), y: parseFloat($("#y").val()), z: -parseFloat($("#z").val()) * Math.cos((-parseFloat($("#x").val())*Math.PI)/180)} + animationComponent.rotationOrigin = {x: 0, y: selectedEntity.getAttribute('angle').x, z: parseFloat($("#rotationZ").val())} + + } + + /* Updates Movement Animation */ + + let selectedIndex = parseFloat(animationList.getAttribute('selectedIndex')) + let i = 0; + let counter = -1; + + // have to take into account rebounds from rubberband + while(i < animationComponent.types.length){ + if(animationComponent.types[i] != 'Rebound'){ + counter++; + if(counter == selectedIndex){ + break + } + } + i++; + } + + // if old animation type is rebound, remove resulting rebounds + if(i < animationComponent.types.length-1 && animationComponent.types[i+1] == 'Rebound'){ + animationComponent.startPoints.splice(i+1,1); + animationComponent.endPoints.splice(i+1,1); + animationComponent.initialVelocities.splice(i+1,1); + animationComponent.accelerations.splice(i+1,1); + animationComponent.types.splice(i+1,1); + } + + // add new movement + if($('#movementTypeIn').val() == 'Pause'){ + animationComponent.types[i] = 'Pause' + animationComponent.initialVelocities[i] = parseFloat($("#startY").val()); + animationComponent.accelerations[i] = 0; + animationComponent.status = -1 + animationComponent.index = 0 + animationComponent.timeElapsed = 0 + animationComponent.currentVelocity = 0 + } else if($('#movementTypeIn').val() == 'Rubberband'){ + animationComponent.startPoints[i] = p1; + animationComponent.startPoints.splice(i+1,0,p2); + animationComponent.endPoints[i] = p2; + animationComponent.endPoints.splice(i+1,0,p1); + animationComponent.initialVelocities[i] = parseFloat($("#speed").val()); + animationComponent.initialVelocities.splice(i+1,0,Math.sqrt(parseFloat($("#speed").val())**2+2*parseFloat($("#acceleration").val())*d)); + animationComponent.accelerations[i] = parseFloat($("#acceleration").val()); + animationComponent.accelerations.splice(i+1,0,parseFloat($("#acceleration").val())); + animationComponent.types[i] = 'Rubberband' + animationComponent.types.splice(i+1,0,'Rebound'); + animationComponent.status = -1 + animationComponent.index = 0 + animationComponent.timeElapsed = 0 + animationComponent.currentVelocity = 0 + + } else if($('#movementTypeIn').val() != 'None'){ + + animationComponent.startPoints[i] = p1; + animationComponent.endPoints[i] = p2; + animationComponent.initialVelocities[i] = parseFloat($("#speed").val()); + animationComponent.accelerations[i] = parseFloat($("#acceleration").val()); + animationComponent.types[i] = ($('#movementTypeIn').val() == 'Start') ? 'Start' :'Discontinuous' + animationComponent.status = -1 + animationComponent.index = 0 + animationComponent.timeElapsed = 0 + animationComponent.currentVelocity = 0 + } + + selectedEntity.setAttribute('movement',animationComponent) + updateMovementSettings() + + // create new animation list child + if(animationList.childElementCount != 0){ + animationList.children[selectedIndex].innerText = $('#movementTypeIn').val() + } + + // handle texture change + if(!selectedEntity.getAttribute('id').includes("plane")){ + selectedEntity.setAttribute("material",{shader: "flat", src: "", color: $("#color").val()}); + } else { + /* Adds a texture if a texture input is selected */ + if($("#texture").val() == "none"){ + selectedEntity.setAttribute("material",{shader: "flat", src: "", color: $("#color").val()}); + } else { + selectedEntity.setAttribute("material",{shader: "flat", src: "#"+texture.selectedOptions[0].value, color: $("#color").val()}); + } + } + + + // update appropriate entity + if(selectedEntity.getAttribute("id").includes("circle")){ /* circle only changes */ + if(!editCircle(selectedEntity)){ + return; + } + + } else if (selectedEntity.getAttribute("id").includes("plane")){ /* plane only changes */ + if(!editPlane(selectedEntity)){ + return; + } + } else if (selectedEntity.getAttribute("id").includes("triangle")){ /* triangle only changes */ + if(!editTriangle(selectedEntity)){ + return; + } + } else if (selectedEntity.getAttribute("id").includes("gradient")) { + if(!editGradient(selectedEntity)){ + return; + } + } else if(selectedEntity.getAttribute("id").includes("grille")){ /* gradient and grille only changes */ + if(!editGrille(selectedEntity)){ + return; + } + } else if (selectedEntity.getAttribute("id").includes("checkerboard")){ /* checkerboard only changes */ + if(!editCheckerboard(selectedEntity)){ + return; + } + } else if (selectedEntity.getAttribute("id").includes("circularDotarray")){ /* checkerboard only changes */ + if(!editCircularDotArray(selectedEntity)){ + return; + } + } else if (selectedEntity.getAttribute("id").includes("dotarray")){ /* checkerboard only changes */ + if(!editDotArray(selectedEntity)){ + return; + } + } else if (selectedEntity.getAttribute("id").includes("bullseye")){ /* checkerboard only changes */ + if(!editBullseye(selectedEntity)){ + return; + } + } else if (selectedEntity.getAttribute("id").includes("text")){ /* text only changes */ + if(!editText(selectedEntity)){ + return; + } + } else if (selectedEntity.getAttribute("id").includes("timer")){ /* text only changes */ + if(!editTimer(selectedEntity)){ + return; + } + } + + updateJSON() // update the json file of current scene +} \ No newline at end of file diff --git a/Custom/entities/gradient.js b/Custom/entities/gradient.js new file mode 100644 index 0000000..857f9b0 --- /dev/null +++ b/Custom/entities/gradient.js @@ -0,0 +1,82 @@ +/* Contains code related to gradient entity */ + +// creates a gradient entity +// called when entity is added to the scene +function createGradient() { + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","gradient"+gradientNum++); + drawGradient(.025*250,.075*250,32,hexToRgb("#"+R+G+B),hexToRgb("#000000"),el); + el.setAttribute("material",{shader: "flat", color: "#"+R+G+B}); + el.setAttribute("color2",{val: "#000000"}) + + return el; +} + +/* draws gradient */ +function drawGradient(width,height,numBars,color1,color2,parent){ + /* creates evenly spaced planes that progressively trend towards full color */ + rDiff = color2.r-color1.r + gDiff = color2.g-color1.g + bDiff = color2.b-color1.b + rStep = rDiff/(numBars-1) + gStep = gDiff/(numBars-1) + bStep = bDiff/(numBars-1) + var j = numBars-1; + while(j >= 0){ + let elChild = document.createElement('a-entity'); // creates plane + elChild.setAttribute("id",parent.id+"-"+(numBars-j).toString()); // sets id correctly + elChild.setAttribute("geometry",{primitive: "plane", width: width, height: height}); // sets size + elChild.setAttribute("position",{x: width*numBars/2-(width*j)-(width/2), y: 0, z: 0}); // sets proper position + elChild.setAttribute("material",{shader: "flat", color: "rgb("+Math.ceil(color2.r-(rStep*j)).toString()+","+Math.ceil(color2.g-(gStep*j)).toString()+","+Math.floor(color2.b-(bStep*j)).toString()+")"}); // sets color + parent.appendChild(elChild); // adds entity to parent + j--; + } + +} + +// edits gradient entity +// called when gradient attributes are changed +function editGradient(ent) { + // check for valid inputs + if(isNaN(parseFloat($("#width").val()))){ + alert("Please enter a valid width"); + return false; + } + if(isNaN(parseFloat($("#height").val()))){ + alert("Please enter a valid height"); + return false; + } + if(isNaN(parseFloat($("#numBarsIn").val()))){ + alert("Please enter a valid number of bars"); + return false; + } + if(parseFloat($("#height").val()) < 0){ + alert("Please enter a valid height ( >= 0 )"); + return false; + } else if(parseFloat($("#width").val()) < 0){ + alert("Please enter a valid width ( >= 0 )"); + return false; + } else if(parseFloat($("#numBarsIn").val()) < 0 || parseFloat($("#numBarsIn").val()) % 1 != 0){ + alert("Please enter a valid number of bars ( >= 0 and a whole number )"); + return false; + } + + // remove bars + let i = ent.children.length-1; + while (i >= 0) { + ent.children[i].parentNode.removeChild(ent.children[i]); + i--; + } + + drawGradient(parseFloat($("#width").val()),parseFloat($("#height").val()),parseFloat($("#numBarsIn").val()),hexToRgb($("#color").val()),hexToRgb($("#color2").val()),ent); + + ent.setAttribute('color2',{val: $("#color2").val()}) + + return true; +} \ No newline at end of file diff --git a/Custom/entities/grille.js b/Custom/entities/grille.js new file mode 100644 index 0000000..878b585 --- /dev/null +++ b/Custom/entities/grille.js @@ -0,0 +1,82 @@ +/* Contains code related to grille entity */ + +// creates a grille entity +// called when entity is added to the scene +function createGrill() { + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","grille"+grilleNum++); + drawGrille(.025*250,.125*250,32,"#"+R+G+B,"#000000",el); + el.setAttribute("material",{shader: "flat", color: "#"+R+G+B}); + el.setAttribute("color2",{val: "#000000"}) + + return el; +} + +/* draws grille */ +function drawGrille(width,height,numBars,color1, color2,parent){ + /* creates evenly spaced planes that alternate between some color and black */ + + var j = 0; + var isBlack = false; + while(j < numBars){ + let elChild = document.createElement('a-entity'); + elChild.setAttribute("id",parent.id+"-"+(numBars-j).toString()); + elChild.setAttribute("geometry",{primitive: "plane", width: width, height: height}); + elChild.setAttribute("position",{x: width*numBars/2-(width*j)-(width/2), y: 0, z: 0}); + if(isBlack){ // if on black bar + elChild.setAttribute("material",{shader: "flat", color: color2}); + } else { + elChild.setAttribute("material",{shader: "flat", color: color1}); + } + isBlack = !isBlack; + parent.appendChild(elChild); + j++; + } +} + +// edits a grille entity +// called on grille attribute change +function editGrille(ent) { + // check for valid inputs + if(isNaN(parseFloat($("#width").val()))){ + alert("Please enter a valid width"); + return false; + } + if(isNaN(parseFloat($("#height").val()))){ + alert("Please enter a valid height"); + return false; + } + if(isNaN(parseFloat($("#numBarsIn").val()))){ + alert("Please enter a valid number of bars"); + return false; + } + if(parseFloat($("#height").val()) < 0){ + alert("Please enter a valid height ( >= 0 )"); + return false; + } else if(parseFloat($("#width").val()) < 0){ + alert("Please enter a valid width ( >= 0 )"); + return false; + } else if(parseFloat($("#numBarsIn").val()) < 0 || parseFloat($("#numBarsIn").val()) % 1 != 0){ + alert("Please enter a valid number of bars ( >= 0 and a whole number )"); + return false; + } + + // remove bars + let i = ent.children.length-1; + while (i >= 0) { + ent.children[i].parentNode.removeChild(ent.children[i]); + i--; + } + + drawGrille(parseFloat($("#width").val()),parseFloat($("#height").val()),parseFloat($("#numBarsIn").val()),hexToRgb($("#color").val()),hexToRgb($("#color2").val()),ent); + + ent.setAttribute('color2',{val: $("#color2").val()}) + + return true; +} \ No newline at end of file diff --git a/Custom/entities/plane.js b/Custom/entities/plane.js new file mode 100644 index 0000000..84924f6 --- /dev/null +++ b/Custom/entities/plane.js @@ -0,0 +1,110 @@ +/* Contains code related to plane entity */ + +// creates a plane entity +// called when plane is added to the scene +function createPlane() { + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","plane"+planeNum++); + el.setAttribute("geometry",{primitive: "plane", width: .125*250, height: .25*250}); + el.setAttribute("fill",{val: .125*250, isFull: true}); + el.setAttribute("material", {shader: "flat", color: "#"+R+G+B}); + + return el; +} + +/* draws hollow plane */ +function drawPlaneBorder(width,height,fill,color,parent){ + + let newWidth = fill/2; + let newHeight = fill/2; + + /* Creates 4 new planes that will represent each side of the plane */ + vertL = document.createElement('a-entity'); + vertR = document.createElement('a-entity'); + horizU = document.createElement('a-entity'); + horizD = document.createElement('a-entity'); + + /* Sets them to desired size */ + vertL.setAttribute("geometry",{primitive: "plane", width: newWidth, height: height}); + vertR.setAttribute("geometry",{primitive: "plane", width: newWidth, height: height}); + horizU.setAttribute("geometry",{primitive: "plane", width: width, height: newHeight}); + horizD.setAttribute("geometry",{primitive: "plane", width: width, height: newHeight}); + + /* Sets position with very slight forward offset */ + vertL.setAttribute("position",{x: (-1*width/2)+(newWidth/2), y: 0, z: -0.00001}); + vertR.setAttribute("position",{x: (width/2)-(newWidth/2), y: 0, z: -0.00001}); + horizU.setAttribute("position",{x: 0, y: (height/2)-(newHeight/2), z: -0.00001}); + horizD.setAttribute("position",{x: 0, y: (-1*height/2)+(newHeight/2), z: -0.00001}); + + /* Sets color to be parent color */ + vertL.setAttribute("material", {shader: "flat", color: "rgb("+color.r.toString()+","+color.g.toString()+","+color.b.toString()+")"}); + vertR.setAttribute("material", {shader: "flat", color: "rgb("+color.r.toString()+","+color.g.toString()+","+color.b.toString()+")"}); + horizU.setAttribute("material", {shader: "flat", color: "rgb("+color.r.toString()+","+color.g.toString()+","+color.b.toString()+")"}); + horizD.setAttribute("material", {shader: "flat", color: "rgb("+color.r.toString()+","+color.g.toString()+","+color.b.toString()+")"}); + + /* Sets entity ids */ + vertL.setAttribute("id",parent.getAttribute("id")+"-0"); + vertR.setAttribute("id",parent.getAttribute("id")+"-1"); + horizU.setAttribute("id",parent.getAttribute("id")+"-2"); + horizD.setAttribute("id",parent.getAttribute("id")+"-3"); + + /* Adds entities to parent plane */ + parent.appendChild(vertL); + parent.appendChild(vertR); + parent.appendChild(horizU); + parent.appendChild(horizD); + +} + +// edits a plane entity +// called on plane attribute change +function editPlane(ent){ + // check for valid inputs + if(isNaN(parseFloat($("#width").val()))){ + alert("Please enter a valid width"); + return false; + } + if(isNaN(parseFloat($("#height").val()))){ + alert("Please enter a valid height"); + return false; + } + if(parseFloat($("#height").val()) < 0){ + alert("Please enter a valid height ( >= 0 )"); + return false; + } else if(parseFloat($("#width").val()) < 0){ + alert("Please enter a valid width ( >= 0 )"); + return false; + } else if(parseFloat($("#fill").val()) <= 0){ + alert("Border too small (0 < border <= smallest dimension of entity)"); + return false; + } else if((parseFloat($("#width").val()) < parseFloat($("#fill").val())) && (parseFloat($("#width").val()) <= parseFloat($("#height").val())) || (parseFloat($("#height").val()) < parseFloat($("#fill").val())) && (parseFloat($("#width").val()) > parseFloat($("#height").val()))){ + alert("Border too large, will change size of entity (0 < border <= smallest dimension of entity)"); + return false; + } + + // if no texture, draw plane border + if (texture.value == "none"){ + ent.setAttribute("geometry",{primitive: "plane", width: 0, height: parseFloat($("#height").val())}); + let i = ent.children.length-1; + while (i >= 0) { + ent.children[i].parentNode.removeChild(ent.children[i]); + i--; + } + drawPlaneBorder(parseFloat($("#width").val()),parseFloat($("#height").val()),parseFloat($("#fill").val()),hexToRgb($("#color").val()),ent); + } else { // draw solid plane that will be the texture canvas + let i = ent.children.length-1; + while (i >= 0) { + ent.children[i].parentNode.removeChild(ent.children[i]); + i--; + } + ent.setAttribute("geometry",{primitive: "plane", width: parseFloat($("#width").val()), height: parseFloat($("#height").val())}); + } + + return true; +} \ No newline at end of file diff --git a/Custom/entities/saving.js b/Custom/entities/saving.js new file mode 100644 index 0000000..de09ccc --- /dev/null +++ b/Custom/entities/saving.js @@ -0,0 +1,43 @@ +/* updates the current json object for the active scene */ +function updateJSON(){ + const jsonData = {}; + jsonData["sky"] = {skyColor: sky.getAttribute("material").color}; // grab sky color + els.forEach(element => { // go through each entity in the current scene + // stop the movement animation + mov = JSON.parse(JSON.stringify(element.components.movement.attrValue)) + mov.status = -1; + + // check type of element and save relevant data + if(element.id.includes("gradient") || element.id.includes("grille")){ + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, numBars: element.children.length, color2: element.components.color2.attrValue, childGeometry: element.children[0].components.geometry.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("checkerboard")){ + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, rows: element.children.length, cols: element.children[0].children.length, tileSize: element.children[0].children[0].components.geometry.attrValue.width, color2: element.components.color2.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("plane")){ + let mat = JSON.parse(JSON.stringify(element.components.material.attrValue)); + for(let i = 0; i < texture.options.length; i++){ + if("#"+texture.options[i].value == mat.src){ + mat.src = texture.options[i].text; + break; + } + } + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, widthReal: (element.children.length == 0 ? element.components.geometry.attrValue.width : element.children[2].components.geometry.attrValue.width),fill: element.components.fill.attrValue, geometry: element.components.geometry.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: mat, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("circle")){ + jsonData[element.id]={advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, geometry: element.components.geometry.attrValue, fill: element.components.fill.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("triangle")){ + jsonData[element.id]={advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, geometry: element.components.geometry.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("circularDotarray")){ + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, toggleCenterDot: element.components.toggleCenterDot.attrValue, circles: element.children.length-1, dots: element.children[1].children.length, arraySpacing: element.components.arraySpacing.attrValue, circleSize: element.children[0].components.geometry.attrValue.radiusOuter, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("dotarray")){ + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, toggleCenterDot: element.components.toggleCenterDot.attrValue, rows: element.children.length, cols: element.children[0].children.length, circleSize: element.children[0].children[0].components.geometry.attrValue.radiusOuter, spacing: element.components.arraySpacing.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("bullseye")){ + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, numRings: element.children.length-1, ringPitch: element.children[0].components.geometry.attrValue.radiusOuter*2, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, material: element.components.material.attrValue, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("text")){ + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, text: element.components.text.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + } else if(element.id.includes("timer")){ + let text = JSON.parse(JSON.stringify(element.components.text.attrValue)) + text.value = "00:00.000" + jsonData[element.id] = {advanced: element.components.advanced.attrValue, angle: element.components.angle.attrValue, text: element.components.text.attrValue, position: {x: element.components.position.attrValue.x, y: element.components.position.attrValue.y, z: element.components.position.attrValue.z}, rotation: {x: element.components.rotation.attrValue.x, y: element.components.rotation.attrValue.y, z: element.components.rotation.attrValue.z}, movement: mov}; + }}); + + scenes[packageSelect.value][patternList.children[parseFloat(patternList.getAttribute('selectedIndex'))].id] = jsonData; +} \ No newline at end of file diff --git a/Custom/entities/text.js b/Custom/entities/text.js new file mode 100644 index 0000000..a2bf1a1 --- /dev/null +++ b/Custom/entities/text.js @@ -0,0 +1,40 @@ +/* Contains code related to grille entity + No draw function because only one entity +*/ + +// creates a text entity +// called when entity is added to the scene +function createText() { + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","text"+textNum++); + el.setAttribute("text",{"value": "Default Text", "color": "#FFFFFF", width: 65, height: 65, align:"center", "wrapCount": 12}); + + return el; +} + +// edits text entity +// called when text attribute is changed +function editText(ent) { + if(isNaN(parseFloat($("#size").val()))){ + alert("Please enter a valid size"); + return false; + } + if(($("#text").val()) == ""){ + alert("Please enter a valid text option"); + return false; + } + if(parseFloat($("#size").val()) < 0){ + alert("Please enter a valid size ( >= 0 )"); + return false; + } + + ent.setAttribute('text', {value: $("#text").val(), width: parseFloat($("#size").val()), height: parseFloat($("#size").val()), color: $("#color").val(), wrapCount: $("#text").val().length}) + + return true; +} \ No newline at end of file diff --git a/Custom/entities/timer.js b/Custom/entities/timer.js new file mode 100644 index 0000000..51b730c --- /dev/null +++ b/Custom/entities/timer.js @@ -0,0 +1,74 @@ +/* Contains code related to grille entity + No draw function because only one entity +*/ + +// creates a timer entity +// called when entity is added to the scene +function createTimer() { + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","timer"+timerNum++); + el.setAttribute("text",{"value": "00:00.00 ", "color": "#FFFFFF", width: 65, height: 65, align:"center", "wrapCount": 9}); + + return el; +} + +// edits timer entity +// called when timer attribute is changed +function editTimer(ent) { + if(isNaN(parseFloat($("#size").val()))){ + alert("Please enter a valid size"); + return false; + } + if(parseFloat($("#size").val()) < 0){ + alert("Please enter a valid size ( >= 0 )"); + return false; + } + + ent.setAttribute('text', {value: ent.getAttribute('text').value, width: parseFloat($("#size").val()), height: parseFloat($("#size").val()), color: $("#color").val(), wrapCount: ent.getAttribute('text').value.length}) + + return true; +} + +// starts the timer and updates its value +var time; +var timeElapsed = 0; +function startTimer(){ + time = setInterval(() => { + timeElapsed += 10; + let timer = document.getElementById('timer0') + let textVal = timer.getAttribute('text') + let time = Math.floor(timeElapsed) + let minutes = Math.floor(time/1000/60) + time -= minutes*1000*60 + if(minutes < 10){ + minutes = "0"+minutes + } else { + minutes = ""+minutes + } + + let seconds = Math.floor(time/1000) + time -= seconds*1000 + if(seconds < 10){ + seconds = "0"+seconds + } else { + seconds = ""+seconds + } + if(time < 10){ + time = "00"+time + } else if(time < 100){ + time = "0"+time + } else { + time = ""+time + } + + textVal.value = minutes+":"+seconds+":"+time[0]+time[1]+" " + timer.setAttribute('text',textVal) + + },10) +} \ No newline at end of file diff --git a/Custom/entities/triangle.js b/Custom/entities/triangle.js new file mode 100644 index 0000000..83e2adc --- /dev/null +++ b/Custom/entities/triangle.js @@ -0,0 +1,55 @@ +/* Contains code related to triangle entity + No draw function because only one entity +*/ + +// creates a triangle entity +// called when entity is added to the scene +function createTriangle() { + let el = document.createElement('a-entity'); /* creates entity */ + + /* get random colors */ + let R = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let G = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + let B = ((Math.floor(Math.random()*255).toString(16)).toUpperCase()).padStart(2, '0'); + + el.setAttribute("id","triangle"+triangleNum++); + el.setAttribute("geometry",{primitive: "triangle", vertexA: {x: 0, y: 0.1875*125, z: 0}, vertexB: {x: -0.25*125, y: -0.25*125, z: 0}, vertexC: {x: 0.25*125, y: -0.25*125, z: 0}}); + el.setAttribute("material", {shader: "flat", color: "#"+R+G+B}); + + return el; +} + +// edits triangle entity +// called when triangle attribute is changed +function editTriangle(ent) { + // check for valid inputs + if(isNaN(parseFloat($("#vax").val()))){ + alert("Please enter a valid position"); + return false; + } + if(isNaN(parseFloat($("#vay").val()))){ + alert("Please enter a valid position"); + return false; + } + if(isNaN(parseFloat($("#vbx").val()))){ + alert("Please enter a valid position"); + return false; + } + if(isNaN(parseFloat($("#vby").val()))){ + alert("Please enter a valid position"); + return false; + } + if(isNaN(parseFloat($("#vcx").val()))){ + alert("Please enter a valid position"); + return false; + } + if(isNaN(parseFloat($("#vcy").val()))){ + alert("Please enter a valid position"); + return false; + } + // update stats + ent.setAttribute("geometry",{primitive: "triangle", vertexA: {x: parseFloat($("#vax").val()), y: parseFloat($("#vay").val()), z: 0}, + vertexB: {x: parseFloat($("#vbx").val()), y: parseFloat($("#vby").val()), z: 0}, vertexC: {x: parseFloat($("#vcx").val()), y: parseFloat($("#vcy").val()), z: 0}}); + + return true; +} \ No newline at end of file diff --git a/Custom/entityMovement.js b/Custom/entityMovement.js new file mode 100644 index 0000000..55d7fc5 --- /dev/null +++ b/Custom/entityMovement.js @@ -0,0 +1,267 @@ +movementKeyBinds = {} +function calcNextPoint(p1,p2,covered){ + if(p1.r){ + thetaDelta = p2.theta-p1.theta + yDelta = p2.y-p1.y + newPoint = {theta: p1.theta+(thetaDelta*covered), y: p1.y+(yDelta*covered), r: p1.r} + } else { + xDelta = p2.x-p1.x + yDelta = p2.y-p1.y + zDelta = p2.z-p1.z + newPoint = {x: p1.x+(xDelta*covered), y: p1.y+(yDelta*covered), z: p1.z+(zDelta*covered)} + } + + + return newPoint; +} + +function toggleMovement(entPos){ + // Teleport + let ent = entityCanvas.children[entPos]; + if(ent.getAttribute('mov').type == 'Teleport'){ + toggle = false; + var tmp = setInterval((entPos) => { + let ent = entityCanvas.children[entPos]; + if(ent.getAttribute('mov').endPoint.r == null){ + if(toggle == false){ + ent.setAttribute('position',ent.getAttribute('mov').endPoint) + //ent.setAttribute("rotation", {x: 0, y: THETAX, z: 0}); // set rotation to be 0 + } else { + ent.setAttribute('position',ent.getAttribute('mov').startPoint) + } + } else { + if(toggle == false){ + ent.setAttribute('position',{x: -ent.getAttribute('mov').endPoint.r*Math.sin((ent.getAttribute('mov').endPoint.theta*Math.PI)/180), y: ent.getAttribute('mov').endPoint.y, z: ent.getAttribute('mov').endPoint.r * Math.cos((ent.getAttribute('mov').endPoint.theta*Math.PI)/180)}) + ent.setAttribute("rotation", {x: 0, y: -ent.getAttribute('mov').endPoint.theta, z: 0}); // set rotation to be 0 + //ent.setAttribute("rotation", {x: 0, y: THETAX, z: 0}); // set rotation to be 0 + } else { + ent.setAttribute('position',ent.getAttribute('mov').startPoint) + ent.setAttribute("rotation", {x: 0, y: ent.getAttribute('angle').x, z: 0}); // set rotation to be 0 + } + } + + toggle = !toggle; + },ent.getAttribute('mov').speed, entPos) + mov = ent.getAttribute('mov') + mov.status = tmp + ent.setAttribute('mov',mov) + return + } + + + if(ent.getAttribute('mov').endPoint.r == null){ + p1 = ent.getAttribute('mov').startPoint + xDelta = Math.abs(ent.getAttribute('mov').endPoint.x-p1.x) + yDelta = Math.abs(ent.getAttribute('mov').endPoint.y-p1.y) + zDelta = Math.abs(ent.getAttribute('mov').endPoint.z-p1.z) + d = Math.sqrt((xDelta)*(xDelta) + (yDelta)*(yDelta) + (zDelta)*(zDelta)) + steps = d/ent.getAttribute('mov').speed*60 + + } else { + p1 = {r: ent.getAttribute('angle').z, theta: -ent.getAttribute('angle').x, y: ent.getAttribute('mov').startPoint.y} + thetaDelta = (ent.getAttribute('mov').endPoint.theta-p1.theta) + arcLen = Math.abs(thetaDelta*Math.PI/180)*Math.abs(ent.getAttribute('mov').endPoint.r); + yDelta = Math.abs(ent.getAttribute('mov').endPoint.y-p1.y) + d = Math.sqrt((arcLen)*(arcLen) + (yDelta)*(yDelta)) + steps = d/ent.getAttribute('mov').speed*60 + } + var prev = p1; + var num = 1; + var time = 1; + var toggle = true; + var endPoint = ent.getAttribute('mov').endPoint + var initialVelocity = ent.getAttribute('mov').speed + var entAcceleration = ent.getAttribute('mov').acceleration + var tmp = setInterval((entPos,p1,endPoint) => { + let ent = entityCanvas.children[entPos] + distanceCovered = (initialVelocity)*(num*(.017)) + 0.5*entAcceleration*((num*(.017))*(num*(.017))) + amtCovered = (distanceCovered/d) + if(amtCovered > 1){ + if(ent.getAttribute('mov').type == 'Rubberband'){ + num = 0; + if(toggle){ + //endPoint = Object.assign({},p1) + //p1 = ent.getAttribute('mov').endPoint + toggle = false; + } else { + //p1 = Object.assign({},endPoint) + //endPoint = ent.getAttribute('mov').endPoint + toggle = true; + } + + initialVelocity = initialVelocity + entAcceleration*time*(.017) + distanceCovered = (initialVelocity)*(num*(.017)) + 0.5*entAcceleration*((num*(.017))*(num*(.017))) + + amtCovered = (distanceCovered/d) + + } else { + num = 0; + } + } + + if(!toggle){ + res = calcNextPoint(endPoint,p1,amtCovered) + } else { + res = calcNextPoint(p1,endPoint,amtCovered) + } + + + if(ent.getAttribute('advanced').val){ + ent.setAttribute('position',{x: res.x, y: res.y, z: res.z}) + //ent.setAttribute("rotation", {x: 0, y: THETAX, z: 0}); // set rotation to be 0 + } else { + ent.setAttribute('position',{x: -res.r*Math.sin((res.theta*Math.PI)/180), y: res.y, z: res.r * Math.cos((res.theta*Math.PI)/180)}) + ent.setAttribute("rotation", {x: 0, y: -res.theta, z: 0}); // set rotation to be 0 + } + /*if(toggle){ + num++ + } else { + num-- + }*/ + num++; + time++; + },17,entPos,p1,endPoint) + mov = ent.getAttribute('mov') + mov.status = tmp + ent.setAttribute('mov',mov) +} + +function stopMovement(el){ + mov = el.getAttribute('movement') + clearInterval(mov.status) + mov.status = -1 + el.setAttribute('movement',mov) + movementIcon.className = "fa-solid fa-play"; +} + +/*function handleMovementToggle(e){ + e.stopPropagation() + if(selectedEntity.getAttribute('mov').type == 'None'){ + return + } + i = 0; + while(i < entityCanvas.children.length){ + if(entityCanvas.children[i] == selectedEntity){ + break; + } + i++; + } + if(movementIcon.className == "fa-solid fa-play"){ + toggleMovement(i); + movementIcon.className = "fa-solid fa-pause" + } else { + stopMovement(selectedEntity); + movementIcon.className = "fa-solid fa-play" + } +}*/ + + + function stopAllMovement(){ + let i = 0; + while(i < entityCanvas.children.length){ + mov = entityCanvas.children[i].getAttribute('movement') + mov.status = -1; + i++; + } + } + + function startAllMovement(){ + let i = 0; + while(i < entityCanvas.children.length){ + mov = entityCanvas.children[i].getAttribute('mov') + if(mov.type != 'None'){ + toggleMovement(i) + } + i++; + } + } + + function processConsecutiveMovement(entPos, movementArr){ + let ent = entityCanvas.children[entPos]; + let i = 0; + // movementArr [[startP,endP,speed,acc,type]...] + + if(movementArr[i][1].r == null){ + p1 = movementArr[i][0] + xDelta = Math.abs(movementArr[i][1].x-p1.x) + yDelta = Math.abs(movementArr[i][1].y-p1.y) + zDelta = Math.abs(movementArr[i][1].z-p1.z) + d = Math.sqrt((xDelta)*(xDelta) + (yDelta)*(yDelta) + (zDelta)*(zDelta)) + steps = d/movementArr[i][2]*60 + + } else { + // angle, angle, mov + p1 = movementArr[i][0] + thetaDelta = (movementArr[i][1].theta-p1.theta) + arcLen = Math.abs(thetaDelta*Math.PI/180)*Math.abs(movementArr[i][1].r); + yDelta = Math.abs(movementArr[i][1].y-p1.y) + d = Math.sqrt((arcLen)*(arcLen) + (yDelta)*(yDelta)) + steps = d/movementArr[i][2]*60 + } + var prev = p1; + var num = 1; + var time = 1; + var toggle = true; + var endPoint = movementArr[i][1] + var initialVelocity = movementArr[i][2] + var entAcceleration = movementArr[i][3] + var tmp = setInterval((entPos,p1,endPoint, movementArr, i) => { + let ent = entityCanvas.children[entPos] + distanceCovered = (initialVelocity)*(num*(.017)) + 0.5*entAcceleration*((num*(.017))*(num*(.017))) + amtCovered = (distanceCovered/d) + if(amtCovered > 1){ + i++; + if(i > movementArr.length){ + clearInterval(tmp) + } + p1 = movementArr[i][0] + num = 1 + if(movementArr[i][1].r == null){ + p1 = movementArr[i][0] + xDelta = Math.abs(movementArr[i][1].x-p1.x) + yDelta = Math.abs(movementArr[i][1].y-p1.y) + zDelta = Math.abs(movementArr[i][1].z-p1.z) + d = Math.sqrt((xDelta)*(xDelta) + (yDelta)*(yDelta) + (zDelta)*(zDelta)) + steps = d/movementArr[i][2]*60 + + } else { + // angle, angle, mov + p1 = {r: movementArr[i][0].z, theta: -movementArr[i][0].x, y: movementArr[i][0].y} + thetaDelta = (movementArr[i][1].theta-p1.theta) + arcLen = Math.abs(thetaDelta*Math.PI/180)*Math.abs(movementArr[i][1].r); + yDelta = Math.abs(movementArr[i][1].y-p1.y) + d = Math.sqrt((arcLen)*(arcLen) + (yDelta)*(yDelta)) + steps = d/movementArr[i][2]*60 + } + endPoint = movementArr[i][1] + initialVelocity = movementArr[i][2] + entAcceleration = movementArr[i][3] + distanceCovered = (initialVelocity)*(num*(.017)) + 0.5*entAcceleration*((num*(.017))*(num*(.017))) + amtCovered = (distanceCovered/d) + + } + + res = calcNextPoint(endPoint,p1,amtCovered) + + + if(ent.getAttribute('advanced').val){ + ent.setAttribute('position',{x: res.x, y: res.y, z: res.z}) + //ent.setAttribute("rotation", {x: 0, y: THETAX, z: 0}); // set rotation to be 0 + } else { + ent.setAttribute('position',{x: -res.r*Math.sin((res.theta*Math.PI)/180), y: res.y, z: res.r * Math.cos((res.theta*Math.PI)/180)}) + ent.setAttribute("rotation", {x: 0, y: -res.theta, z: 0}); // set rotation to be 0 + } + /*if(toggle){ + num++ + } else { + num-- + }*/ + num++; + time++; + },17,entPos,p1,endPoint) + mov = ent.getAttribute('mov') + mov.status = tmp + ent.setAttribute('mov',mov) + + } + diff --git a/Custom/graphicInterface.js b/Custom/graphicInterface.js index 8cddb61..c157b83 100644 --- a/Custom/graphicInterface.js +++ b/Custom/graphicInterface.js @@ -15,22 +15,15 @@ function hideEditStats(){ area4.style.display = "none"; area5.style.display = "none"; specificSettings.style.gridTemplateRows = "13% 17% 16% 17% 16% 17%"; - removeButton.style.display = "none"; duplicateButton.display = "none"; background.style.display = "none"; vertices.style.display = "none"; - //entitySelectorText.style.display = "none"; - //ent.style.display = "none"; - //nonUni.style.display = "none"; - //entitySelectorText.style.display = "none"; - //posIn.style.display = "none"; - //colIn.style.display = "none"; colIn2.style.display = "none"; heightIn.style.display = "none"; + sizeIn.style.display = "none"; widthIn.style.display = "none"; radiusIn.style.display = "none"; - //rotIn.style.display = "none"; va.style.display = "none"; vb.style.display = "none"; vc.style.display = "none"; @@ -41,7 +34,6 @@ function hideEditStats(){ textureIn.style.display = "none"; uploadTextureIn.style.display = "none"; fillIn.style.display = "none"; - //advanced.style.display = "none"; circleSize.style.display = "none"; spacing.style.display = "none"; numDots.style.display = "none"; @@ -50,7 +42,90 @@ function hideEditStats(){ ringPitch.style.display = "none"; numRings.style.display = "none"; toggleCenterDot.style.display = "none"; + textIn.style.display = "none"; } + +function updateMovementSettings(){ + if(movementTypeIn.value == "Pause"){ + // + startHeader.style.display = "block" + startHeader.textContent = 'Time (ms)' + startX.style.display = "none" + startY.style.display = "block" + startZ.style.display = "none" + endHeader.style.display = "none" + endX.style.display = "none" + endY.style.display = "none" + endZ.style.display = "none" + speedHeader.style.display = "none" + accelerationHeader.style.display = "none" + keyHeader.style.display = "none" + speed.style.display = "none" + acceleration.style.display = "none" + movementButtonContainer.style.display = "none" + } else if(movementTypeIn.value == "None"){ + if(selectedEntity.getAttribute('advanced').val){ + startHeader.innerHTML = 'Start Point (x: m, y: m, z: m)' + } else { + startHeader.innerHTML = 'Start Point (\u03B1: deg, y: m, r: m):' + } + startHeader.style.display = "none" + startX.style.display = "none" + startY.style.display = "none" + startZ.style.display = "none" + endHeader.style.display = "none" + endX.style.display = "none" + endY.style.display = "none" + endZ.style.display = "none" + speedHeader.style.display = "none" + accelerationHeader.style.display = "none" + keyHeader.style.display = "none" + speed.style.display = "none" + acceleration.style.display = "none" + movementButtonContainer.style.display = "none" + } else if(movementTypeIn.value == "Start" || movementTypeIn.value == "Rubberband"){ + if(selectedEntity.getAttribute('advanced').val){ + startHeader.innerHTML = 'Start Point (x: m, y: m, z: m)' + } else { + startHeader.innerHTML = 'Start Point (\u03B1: deg, y: m, r: m):' + } + startHeader.style.display = "block" + startX.style.display = "block" + startY.style.display = "block" + startZ.style.display = "block" + endHeader.style.display = "block" + endX.style.display = "block" + endY.style.display = "block" + endZ.style.display = "block" + speedHeader.style.display = "block" + speedHeader.innerText = "Speed (m/s)" + accelerationHeader.style.display = "block" + speed.style.display = "block" + acceleration.style.display = "block" + movementButtonContainer.style.display = "inline" + } else { + if(selectedEntity.getAttribute('advanced').val){ + startHeader.innerHTML = 'Start Point (x: m, y: m, z: m)' + } else { + startHeader.innerHTML = 'Start Point (\u03B1: deg, y: m, r: m):' + } + startHeader.style.display = "block" + startX.style.display = "block" + startY.style.display = "block" + startZ.style.display = "block" + endHeader.style.display = "block" + endX.style.display = "block" + endY.style.display = "block" + endZ.style.display = "block" + speedHeader.style.display = "block" + speedHeader.innerText = "Time (ms)" + accelerationHeader.style.display = "none" + speed.style.display = "block" + acceleration.style.display = "none" + movementButtonContainer.style.display = "inline" + } +} + var flag = false; /* updates values in edit section */ function updateStats(){ @@ -59,57 +134,96 @@ function updateStats(){ $('#skyCol').minicolors("value",sky.components.material.attrValue.color); entity = selectedEntity; if(selectedEntity.getAttribute('advanced').val){ + endZ.disabled = false + advanced.style.backgroundColor = '#00FF00' posIn.innerHTML = 'Position (x: m, y: m, z: m):' + startHeader.innerHTML = 'Start Point (x: m, y: m, z: m)' + endHeader.innerHTML = 'End Point (x: m, y: m, z: m)' rotationY.style.display = 'block' rotationX.style.display = 'block' - - $("#x").change(function() { - editEntity(); - }); - - /* If the textbox for y value is changed */ - $("#y").change(function() { - editEntity(); - }); - - /* If the textbox for z value is changed */ - $("#z").change(function() { - editEntity(); - }); xIn.value = (entity.components.position.attrValue.x).toFixed(3); yIn.value = (entity.components.position.attrValue.y).toFixed(3); zIn.value = (-entity.components.position.attrValue.z).toFixed(3); + updateAnimationList(entity) + + if(animationList.getAttribute('selectedIndex') == ""){ + movementTypeIn.value = 'None' + } else { + movementTypeIn.value = entity.components.movement.attrValue.types[0] + updateMovementSettings() + + if(entity.components.movement.attrValue.startPoints.length != 0){ + + startX.value = entity.components.movement.attrValue.startPoints[0].x + startY.value = entity.components.movement.attrValue.startPoints[0].y + startZ.value = -entity.components.movement.attrValue.startPoints[0].z + endX.value = entity.components.movement.attrValue.endPoints[0].x + endY.value = entity.components.movement.attrValue.endPoints[0].y + endZ.value = -entity.components.movement.attrValue.endPoints[0].z + acceleration.value = entity.components.movement.attrValue.accelerations[0] + speed.value = entity.components.movement.attrValue.initialVelocities[0] + if(entity.components.movement.attrValue.types[0] == 'Pause'){ + startY.value = entity.components.movement.attrValue.initialVelocities[0] + } + } + + + } + } else { + endZ.disabled = true + advanced.style.backgroundColor ='' posIn.innerHTML = 'Position (\u03B1: deg, y: m, r: m):' + startHeader.innerHTML = 'Start Point (\u03B1: deg, y: m, r: m):' + endHeader.innerHTML = 'End Point (\u03B1: deg, y: m, r: m):' rotationY.style.display = 'none' rotationX.style.display = 'none' - - $("#x").change(function() { - editEntity(); - }); - - /* If the textbox for y value is changed */ - $("#y").change(function() { - editEntity(); - }); - - /* If the textbox for z value is changed */ - $("#z").change(function() { - editEntity(); - }); xIn.value = (-entity.components.angle.attrValue.x).toFixed(3); yIn.value = (entity.components.position.attrValue.y).toFixed(3); zIn.value = (-entity.components.angle.attrValue.z).toFixed(3); + + if(animationList.getAttribute('selectedIndex') == ""){ + movementTypeIn.value = 'None' + } else { + movementTypeIn.value = entity.components.movement.attrValue.types[0] + updateMovementSettings() + + if(entity.components.movement.attrValue.startPoints.length != 0){ + startX.value = entity.components.movement.attrValue.startPoints[0].theta + startY.value = entity.components.movement.attrValue.startPoints[0].y + startZ.value = -entity.components.movement.attrValue.startPoints[0].r + endX.value = entity.components.movement.attrValue.endPoints[0].theta + endY.value = entity.components.movement.attrValue.endPoints[0].y + endZ.value = -entity.components.movement.attrValue.endPoints[0].r + acceleration.value = entity.components.movement.attrValue.accelerations[0] + speed.value = entity.components.movement.attrValue.initialVelocities[0] + if(entity.components.movement.attrValue.types[0] == 'Pause'){ + startY.value = entity.components.movement.attrValue.initialVelocities[0] + } + } + } + + } + + if(entity.components.movement.attrValue.status == -1){ + movementIcon.className = "fa-solid fa-play" + } else { + movementIcon.className = "fa-solid fa-pause" } rotationZ.value = (entity.components.rotation.attrValue.z).toFixed(3); rotationY.value = (entity.components.rotation.attrValue.y).toFixed(3); rotationX.value = (entity.components.rotation.attrValue.x).toFixed(3); - color.value = entity.components.material.attrValue.color; flag = true; - $('#color').minicolors("value",entity.components.material.attrValue.color); + if(selectedEntity.getAttribute("id").includes("text") || selectedEntity.getAttribute("id").includes("timer")){ + color.value = entity.components.text.attrValue.color; + $('#color').minicolors("value",entity.components.text.attrValue.color); + } else { + color.value = entity.components.material.attrValue.color; + $('#color').minicolors("value",entity.components.material.attrValue.color); + } flag = false; - if (entity.components.material.attrValue.src == "" || entity.components.material.attrValue.src == null){ + if (entity.components.text || entity.components.material.attrValue.src == "" || entity.components.material.attrValue.src == null){ texture.selectedIndex = 0; texture.options[0].selected = true; } else { @@ -168,14 +282,23 @@ function updateStats(){ } else if(selectedEntity.getAttribute("id").includes("bullseye")){ numRingsIn.value = (selectedEntity.children.length-1); ringPitchIn.value = selectedEntity.children[0].components.geometry.attrValue.radiusOuter*2; + } else if(selectedEntity.getAttribute("id").includes("text")){ + width.value = entity.components.text.attrValue.width; + height.value = entity.components.text.attrValue.height; + textIn.value = entity.components.text.attrValue.value; + } else if(selectedEntity.getAttribute("id").includes("timer")){ + width.value = entity.components.text.attrValue.width; + height.value = entity.components.text.attrValue.height; } } /* switches between add or edit mode or refreshes current mode display */ function toggleDisplayEdit(swap){ + /* check if swapping modes or refreshing display */ if(swap){ /* if the button to swap was pressed */ + stopAll() boolDisplayEdit = !boolDisplayEdit; /* change current mode */ } utility.checked = false; @@ -187,9 +310,13 @@ function toggleDisplayEdit(swap){ addEditContent.style.display = "none" packageLayout.style.display = "grid" swapContainer.style.width = "" + animationListButtonContainer.style.display = "none" + openAnimationList(true) + //settingsButton.setAttribute('') //scene_input.value = "" //toggleAddEdit(false); } else { /* if add */ + if(!isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ if(coreLayout.style.gridTemplateColumns != "100% 0%"){ openSettings() @@ -197,10 +324,11 @@ function toggleDisplayEdit(swap){ settingsButtonContainer.style.display = "none" addEditContent.style.display = "block" packageLayout.style.display = "none" - swapContainer.style.textAlign = "center" - swapContainer.style.width = "224px" - swapContainer.style.paddingTop = "10px" - swapContainer.style.paddingLeft = "9px" + swapContainer.style.float = "left"; + swapContainer.style.paddingTop = "12px" + swapContainer.style.paddingLeft = "18px" + swapContainer.style.textAlign = "" + //animationListButtonContainer.style.display = "inline-block" //$('#skyCol').minicolors('value', scenes[patternDisplay.value]['sky'].skyColor); } else { alert("You must select a pattern"); @@ -210,7 +338,9 @@ function toggleDisplayEdit(swap){ swapContainer.style.paddingTop = "12px" swapContainer.style.paddingLeft = "18px" swapContainer.style.textAlign = "" + animationListButtonContainer.style.display = "none" } + } } @@ -218,6 +348,7 @@ function toggleDisplayEdit(swap){ function toggleAddEdit(swap){ /* check if swapping modes or refreshing display */ if(swap){ /* if the button to swap was pressed */ + stopAll() if(numAdded == 0){ alert("You must add an entity before editing the scene"); utility.checked = false; @@ -236,11 +367,12 @@ function toggleAddEdit(swap){ } /* check if current mode is add or edit */ if(boolAddEdit){ /* if edit */ + editContent.style.display = 'grid'; addContent.style.display = 'none'; background.style.display = "none"; skyIn.style.display = "none"; - + animationListButtonContainer.style.display = 'inline-block' /* check geometry of object */ @@ -317,7 +449,22 @@ function toggleAddEdit(swap){ numRings.style.display = "flex"; area2.style.display = "block"; ringPitch.style.display = "flex"; + } else if (selectedEntity.getAttribute("id").includes("text")){ + area1.style.display = "block"; + sizeIn.style.display = "flex"; + area2.style.display = "block"; + textIn.style.display = "flex"; + } else if (selectedEntity.getAttribute("id").includes("timer")){ + area1.style.display = "block"; + sizeIn.style.display = "flex"; } + updateAnimationList(entity) + if(animationList.childElementCount == 0){ + updateAnimationUI(entity,-1) + } else { + updateAnimationUI(entity,0) + } + } else { /* if add */ editContent.style.display = 'none' addContent.style.display = 'block' @@ -325,17 +472,23 @@ function toggleAddEdit(swap){ background.style.display = 'block' entitySelectorText.style.display = 'flex' skyIn.style.display = 'block' + animationListButtonContainer.style.display = 'none' + openAnimationList(true) } } function openSettings(){ if(coreLayout.style.gridTemplateColumns == "100% 0%"){ - coreLayout.style.width = "400px" - coreLayout.style.gridTemplateColumns = "65% 35%" + coreLayout.style.width = "500px" + coreLayout.style.gridTemplateColumns = "52% 48%" settingsButtonContainer.style.paddingRight = "40px" settingsButton.className = "button reset" settingsButton.title = "Close settings" settingsIcon.className = "fa-solid fa-close" + settingsButtonContainer.style.position = 'relative' + settingsButtonContainer.style.float = 'none' + settingsButtonContainer.style.right = '-50px' + } else { coreLayout.style.width = "260px" coreLayout.style.gridTemplateColumns = "100% 0%" @@ -343,6 +496,48 @@ function openSettings(){ settingsButton.className = "button add" settingsButton.title = "Open settings" settingsIcon.className = "fa-solid fa-gear" + settingsButtonContainer.style.float = 'right' + settingsButtonContainer.style.right = '' + settingsButtonContainer.style.position = '' + } +} + +function openAnimationList(forceClose){ + if(forceClose){ + coreLayout.style.width = "260px" + coreLayout.style.gridTemplateColumns = "100% 0%" + settingsButtonContainer.style.paddingRight = "14px" + animationListButton.className = "button add" + animationListButton.title = "Open animation list" + animationListIcon.className = "fa-solid fa-wand-sparkles" + animationListButtonContainer.style.float = 'right' + animationListButtonContainer.style.right = '' + animationListButtonContainer.style.position = '' + settingsLayout.style.gridTemplateRows = '30% 70% 100%' + settingsLayout.style.overflowY = 'hidden' + return + } + if(coreLayout.style.gridTemplateColumns == "100% 0%"){ + coreLayout.style.width = "500px" + coreLayout.style.gridTemplateColumns = "55% 45%" + settingsButtonContainer.style.paddingRight = "40px" + animationListButton.className = "button reset" + animationListButton.title = "Close animation list" + animationListIcon.className = "fa-solid fa-close" + animationListButtonContainer.style.position = 'relative' + animationListButtonContainer.style.float = 'none' + animationListButtonContainer.style.right = '-40px' + settingsLayout.style.gridTemplateRows = '0% 0% 100%' + } else { + coreLayout.style.width = "260px" + coreLayout.style.gridTemplateColumns = "100% 0%" + settingsButtonContainer.style.paddingRight = "14px" + animationListButton.className = "button add" + animationListButton.title = "Open animation list" + animationListIcon.className = "fa-solid fa-wand-sparkles" + animationListButtonContainer.style.float = 'right' + animationListButtonContainer.style.right = '' + animationListButtonContainer.style.position = '' } } @@ -376,10 +571,14 @@ function hideUniversal(){ //universalHeader.style.display = "none"; //uni.style.borderBottom = "0px solid #999"; uni.style.gridTemplateRows = "100% 0% 0% 0% 0% 0%" - if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ - editContent.style.gridTemplateRows = "8% 8% 5%" + if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "8% 8% 8% 8%" + } else if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 8% 50% 8%" + } else if(specificSettings.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 50% 8% 8%" } else { - editContent.style.gridTemplateRows = "8% 50% 5%" + editContent.style.gridTemplateRows = "8% 50% 50% 8%" } } else { @@ -398,13 +597,18 @@ function hideUniversal(){ //universalHeader.style.display = "block"; //uni.style.borderBottom = "1px solid #999"; uni.style.gridTemplateRows = "17% 17% 16% 17% 16% 17%" - if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ - editContent.style.gridTemplateRows = "40% 8% 5%" + if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "50% 8% 8% 8%" + } else if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 8% 50% 8%" + } else if(specificSettings.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 50% 8% 8%" } else { - editContent.style.gridTemplateRows = "40% 50% 5%" + editContent.style.gridTemplateRows = "50% 50% 50% 8%" } } } + var oldSize = null; function hideSpecific(){ if(specificSettings.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%"){ @@ -417,10 +621,14 @@ function hideSpecific(){ area4.style.display = "none" area5.style.display = "none" specificSettings.style.gridTemplateRows = "100% 0% 0% 0% 0% 0%" - if(uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ - editContent.style.gridTemplateRows = "8% 8% 5%" + if(uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "8% 8% 8% 8%" + } else if(uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 8% 50% 8%" + } else if(uni.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 8% 8% 8%" } else { - editContent.style.gridTemplateRows = "40% 8% 5%" + editContent.style.gridTemplateRows = "50% 8% 50% 8%" } } else { @@ -430,10 +638,82 @@ function hideSpecific(){ specificSettings.style.gridTemplateRows = oldSize //universalHeader.style.display = "block"; //uni.style.borderBottom = "1px solid #999"; - if(uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ - editContent.style.gridTemplateRows = "8% 50% 5%" + if(uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "8% 50% 8% 8%" + } else if(uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 50% 50% 8%" + } else if(uni.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 50% 8% 8%" } else { - editContent.style.gridTemplateRows = "40% 50% 5%" + editContent.style.gridTemplateRows = "50% 50% 50% 8%" + } + } +} + +function hideMovement(){ + + if(movement.style.gridTemplateRows != "100% 0% 0% 0% 0% 0% 0% 0%"){ + // hide + //hideUniversalIcon.className = "fa-solid fa-chevron-right" + movementType.style.display = "none" + startHeader.style.display = "none" + startX.style.display = "none" + startY.style.display = "none" + startZ.style.display = "none" + endHeader.style.display = "none" + endX.style.display = "none" + endY.style.display = "none" + endZ.style.display = "none" + speedHeader.style.display = "none" + accelerationHeader.style.display = "none" + keyHeader.style.display = "none" + speed.style.display = "none" + acceleration.style.display = "none" + //keyBind.style.display = "none" + movementButtonContainer.style.display = "none" + //universalHeader.style.display = "none"; + //uni.style.borderBottom = "0px solid #999"; + movement.style.gridTemplateRows = "100% 0% 0% 0% 0% 0% 0% 0%" + if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "8% 8% 8% 8%" + } else if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 8% 8% 8%" + } else if(specificSettings.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 50% 8% 8%" + } else { + editContent.style.gridTemplateRows = "50% 50% 8% 8%" + } + + } else { + // show + //hideUniversalIcon.className = "fa-solid fa-chevron-down" + movementType.style.display = "block" + startHeader.style.display = "block" + startX.style.display = "block" + startY.style.display = "block" + startZ.style.display = "block" + endHeader.style.display = "block" + endX.style.display = "block" + endY.style.display = "block" + endZ.style.display = "block" + speedHeader.style.display = "block" + accelerationHeader.style.display = "block" + //keyHeader.style.display = "block" + speed.style.display = "block" + acceleration.style.display = "block" + //key.style.display = "block" + movementButtonContainer.style.display = "inline" + //universalHeader.style.display = "block"; + //uni.style.borderBottom = "1px solid #999"; + movement.style.gridTemplateRows = "11% 13% 11% 13% 11% 13% 12% 12%" + if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "8% 8% 50% 8%" + } else if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 8% 50% 8%" + } else if(specificSettings.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 50% 50% 8%" + } else { + editContent.style.gridTemplateRows = "50% 50% 50% 8%" } } } @@ -467,11 +747,50 @@ let list; indexDrop = i; } } - console.log(index, indexDrop); if(index > indexDrop) { target.before( dragged ); } else { target.after( dragged ); } } - }); \ No newline at end of file + }); + + function updateAnimationList(entity){ + if(entity == null){ + animationList.innerHTML = "" + animationList.setAttribute('selectedIndex',"") + return + } + + let anims = entity.getAttribute('movement').types; + out = [] + let i = 0; + animationList.innerHTML = "" + animationList.setAttribute('selectedIndex',"") + while(i < anims.length){ + if(anims[i] != 'Rebound'){ + let el = document.createElement('li'); + el.innerText = anims[i] + if(i == 0){ + el.style.background ='#F39814' + animationList.setAttribute('selectedIndex',0) + } + + el.setAttribute('draggable',true) + el.addEventListener('dragstart', dragStart) + el.addEventListener('drop', droppedAnim) + el.addEventListener('dragenter', cancelDefault) + el.addEventListener('dragover', cancelDefault) + el.addEventListener('click',selectAnimation) + + animationList.appendChild(el) + animationList.scrollTo({ + top: 1000000000, + left: 0, + behavior: "smooth", + }); + } + i++; + } + + } \ No newline at end of file diff --git a/Custom/graphics/draggableLists.js b/Custom/graphics/draggableLists.js new file mode 100644 index 0000000..18392a2 --- /dev/null +++ b/Custom/graphics/draggableLists.js @@ -0,0 +1,482 @@ +/* Contains code related to draggable lists */ + +let items = document.querySelectorAll('#items-list > li'); // list of patterns + +// for each list item, listen for following events +items.forEach(item => { + $(item).prop('draggable', true) + item.addEventListener('dragstart', dragStart) + item.addEventListener('drop', dropped) + item.addEventListener('dragenter', cancelDefault) + item.addEventListener('dragover', cancelDefault) + item.addEventListener('click',selectPattern) +}) + +// reorders keys in json package when necessary +let i = 0; +let reorderedScene = {} +while(i < patternList.children.length){ + reorderedScene[patternList.children[i].id] = scenes[packageSelect.value][patternList.children[i].id] + i++; +} +scenes[packageSelect.value] = reorderedScene + +// handles a pattern being selected from the pattern list +function selectPattern (e){ + stopAll() + if(e.target.style.background == 'rgb(243, 152, 20)' && patternList.getAttribute("multi-select") == false){ // if selected pattern is highlighted, unselect it + e.target.style.background = '#FFF' + patternList.setAttribute("selectedIndex","") + patternList.setAttribute("multi-select",false); + revertChanges() + return; + } else if(e.target.style.background == 'rgb(243, 152, 20)' && patternList.getAttribute("multi-select") == 'true'){ + e.target.style.background = '#FFF'; + first = true; + items.forEach(item => { // changes displayed pattern to selected pattern + if(item.style.background == 'rgb(243, 152, 20)' && first) { + first = false; + patternList.setAttribute("selectedIndex",$(item).index()) + + revertChanges() + addEntitiesFromScene(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id]) + if(els.length > 0){ + selectedEntity = els[0] + } + } else if(item.style.background == 'rgb(243, 152, 20)' && !first){ + patternList.setAttribute("multi-select",true); + } + }) + return; + } + + e.target.style.background = '#F39814' // highlights selected pattern + items = document.querySelectorAll('#items-list > li'); + if(keysPressed['ctrl'] && !isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ // checks for multiselect + patternList.setAttribute("multi-select",true); + return; + } + items.forEach(item => { // changes displayed pattern to selected pattern + if(item != e.target){ + item.style.background = '#FFF' + } else { + patternList.setAttribute("selectedIndex",$(item).index()) + patternList.setAttribute("multi-select",false); + revertChanges() + addEntitiesFromScene(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id]) + if(els.length > 0){ + selectedEntity = els[0] + } + } + }) + + setTimeout(() => { + if(els[0] && els[0].getAttribute('movement').types.length > 0){ + updateAnimationList(els[0]) + animationList.setAttribute('selectedIndex',0) + updateStats() + + } else { + updateAnimationList(els[0]) + } + + },200) + +} + +/* Code to make pattern list elements draggable */ +function dragStart (e) { + var index = $(e.target).index() + e.dataTransfer.setData('text/plain', index) +} + +function dropped (e) { + cancelDefault(e) + + // get new and old index + let oldIndex = e.dataTransfer.getData('text/plain') + let target = $(e.target) + let newIndex = target.index() + + // remove dropped items at old place + if(newIndex != oldIndex){ + let dropped = $(this).parent().children().eq(oldIndex).remove() + + // insert the dropped items at new place + if (newIndex < oldIndex) { + target.before(dropped) + } else { + target.after(dropped) + } + } + + let i = 0; + let reorderedScene = {} + while(i < patternList.children.length){ + reorderedScene[patternList.children[i].id] = scenes[packageSelect.value][patternList.children[i].id] + i++; + } + scenes[packageSelect.value] = reorderedScene + +} + +function cancelDefault (e) { + e.preventDefault() + e.stopPropagation() + return false +} + + +// animation list functionality +let animations = document.querySelectorAll('#movementAnims-list > li'); + +animations.forEach(item => { + $(item).prop('draggable', true) + item.addEventListener('dragstart', dragStart) + item.addEventListener('drop', droppedAnim) + item.addEventListener('dragenter', cancelDefault) + item.addEventListener('dragover', cancelDefault) + item.addEventListener('click',selectAnimation) +}) + +let j = 0; + + +// handles a pattern being selected from the pattern list +function selectAnimation (e){ + stopMovement(selectedEntity) + if(e.target.style.background == 'rgb(243, 152, 20)'){ // if selected pattern is highlighted, unselect it + e.target.style.background = '#FFF' + animationList.setAttribute("selectedIndex","") + movementTypeIn.disabled = true + return; + } + e.target.style.background = '#F39814' // highlights selected pattern + items = document.querySelectorAll('#movementAnims-list > li'); + items.forEach(item => { // changes displayed pattern to selected pattern + if(item != e.target){ + item.style.background = '#FFF' + } else { + animationList.setAttribute("selectedIndex",$(item).index()) + movementTypeIn.disabled = false + updateAnimationUI(selectedEntity,$(item).index()) + + + // add in functionality that will update animation info based on selected + + } + }) +} + +// when an animation list item is dropped somewhere +function droppedAnim (e) { + cancelDefault(e) + stopMovement(selectedEntity) + + // get new and old index + let oldIndex = e.dataTransfer.getData('text/plain') + let target = $(e.target) + let newIndex = target.index() + + // find oldIndex equivalent and newIndex equivalent in types + let i = 0; + let oldI = -1; + let newI = -1; + let counterOld = -1; + let counterNew = -1; + + let movementComponent = selectedEntity.getAttribute('movement') + // have to take into account rebounds from rubberband + while(i < movementComponent.types.length){ + if(movementComponent.types[i] != 'Rebound'){ + if(counterOld == oldIndex && counterNew == newIndex){ + break + } + + if(counterOld != oldIndex){ + counterOld++; + if(counterOld == oldIndex){ + oldI = i + } + } + if(counterNew != newIndex){ + counterNew++; + if(counterNew == newIndex){ + newI = i + } + } + } + i++; + } + + + // remove dropped items at old place + if(newIndex != oldIndex){ + let dropped = $(this).parent().children().eq(oldIndex).remove() + + // insert the dropped items at new place + if (newIndex < oldIndex) { + target.before(dropped) + } else { + target.after(dropped) + } + if(animationList.getAttribute('selectedIndex') == oldIndex){ + animationList.setAttribute('selectedIndex',newIndex); + } + } + + if(movementComponent.types[newIndex] == 'Rubberband'){ + // insert at newIndex+2 + newIndex += 1; + } + + if(oldI > newI){ + if(movementComponent.types[oldI] == 'Rubberband'){ + // remove both oldI and oldI+1 + let points = movementComponent.startPoints.splice(oldI,2); + let points2 = movementComponent.endPoints.splice(oldI,2); + let vels = movementComponent.initialVelocities.splice(oldI,2); + let accelerations = movementComponent.accelerations.splice(oldI,2); + let types = movementComponent.types.splice(oldI,2); + + movementComponent.startPoints.splice(newI,0,...points) + movementComponent.endPoints.splice(newI,0,...points2) + movementComponent.initialVelocities.splice(newI,0,...vels) + movementComponent.accelerations.splice(newI,0,...accelerations) + movementComponent.types.splice(newI,0,...types) + + } else { + // remove oldI + let points = movementComponent.startPoints.splice(oldI,1); + let points2 = movementComponent.endPoints.splice(oldI,1); + let vels = movementComponent.initialVelocities.splice(oldI,1); + let accelerations = movementComponent.accelerations.splice(oldI,1); + let types = movementComponent.types.splice(oldI,1); + + movementComponent.startPoints.splice(newI,0,...points) + movementComponent.endPoints.splice(newI,0,...points2) + movementComponent.initialVelocities.splice(newI,0,...vels) + movementComponent.accelerations.splice(newI,0,...accelerations) + movementComponent.types.splice(newI,0,...types) + } + + } else if(newI > oldI) { + // remove then insert at newI - 1 or - 2 if newI + if(movementComponent.types[oldI] == 'Rubberband'){ + // remove both oldI and oldI+1 + let points = movementComponent.startPoints.splice(oldI,2); + let points2 = movementComponent.endPoints.splice(oldI,2); + let vels = movementComponent.initialVelocities.splice(oldI,2); + let accelerations = movementComponent.accelerations.splice(oldI,2); + let types = movementComponent.types.splice(oldI,2); + + if(movementComponent.types[newI-1] == 'Rubberband'){ + movementComponent.startPoints.splice(newI,0,...points) + movementComponent.endPoints.splice(newI,0,...points2) + movementComponent.initialVelocities.splice(newI,0,...vels) + movementComponent.accelerations.splice(newI,0,...accelerations) + movementComponent.types.splice(newI,0,...types) + } else { + movementComponent.startPoints.splice(newI-1,0,...points) + movementComponent.endPoints.splice(newI-1,0,...points2) + movementComponent.initialVelocities.splice(newI-1,0,...vels) + movementComponent.accelerations.splice(newI-1,0,...accelerations) + movementComponent.types.splice(newI-1,0,...types) + } + + } else { + // remove oldI + let points = movementComponent.startPoints.splice(oldI,1); + let points2 = movementComponent.endPoints.splice(oldI,1); + let vels = movementComponent.initialVelocities.splice(oldI,1); + let accelerations = movementComponent.accelerations.splice(oldI,1); + let types = movementComponent.types.splice(oldI,1); + + if(movementComponent.types[newI-1] == 'Rubberband'){ + movementComponent.startPoints.splice(newI+1,0,...points) + movementComponent.endPoints.splice(newI+1,0,...points2) + movementComponent.initialVelocities.splice(newI+1,0,...vels) + movementComponent.accelerations.splice(newI+1,0,...accelerations) + movementComponent.types.splice(newI+1,0,...types) + } else { + movementComponent.startPoints.splice(newI,0,...points) + movementComponent.endPoints.splice(newI,0,...points2) + movementComponent.initialVelocities.splice(newI,0,...vels) + movementComponent.accelerations.splice(newI,0,...accelerations) + movementComponent.types.splice(newI,0,...types) + } + } + + } + + updateJSON() +} + +let dragged; +let id; +let index; +let indexDrop; +let list; + + document.addEventListener("dragstart", ({target}) => { + dragged = target; + id = target.id; + list = target.parentNode.children; + for(let i = 0; i < list.length; i += 1) { + if(list[i] === dragged){ + index = i; + } + } + }); + + document.addEventListener("dragover", (event) => { + event.preventDefault(); + }); + + document.addEventListener("drop", ({target}) => { + if(target.className == "dropzone" && target.id !== id) { + dragged.remove( dragged ); + for(let i = 0; i < list.length; i += 1) { + if(list[i] === target){ + indexDrop = i; + } + } + if(index > indexDrop) { + target.before( dragged ); + } else { + target.after( dragged ); + } + } + }); + + // updates contents of animation list + // takes in the entity to use for this + function updateAnimationList(entity){ + // add types of movement present in new entity to animation list + if(entity == null){ + animationList.innerHTML = "" + animationList.setAttribute('selectedIndex',"") + return + } + + let anims = entity.getAttribute('movement').types; + out = [] + let i = 0; + animationList.innerHTML = "" + animationList.setAttribute('selectedIndex',"") + while(i < anims.length){ + if(anims[i] != 'Rebound'){ + let el = document.createElement('li'); + el.innerText = anims[i] + if(i == 0){ + el.style.background ='#F39814' + animationList.setAttribute('selectedIndex',0) + } + + el.setAttribute('draggable',true) + el.addEventListener('dragstart', dragStart) + el.addEventListener('drop', droppedAnim) + el.addEventListener('dragenter', cancelDefault) + el.addEventListener('dragover', cancelDefault) + el.addEventListener('click',selectAnimation) + + animationList.appendChild(el) + animationList.scrollTo({ + top: 1000000000, + left: 0, + behavior: "smooth", + }); + } + i++; + } + + } + + // adds movement to the list + function addMovementAnim(){ + var toggle_button = '
  • Pause
  • '; + let el = document.createElement('li'); + el.innerText = "Pause" + + el.setAttribute('draggable',true) + el.addEventListener('dragstart', dragStart) + el.addEventListener('drop', droppedAnim) + el.addEventListener('dragenter', cancelDefault) + el.addEventListener('dragover', cancelDefault) + el.addEventListener('click',selectAnimation) + + animationList.appendChild(el) + + + + + + animationList.scrollTo({ + top: 1000000000, + left: 0, + behavior: "smooth", + }); + let movementComponent = selectedEntity.getAttribute('movement'); + if(selectedEntity.getAttribute('advanced').val){ + movementComponent.startPoints.push({x: 0, y: 0, z: -250}); + movementComponent.endPoints.push({x: 0, y: 0, z: -250}); + movementComponent.initialVelocities.push(0); + movementComponent.accelerations.push(0); + movementComponent.types.push('Pause'); + } else { + movementComponent.startPoints.push({theta: 0, y: 0, r: -250}); + movementComponent.endPoints.push({theta: 0, y: 0, r: -250}); + movementComponent.initialVelocities.push(0); + movementComponent.accelerations.push(0); + movementComponent.types.push('Pause'); + } + + + +} + +// removes movement from list +function removeMovementAnim(){ + // remove animation from entity + if(animationList.childElementCount == 0){ + return + } + stopMovement(selectedEntity) + + let index = parseInt(animationList.getAttribute('selectedIndex')) + let i = 0; + let counter = -1; + let movementComponent = selectedEntity.getAttribute('movement'); + // have to take into account rebounds from rubberband + while(i < movementComponent.types.length){ + if(movementComponent.types[i] != 'Rebound'){ + counter++; + if(counter == index){ + break + } + } + i++; + } + + + if(movementComponent.types[i] == 'Rubberband'){ + // need to remove this and also and rebound entry + movementComponent.types.splice(i,2) + movementComponent.startPoints.splice(i,2) + movementComponent.endPoints.splice(i,2) + movementComponent.initialVelocities.splice(i,2) + movementComponent.accelerations.splice(i,2) + } else { + movementComponent.types.splice(i,1) + movementComponent.startPoints.splice(i,1) + movementComponent.endPoints.splice(i,1) + movementComponent.initialVelocities.splice(i,1) + movementComponent.accelerations.splice(i,1) + } + animationList.removeChild(animationList.children[i]) + animationList.setAttribute('selectedIndex',"") + updateAnimationUI(selectedEntity,-1) + movementTypeIn.disabled = true + + updateJSON(); +} \ No newline at end of file diff --git a/Custom/graphics/expandableMenus.js b/Custom/graphics/expandableMenus.js new file mode 100644 index 0000000..aa2729a --- /dev/null +++ b/Custom/graphics/expandableMenus.js @@ -0,0 +1,228 @@ +// opens/closes the settings tab +function openSettings(){ + if(coreLayout.style.gridTemplateColumns == "100% 0%"){ + coreLayout.style.width = "500px" + coreLayout.style.gridTemplateColumns = "52% 48%" + settingsButtonContainer.style.paddingRight = "40px" + settingsButton.className = "button reset" + settingsButton.title = "Close settings" + settingsIcon.className = "fa-solid fa-close" + settingsButtonContainer.style.position = 'relative' + settingsButtonContainer.style.float = 'none' + settingsButtonContainer.style.right = '-50px' + + } else { + coreLayout.style.width = "260px" + coreLayout.style.gridTemplateColumns = "100% 0%" + settingsButtonContainer.style.paddingRight = "14px" + settingsButton.className = "button add" + settingsButton.title = "Open settings" + settingsIcon.className = "fa-solid fa-gear" + settingsButtonContainer.style.float = 'right' + settingsButtonContainer.style.right = '' + settingsButtonContainer.style.position = '' + } +} + +// opens/closes the animation list tab +// takes in a parameter that indicates whether we are forcing the closing of this tab due to navigation +function openAnimationList(forceClose){ + if(forceClose){ + coreLayout.style.width = "260px" + coreLayout.style.gridTemplateColumns = "100% 0%" + settingsButtonContainer.style.paddingRight = "14px" + animationListButton.className = "button add" + animationListButton.title = "Open animation list" + animationListIcon.className = "fa-solid fa-wand-sparkles" + animationListButtonContainer.style.float = 'right' + animationListButtonContainer.style.right = '' + animationListButtonContainer.style.position = '' + settingsLayout.style.gridTemplateRows = '30% 70% 100%' + settingsLayout.style.overflowY = 'hidden' + return + } + if(coreLayout.style.gridTemplateColumns == "100% 0%"){ + coreLayout.style.width = "500px" + coreLayout.style.gridTemplateColumns = "55% 45%" + settingsButtonContainer.style.paddingRight = "40px" + animationListButton.className = "button reset" + animationListButton.title = "Close animation list" + animationListIcon.className = "fa-solid fa-close" + animationListButtonContainer.style.position = 'relative' + animationListButtonContainer.style.float = 'none' + animationListButtonContainer.style.right = '-40px' + settingsLayout.style.gridTemplateRows = '0% 0% 100%' + } else { + coreLayout.style.width = "260px" + coreLayout.style.gridTemplateColumns = "100% 0%" + settingsButtonContainer.style.paddingRight = "14px" + animationListButton.className = "button add" + animationListButton.title = "Open animation list" + animationListIcon.className = "fa-solid fa-wand-sparkles" + animationListButtonContainer.style.float = 'right' + animationListButtonContainer.style.right = '' + animationListButtonContainer.style.position = '' + } +} + +// collapses the ui +function collapseTab(){ + if(coreLayout.style.gridTemplateRows == "7% 7% 86%"){ + coreLayout.style.gridTemplateRows = "100% 0% 0%" + coreLayout.style.height = "59.02" + coreLayout.style.overflowY = "hidden" + } else { + coreLayout.style.gridTemplateRows = "7% 7% 86%" + coreLayout.style.height = "" + coreLayout.style.overflowY = "auto" + } +} + +// collapses universal settings section +function hideUniversal(){ + + if(uni.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%"){ + // hide + posIn.style.display = "none" + xIn.style.display = "none" + yIn.style.display = "none" + zIn.style.display = "none" + rotIn.style.display = "none" + pitch.style.display = "none" + roll.style.display = "none" + yaw.style.display = "none" + entColor.style.display = "none" + advanced.style.display = "none" + uni.style.gridTemplateRows = "100% 0% 0% 0% 0% 0%" + if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "8% 8% 8% 8%" + } else if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 8% 50% 8%" + } else if(specificSettings.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 50% 8% 8%" + } else { + editContent.style.gridTemplateRows = "8% 50% 50% 8%" + } + + } else { + // show + posIn.style.display = "block" + xIn.style.display = "block" + yIn.style.display = "block" + zIn.style.display = "block" + rotIn.style.display = "block" + pitch.style.display = "block" + roll.style.display = "block" + yaw.style.display = "block" + entColor.style.display = "block" + advanced.style.display = "block" + uni.style.gridTemplateRows = "17% 17% 16% 17% 16% 17%" + if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "50% 8% 8% 8%" + } else if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 8% 50% 8%" + } else if(specificSettings.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 50% 8% 8%" + } else { + editContent.style.gridTemplateRows = "50% 50% 50% 8%" + } + } +} + +// collapses specific section +var oldSize = null; +function hideSpecific(){ + if(specificSettings.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%"){ + // hide + oldSize = specificSettings.style.gridTemplateRows + area1.style.display = "none" + area2.style.display = "none" + area3.style.display = "none" + area4.style.display = "none" + area5.style.display = "none" + specificSettings.style.gridTemplateRows = "100% 0% 0% 0% 0% 0%" + if(uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "8% 8% 8% 8%" + } else if(uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 8% 50% 8%" + } else if(uni.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 8% 8% 8%" + } else { + editContent.style.gridTemplateRows = "50% 8% 50% 8%" + } + + } else { + // show + toggleAddEdit(null); + specificSettings.style.gridTemplateRows = oldSize + if(uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "8% 50% 8% 8%" + } else if(uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 50% 50% 8%" + } else if(uni.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && movement.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 50% 8% 8%" + } else { + editContent.style.gridTemplateRows = "50% 50% 50% 8%" + } + } +} + +// collapses movement section +function hideMovement(){ + + if(movement.style.gridTemplateRows != "100% 0% 0% 0% 0% 0% 0% 0%"){ + // hide + movementType.style.display = "none" + startHeader.style.display = "none" + startX.style.display = "none" + startY.style.display = "none" + startZ.style.display = "none" + endHeader.style.display = "none" + endX.style.display = "none" + endY.style.display = "none" + endZ.style.display = "none" + speedHeader.style.display = "none" + accelerationHeader.style.display = "none" + keyHeader.style.display = "none" + speed.style.display = "none" + acceleration.style.display = "none" + movementButtonContainer.style.display = "none" + movement.style.gridTemplateRows = "100% 0% 0% 0% 0% 0% 0% 0%" + if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "8% 8% 8% 8%" + } else if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 8% 8% 8%" + } else if(specificSettings.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 50% 8% 8%" + } else { + editContent.style.gridTemplateRows = "50% 50% 8% 8%" + } + + } else { + // show + movementType.style.display = "block" + startHeader.style.display = "block" + startX.style.display = "block" + startY.style.display = "block" + startZ.style.display = "block" + endHeader.style.display = "block" + endX.style.display = "block" + endY.style.display = "block" + endZ.style.display = "block" + speedHeader.style.display = "block" + accelerationHeader.style.display = "block" + speed.style.display = "block" + acceleration.style.display = "block" + movementButtonContainer.style.display = "inline" + movement.style.gridTemplateRows = "11% 13% 11% 13% 11% 13% 12% 12%" + if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%"){ + editContent.style.gridTemplateRows = "8% 8% 50% 8%" + } else if(specificSettings.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "50% 8% 50% 8%" + } else if(specificSettings.style.gridTemplateRows != "100% 0% 0% 0% 0% 0%" && uni.style.gridTemplateRows == "100% 0% 0% 0% 0% 0%") { + editContent.style.gridTemplateRows = "8% 50% 50% 8%" + } else { + editContent.style.gridTemplateRows = "50% 50% 50% 8%" + } + } +} \ No newline at end of file diff --git a/Custom/graphics/toggleModes.js b/Custom/graphics/toggleModes.js new file mode 100644 index 0000000..9f7ac45 --- /dev/null +++ b/Custom/graphics/toggleModes.js @@ -0,0 +1,180 @@ +/* switches between add or edit mode or refreshes current mode display */ +function toggleDisplayEdit(swap){ + + /* check if swapping modes or refreshing display */ + if(swap){ /* if the button to swap was pressed */ + stopAll() + boolDisplayEdit = !boolDisplayEdit; /* change current mode */ + } + utility.checked = false; + boolAddEdit = false; + toggleAddEdit(null) + /* check if current mode is add or edit */ + if(boolDisplayEdit){ /* if display */ + settingsButtonContainer.style.display = "inline-block" + addEditContent.style.display = "none" + packageLayout.style.display = "grid" + swapContainer.style.width = "" + animationListButtonContainer.style.display = "none" + openAnimationList(true) + + } else { /* if add */ + + if(!isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ + if(coreLayout.style.gridTemplateColumns != "100% 0%"){ + openSettings() + } + settingsButtonContainer.style.display = "none" + addEditContent.style.display = "block" + packageLayout.style.display = "none" + swapContainer.style.float = "left"; + swapContainer.style.paddingTop = "12px" + swapContainer.style.paddingLeft = "18px" + swapContainer.style.textAlign = "" + + } else { + alert("You must select a pattern"); + displayUtility.checked = false; + boolDisplayEdit = !boolDisplayEdit; + swapContainer.style.float = "left"; + swapContainer.style.paddingTop = "12px" + swapContainer.style.paddingLeft = "18px" + swapContainer.style.textAlign = "" + animationListButtonContainer.style.display = "none" + } + + } +} + +/* switches between add or edit mode or refreshes current mode display */ +function toggleAddEdit(swap){ + /* check if swapping modes or refreshing display */ + if(swap){ /* if the button to swap was pressed */ + stopAll() + if(numAdded == 0){ + alert("You must add an entity before editing the scene"); + utility.checked = false; + return; + } + boolAddEdit = !boolAddEdit; /* change current mode */ + if(boolAddEdit){ + if(selectedEntity == null) { + selectedEntity = els[0] /* set selected entity to be first entity created */ + } + updateStats(); + } /* update stats */ + } else if(swap == false){ + utility.checked = false; + boolAddEdit = false; + } + /* check if current mode is add or edit */ + if(boolAddEdit){ /* if edit */ + + editContent.style.display = 'grid'; + addContent.style.display = 'none'; + background.style.display = "none"; + skyIn.style.display = "none"; + animationListButtonContainer.style.display = 'inline-block' + + + /* check geometry of object */ + if (selectedEntity.getAttribute("id").includes("plane")){ /* plane exclusive containers shown */ + area1.style.display = "block"; + heightIn.style.display = "flex"; + area2.style.display = "block"; + widthIn.style.display = "flex"; + if(selectedEntity.components.material.attrValue.src == null || selectedEntity.components.material.attrValue.src == ""){ + area3.style.display = "block"; + fillIn.style.display = "flex"; + } + area4.style.display = "block"; + textureIn.style.display = "flex"; + area5.style.display = "block"; + uploadTextureIn.style.display = "block"; + } else if (selectedEntity.getAttribute("id").includes("circle")){ /* circle exclusive containers shown */ + specificSettings.style.gridTemplateRows = "17% 17% 0% 17% 16% 17%"; + area1.style.display = "block"; + radiusIn.style.display = "flex"; + area3.style.display = "block"; + fillIn.style.display = "flex"; + } else if (selectedEntity.getAttribute("id").includes("triangle")) { /* triangle exlcusive containers shown */ + area1.style.display = "block"; + vertices.style.display = "block"; + area2.style.display = "block"; + va.style.display = "flex"; + area3.style.display = "block"; + vb.style.display = "flex"; + area4.style.display = "block" + vc.style.display = "flex"; + } else if (selectedEntity.getAttribute("id").includes("gradient") || selectedEntity.getAttribute("id").includes("grille")) { /* gradient exlcusive containers shown */ + area1.style.display = "block"; + heightIn.style.display = "flex"; + area2.style.display = "block"; + widthIn.style.display = "flex"; + area3.style.display = "block"; + numBars.style.display = "flex"; + area4.style.display = "block"; + colIn2.style.display = "flex"; + } else if (selectedEntity.getAttribute("id").includes("checkerboard")) { /* checkerboard exlcusive containers shown */ + area1.style.display = "block"; + cols.style.display = "flex"; + area2.style.display = "block"; + rows.style.display = "flex"; + area3.style.display = "block"; + tileSize.style.display = "flex"; + area4.style.display = "block"; + colIn2.style.display = "flex"; + } else if (selectedEntity.getAttribute("id").includes("circularDotarray")){ /* circular dot array exlcusive containers shown */ + area1.style.display = "block"; + numDots.style.display = "flex"; + area2.style.display = "block"; + numCircles.style.display = "flex"; + area3.style.display = "block"; + circleSize.style.display = "flex"; + area4.style.display = "block"; + arraySpacing.style.display = "flex"; + area5.style.display = "block"; + toggleCenterDot.style.display = "flex" + } else if (selectedEntity.getAttribute("id").includes("dotarray")){ /* dot array exlcusive containers shown */ + area1.style.display = "block"; + cols.style.display = "flex"; + area2.style.display = "block"; + rows.style.display = "flex"; + area3.style.display = "block"; + circleSize.style.display = "flex"; + area4.style.display = "block"; + spacing.style.display = "flex"; + area5.style.display = "block"; + toggleCenterDot.style.display = "flex" + } else if (selectedEntity.getAttribute("id").includes("bullseye")){ /* bullseye exlcusive containers shown */ + area1.style.display = "block"; + numRings.style.display = "flex"; + area2.style.display = "block"; + ringPitch.style.display = "flex"; + } else if (selectedEntity.getAttribute("id").includes("text")){ /* text exlcusive containers shown */ + area1.style.display = "block"; + sizeIn.style.display = "flex"; + area2.style.display = "block"; + textIn.style.display = "flex"; + } else if (selectedEntity.getAttribute("id").includes("timer")){ + area1.style.display = "block"; + sizeIn.style.display = "flex"; + } + updateAnimationList(entity) + if(animationList.childElementCount == 0){ + updateAnimationUI(entity,-1) + } else { + updateAnimationUI(entity,0) + } + + } else { /* if add */ + editContent.style.display = 'none' + addContent.style.display = 'block' + hideEditStats(); /* hide edit containers */ + background.style.display = 'block' + entitySelectorText.style.display = 'flex' + skyIn.style.display = 'block' + animationListButtonContainer.style.display = 'none' + openAnimationList(true) + } +} \ No newline at end of file diff --git a/Custom/graphics/updateInterface.js b/Custom/graphics/updateInterface.js new file mode 100644 index 0000000..f7e5914 --- /dev/null +++ b/Custom/graphics/updateInterface.js @@ -0,0 +1,354 @@ +/* + Contains functions that are integral to the functionality of graphical interface. + These include showing and hiding textboxes, updating currently displayed stats, and changing between modes +*/ + +/* Initial webpage layout hides all edit related containers */ +toggleDisplayEdit(null); +toggleAddEdit(null); + +/* hides edit section */ +function hideEditStats(){ + area1.style.display = "none"; + area1.style.display = "none"; + area3.style.display = "none"; + area4.style.display = "none"; + area5.style.display = "none"; + specificSettings.style.gridTemplateRows = "13% 17% 16% 17% 16% 17%"; + removeButton.style.display = "none"; + duplicateButton.display = "none"; + background.style.display = "none"; + vertices.style.display = "none"; + colIn2.style.display = "none"; + heightIn.style.display = "none"; + sizeIn.style.display = "none"; + widthIn.style.display = "none"; + radiusIn.style.display = "none"; + va.style.display = "none"; + vb.style.display = "none"; + vc.style.display = "none"; + numBars.style.display = "none"; + rows.style.display = "none"; + cols.style.display = "none"; + tileSize.style.display = "none"; + textureIn.style.display = "none"; + uploadTextureIn.style.display = "none"; + fillIn.style.display = "none"; + circleSize.style.display = "none"; + spacing.style.display = "none"; + numDots.style.display = "none"; + numCircles.style.display = "none"; + arraySpacing.style.display = "none"; + ringPitch.style.display = "none"; + numRings.style.display = "none"; + toggleCenterDot.style.display = "none"; + textIn.style.display = "none"; +} + +// updates movement settings UI +function updateMovementSettings(){ + if(movementTypeIn.value == "Pause"){ + // pause + startHeader.style.display = "block" + startHeader.textContent = 'Time (ms)' + startX.style.display = "none" + startY.style.display = "block" + startZ.style.display = "none" + endHeader.style.display = "none" + endX.style.display = "none" + endY.style.display = "none" + endZ.style.display = "none" + speedHeader.style.display = "none" + accelerationHeader.style.display = "none" + keyHeader.style.display = "none" + speed.style.display = "none" + acceleration.style.display = "none" + movementButtonContainer.style.display = "none" + } else if(movementTypeIn.value == "None"){ + // none + if(selectedEntity.getAttribute('advanced').val){ + startHeader.innerHTML = 'Start Point (x: m, y: m, z: m)' + } else { + startHeader.innerHTML = 'Start Point (\u03B1: deg, y: m, r: m):' + } + startHeader.style.display = "none" + startX.style.display = "none" + startY.style.display = "none" + startZ.style.display = "none" + endHeader.style.display = "none" + endX.style.display = "none" + endY.style.display = "none" + endZ.style.display = "none" + speedHeader.style.display = "none" + accelerationHeader.style.display = "none" + keyHeader.style.display = "none" + speed.style.display = "none" + acceleration.style.display = "none" + movementButtonContainer.style.display = "none" + } else if(movementTypeIn.value == "Start" || movementTypeIn.value == "Rubberband"){ + // rubberband + if(selectedEntity.getAttribute('advanced').val){ + startHeader.innerHTML = 'Start Point (x: m, y: m, z: m)' + } else { + startHeader.innerHTML = 'Start Point (\u03B1: deg, y: m, r: m):' + } + startHeader.style.display = "block" + startX.style.display = "block" + startY.style.display = "block" + startZ.style.display = "block" + endHeader.style.display = "block" + endX.style.display = "block" + endY.style.display = "block" + endZ.style.display = "block" + speedHeader.style.display = "block" + speedHeader.innerText = "Speed (m/s)" + accelerationHeader.style.display = "block" + speed.style.display = "block" + acceleration.style.display = "block" + movementButtonContainer.style.display = "inline" + } else { + // start to finish + if(selectedEntity.getAttribute('advanced').val){ + startHeader.innerHTML = 'Start Point (x: m, y: m, z: m)' + } else { + startHeader.innerHTML = 'Start Point (\u03B1: deg, y: m, r: m):' + } + startHeader.style.display = "block" + startX.style.display = "block" + startY.style.display = "block" + startZ.style.display = "block" + endHeader.style.display = "block" + endX.style.display = "block" + endY.style.display = "block" + endZ.style.display = "block" + speedHeader.style.display = "block" + speedHeader.innerText = "Time (ms)" + accelerationHeader.style.display = "none" + speed.style.display = "block" + acceleration.style.display = "none" + movementButtonContainer.style.display = "inline" + } +} + + var flag = false; +/* updates values in edit section */ +function updateStats(){ + /* update universal values */ + skyColor.value = sky.components.material.attrValue.color; + $('#skyCol').minicolors("value",sky.components.material.attrValue.color); + entity = selectedEntity; + // if advanced + if(selectedEntity.getAttribute('advanced').val){ + endZ.disabled = false + advanced.style.backgroundColor = '#00FF00' + posIn.innerHTML = 'Position (x: m, y: m, z: m):' + startHeader.innerHTML = 'Start Point (x: m, y: m, z: m)' + endHeader.innerHTML = 'End Point (x: m, y: m, z: m)' + rotationY.style.display = 'block' + rotationX.style.display = 'block' + xIn.value = (entity.components.position.attrValue.x).toFixed(3); + yIn.value = (entity.components.position.attrValue.y).toFixed(3); + zIn.value = (-entity.components.position.attrValue.z).toFixed(3); + updateAnimationList(entity) + + if(animationList.getAttribute('selectedIndex') == ""){ + movementTypeIn.value = 'None' + } else { + movementTypeIn.value = entity.components.movement.attrValue.types[0] + updateMovementSettings() + + if(entity.components.movement.attrValue.startPoints.length != 0){ + + startX.value = entity.components.movement.attrValue.startPoints[0].x + startY.value = entity.components.movement.attrValue.startPoints[0].y + startZ.value = -entity.components.movement.attrValue.startPoints[0].z + endX.value = entity.components.movement.attrValue.endPoints[0].x + endY.value = entity.components.movement.attrValue.endPoints[0].y + endZ.value = -entity.components.movement.attrValue.endPoints[0].z + acceleration.value = entity.components.movement.attrValue.accelerations[0] + speed.value = entity.components.movement.attrValue.initialVelocities[0] + if(entity.components.movement.attrValue.types[0] == 'Pause'){ + startY.value = entity.components.movement.attrValue.initialVelocities[0] + } + } + + + } + + } else { + // if not advanced + endZ.disabled = true + advanced.style.backgroundColor ='' + posIn.innerHTML = 'Position (\u03B1: deg, y: m, r: m):' + startHeader.innerHTML = 'Start Point (\u03B1: deg, y: m, r: m):' + endHeader.innerHTML = 'End Point (\u03B1: deg, y: m, r: m):' + rotationY.style.display = 'none' + rotationX.style.display = 'none' + xIn.value = (-entity.components.angle.attrValue.x).toFixed(3); + yIn.value = (entity.components.position.attrValue.y).toFixed(3); + zIn.value = (-entity.components.angle.attrValue.z).toFixed(3); + + if(animationList.getAttribute('selectedIndex') == ""){ + movementTypeIn.value = 'None' + } else { + movementTypeIn.value = entity.components.movement.attrValue.types[0] + updateMovementSettings() + + if(entity.components.movement.attrValue.startPoints.length != 0){ + startX.value = entity.components.movement.attrValue.startPoints[0].theta + startY.value = entity.components.movement.attrValue.startPoints[0].y + startZ.value = -entity.components.movement.attrValue.startPoints[0].r + endX.value = entity.components.movement.attrValue.endPoints[0].theta + endY.value = entity.components.movement.attrValue.endPoints[0].y + endZ.value = -entity.components.movement.attrValue.endPoints[0].r + acceleration.value = entity.components.movement.attrValue.accelerations[0] + speed.value = entity.components.movement.attrValue.initialVelocities[0] + if(entity.components.movement.attrValue.types[0] == 'Pause'){ + startY.value = entity.components.movement.attrValue.initialVelocities[0] + } + } + } + + } + + if(entity.components.movement.attrValue.status == -1){ + movementIcon.className = "fa-solid fa-play" + } else { + movementIcon.className = "fa-solid fa-pause" + } + + rotationZ.value = (entity.components.rotation.attrValue.z).toFixed(3); + rotationY.value = (entity.components.rotation.attrValue.y).toFixed(3); + rotationX.value = (entity.components.rotation.attrValue.x).toFixed(3); + flag = true; + + if(selectedEntity.getAttribute("id").includes("text") || selectedEntity.getAttribute("id").includes("timer")){ + color.value = entity.components.text.attrValue.color; + $('#color').minicolors("value",entity.components.text.attrValue.color); + } else { + color.value = entity.components.material.attrValue.color; + $('#color').minicolors("value",entity.components.material.attrValue.color); + } + + flag = false; // this is used to stop color changer events from firing + if (entity.components.text || entity.components.material.attrValue.src == "" || entity.components.material.attrValue.src == null){ + texture.selectedIndex = 0; + texture.options[0].selected = true; + } else { + $(texture.options).each(function() { + if((entity.components.material.attrValue.src).includes(this.value)) + this.selected = true; + }); + } + + // specific values + + if(selectedEntity.getAttribute("id").includes("plane")){ /* plane exclusives */ + if(selectedEntity.children.length == 0){ + width.value = (selectedEntity.components.geometry.attrValue.width).toFixed(3); + + } else { + width.value = (selectedEntity.children[2].components.geometry.attrValue.width).toFixed(3); + } + height.value = (selectedEntity.components.geometry.attrValue.height).toFixed(3); + fill.value = (selectedEntity.components.fill.attrValue.val).toFixed(3); + } else if(selectedEntity.getAttribute("id").includes("circle")){ /* circle exclusives */ + radius.value = (selectedEntity.components.geometry.attrValue.radiusOuter).toFixed(3); + fill.value = (selectedEntity.components.fill.attrValue.val).toFixed(3); + } else if(selectedEntity.getAttribute("id").includes("triangle")){ /* triangle exclusives */ + vax.value = (selectedEntity.components.geometry.attrValue.vertexA.x).toFixed(3); + vay.value = (selectedEntity.components.geometry.attrValue.vertexA.y).toFixed(3); + vbx.value = (selectedEntity.components.geometry.attrValue.vertexB.x).toFixed(3); + vby.value = (selectedEntity.components.geometry.attrValue.vertexB.y).toFixed(3); + vcx.value = (selectedEntity.components.geometry.attrValue.vertexC.x).toFixed(3); + vcy.value = (selectedEntity.components.geometry.attrValue.vertexC.y).toFixed(3); + } else if(selectedEntity.getAttribute("id").includes("gradient")){ /* gradient exclusives */ + width.value = (selectedEntity.children[0].components.geometry.attrValue.width).toFixed(3); + height.value = (selectedEntity.children[0].components.geometry.attrValue.height).toFixed(3); + numBarsIn.value = (selectedEntity.children.length); + $('#color2').minicolors("value",entity.components.color2.attrValue.val); + } else if(selectedEntity.getAttribute("id").includes("checkerboard")){ /* checkerboard */ + rowsIn.value = (selectedEntity.children.length); + colsIn.value = (selectedEntity.children[0].children.length); + tileSizeIn.value = (selectedEntity.children[0].children[0].components.geometry.attrValue.width).toFixed(3); + $('#color2').minicolors("value",entity.components.color2.attrValue.val); + } else if(selectedEntity.getAttribute("id").includes("grille")){ /* grille */ + width.value = (selectedEntity.children[0].components.geometry.attrValue.width).toFixed(3); + height.value = (selectedEntity.children[0].components.geometry.attrValue.height).toFixed(3); + numBarsIn.value = (selectedEntity.children.length); + $('#color2').minicolors("value",entity.components.color2.attrValue.val); + } else if(selectedEntity.getAttribute("id").includes("circularDotarray")){ /* circular dot array */ + numDotsIn.value = (selectedEntity.children[1].children.length); + numCirclesIn.value = (selectedEntity.children.length-1); + arraySpacingIn.value = selectedEntity.components.arraySpacing.attrValue.val; + circleSizeIn.value = (selectedEntity.children[0].components.geometry.attrValue.radiusOuter).toFixed(3); + toggleCenterDotIn.checked = (selectedEntity.components.toggleCenterDot.attrValue.val); + } else if(selectedEntity.getAttribute("id").includes("dotarray")){ /* dot array */ + rowsIn.value = (selectedEntity.children.length); + colsIn.value = (selectedEntity.children[0].children.length); + circleSizeIn.value = (selectedEntity.children[0].children[0].components.geometry.attrValue.radiusOuter).toFixed(3); + spacingIn.value = selectedEntity.components.arraySpacing.attrValue.val; + toggleCenterDotIn.checked = (selectedEntity.components.toggleCenterDot.attrValue.val); + } else if(selectedEntity.getAttribute("id").includes("bullseye")){ /* bullseye */ + numRingsIn.value = (selectedEntity.children.length-1); + ringPitchIn.value = selectedEntity.children[0].components.geometry.attrValue.radiusOuter*2; + } else if(selectedEntity.getAttribute("id").includes("text")){ /* text */ + width.value = entity.components.text.attrValue.width; + textIn.value = entity.components.text.attrValue.value; + } else if(selectedEntity.getAttribute("id").includes("timer")){ /* timer */ + width.value = entity.components.text.attrValue.width; + height.value = entity.components.text.attrValue.height; + } + +} + +// updates all aspects of the animation UI on change from animation list +function updateAnimationUI(entity, ind) { + if(ind == -1){ + movementTypeIn.value = "None" + movementTypeIn.disabled = true + updateMovementSettings() + return + } + + let mov = entity.getAttribute('movement') + let i = 0; + let counter = -1; + + // have to take into account rebounds from rubberband + while(i < mov.types.length){ + if(mov.types[i] != 'Rebound'){ + counter++; + if(counter == ind){ + break + } + } + i++; + } + + if(entity.getAttribute('advanced').val){ + startX.value = mov.startPoints[i].x + startY.value = mov.startPoints[i].y + startZ.value = -mov.startPoints[i].z + endX.value = mov.endPoints[i].x + endY.value = mov.endPoints[i].y + endZ.value = -mov.endPoints[i].z + } else { + startX.value = mov.startPoints[i].theta + startY.value = mov.startPoints[i].y + startZ.value = -mov.startPoints[i].r + endX.value = mov.endPoints[i].theta + endY.value = mov.endPoints[i].y + endZ.value = -mov.endPoints[i].r + } + + movementTypeIn.value = mov.types[i] + acceleration.value = mov.accelerations[i] + speed.value = mov.initialVelocities[i] + movementTypeIn.disabled = false + if(mov.types[i] == 'Pause'){ + startY.value = mov.initialVelocities[i] + } + updateMovementSettings() + + } \ No newline at end of file diff --git a/Custom/index.html b/Custom/index.html index 3418abb..0f7fdee 100644 --- a/Custom/index.html +++ b/Custom/index.html @@ -8,7 +8,7 @@ - + + + @@ -133,20 +134,20 @@ - - + + -
    +
    Display - Edit
    + Edit
    Packages:
    @@ -163,20 +164,21 @@
    Pattern List:
      -
    1. red
    2. -
    3. green
    4. -
    5. blue
    6. -
    7. white
    8. -
    9. grille
    10. -
    11. crosshair
    12. -
    13. line
    14. -
    15. circular dot array
    16. -
    17. dot array
    18. -
    19. checkerboard_w
    20. -
    21. checkerboard_b
    22. -
    23. ring_w1
    24. -
    25. ring_w2
    26. -
    27. ring_w5
    28. +
    29. red
    30. +
    31. green
    32. +
    33. blue
    34. +
    35. white
    36. +
    37. grille
    38. +
    39. crosshair
    40. +
    41. line
    42. +
    43. circular dot array
    44. +
    45. dot array
    46. +
    47. checkerboard_w
    48. +
    49. checkerboard_b
    50. +
    51. ring_w1
    52. +
    53. ring_w2
    54. +
    55. ring_w5
    56. +
    57. Flying Spot
    + +
    +
    Movement Settings
    +
    Movement Type:
    +
    Start Point (α: deg, y: m, r: m):
    +
    +
    +
    +
    End Point (α: deg, y: m, r: m):
    +
    +
    +
    +
    Speed (m/s)
    +
    Acceleration (m/s2)
    + +
    +
    + + +
    @@ -265,6 +295,8 @@ + +

    @@ -278,6 +310,12 @@

    +

    Animation Settings

    +

    + + + +

    @@ -286,13 +324,14 @@
    -
    +
    Package Settings:
    -
    +
    +
    -
    +
    Pattern Settings:
    @@ -300,19 +339,47 @@
    +
    +
    Animation List:
      +
    1. None
    2. +
    +
    - - + +
    +
    +
    -
    + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Custom/input/changeListeners.js b/Custom/input/changeListeners.js new file mode 100644 index 0000000..cdd0719 --- /dev/null +++ b/Custom/input/changeListeners.js @@ -0,0 +1,288 @@ +/* + Handles user input + +*/ + +/* If the textbox for sky color is changed */ +$('#skyCol').minicolors({ + control: 'hue', + position:'top', + change: function () { + sky.setAttribute("material",{color: $("#skyCol").val()}); + if(colorChange){ + updateJSON(); + } + colorChange = true + }, +}); + +/* If the textbox for x value is changed */ +$("#x").change(function() { + editEntity(); + }); + +/* If the textbox for y value is changed */ +$("#y").change(function() { + editEntity(); + }); + +/* If the textbox for z value is changed */ +$("#z").change(function() { + editEntity(); + }); + +/* If the textbox for endX value is changed */ +$("#startX").change(function() { + editEntity(); + }); + +/* If the textbox for endY value is changed */ +$("#startY").change(function() { + editEntity(); + }); + +/* If the textbox for endZ value is changed */ +$("#startZ").change(function() { + editEntity(); + }); + +/* If the textbox for endX value is changed */ +$("#endX").change(function() { + editEntity(); + }); + +/* If the textbox for endY value is changed */ +$("#endY").change(function() { + editEntity(); + }); + +/* If the textbox for endZ value is changed */ +$("#endZ").change(function() { + editEntity(); + }); + + /* If the textbox for endX value is changed */ +$("#speedIn").change(function() { + editEntity(); + }); + +/* If the textbox for endY value is changed */ +$("#accelerationIn").change(function() { + editEntity(); + }); + +/* If the textbox for endZ value is changed */ +$("#movementTypeIn").change(function() { + editEntity(); + }); + +/* If the textbox for color value is changed */ +$('#color').minicolors({ + control: 'hue', + position:'top', + change: function () { + if(!flag && $("#color").val().length == 7){ + editEntity(); + } + }, +}); + +/* If the textbox for secondary color value is changed */ +$('#color2').minicolors({ + control: 'hue', + position:'top', + change: function () { + if(!flag && $("#color").val().length == 7){ + editEntity(); + } + }, +}); + +/* If the textbox for fill value is changed */ +$("#fill").change(function() { + if(selectedEntity.id.includes("plane")){ + if(parseFloat($("#fill").val()) <= 0){ + alert("Border too small (0 < border <= smallest dimension of entity)"); + return; + } else if((parseFloat($("#width").val()) < parseFloat($("#fill").val())) && (parseFloat($("#width").val()) <= parseFloat($("#height").val())) || (parseFloat($("#height").val()) < parseFloat($("#fill").val())) && (parseFloat($("#width").val()) > parseFloat($("#height").val()))){ + alert("Border too large, will change size of entity (0 < border <= smallest dimension of entity)"); + return; + } + if(parseFloat($("#fill").val()) == parseFloat($("#width").val()) && parseFloat($("#height").val()) > parseFloat($("#width").val())){ + selectedEntity.setAttribute("fill",{val: parseFloat($("#fill").val()), isFull: true}); + } else if(parseFloat($("#fill").val()) == parseFloat($("#height").val()) && parseFloat($("#height").val()) < parseFloat($("#width").val())){ + selectedEntity.setAttribute("fill",{val: parseFloat($("#fill").val()), isFull: true}); + } else { + selectedEntity.setAttribute("fill",{val: parseFloat($("#fill").val()), isFull: false}); + } + } else { + if(parseFloat($("#fill").val()) <= 0){ + alert("Border too small (0 < border <= smallest dimension of entity)"); + return; + } else if(parseFloat($("#radius").val()) < parseFloat($("#fill").val())){ + alert("Border too large, will change size of entity (0 < border <= radius)"); + return; + } + if(parseFloat($("#fill").val()) == parseFloat($("#radius").val())){ + selectedEntity.setAttribute("fill",{val: parseFloat($("#fill").val()), isFull: true}); + } else { + selectedEntity.setAttribute("fill",{val: parseFloat($("#fill").val()), isFull: false}); + } + } + editEntity(); + }); + +/* If the textbox for rotation value is changed */ +$("#rotationZ").change(function() { + editEntity(); + }); + +/* If the textbox for rotation value is changed */ +$("#rotationY").change(function() { + editEntity(); + }); + +/* If the textbox for rotation value is changed */ +$("#rotationX").change(function() { + editEntity(); + }); + +/* If the textbox for radius value is changed */ +$("#radius").change(function() { + if(parseFloat($("#fill").val()) > parseFloat($("#radius").val()) || selectedEntity.getAttribute("fill").isFull){ + fill.value = parseFloat($("#radius").val()); + selectedEntity.setAttribute("fill",{val: parseFloat($("#radius").val()), isFull: true}); + } + editEntity(); + }); + +/* If the textbox for width value is changed */ +$("#width").change(function() { + if(selectedEntity.id.includes("plane")){ + if((parseFloat($("#fill").val()) > parseFloat($("#width").val()) && parseFloat($("#height").val()) > parseFloat($("#width").val())) || (parseFloat($("#height").val()) > parseFloat($("#width").val()) && selectedEntity.getAttribute("fill").isFull)){ + fill.value = parseFloat($("#width").val()); + selectedEntity.setAttribute("fill",{val: parseFloat($("#width").val()), isFull: true}); + } + } + + + editEntity(); + }); + +/* If the textbox for height value is changed */ +$("#height").change(function() { + if(selectedEntity.id.includes("plane")){ + if((parseFloat($("#fill").val()) > parseFloat($("#height").val()) && parseFloat($("#height").val()) < parseFloat($("#width").val())) || (parseFloat($("#height").val()) < parseFloat($("#width").val()) && selectedEntity.getAttribute("fill").isFull)){ + fill.value = parseFloat($("#height").val()); + selectedEntity.setAttribute("fill",{val: parseFloat($("#height").val()), isFull: true}); + } + } + editEntity(); + }); + +/* If the textbox for vax value is changed */ +$("#vax").change(function() { + editEntity(); + }); + + /* If the textbox for vax value is changed */ +$("#size").change(function() { + editEntity(); + }); + +/* If the textbox for vay value is changed */ +$("#vay").change(function() { + editEntity(); + }); + +/* If the textbox for vbx value is changed */ +$("#vbx").change(function() { + editEntity(); + }); + +/* If the textbox for vby value is changed */ +$("#vby").change(function() { + editEntity(); + }); + +/* If the textbox for vcx value is changed */ +$("#vcx").change(function() { + editEntity(); + }); + +/* If the textbox for vcy value is changed */ +$("#vcy").change(function() { + editEntity(); + }); + +/* If the textbox for numbers of bars value is changed */ +$("#numBarsIn").change(function() { + editEntity(); + }); + +/* If the textbox for numbers of rows is changed */ +$("#rowsIn").change(function() { + editEntity(); + }); + +/* If the textbox for numbers of cols is changed */ +$("#colsIn").change(function() { + editEntity(); + }); + +/* If the textbox for size of tiles is changed */ +$("#tileSizeIn").change(function() { + editEntity(); + }); + + /* If the textbox for size of tiles is changed */ +$("#circleSizeIn").change(function() { + editEntity(); + }); + + /* If the textbox for size of tiles is changed */ +$("#spacingIn").change(function() { + editEntity(); + }); + +/* If the textbox for size of tiles is changed */ +$("#numDotsIn").change(function() { + editEntity(); + }); + + /* If the textbox for size of tiles is changed */ +$("#numCirclesIn").change(function() { + editEntity(); + }); + + /* If the textbox for size of tiles is changed */ +$("#arraySpacingIn").change(function() { + editEntity(); + }); + + /* If the textbox for size of tiles is changed */ +$("#numRingsIn").change(function() { + editEntity(); + }); + + /* If the textbox for size of tiles is changed */ +$("#ringSpacingIn").change(function() { + editEntity(); + }); + + /* If the textbox for size of tiles is changed */ +$("#ringThicknessIn").change(function() { + editEntity(); + }); + +$("#toggleCenterDotIn").change(function() { + editEntity(); +}); + +$("#ringPitchIn").change(function() { + editEntity(); +}); + +$("#text").change(function() { + editEntity(); +}); \ No newline at end of file diff --git a/Custom/input/controllerComponent.js b/Custom/input/controllerComponent.js new file mode 100644 index 0000000..fd9b0cf --- /dev/null +++ b/Custom/input/controllerComponent.js @@ -0,0 +1,253 @@ +/* + Listens for button inputs and handles them accordingly. + + // trigger/grip controls pattern selection + // trigger while holding up- go to previous pattern + // trigger while holding down- go to next pattern + + // thumbstick/trackpad controls animations + // thumbstick while holding right- start/pause + // thumbstick while holding down- stop animation +*/ + +/* Code to register thumbstick behavior for left hand */ +AFRAME.registerComponent('thumbstick-left',{ + init: function () { + this.el.addEventListener('thumbstickmoved', this.logThumbstick); + }, + logThumbstick: function (evt) { + thumbstickDetailL.x = evt.detail.x + thumbstickDetailL.y = evt.detail.y + } + }); + + /* */ + AFRAME.registerComponent('thumbstick-right',{ + init: function () { + this.el.addEventListener('thumbstickmoved', this.logThumbstick); + }, + logThumbstick: function (evt) { + thumbstickDetailR.x = evt.detail.x + thumbstickDetailR.y = evt.detail.y + + } + }); + + AFRAME.registerComponent('trackpad-left',{ + init: function () { + this.el.addEventListener('trackpadmoved', this.logTrackpad); + }, + logTrackpad: function (evt) { + trackpadDetailL.x = evt.detail.x; + trackpadDetailL.y = evt.detail.y; + + } + }); + + /* */ + AFRAME.registerComponent('trackpad-right',{ + init: function () { + this.el.addEventListener('trackpadmoved', this.logTrackpad); + }, + logTrackpad: function (evt) { + console.log(JSON.stringify(evt.detail)); + trackpadDetailR.x = evt.detail.x; + trackpadDetailR.y = evt.detail.y; + + } + }); + + AFRAME.registerComponent('button-listener-r', { + init: function () { + var el = this.el; + + el.addEventListener('gripdown', function (evt) { + buttonsDownR['grip'] = true; + if(thumbstickDetailR.y <= -.75 || trackpadDetailR.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailR.y >= .75 || trackpadDetailR.y >= .75){ + displayNext(true); + } + + }); + + el.addEventListener('gripup', function (evt) { + buttonsDownR['grip'] = false; + }); + + el.addEventListener('triggerdown', function (evt) { + buttonsDownR['trigger'] = true; + if(thumbstickDetailR.y <= -.75 || trackpadDetailR.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailR.y >= .75 || trackpadDetailR.y >= .75){ + displayNext(true); + } + }); + + el.addEventListener('triggerup', function (evt) { + buttonsDownR['grip'] = false; + }); + + el.addEventListener('abuttondown', function (evt) { + buttonsDownR['abutton'] = true; + if(thumbstickDetailR.y <= -.75 || trackpadDetailR.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailR.y >= .75 || trackpadDetailR.y >= .75){ + displayNext(true); + } + }); + el.addEventListener('abuttonup', function (evt) { + buttonsDownR['abutton'] = false; + }); + + el.addEventListener('bbuttondown', function (evt) { + buttonsDownR['bbutton'] = true; + if(thumbstickDetailR.y <= -.75 || trackpadDetailR.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailR.y >= .75 || trackpadDetailR.y >= .75){ + displayNext(true); + } + }); + el.addEventListener('bbuttonup', function (evt) { + buttonsDownR['bbutton'] = false; + }); + + el.addEventListener('thumbstickdown', function (evt) { + buttonsDownR['thumbstick'] = true; + if(trackpadDetailR.y >= .75 || thumbstickDetailR.y >= .75 ){ + stopAll() + } else if(trackpadDetailR.x >= .75 || thumbstickDetailR.x >= .75 ){ + if(startAllButton.disabled){ + pauseAll(); + } else { + startAll(); + } + } + }); + el.addEventListener('thumbstickup', function (evt) { + buttonsDownR['thumbstick'] = false; + }); + + el.addEventListener('trackpaddown', function (evt) { + console.log("Trackpad detail: "+JSON.stringify(trackpadDetailR)) + console.log("Thumbstick detail: "+JSON.stringify(thumbstickDetailR)) + buttonsDownR['trackpad'] = true; + if(trackpadDetailR.y >= .75 || thumbstickDetailR.y >= .75 ){ + stopAll() + } else if(trackpadDetailR.x >= .75 || thumbstickDetailR.x >= .75 ){ + if(startAllButton.disabled){ + pauseAll(); + } else { + startAll(); + } + } + }); + + el.addEventListener('trackpadup', function (evt) { + buttonsDownR['trackpad'] = false; + }); + } + }); + + AFRAME.registerComponent('button-listener-l', { + init: function () { + var el = this.el; + + el.addEventListener('gripdown', function (evt) { + buttonsDownL['grip'] = true; + if(thumbstickDetailL.y <= -.75 || trackpadDetailL.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailL.y >= .75 || trackpadDetailL.y >= .75){ + displayNext(true); + } + }); + + el.addEventListener('gripup', function (evt) { + buttonsDownL['grip'] = false; + }); + + el.addEventListener('triggerdown', function (evt) { + buttonsDownL['trigger'] = true; + if(thumbstickDetailL.y <= -.75 || trackpadDetailL.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailL.y >= .75 || trackpadDetailL.y >= .75){ + displayNext(true); + } + }); + + el.addEventListener('triggerup', function (evt) { + buttonsDownL['grip'] = false; + }); + + el.addEventListener('xbuttondown', function (evt) { + buttonsDownL['xbutton'] = true; + if(thumbstickDetailL.y <= -.75 || trackpadDetailL.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailL.y >= .75 || trackpadDetailL.y >= .75){ + displayNext(true); + } + }); + + el.addEventListener('xbuttonup', function (evt) { + buttonsDownL['xbutton'] = false; + if(thumbstickDetailL.y <= -.75 || trackpadDetailL.y <= -.75){ + displayNext(false) + } else if(thumbstickDetailL.y >= .75 || trackpadDetailL.y >= .75){ + displayNext(true); + } + }); + + el.addEventListener('ybuttondown', function (evt) { + buttonsDownL['ybutton'] = true; + }); + + el.addEventListener('ybuttonup', function (evt) { + buttonsDownL['ybutton'] = false; + }); + + el.addEventListener('thumbstickdown', function (evt) { + buttonsDownL['thumbstick'] = true + if(trackpadDetailL.y >= .75 || thumbstickDetailL.y >= .75){ + stopAll() + } else if(trackpadDetailL.x >= .75 || thumbstickDetailL.x >= .75){ + if(startAllButton.disabled){ + pauseAll(); + } else { + startAll(); + } + } + }); + el.addEventListener('thumbstickup', function (evt) { + buttonsDownL['thumbstick'] = false; + }); + + el.addEventListener('trackpaddown', function (evt) { + buttonsDownL['trackpad'] = true + if(trackpadDetailL.y >= .75 || thumbstickDetailL.y >= .75){ + stopAll() + } else if(trackpadDetailL.x >= .75 || thumbstickDetailL.x >= .75){ + if(startAllButton.disabled){ + pauseAll(); + } else { + startAll(); + } + } + }); + + el.addEventListener('trackpadup', function (evt) { + buttonsDownL['trackpad'] = false; + }); + + } + }); + + thumbstickDetailL = {x: 0, y: 0} + trackpadDetailL = {x: 0, y: 0} + buttonsDownL = {trigger: false, grip: false, trackpad: false, thumbstick: false, abutton: false, bbutton: false} + + thumbstickDetailR = {x: 0, y: 0} + trackpadDetailR = {x: 0, y: 0} + buttonsDownR = {trigger: false, grip: false, trackpad: false, thumbstick: false, xbutton: false, ybutton: false} + + + diff --git a/Custom/input/controllerInput.js b/Custom/input/controllerInput.js new file mode 100644 index 0000000..c8936f1 --- /dev/null +++ b/Custom/input/controllerInput.js @@ -0,0 +1,336 @@ +/* + Reads in controller input profiles and sets event listeners. + Event listeners will fire and dispatch correctly encoded event to controller button listeners + Controller schemes are windows, oculus touch, oculus go, vive, vive focus, and a catch all that contains all buttons + +*/ + +//import data from '../Compatibility/controller_profiles.json' assert { type: "json" }; +const windows = data[0]['windows'] +const oc_touch = data[0]['oc_touch'] +const oc_go = data[0]['oc_go'] +const vive = data[0]['vive'] +const vive_focus = data[0]['vive_focus'] +const magic = data[0]['magic'] +// contains all possible buttons and axes +const generic = data[0]['generic'] + +// function to determine current controller scheme +let isRight, isLeft, scheme; +function findControls(){ + isRight = !(conRight.getAttribute("position").x == 0 && conRight.getAttribute("position").y == 0 && conRight.getAttribute("position").z == 0); + isLeft = !(conLeft.getAttribute("position").x == 0 && conLeft.getAttribute("position").y == 0 && conLeft.getAttribute("position").z == 0); + if(isRight){ + + if(conRight.components['tracked-controls'].attrValue.hasOwnProperty("id") && conRight.components['tracked-controls'].attrValue.id != ""){ + scheme = conRight.components['tracked-controls-webxr'].attrValue.id + clearInterval(controlsInterval) + if(scheme.includes("touch")){ + conLeft.removeAttribute("oculus-touch-controls") + conRight.removeAttribute("oculus-touch-controls") + } else if(scheme.includes("go")){ + conLeft.removeAttribute("oculus-go-controls") + conRight.removeAttribute("oculus-go-controls") + } else if(scheme.includes("focus")){ + conLeft.removeAttribute("vive-focus-controls") + conRight.removeAttribute("vive-focus-controls") + } else if(scheme.includes("vive")){ + conLeft.removeAttribute("vive-controls") + conRight.removeAttribute("vive-controls") + } else if(scheme.includes("windows")){ + conLeft.removeAttribute("windows-motion-controls") + conRight.removeAttribute("windows-motion-controls") + } else if(scheme.includes("magicleap")) { + conLeft.removeAttribute("magicleap-controls") + conRight.removeAttribute("magicleap-controls") + } + } else if(conRight.components['tracked-controls'].attrValue.hasOwnProperty("idPrefix") && conRight.components['tracked-controls'].attrValue.idPrefix != ""){ + scheme = conRight.getAttribute("tracked-controls").idPrefix; + clearInterval(controlsInterval) + if(scheme.includes("touch")){ + conLeft.removeAttribute("oculus-touch-controls") + conRight.removeAttribute("oculus-touch-controls") + } else if(scheme.includes("go")){ + conLeft.removeAttribute("oculus-go-controls") + conRight.removeAttribute("oculus-go-controls") + } else if(scheme.includes("focus")){ + conLeft.removeAttribute("vive-focus-controls") + conRight.removeAttribute("vive-focus-controls") + } else if(scheme.includes("vive")){ + conLeft.removeAttribute("vive-controls") + conRight.removeAttribute("vive-controls") + } else if(scheme.includes("windows")){ + conLeft.removeAttribute("windows-motion-controls") + conRight.removeAttribute("windows-motion-controls") + } else if(scheme.includes("magicleap")) { + conLeft.removeAttribute("magicleap-controls") + conRight.removeAttribute("magicleap-controls") + } + } else { + scheme = null + } + + } else if(isLeft) { + if(conLeft.components['tracked-controls'].attrValue.hasOwnProperty("id") && conLeft.components['tracked-controls'].attrValue.id != ""){ + scheme = conLeft.components['tracked-controls-webxr'].attrValue.id + clearInterval(controlsInterval) + if(scheme.includes("touch")){ + conLeft.removeAttribute("oculus-touch-controls") + conRight.removeAttribute("oculus-touch-controls") + } else if(scheme.includes("go")){ + conLeft.removeAttribute("oculus-go-controls") + conRight.removeAttribute("oculus-go-controls") + } else if(scheme.includes("focus")){ + conLeft.removeAttribute("vive-focus-controls") + conRight.removeAttribute("vive-focus-controls") + } else if(scheme.includes("vive")){ + conLeft.removeAttribute("vive-controls") + conRight.removeAttribute("vive-controls") + } else if(scheme.includes("windows")){ + conLeft.removeAttribute("windows-motion-controls") + conRight.removeAttribute("windows-motion-controls") + } else if(scheme.includes("magicleap")) { + conLeft.removeAttribute("magicleap-controls") + conRight.removeAttribute("magicleap-controls") + } + } else if(conLeft.components['tracked-controls'].attrValue.hasOwnProperty("idPrefix") && conLeft.components['tracked-controls'].attrValue.idPrefix != ""){ + scheme = conLeft.getAttribute("tracked-controls").idPrefix; + clearInterval(controlsInterval) + if(scheme.includes("touch")){ + conLeft.removeAttribute("oculus-touch-controls") + conRight.removeAttribute("oculus-touch-controls") + } else if(scheme.includes("go")){ + conLeft.removeAttribute("oculus-go-controls") + conRight.removeAttribute("oculus-go-controls") + } else if(scheme.includes("focus")){ + conLeft.removeAttribute("vive-focus-controls") + conRight.removeAttribute("vive-focus-controls") + } else if(scheme.includes("vive")){ + conLeft.removeAttribute("vive-controls") + conRight.removeAttribute("vive-controls") + } else if(scheme.includes("windows")){ + conLeft.removeAttribute("windows-motion-controls") + conRight.removeAttribute("windows-motion-controls") + } else if(scheme.includes("magicleap")) { + conLeft.removeAttribute("magicleap-controls") + conRight.removeAttribute("magicleap-controls") + } + + } else { + scheme = null + } + } + +} + +// handles button presses and dispatches proper event type +conLeft.addEventListener('buttondown', function (evt) { + console.log("left") + console.log(JSON.stringify(evt.detail.id)) + if(scheme == 'windows-mixed-reality'){ + conLeft.dispatchEvent(new CustomEvent(windows['left'][evt.detail.id]+'down', {detail: true})) + } else if(scheme == 'oculus-touch' || scheme == 'oculus-touch-v2' || scheme == 'oculus-touch-v3' || scheme == 'meta-quest-touch-pro') { + conLeft.dispatchEvent(new CustomEvent(oc_touch['left'][evt.detail.id]+'down', {detail: true})) + } else if (scheme == 'oculus-go'){ + conLeft.dispatchEvent(new CustomEvent(oc_go['left'][evt.detail.id]+'down', {detail: true})) + } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus') { + conLeft.dispatchEvent(new CustomEvent(vive['left'][evt.detail.id]+'down', {detail: true})) + } else if(scheme == 'htc-vive-focus') { + conLeft.dispatchEvent(new CustomEvent(vive_focus['left'][evt.detail.id]+'down', {detail: true})) + } else if(scheme == 'magicleap-one') { + conLeft.dispatchEvent(new CustomEvent(magic['left'][evt.detail.id]+'down', {detail: true})) + } else { + conLeft.dispatchEvent(new CustomEvent(generic['left'][evt.detail.id]+'down', {detail: true})) + } +}); + +conRight.addEventListener('buttondown', function (evt) { + console.log("right") + console.log(JSON.stringify(evt.detail.id)) + console.log(scheme) + if(scheme == 'windows-mixed-reality'){ + conRight.dispatchEvent(new CustomEvent(windows['right'][evt.detail.id]+'down', {detail: true})) + } else if(scheme == 'oculus-touch' || scheme == 'oculus-touch-v2' || scheme == 'oculus-touch-v3' || scheme == 'meta-quest-touch-pro') { + conRight.dispatchEvent(new CustomEvent(oc_touch['right'][evt.detail.id]+'down', {detail: true})) + } else if (scheme == 'oculus-go'){ + conRight.dispatchEvent(new CustomEvent(oc_go['right'][evt.detail.id]+'down', {detail: true})) + } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus') { + conRight.dispatchEvent(new CustomEvent(vive['right'][evt.detail.id]+'down', {detail: true})) + } else if(scheme == 'htc-vive-focus') { + conRight.dispatchEvent(new CustomEvent(vive_focus['right'][evt.detail.id]+'down', {detail: true})) + } else if(scheme == 'magicleap-one') { + console.log(magic['right'][evt.detail.id]+'down') + conRight.dispatchEvent(new CustomEvent(magic['right'][evt.detail.id]+'down', {detail: true})) + } else { + console.log(JSON.stringify(generic['right'][evt.detail.id]+'down')) + conRight.dispatchEvent(new CustomEvent(generic['right'][evt.detail.id]+'down', {detail: true})) + } +}); + +conLeft.addEventListener('buttonup', function (evt) { + if(scheme == 'windows-mixed-reality'){ + conLeft.dispatchEvent(new CustomEvent(windows['left'][evt.detail.id]+'up', {detail: true})) + } else if(scheme == 'oculus-touch' || scheme == 'oculus-touch-v2' || scheme == 'oculus-touch-v3' || scheme == 'meta-quest-touch-pro') { + conLeft.dispatchEvent(new CustomEvent(oc_touch['left'][evt.detail.id]+'up', {detail: true})) + } else if (scheme == 'oculus-go'){ + conLeft.dispatchEvent(new CustomEvent(oc_go['left'][evt.detail.id]+'up', {detail: true})) + } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus') { + conLeft.dispatchEvent(new CustomEvent(vive['left'][evt.detail.id]+'up', {detail: true})) + } else if(scheme == 'htc-vive-focus') { + conLeft.dispatchEvent(new CustomEvent(vive_focus['left'][evt.detail.id]+'up', {detail: true})) + } else if(scheme == 'magicleap-one') { + conLeft.dispatchEvent(new CustomEvent(magic['left'][evt.detail.id]+'up', {detail: true})) + } else { + conLeft.dispatchEvent(new CustomEvent(generic['left'][evt.detail.id]+'up', {detail: true})) + } +}); + +conRight.addEventListener('buttonup', function (evt) { + if(scheme == 'windows-mixed-reality'){ + conRight.dispatchEvent(new CustomEvent(windows['right'][evt.detail.id]+'up', {detail: true})) + } else if(scheme == 'oculus-touch' || scheme == 'oculus-touch-v2' || scheme == 'oculus-touch-v3' || scheme == 'meta-quest-touch-pro') { + conRight.dispatchEvent(new CustomEvent(oc_touch['right'][evt.detail.id]+'up', {detail: true})) + } else if (scheme == 'oculus-go'){ + conRight.dispatchEvent(new CustomEvent(oc_go['right'][evt.detail.id]+'up', {detail: true})) + } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus') { + conRight.dispatchEvent(new CustomEvent(vive['right'][evt.detail.id]+'up', {detail: true})) + } else if(scheme == 'htc-vive-focus') { + conRight.dispatchEvent(new CustomEvent(vive_focus['right'][evt.detail.id]+'up', {detail: true})) + } else if(scheme == 'magicleap-one') { + conRight.dispatchEvent(new CustomEvent(magic['right'][evt.detail.id]+'up', {detail: true})) + } else { + conRight.dispatchEvent(new CustomEvent(generic['right'][evt.detail.id]+'up', {detail: true})) + } +}); + +conLeft.addEventListener('axismove', function (evt) { + if(scheme == "windows-mixed-reality"){ + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conLeft.dispatchEvent(trackpadmove) + let thumbstickmove = new CustomEvent('thumbstickmoved', { + detail: { + x: evt.detail.axis[2], + y: evt.detail.axis[3] + } + }) + conLeft.dispatchEvent(thumbstickmove) + } else if(scheme == "oculus-touch" || scheme == 'oculus-touch-v2' || scheme == 'oculus-touch-v3' || scheme == 'meta-quest-touch-pro'){ + let thumbstickmove = new CustomEvent('thumbstickmoved', { + detail: { + x: evt.detail.axis[2], + y: evt.detail.axis[3] + } + }) + conLeft.dispatchEvent(thumbstickmove) + } else if (scheme == 'oculus-go'){ + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conLeft.dispatchEvent(trackpadmove) + } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus' || scheme == 'htc-vive-focus') { + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conLeft.dispatchEvent(trackpadmove) + } else if(scheme == "magicleap-one") { + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conLeft.dispatchEvent(trackpadmove) + } else { + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conLeft.dispatchEvent(trackpadmove) + let thumbstickmove = new CustomEvent('thumbstickmoved', { + detail: { + x: evt.detail.axis[2], + y: evt.detail.axis[3] + } + }) + conLeft.dispatchEvent(thumbstickmove) + } +}); + +conRight.addEventListener('axismove', function (evt) { + if(scheme == "windows-mixed-reality"){ + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conRight.dispatchEvent(trackpadmove) + let thumbstickmove = new CustomEvent('thumbstickmoved', { + detail: { + x: evt.detail.axis[2], + y: evt.detail.axis[3] + } + }) + conRight.dispatchEvent(thumbstickmove) + } else if(scheme == "oculus-touch" || scheme == 'oculus-touch-v2' || scheme == 'oculus-touch-v3' || scheme == 'meta-quest-touch-pro'){ + let thumbstickmove = new CustomEvent('thumbstickmoved', { + detail: { + x: evt.detail.axis[2], + y: evt.detail.axis[3] + } + }) + conRight.dispatchEvent(thumbstickmove) + } else if (scheme == 'oculus-go'){ + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conRight.dispatchEvent(trackpadmove) + } else if(scheme == 'htc-vive' || scheme == 'htc-vive-focus-plus' || scheme == 'htc-vive-focus') { + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conRight.dispatchEvent(trackpadmove) + } else if(scheme == "magicleap-one") { + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conRight.dispatchEvent(trackpadmove) + } else { + let trackpadmove = new CustomEvent('trackpadmoved', { + detail: { + x: evt.detail.axis[0], + y: evt.detail.axis[1] + } + }) + conRight.dispatchEvent(trackpadmove) + let thumbstickmove = new CustomEvent('thumbstickmoved', { + detail: { + x: evt.detail.axis[2], + y: evt.detail.axis[3] + } + }) + conRight.dispatchEvent(thumbstickmove) + } +}); \ No newline at end of file diff --git a/Custom/input/keyboardInput.js b/Custom/input/keyboardInput.js new file mode 100644 index 0000000..b0d8403 --- /dev/null +++ b/Custom/input/keyboardInput.js @@ -0,0 +1,123 @@ +/* Contains code that handles keyboard input */ + +keysPressed = {ctrl: false, x: false, c: false, v: false, i: false, m: false, r: false} + +/* listens for key presses to change pattern */ +document.addEventListener('keyup', (e) => { + if (e.code === "ArrowUp"){ + if( boolAddEdit == false || block == false){ + if(isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ + return + } + if(parseInt(patternList.getAttribute('selectedIndex')) == 0){ + patternList.children[patternList.children.length-1].dispatchEvent(new Event('click',{target: patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1]})); + } else { + patternList.children[parseInt(patternList.getAttribute('selectedIndex'))-1].dispatchEvent(new Event('click',{target: patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1]})); + } + + } + } else if (e.code === "ArrowDown"){ + if( boolAddEdit == false || block == false){ + if(isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ + return + } + if(parseInt(patternList.getAttribute('selectedIndex')) == patternList.children.length-1){ + patternList.children[0].dispatchEvent(new Event('click',{target: patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1]})); + } else { + patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1].dispatchEvent(new Event('click',{target: patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1]})); + } + + } + } else if (e.key === "Control"){ + keysPressed["ctrl"] = false; + } else if(e.code === "KeyC"){ + keysPressed["c"] = false; + } else if(e.code === "KeyX"){ + keysPressed["x"] = false; + } else if(e.code === "KeyV"){ + keysPressed["v"] = false; + } else if(e.code === "KeyI"){ + keysPressed["i"] = false; + } else if(e.code === "KeyM"){ + keysPressed["m"] = false; + } else if(e.code === "KeyR"){ + keysPressed["r"] = false; + } + }); + +/* listens for key presses to change pattern */ +document.addEventListener('keydown', (e) => { + if (e.key === "Control"){ + keysPressed["ctrl"] = true; + if(keysPressed["c"]){ + // copy + copyPattern(); + } else if(keysPressed["x"]){ + // cut + cutPattern(); + + } else if(keysPressed["v"]){ + // paste + pastePattern(); + } else if(keysPressed["i"]){ + document.querySelector("#debug").style.display == 'block' ? document.querySelector("#debug").style.display = 'none' : document.querySelector("#debug").style.display = 'block' + } + } else if(e.code === "KeyC"){ + keysPressed["c"] = true; + if(keysPressed["ctrl"]){ + // copy + copyPattern(); + } + } else if(e.code === "KeyX"){ + keysPressed["x"] = true; + if(keysPressed["ctrl"]){ + // cut + cutPattern(); + } + } else if(e.code === "KeyV"){ + keysPressed["v"] = true; + if(keysPressed["ctrl"]){ + // paste + pastePattern(); + } + } else if(e.code === "KeyI"){ + keysPressed["i"] = true; + if(keysPressed["ctrl"]){ + // paste + document.querySelector("#debug").style.display == 'block' ? document.querySelector("#debug").style.display = 'none' : document.querySelector("#debug").style.display = 'block' + } + } else if(e.code === "KeyM"){ + keysPressed['m'] = true; + let i = 0; + while(i < entityCanvas.children.length){ + mov = entityCanvas.children[i].getAttribute('mov') + if(mov.status != 0){ + break; + } + i++; + } + if(i != entityCanvas.children.length){ + stopAllMovement() + } else { + let i = 0; + sum = 0; + while(i < entityCanvas.children.length){ + if((entityCanvas.children[i].getAttribute('position').x == entityCanvas.children[i].getAttribute('mov').startPoint.x && entityCanvas.children[i].getAttribute('position').y == entityCanvas.children[i].getAttribute('mov').startPoint.y && entityCanvas.children[i].getAttribute('position').z == entityCanvas.children[i].getAttribute('mov').startPoint.z) && (entityCanvas.children[i].getAttribute('rotation').x == entityCanvas.children[i].getAttribute('mov').startRotation.x && entityCanvas.children[i].getAttribute('rotation').y == entityCanvas.children[i].getAttribute('mov').startRotation.y && entityCanvas.children[i].getAttribute('rotation').z == entityCanvas.children[i].getAttribute('mov').startRotation.z)){ + sum++; + } + i++; + } + if(sum != i){ + let i = 0; + while(i < entityCanvas.children.length){ + mov = entityCanvas.children[i].getAttribute('mov') + entityCanvas.children[i].setAttribute('position',mov.startPoint) + entityCanvas.children[i].setAttribute('rotation',mov.startRotation) + i++; + } + } else { + startAllMovement() + } + } + } + }); diff --git a/Custom/load.js b/Custom/load.js index f75f00f..f0b109d 100644 --- a/Custom/load.js +++ b/Custom/load.js @@ -1,88 +1,31 @@ /* Loads in prebuilt scenes and adds them to scenes json */ -import red from './patterns/red.JSON' assert { type: "json" }; scenes['default']['red'] = red['scenes']['default'] -import green from './patterns/green.JSON' assert { type: "json" }; scenes['default']['green'] = green['scenes']['default'] -import blue from './patterns/blue.JSON' assert { type: "json" }; scenes['default']['blue'] = blue['scenes']['default'] -import white from './patterns/white.JSON' assert { type: "json" }; scenes['default']['white'] = white['scenes']['default'] -import grille from './patterns/grille.JSON' assert { type: "json" }; scenes['default']['grille'] = grille['scenes']['default'] -import line from './patterns/line.JSON' assert { type: "json" }; scenes['default']['line'] = line['scenes']['line'] -import checkerboard_w from './patterns/checkerboard_w.JSON' assert { type: "json" }; scenes['default']['checkerboard_w'] = checkerboard_w['scenes']['checkerboard_w'] -import checkerboard_b from './patterns/checkerboard_b.JSON' assert { type: "json" }; scenes['default']['checkerboard_b'] = checkerboard_b['scenes']['checkerboard_b'] -import dot_array from './patterns/dot array.JSON' assert { type: "json" }; scenes['default']['dot array'] = dot_array['scenes']['dot array'] -import circular_dot_array from './patterns/circularDotArray.JSON' assert { type: "json" }; scenes['default']['circular dot array'] = circular_dot_array['scenes']['circularDotArray'] -import crosshair from './patterns/crosshair.JSON' assert { type: "json" }; scenes['default']['crosshair'] = crosshair['scenes']['crosshair'] -/*import bullseye from './patterns/bullseye.JSON' assert { type: "json" }; -scenes['default']['bullseye'] = bullseye['scenes']['bullseye']*/ - -/*import ring_package from './patterns/pattern_package_ring_w5_10_20.JSON' assert { type: "json" }; -scenes['default']['ring_w5'] = ring_package['scenes']['ring_w5'] -scenes['default']['ring_w10'] = ring_package['scenes']['ring_w10'] -scenes['default']['ring_w20'] = ring_package['scenes']['ring_w20']*/ - -import ring_w1 from './patterns/ring_w1.JSON' assert { type: "json" }; scenes['default']['ring_w1'] = ring_w1['scenes']['ring_w1'] -import ring_w2 from './patterns/ring_w2.JSON' assert { type: "json" }; scenes['default']['ring_w2'] = ring_w2['scenes']['ring_w2'] -import ring_w5 from './patterns/ring_w5.JSON' assert { type: "json" }; scenes['default']['ring_w5'] = ring_w5['scenes']['ring_w5'] -/*let arr = Object.keys(scenes) - let len = arr.length - let i = 0; - let len2 = texture.options.length; - let currTextures = []; - while(i < len2){ - currTextures.push(texture.options[i].text) - i++; - } - i = 0; - let uploadedTextures = [] - while(i < ring_package['textures']['textureValues'].length){ - uploadedTextures.push(ring_package['textures']['textureValues'][i].text) - i++; - } - let newTextures = [...new Set([...uploadedTextures,...currTextures])] - newTextures.forEach(text => { - var option = document.createElement("option"); - if(uploadedTextures.indexOf(text) != -1 && currTextures.indexOf(text) == -1){ - option.text = ring_package['textures']['textureValues'][uploadedTextures.indexOf(text)].text - option.value = ring_package['textures']['textureValues'][uploadedTextures.indexOf(text)].val - texture.add(option); - }}); - - // combines uploaded textures in uploaded file and current file - let newUploadedTextureFormat = [...new Set([...Object.keys(uploadedTextureFormat),...Object.keys(ring_package['textures']['uploadedTextureFormats'])])] - let tmp = {} - newUploadedTextureFormat.forEach(texture => { - if(Object.keys(uploadedTextureFormat).indexOf(texture) != -1){ - tmp[texture] = uploadedTextureFormat[texture] - } else { - tmp[texture] = ring_package['textures']['uploadedTextureFormats'][texture] - } - }); - uploadedTextureFormat = tmp*/ - +scenes['default']['Flying Spot'] = flying_spot['scenes']['Flying Spot'] diff --git a/Custom/movementComponent.js b/Custom/movementComponent.js new file mode 100644 index 0000000..a8a05f9 --- /dev/null +++ b/Custom/movementComponent.js @@ -0,0 +1,221 @@ +AFRAME.registerComponent('movement', { + schema: { + origin: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, + rotationOrigin: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, + startPoints: {type: 'array', default: []}, + endPoints: {type: 'array', default: []}, + initialVelocities: {type: 'array', default: []}, + accelerations: {type: 'array', default: []}, + status: {type: 'float', default: -1}, + types: {type: 'array', default: []}, + index: {type: 'float', default: 0}, + currentVelocity: {type: 'float', default: 0}, + timeElapsed: {type: 'float', default: 0}, + }, + + init: function () { + // Do something when component first attached. + + }, + + update: function (oldData) { + // Do something when component's data is updated. + if(oldData.status != -1 && this.data.status == -1){ + // then we have been moved to the stopped status + // restore position to origin and rotation to rotationOrigin + this.el.setAttribute('position',this.data.origin) + this.el.setAttribute('rotation',this.data.rotationOrigin) + } + + }, + + remove: function () { + // Do something the component or its entity is detached. + }, + + tick: function (time, timeDelta) { + // Do something on every scene tick or frame. + if(this.data.status == 1){ + if(this.data.timeElapsed < 0){ + this.data.timeElapsed = 0 + + } else { + this.data.timeElapsed = this.data.timeElapsed + timeDelta + } + this.updatePosition() + + } else if(this.data.status == -1){ + this.data.timeElapsed = -1; + + } + + + + }, + + updatePosition: function (time, timeDelta) { + // this algorithm will ignore cylindical movement to start + let data = this.data + + let startPoint = data.startPoints[data.index] + + // get endPoint + let endPoint = data.endPoints[data.index] + + + if(startPoint.x){ + // get startPoint + let startPoint = data.startPoints[data.index] + + // get endPoint + let endPoint = data.endPoints[data.index] + + if(data.types[data.index] == 'Pause'){ + /*if(this.data.index != 0 && data.types[data.index-1] != 'Pause'){ + this.el.setAttribute('position',data.endPoints[data.index-1]) + }*/ + if(data.timeElapsed >= data.initialVelocities[data.index]){ + //this.el.setAttribute('position',endPoint) + this.data.timeElapsed = 0 + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + } + + } else if(data.types[data.index] == 'Discontinuous'){ + if(data.timeElapsed >= data.initialVelocities[data.index]){ + this.el.setAttribute('position',endPoint) + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + } + } else { + // if the current type is start-to-finish or rubberband + // these are handled the same + // get total distance between points and amount of that distance covered + xDelta = endPoint.x-startPoint.x + yDelta = endPoint.y-startPoint.y + zDelta = endPoint.z-startPoint.z + d = Math.sqrt((xDelta)*(xDelta) + (yDelta)*(yDelta) + (zDelta)*(zDelta)) + distanceCovered = (data.initialVelocities[data.index])*(data.timeElapsed/1000) + 0.5*(data.accelerations[data.index])*((data.timeElapsed/1000)**2) + amtCovered = (distanceCovered/d) + amtCovered = d ? (distanceCovered/d) : 0 + if(amtCovered > 1){ + this.data.timeElapsed = 0 + amtCovered = 0 + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + startPoint = data.startPoints[data.index] + + // get endPoint + xDelta = endPoint.x-startPoint.x + yDelta = endPoint.y-startPoint.y + zDelta = endPoint.z-startPoint.z + d = Math.sqrt((xDelta)*(xDelta) + (yDelta)*(yDelta) + (zDelta)*(zDelta)) + distanceCovered = (data.initialVelocities[data.index])*(data.timeElapsed/1000) + 0.5*(data.accelerations[data.index])*((data.timeElapsed/1000)**2) + amtCovered = (distanceCovered/d) + amtCovered = d ? (distanceCovered/d) : 0 + + + } + + + + if(this.data.types[this.data.index] != 'Pause'){ + xDelta = endPoint.x-startPoint.x + yDelta = endPoint.y-startPoint.y + zDelta = endPoint.z-startPoint.z + res = {x: startPoint.x+(xDelta*amtCovered), y: startPoint.y+(yDelta*amtCovered), z: startPoint.z+(zDelta*amtCovered)} + this.el.setAttribute('position',{x: res.x, y: res.y, z: res.z}) + } + + + } + } else { + + + if(data.types[data.index] == 'Pause'){ + /*if(this.data.index != 0 && data.types[data.index-1] != 'Pause'){ + this.el.setAttribute('position',{x: -data.endPoints[data.index-1].r*Math.sin((data.endPoints[data.index-1].theta*Math.PI)/180), y: data.endPoints[data.index-1].y, z: data.endPoints[data.index-1].r * Math.cos((data.endPoints[data.index-1].theta*Math.PI)/180)}) + } + console.log(this.el.getAttribute('position'))*/ + if(data.timeElapsed >= data.initialVelocities[data.index]){ + //this.el.setAttribute('position',endPoint) + this.data.timeElapsed = 0 + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + } + + } else if(data.types[data.index] == 'Discontinuous'){ + if(data.timeElapsed >= data.initialVelocities[data.index]){ + this.el.setAttribute('position',{x: -endPoint.r*Math.sin((endPoint.theta*Math.PI)/180), y: endPoint.y, z: endPoint.r * Math.cos((endPoint.theta*Math.PI)/180)}) + this.el.setAttribute('rotation',{x: 0, y: -endPoint.theta, z: 0}) + this.data.timeElapsed = 0 + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + + } + + + } else { + + + thetaDelta = Math.abs(endPoint.theta-startPoint.theta) + arcLen = Math.abs(thetaDelta*Math.PI/180)*Math.abs(endPoint.r); + yDelta = Math.abs(endPoint.y-startPoint.y) + d = Math.sqrt((arcLen)*(arcLen) + (yDelta)*(yDelta)) + + distanceCovered = (data.initialVelocities[data.index])*(data.timeElapsed/1000) + 0.5*(data.accelerations[data.index])*((data.timeElapsed/1000)**2) + + amtCovered = d ? (distanceCovered/d) : 0 + //console.log(amtCovered) + + + if(amtCovered > 1){ + this.data.timeElapsed = 0 + amtCovered = 0 + if(data.index < data.startPoints.length-1){ + this.data.index = this.data.index + 1 + } else { + this.data.index = 0; + } + startPoint = data.startPoints[data.index] + + // get endPoint + endPoint = data.endPoints[data.index] + thetaDelta = Math.abs(endPoint.theta-startPoint.theta) + arcLen = Math.abs(thetaDelta*Math.PI/180)*Math.abs(endPoint.r); + yDelta = Math.abs(endPoint.y-startPoint.y) + d = Math.sqrt((arcLen)*(arcLen) + (yDelta)*(yDelta)) + distanceCovered = (data.initialVelocities[data.index])*(data.timeElapsed/1000) + 0.5*(data.accelerations[data.index])*((data.timeElapsed/1000)**2) + amtCovered = (distanceCovered/d) + + + } + if(this.data.types[this.data.index] != 'Pause'){ + thetaDelta = endPoint.theta-startPoint.theta + yDelta = endPoint.y-startPoint.y + res = {theta: startPoint.theta+(thetaDelta*amtCovered), y: startPoint.y+(yDelta*amtCovered), r: startPoint.r} + + this.el.setAttribute('position',{x: -res.r*Math.sin((res.theta*Math.PI)/180), y: res.y, z: res.r * Math.cos((res.theta*Math.PI)/180)}) + this.el.setAttribute("rotation", {x: 0, y: -res.theta, z: 0}); // set rotation to be 0 + } + + } + } + + } +}); \ No newline at end of file diff --git a/Custom/packages/handlePackageUpload.js b/Custom/packages/handlePackageUpload.js new file mode 100644 index 0000000..9517ae4 --- /dev/null +++ b/Custom/packages/handlePackageUpload.js @@ -0,0 +1,227 @@ +/* + Handles processes involved once a package is uploaded +*/ + +var defaultStr = "default" + +/* if JSON is uploaded */ + +// gets the extension of the current file name +function getExtension(filename) { + var parts = filename.split('.'); + return parts[parts.length - 1]; + } + + +/* loads entities from JSON to scene */ +function entityLoader(fileContent,name,def,isSingle){ + let skip = false; + // check for json file validity + if(Object.keys(fileContent).length == 0){ + alert("Cannot parse file"); + if(isSingle){ + scene_input.value = ""; + } else { + scene_display_input.value = "" + } + return; + } + Object.keys(fileContent).forEach(key => { + if (!key.includes("scenes") && !key.includes("textures") && !key.includes("date")){ + alert("Cannot parse file"); + if(isSingle){ + scene_input.value = ""; + } else { + scene_display_input.value = "" + } + skip = true; + } + }); + if(skip){ + return; + } + + // check to see if proper amount of scenes were uploaded + if(isSingle && Object.keys(fileContent['scenes']).length != 1){ + alert("Cannot parse file, more than one scene detected"); + scene_input.value = ""; + return; + } else if(isSingle && Object.keys(fileContent['scenes']).length == 1){ + addEntitiesFromScene(fileContent['scenes'][Object.keys(fileContent['scenes'])[0]]) + updateJSON() + } else if(!isSingle){ + // adds entities from file to scene + Object.keys(fileContent['scenes']).forEach(key => { + addEntitiesFromScene(fileContent['scenes'][key]) + }) + // if this scene is the default scene + if(def){ + let len = pattern.childElementCount + let i = 0; + const obj = scenes[defaultStr] + scenes[name] = obj; + delete scenes[defaultStr]; + while(i < len){ + if(pattern.children[i].text == defaultStr){ + pattern.children[i].text = name + pattern.children[i].value = name + } + i++; + } + + len = patternDisplay.childElementCount + i = 0; + while(i < len){ + if(patternDisplay.children[i].text == defaultStr){ + patternDisplay.children[i].text = name + patternDisplay.children[i].value = name + defaultStr = name + } + i++; + } + scenes[name] = fileContent + } + } + // combines textures in uploaded file and current file + let arr = Object.keys(scenes) + let len = arr.length + let i = 0; + let len2 = texture.options.length; + currTextures = [] + while(i < len2){ + currTextures.push(texture.options[i].text) + i++; + } + i = 0; + uploadedTextures = [] + while(i < fileContent['textures']['textureValues'].length){ + uploadedTextures.push(fileContent['textures']['textureValues'][i].text) + i++; + } + newTextures = [...new Set([...uploadedTextures,...currTextures])] + newTextures.forEach(text => { + var option = document.createElement("option"); + if(uploadedTextures.indexOf(text) != -1 && currTextures.indexOf(text) == -1){ + option.text = fileContent['textures']['textureValues'][uploadedTextures.indexOf(text)].text + option.value = fileContent['textures']['textureValues'][uploadedTextures.indexOf(text)].val + texture.add(option); + } + + + }) + + // combines uploaded textures in uploaded file and current file + newUploadedTextureFormat = [...new Set([...Object.keys(uploadedTextureFormat),...Object.keys(fileContent['textures']['uploadedTextureFormats'])])] + tmp = {} + newUploadedTextureFormat.forEach(texture => { + if(Object.keys(uploadedTextureFormat).indexOf(texture) != -1){ + tmp[texture] = uploadedTextureFormat[texture] + } else { + tmp[texture] = fileContent['textures']['uploadedTextureFormats'][texture] + } + }); + uploadedTextureFormat = tmp +} + +/* adds entities from json to current scene */ +function addEntitiesFromScene(scene){ + // go through each key in the scene + Object.keys(scene).forEach(key => { + // handle adding entity similar to drawing entity + if(key.includes("sky")){ + sky.setAttribute("material",{color: scene[key].skyColor}); + colorChange = false; + $('#skyCol').minicolors('value', scene[key].skyColor); + } else if (key.includes("texture")){ + let i = 0; + $("#texture").empty(); + while( i < scene[key].vals.length){ + var option = document.createElement("option"); + option.text = scene[key].vals[i].text; + option.value = scene[key].vals[i].val; + texture.add(option); + i++; + } + } else { + el = document.createElement("a-entity"); /* create entity */ + if(key.includes("circle")){ /* circle exclusive */ + el.setAttribute("id","circle"+circleNum++); + el.setAttribute("geometry", scene[key].geometry); + el.setAttribute("fill",scene[key].fill); + } else if(key.includes("plane")){ /* plane exclusive */ + el.setAttribute("id","plane"+planeNum++); + el.setAttribute("geometry", scene[key].geometry); + if(scene[key].material.src == ""){ + drawPlaneBorder(scene[key].widthReal,scene[key].geometry.height,scene[key].fill.val,hexToRgb(scene[key].material.color),el); + } + el.setAttribute("fill",scene[key].fill); + } else if(key.includes("triangle")){ /* triangle exclusive */ + el.setAttribute("id","triangle"+triangleNum++); + el.setAttribute("geometry", scene[key].geometry); + } else if (key.includes("gradient")){ + el.setAttribute("id", "gradient"+gradientNum++); + drawGradient(scene[key].childGeometry.width,scene[key].childGeometry.height,scene[key].numBars,hexToRgb(scene[key].material.color),hexToRgb(scene[key].color2.val),el); + el.setAttribute('color2',scene[key].color2) + } else if (key.includes("grille")){ + el.setAttribute("id", "grille"+grilleNum++); + drawGrille(scene[key].childGeometry.width,scene[key].childGeometry.height,scene[key].numBars,scene[key].material.color,scene[key].color2.val,el); + el.setAttribute('color2',scene[key].color2) + } else if (key.includes("checkerboard")){ + el.setAttribute("id", "checkerboard"+checkerboardNum++); + drawCheckerboard(scene[key].rows,scene[key].cols,scene[key].tileSize,scene[key].material.color,scene[key].color2.val,el); + el.setAttribute('color2',scene[key].color2) + } else if (key.includes("circularDotarray")){ + el.setAttribute("id", "circularDotarray"+circularDotarrayNum++); + drawCircularDotArray(scene[key].arraySpacing.val,scene[key].circles,scene[key].dots,scene[key].circleSize,scene[key].material.color,scene[key].toggleCenterDot.val,el); + el.setAttribute("arraySpacing",scene[key].arraySpacing); + el.setAttribute('toggleCenterDot',scene[key].toggleCenterDot); + } else if (key.includes("dotarray")){ + el.setAttribute("id", "dotarray"+dotarrayNum++); + drawDotArray(scene[key].rows,scene[key].cols,scene[key].circleSize,scene[key].spacing.val,scene[key].material.color,scene[key].toggleCenterDot.val,el); + el.setAttribute('arraySpacing',scene[key].spacing); + el.setAttribute('toggleCenterDot',scene[key].toggleCenterDot); + } else if (key.includes("bullseye")){ + el.setAttribute("id", "bullseye"+bullseyeNum++); + drawBullseye(scene[key].ringPitch,scene[key].numRings,scene[key].material.color,el); + } else if (key.includes("text")){ + el.setAttribute("id", "text"+textNum++); + el.setAttribute("text",scene[key].text) + } else if (key.includes("timer")){ + el.setAttribute("id", "timer"+timerNum++); + el.setAttribute("text",scene[key].text) + } + /* sets stats */ + el.setAttribute("angle", scene[key].angle); + el.setAttribute("advanced", scene[key].advanced); + el.setAttribute("position", {x: scene[key].position.x, y: scene[key].position.y, z: scene[key].position.z}); + let mat = null; + if(scene[key].material){ + mat = JSON.parse(JSON.stringify(scene[key].material)) + + for(let i = 0; i < texture.options.length; i++){ + if(texture.options[i].text == mat.src){ + mat.src = "#"+texture.options[i].value; + break; + } + } + } + + + el.setAttribute("material", mat); + el.setAttribute("rotation", scene[key].rotation); + el.setAttribute("movement",JSON.parse(JSON.stringify(scene[key].movement))) + el.setAttribute("click-checker",""); + numAdded++; + entityCanvas.appendChild(el); /* adds entity to scene */ + + + /* adds option to dropdown */ + var option = document.createElement("option"); + option.text = el.getAttribute("id"); + entitySelector.add(option); + + els.push(el);/* adds entity to list of created entities */ + pool.push(el.object3D); + } + }) +} diff --git a/Custom/packages/loadDefault.js b/Custom/packages/loadDefault.js new file mode 100644 index 0000000..f0b109d --- /dev/null +++ b/Custom/packages/loadDefault.js @@ -0,0 +1,31 @@ +/* Loads in prebuilt scenes and adds them to scenes json */ + +scenes['default']['red'] = red['scenes']['default'] + +scenes['default']['green'] = green['scenes']['default'] + +scenes['default']['blue'] = blue['scenes']['default'] + +scenes['default']['white'] = white['scenes']['default'] + +scenes['default']['grille'] = grille['scenes']['default'] + +scenes['default']['line'] = line['scenes']['line'] + +scenes['default']['checkerboard_w'] = checkerboard_w['scenes']['checkerboard_w'] + +scenes['default']['checkerboard_b'] = checkerboard_b['scenes']['checkerboard_b'] + +scenes['default']['dot array'] = dot_array['scenes']['dot array'] + +scenes['default']['circular dot array'] = circular_dot_array['scenes']['circularDotArray'] + +scenes['default']['crosshair'] = crosshair['scenes']['crosshair'] + +scenes['default']['ring_w1'] = ring_w1['scenes']['ring_w1'] + +scenes['default']['ring_w2'] = ring_w2['scenes']['ring_w2'] + +scenes['default']['ring_w5'] = ring_w5['scenes']['ring_w5'] + +scenes['default']['Flying Spot'] = flying_spot['scenes']['Flying Spot'] diff --git a/Custom/packages/packageInput.js b/Custom/packages/packageInput.js new file mode 100644 index 0000000..b75ffd2 --- /dev/null +++ b/Custom/packages/packageInput.js @@ -0,0 +1,164 @@ +/* Contains code that loads in a package */ + +finished = false +var ind = 0 +var block = false +var fileName; +var myLoop; +var test; +/* if JSON is uploaded */ +scene_display_input.addEventListener("change", function() { + + myLoop = slowLoop(scene_display_input.files, (itm, idx, cb)=>{ + test = itm; + setTimeout(()=>{ + const reader = new FileReader(); + + reader.onload = function() { + fileContent = JSON.parse(reader.result); + if(!validateJSON(fileContent)){ + alert('Invalid package'); + return + } + names[fileName] = "" + for (const [name, value] of Object.entries(fileContent['scenes'])) { + const re = /^[a-zA-Z0-9-_ ]+( \([0-9]+\))?$/ + if(!re.test(name)){ + alert('Pattern name is invalid. '+name+' Limit names to only alphanumerics, -, _, or spaces.') + delete names[fileName] + return + } + currName = name.split(' (')[0]; + if(names[fileName][currName]){ + currName = currName + ' ('+names[fileName][currName]+')' + names[fileName][name.split(' (')[0]] = names[fileName][name.split(' (')[0]] + 1 + } else { + names[fileName][currName] = 1 + } + } + packageSelect.options.add(new Option(fileName,fileName)) + scenes[fileName] = fileContent['scenes'] + names[fileName] = {} + textures = fileContent['textures']['textureValues'] + uploadedTextureFormats = fileContent['textures']['uploadedTextureFormats'] + cb(); + }; + + reader.onabort = function() { + console.log('aborted') + return false; + }; + reader.addEventListener("error", (event) => { + console.log('error:') + console.log(event.currentTarget.error) + }); + + if(itm.name.split(".")[1] != "JSON"){ + alert("Invalid file type"); + scene_display_input.value = "" + return false; + } + if(Object.keys(scenes).indexOf(itm.name.split(".")[0]) != -1){ + itm.name = itm.name.split(".")[0]+"1"+itm.name.split(".")[1] + fileName = itm.name; + + } + fileName = itm.name.split(".")[0]; + const re = /^[a-zA-Z0-9-_ ]+$/ + if(!re.test(itm.name.split(".")[0])){ + alert('Package name is invalid. '+fileName+' Limit names to only alphanumerics, -, _, or spaces.') + return false; + } + if(scenes[fileName] != null){ + alert('A package with this name already exists'); + return false; + } + reader.readAsText(itm); + + + // call cb when finished + + }, 100); + + }); + + myLoop.catch(() => { + //console.log('error') + }) + // when it's done.... + myLoop.then(()=>{ + + packages[fileName] = '' + let arr = Object.keys(scenes) + let len = arr.length + let i = 0; + let len2 = texture.options.length; + currTextures = [] + while(i < len2){ + currTextures.push(texture.options[i].text) + i++; + } + i = 0; + uploadedTextures = [] + while(i < textures.length){ + uploadedTextures.push(textures[i].text) + i++; + } + newTextures = [...new Set([...uploadedTextures,...currTextures])] + newTextures.forEach(text => { + var option = document.createElement("option"); + if(uploadedTextures.indexOf(text) != -1 && currTextures.indexOf(text) == -1){ + option.text = textures[uploadedTextures.indexOf(text)].text + option.value = textures[uploadedTextures.indexOf(text)].val + texture.add(option); + } + + + }) + + + newUploadedTextureFormat = [...new Set([...Object.keys(uploadedTextureFormat),...Object.keys(uploadedTextureFormats)])] + tmp = {} + newUploadedTextureFormat.forEach(texture => { + if(Object.keys(uploadedTextureFormat).indexOf(texture) != -1){ + tmp[texture] = uploadedTextureFormat[texture] + } else { + tmp[texture] = uploadedTextureFormats[texture] + } + }); + uploadedTextureFormat = tmp + flag = false; + i = 0; + + + packageSelect.value = fileName + changePackage() + manageLocalStorage(fileName+" (upload)",scenes[fileName]) + + }); + +/** + * Execute the loopBody function once for each item in the items array, + * waiting for the done function (which is passed into the loopBody function) + * to be called before proceeding to the next item in the array. + * @param {Array} items - The array of items to iterate through + * @param {Function} loopBody - A function to execute on each item in the array. + * This function is passed 3 arguments - + * 1. The item in the current iteration, + * 2. The index of the item in the array, + * 3. A function to be called when the iteration may continue. + * @returns {Promise} - A promise that is resolved when all the items in the + * in the array have been iterated through. + */ + + // code taken from here https://stackoverflow.com/questions/48767979/javascript-how-to-wait-event-listener-end-to-next-iteration +function slowLoop(items, loopBody) { + return new Promise(f => { + done = arguments[2] || f; + idx = arguments[3] || 0; + let cb = items[idx + 1] ? () => slowLoop(items, loopBody, done, idx + 1) : done; + loopBody(items[idx], idx, cb); + }); +} + +}); \ No newline at end of file diff --git a/Custom/packages/savePackage.js b/Custom/packages/savePackage.js new file mode 100644 index 0000000..4210841 --- /dev/null +++ b/Custom/packages/savePackage.js @@ -0,0 +1,67 @@ +/* + Handles processes involved in saving scenes alone or in groups. + +*/ + +/* saves single scene in JSON format */ +function saveScene(){ + // get all of the current texture options + let i = 0; + let arr = []; + let data = {scenes: {}, textures: {uploadedTextureFormats: {}, textureValues: []}, date: ""} + while(i < texture.options.length){ + arr.push({val: texture.options[i].value, text: texture.options[i].text}); + i++; + } + // add textures and uploaded textures + data["textures"]['textureValues'] = arr; + data["textures"]['uploadedTextureFormats'] = uploadedTextureFormat; + // add single scene + data['scenes'][patternDisplay.value] = scenes[[patternDisplay.value]] + // add current date + data['date'] = new Date().toLocaleString(); + download(data,patternDisplay.value+".JSON","text/plain;charset=utf-8") +} + +/* saves package of scenes in JSON format */ +function saveSelected(){ + let useTextures = confirm("Include textures?") + let data = {scenes: {}, textures: {uploadedTextureFormats: {}, textureValues: []}, date: ""} + let i = 0; + textures = [] + // get all textures + while(i < texture.options.length){ + textures.push({val: texture.options[i].value, text: texture.options[i].text}); + i++; + } + // add textures + if(useTextures){ + data['textures']['textureValues'] = textures + data['textures']['uploadedTextureFormats'] = uploadedTextureFormat + } else { + for (const pattern of Object.keys(scenes[packageSelect.value])){ + for (const ent of Object.keys(scenes[packageSelect.value][pattern])){ + if(ent.includes('plane')){ + scenes[packageSelect.value][pattern][ent].material = {shader: scenes[packageSelect.value][pattern][ent].material.shader, color: scenes[packageSelect.value][pattern][ent].material.color, src: ''} + } + } + } + } + + // get all scenes + data['scenes'] = scenes[packageSelect.value] + + // add date + data['date'] = new Date().toLocaleString(); + download(data,packageSelect.value+".JSON","text/plain;charset=utf-8"); + pastebinPost(useTextures) +} + +// downloads a file +function download(content, fileName, contentType) { + var a = document.createElement("a"); + var file = new Blob([JSON.stringify(content, null, '\t')], {type: contentType}); + a.href = URL.createObjectURL(file); + a.download = fileName; + a.click(); +} \ No newline at end of file diff --git a/Custom/pastebinInterface.js b/Custom/pastebinInterface.js index a15fd69..7d19703 100644 --- a/Custom/pastebinInterface.js +++ b/Custom/pastebinInterface.js @@ -1,36 +1,65 @@ -// Contains all code that interfaces with the pastebin.run API +// Contains all code that interfaces with the pastebin.run API and local storage + /* On page load, fetch all packages that are contained within the link. Local storage is updated to reflect links. */ - packages = {default: ''} + window.onload = async function() { + // on page load get rid of the aframe cursor scene.canvas.classList.remove("a-grab-cursor") - let thisPage = new URL(window.location); + // if there is nothing in localStorage, instantiate an empty packages array + if(localStorage.getItem('packages') == null){ + localStorage.setItem('packages',JSON.stringify([])) + } + + // go through url and fetch desired packages + let thisPage = new URL(window.location); if(thisPage.searchParams.size != 0){ - - //let id = window.location.href.split(window.location.origin+'/Custom/index.html?id=')[1].split(',').forEach( async id => { - for (const id of thisPage.searchParams.get('id').split(',')) { - //console.log(id) - if(decodeURIComponent(id) == id){ - const res = await pastebinFetch('https://pastebin.run/'+id+'.txt',true); - } else { - const res = await pastebinFetch(decodeURIComponent(id),true); + // for each search parameter + for (const id of thisPage.searchParams.get('id').split(',')) { + //if the parameter is a pastebin id + + if(encodeURIComponent(id) == id){ + const res = await pastebinFetch('https://didsr.pythonanywhere.com/webxrtools/get?id='+id,true); + } else { + // otherwise the id is an uploaded link + const res = await pastebinFetch(decodeURIComponent(id),true); } } } - - - let i = 1; - while(i < localStorage.length){ + + + // fetch the packages array from localStorage + let localArr = JSON.parse(localStorage.getItem('packages')) + + // for each package in the array + localArr.forEach((package) => { + // get the name of the package + let key = Object.keys(package)[0] + + // add the package as an option to the recent pacakages dropdown var option = document.createElement("option"); - option.text = localStorage.getItem("key"+i) + option.text = key.split(' (')[0] + option.value = key recentPackages.add(option); - i++; - } - recentPackages.selectedIndex = -1 + + }) + recentPackages.selectedIndex = -1 // ensure that there are no selected recent packages + + // update the names dictionary for the default package on load + // done dynamically since we add/remove default patterns + let patternNames = Object.keys(scenes['default']); + patternNames.forEach((patternName) => { + // check if we have some type of name like test (1) and remove the (1) + if(patternName.split(' (').length > 1){ + patternName = patternName.split(' (')[0] + } + names['default'][patternName] = names['default'][patternName] ? names['default'][patternName] + 1 : 1; + }) + }; @@ -38,42 +67,95 @@ window.onload = async function() { Returns true on success and false on failure. */ defaultNum = 1; async function pastebinFetch(url,onload){ -var fileContent = await fetch(url).then((res) => { + + // fetch contents of JSON at url if it exists + var fileContent = await fetch(url).then((res) => { if(res.ok != true){ return null - - } else { - //console.log(url) - return res.json() + + } else { + + return res.json() } - }).catch((error) => alert('Failed to import from: '+url+' with error '+error)); + }).catch((error) => alert('Failed to import from: '+url+' with error '+error)); + // if there is no json object then alert and return if(fileContent == null){ alert('No package found') return false; } - //console.log(fileContent['filename']) - const re = /^[a-zA-Z0-9-_ ]+$/ - if(!re.test(fileContent['filename'])){ - alert('Package name is invalid. '+fileContent['filename']+' Limit names to only alphanumerics, -, _, or spaces.') + + if(!validateJSON(fileContent)){ + alert('Invalid Package') return false; } - if(packages[fileContent['filename']] != null){ - if(onload && fileContent['filename'] == 'default'){ - fileContent['filename'] = 'default ('+ defaultNum++ +')' - } else { - alert('A package with this name already exists') + + // Begin name validation + + const re = /^[a-zA-Z0-9-_ ]+$/ // checks that names contain only alphanumerics, dash, underscore, or spaces + + // filename is present for pastebin files, otherwise it is a different link + if(fileContent['filename']){ + + if(!re.test(fileContent['filename'])){ + // failed regex validation indicating the file may have been tampered with + // since this name is not possible in the first place + alert('Package name is invalid. '+fileContent['filename']+' Limit names to only alphanumerics, -, _, or spaces.') return false; } + + // check if there is already a package that has this name + if(packages[fileContent['filename']] != null){ + // get a new name and continously check validate it until it passes or user aborts + let packageName = prompt('A package with the name '+ fileContent['filename'] +' already exists. Enter a new name for the package: ') + while(packageName == null || packageName == "" || !re.test(packageName) || scenes[packageName] != null){ + if(packageName == null){ + return + } else if(packageName == ""){ + packageName = prompt("Enter a valid package name: ") + } else if(!re.test(packageName)){ + packageName = prompt('Package name is invalid. '+ packageName +' Limit names to only alphanumerics, - , _ , or spaces.') + } else if(scenes[packageName] != null){ + packageName = prompt('A package with this name already exists. Enter a new name for the package: '); + } + } + fileContent['filename'] = packageName + } + + } else { + // the link provided was not from pastebin + + // get a new name and continously check validate it until it passes or user aborts + let packageName = prompt('Enter a name for this package: ') + while(packageName == null || packageName == "" || !re.test(packageName) || scenes[packageName] != null){ + if(packageName == null){ + return + } else if(packageName == ""){ + packageName = prompt("Enter a valid package name: ") + } else if(!re.test(packageName)){ + packageName = prompt('Package name is invalid. '+ packageName +' Limit names to only alphanumerics, - , _ , or spaces.') + } else if(scenes[packageName] != null){ + packageName = prompt('A package with this name already exists. Enter a new name for the package: '); + } + } + fileContent['filename'] = packageName } - names[fileContent['filename']] = {} + + names[fileContent['filename']] = {} // create new dictionary in names object to store names of patterns in requested file + + // Validate each pattern name in the package for (const [name, value] of Object.entries(fileContent['scenes'])) { - const re = /^[a-zA-Z0-9-_ ]+( \([0-9]+\))?$/ + const re = /^[a-zA-Z0-9-_ ]+( \([0-9]+\))?$/ // regex to check for a name like test_one-2 (1) + if(!re.test(name)){ + // failed regex validation indicating the file may have been tampered with + // since this name is not possible in the first place alert('Pattern name is invalid. '+name+' Limit names to only alphanumerics, -, _, or spaces.') delete names[fileContent['filename']] return false; } + + // update name dictionary currName = name.split(' (')[0]; if(names[fileContent['filename']][currName]){ currName = currName + ' ('+names[fileContent['filename']][currName]+')' @@ -81,35 +163,35 @@ var fileContent = await fetch(url).then((res) => { } else { names[fileContent['filename']][currName] = 1 } - } - if(url.split("https://pastebin.run/").length > 1){ - let out = manageLocalStorage(fileContent['filename'] + " ("+url.split("https://pastebin.run/")[1].split(".txt")[0]+")") + } + + // handle local storage updates + if(url.split("https://didsr.pythonanywhere.com/webxrtools/get?id=").length > 1){ + // if link came from pastebin, then only save pastebin id + let out = manageLocalStorage(fileContent['filename'] + " ("+url.split("https://didsr.pythonanywhere.com/webxrtools/get?id=")[1]+")", fileContent['scenes']) if(out == false){ return false; } - packages[fileContent['filename']] = url.split("https://pastebin.run/")[1].split(".txt")[0] + packages[fileContent['filename']] = url.split("https://didsr.pythonanywhere.com/webxrtools/get?id=")[1]// save id in packages } else { - let out = manageLocalStorage(fileContent['filename'] + " ("+encodeURIComponent(url)+")") + // if link is not from pastebin, save entire link address + let out = manageLocalStorage(fileContent['filename'] + " ("+encodeURIComponent(decodeURIComponent(url))+")", fileContent['scenes']) if(out == false){ return false; } packages[fileContent['filename']] = url - } - console.log(localStorage) - + } scenes[fileContent['filename']] = fileContent['scenes'] + // update list of textures + textures = fileContent['textures']['textureValues'] // texture images + uploadedTextureFormats = fileContent['textures']['uploadedTextureFormats'] // texture aspect ratios - - textures = fileContent['textures']['textureValues'] - uploadedTextureFormats = fileContent['textures']['uploadedTextureFormats'] - - let arr = Object.keys(scenes) - let len = arr.length + // merge incoming and existing textures let i = 0; - let len2 = texture.options.length; + let len = texture.options.length; currTextures = [] - while(i < len2){ + while(i < len){ currTextures.push(texture.options[i].text) i++; } @@ -127,11 +209,9 @@ var fileContent = await fetch(url).then((res) => { option.value = textures[uploadedTextures.indexOf(text)].val texture.add(option); } - - }) - + // merge incoming and existing texture formats newUploadedTextureFormat = [...new Set([...Object.keys(uploadedTextureFormat),...Object.keys(uploadedTextureFormats)])] tmp = {} newUploadedTextureFormat.forEach(texture => { @@ -143,28 +223,22 @@ var fileContent = await fetch(url).then((res) => { }); uploadedTextureFormat = tmp flag = false; - i = 0; - while(i < packageSelect.options.length){ - if(packageSelect.options[i].value == fileContent['filename']){ - alert('A package with this name already exists'); - return false; - } - i++; - } + // add option to packageSelect packageSelect.options.add(new Option(fileContent['filename'],fileContent['filename'])) packageSelect.value = fileContent['filename'] - changePackage() - return true; + + changePackage() // invokes the function change packages to the uploaded one + return true; // returns success } -// function to compress textures (unused) +// function to compress textures (unused right now) function compressTextures(textures){ textures.forEach( texture => { if(texture['val'].split("url(data:image/png;base64").length > 1){ // compress texture and post it // save id as pastebin() - //let compressed = LZString.compressToBase64(texture.val.split(',')[1].split(')')[0]) + let compressed = LZString.compressToBase64(texture.val.split(',')[1].split(')')[0]) let code = {name: texture['name'], val: texture.val.split(',')[1].split(')')[0]} //console.log(texture.val.split(',')[1].split(')')[0]) fetch('https://pastebin.run/api/v1/pastes', { @@ -186,53 +260,69 @@ function compressTextures(textures){ }).catch((error) => alert('Failed to import from: '+url+' with error '+error)) } + /* Posts content to pastebin. Navigates to new link on success and returns false on failure. */ async function pastebinPost(useTextures){ - textures = [] - // get all textures - i = 0; - while(i < texture.options.length){ - textures.push({val: texture.options[i].value, text: texture.options[i].text}); - i++; - } - let code = {filename: packageSelect.value, scenes: {}, textures: {uploadedTextureFormats: {}, textureValues: []}, date: ""} - - //let compressedTextures = compressTextures(textures); + // create object to be posted + let code = {filename: packageSelect.value, scenes: JSON.parse(JSON.stringify(scenes[packageSelect.value])), textures: {uploadedTextureFormats: {}, textureValues: []}, date: ""} + + // if we want to save textures if(useTextures){ + // get all textures + textures = [] + i = 0; + while(i < texture.options.length){ + textures.push({val: texture.options[i].value, text: texture.options[i].text}); + i++; + } code['textures']['textureValues'] = textures code['textures']['uploadedTextureFormats'] = uploadedTextureFormat } else { - for (const pattern of Object.keys(scenes[packageSelect.value])){ - for (const ent of Object.keys(scenes[packageSelect.value][pattern])){ + // clear all textures + for (const pattern of Object.keys(code['scenes'])){ + for (const ent of Object.keys(code['scenes'][pattern])){ if(ent.includes('plane')){ - scenes[packageSelect.value][pattern][ent].material = {shader: scenes[packageSelect.value][pattern][ent].material.shader, color: scenes[packageSelect.value][pattern][ent].material.color, src: ''} + code['scenes'][pattern][ent].material = {shader: code['scenes'][pattern][ent].material.shader, color: code['scenes'][pattern][ent].material.color, src: ''} } } } } - code['scenes'] = scenes[packageSelect.value] - // add date + // save date code['date'] = new Date().toLocaleString(); - const size = new TextEncoder().encode(JSON.stringify(code)).length - if(size >= 9995){ + + // check size of package to ensure it can be posted + const size = new TextEncoder().encode(JSON.stringify(code)).length; + /*if(size > 100000){ + // if too large then alert and abort alert('Package is too large, no link can be generated'); + return false - } - await fetch('https://pastebin.run/api/v1/pastes', { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' - }, - body: "code="+JSON.stringify(code) -}) - .then(response => {return response.text()}).then( async function (text) { + }*/ + + // attempt to post package + let response = await fetch('https://didsr.pythonanywhere.com/webxrtools/share', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(code) + }).catch((error) => alert('Failed to post with error:\n '+error)); + + if(!response.ok){ + alert('Failed to post with error:\n '+response.status+"\n"+response.statusText) + return + } + + let text = await response.text(); + try { + // update the link and copy the code to the clipboard let thisPage = new URL(window.location); newUrl = thisPage.origin+thisPage.pathname+"?id="+text - manageLocalStorage(packageSelect.value+" ("+text+")"); + // store the package in local storage + manageLocalStorage(packageSelect.value+" ("+text+")", scenes[packageSelect.value]); tmp = packages[packageSelect.value] packages[packageSelect.value] = text await navigator.clipboard.writeText(newUrl); @@ -244,74 +334,175 @@ async function pastebinPost(useTextures){ let newURL = ''; if(tmp != null && tmp != ''){ newURL = window.location.href.replace(tmp,text) - console.log('test') } else { newURL = window.location.href + "," +text } - console.log(newURL) window.history.pushState('object', document.title, newURL); } } catch (err) { console.error('Failed to copy: ', err); return false } - }).catch((error) => alert('Failed to post with error '+error)) -} + } + /* Adds packages to local storage. Returns true on success and throws an error on failure. */ -function manageLocalStorage(key){ - //console.log(key) - let i = localStorage.length < 11 ? localStorage.length : 10; - while(i > 0){ - localStorage.setItem("key"+i,localStorage.getItem("key"+(i-1))) - i--; +function manageLocalStorage(key, value){ + + // compress the package content + value = LZString.compressToBase64(JSON.stringify(value)) + + // get the contents of localStorage + let localScenes = JSON.parse(localStorage.getItem('packages')) + + // check if replacing existing package + let ind = null; + let j = 0; + localScenes.forEach((package) => { + let name = Object.keys(package)[0] + if(encodeURIComponent(key.split(' (')[0]) == key.split(' (')[0]){ + // check for exact name match + if(name.split(' (')[0] === key.split(' (')[0]){ + ind = j + } + } else { + // check for link match + if(name.includes(key.split('(')[1].split(')')[0])){ + ind = j + } + } + j++; + }) + + + + sum = 0; + let max = localScenes.length-1 + for(let i = 0; i < max; i++){ + if(ind && i == ind){ + continue; + } + sum += ByteSize(localScenes[i]) } - localStorage.setItem("key1",key) - i = 2; - while(i < localStorage.length){ - if(localStorage.getItem("key"+i) == key){ - // delete current entry and shift everything down - while(i < localStorage.length-1){ - localStorage.setItem("key"+i,localStorage.getItem("key"+(i+1))) - i++; + + + // create the object + let packageToInsert = {} + packageToInsert[key] = value + // strinify the object + let stringified = JSON.stringify(packageToInsert) + + // check if package is larger than localStorage space allocation + // 2 comes from the opening and closing braces [] + if(ByteSize(stringified) >= 5242880-2){ + alert('The package '+key+' is too large to save.') + return false + } + + // if we have space + + // check if we are replacing an existing package with a new one + if(ind != null){ + // prompt if this is ok + let allowDelete = confirm('This process will replace the saved package: \n'+ Object.keys(localScenes[ind])[0]+'\nPress OK to confirm.') + if(!allowDelete){ + return true + } + localScenes.splice(ind,1) + max--; + } else { + let deleteCount = 0 + if(localScenes.length == 20 && sum + 2 + ByteSize(stringified) < 5242880){ + // insert without deleting + deleteCount++; + max--; + } else if(sum + 2 + ByteSize(stringified) >= 5242880){ + // check how many we would need to delete + // not optimal + while(sum + 2 + ByteSize(stringified) >= 5242880){ + sum -= ByteSize(JSON.stringify(localScenes[max])) + deleteCount++; + max--; } - localStorage.removeItem("key"+(localStorage.length-1)) - break; + } - i++; + + // check if user permits delete and then proceed + if(deleteCount > 0){ + let allowDelete = confirm('This process will delete '+ deleteCount +' previously saved packages. Press OK to confirm.') + if(!allowDelete){ + return true + } + localScenes.splice(max+1,deleteCount) + } + } - //console.log(localStorage) - return true; + + // add package to the beginning of localStorage array + localScenes.unshift(packageToInsert) + localStorage.setItem('packages',JSON.stringify(localScenes)) + + return true; // return success } -// Called when a package is selected from the local storage options +// Called when a package is selected from one of the local storage options async function changeUrl(){ if(recentPackages.value == "none"){ return } + // only 10 packages allowed in the browser at once if(packageSelect.options.length == 10){ alert('There are already 10 packages.') return; } - let url = recentPackages.value.split('(')[1].split(')')[0]; - if(decodeURIComponent(url) != url){ - url = decodeURIComponent(url) - } else { - url = 'https://pastebin.run/'+url+'.txt' + + // get contents of local storage + let localArr = JSON.parse(localStorage.getItem('packages')) + + // get the desired package from localStorage + let key = Object.keys(localArr[recentPackages.selectedIndex])[0] + + // prompt user for a name for the package and validate the input + let packageName = prompt("Enter a name for this package: ", key) + const re = /^[a-zA-Z0-9-_ ]+$/ + while(packageName == null || packageName == "" || !re.test(packageName) || scenes[packageName] != null){ + if(packageName == null){ + return + } else if(packageName == ""){ + packageName = prompt("Enter a valid package name: ") + } else if(!re.test(packageName)){ + packageName = prompt('Package name is invalid. Limit names to only alphanumerics, - , _ , or spaces.') + } else if(scenes[packageName] != null){ + packageName = prompt('A pattern with this name already exists'); + } + } - await pastebinFetch(url).then( res => { - if(res){ - if(!window.location.href.includes('?')){ - let newURL = window.location.href + "?id=" +recentPackages.value.split('(')[1].split(')')[0] - window.history.pushState('object', document.title, newURL); - } else { - let newURL = window.location.href + "," +recentPackages.value.split('(')[1].split(')')[0] - window.history.pushState('object', document.title, newURL); - } - } - }) + // add the selected package to the options list + packageSelect.options.add(new Option(packageName,packageName)) + packages[packageName] = '' + scenes[packageName] = JSON.parse(LZString.decompressFromBase64(localArr[recentPackages.selectedIndex][key])) + packageSelect.value = packageName + + // update the link + if(!key.includes('(upload)') && !window.location.href.includes(key.split('(')[1].split(')')[0])){ + if(!window.location.href.includes('?')){ + let newURL = window.location.href + "?id=" +key.split('(')[1].split(')')[0] + window.history.pushState('object', document.title, newURL); + } else { + let newURL = ''; + newURL = window.location.href + "," +key.split('(')[1].split(')')[0] + window.history.pushState('object', document.title, newURL); + } + } + + changePackage(); // invoke the function to change packages to the selected option +} + +// condensed function to get size of a string +function ByteSize(str){ + return new Blob([str]).size } diff --git a/Custom/patterns/Flying_Spot.JSON b/Custom/patterns/Flying_Spot.JSON new file mode 100644 index 0000000..7ee88f5 --- /dev/null +++ b/Custom/patterns/Flying_Spot.JSON @@ -0,0 +1,3326 @@ +{ + "scenes": { + "Flying Spot": { + "sky": { + "skyColor": "#000000" + }, + "circle0": { + "advanced": { + "val": false + }, + "angle": { + "x": 20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -85.50503583141717, + "y": -80, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 20, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -85.50503583141717, + "y": -80, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": 20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle1": { + "advanced": { + "val": false + }, + "angle": { + "x": 10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -43.412044416732584, + "y": -80, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 10, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -43.412044416732584, + "y": -80, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": 10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle2": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 0, + "y": -80, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 0, + "y": -80, + "z": -250 + }, + "rotationOrigin": { + "x": 0, + "y": 0, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle3": { + "advanced": { + "val": false + }, + "angle": { + "x": -10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 43.412044416732584, + "y": -80, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -10, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 43.412044416732584, + "y": -80, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": -10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle4": { + "advanced": { + "val": false + }, + "angle": { + "x": -20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 85.50503583141717, + "y": -80, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -20, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 85.50503583141717, + "y": -80, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": -20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle5": { + "advanced": { + "val": false + }, + "angle": { + "x": 20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -85.50503583141717, + "y": 80, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 20, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -85.50503583141717, + "y": 80, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": 20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle6": { + "advanced": { + "val": false + }, + "angle": { + "x": 10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -43.412044416732584, + "y": 80, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 10, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -43.412044416732584, + "y": 80, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": 10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle7": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 0, + "y": 80, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 0, + "y": 80, + "z": -250 + }, + "rotationOrigin": { + "x": 0, + "y": 0, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle8": { + "advanced": { + "val": false + }, + "angle": { + "x": -10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 43.412044416732584, + "y": 80, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -10, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 43.412044416732584, + "y": 80, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": -10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle9": { + "advanced": { + "val": false + }, + "angle": { + "x": -20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 85.50503583141717, + "y": 80, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -20, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 85.50503583141717, + "y": 80, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": -20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle10": { + "advanced": { + "val": false + }, + "angle": { + "x": 20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -85.50503583141717, + "y": 40, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -20, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": -20, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -85.50503583141717, + "y": 40, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": 20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle11": { + "advanced": { + "val": false + }, + "angle": { + "x": 10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -43.412044416732584, + "y": 40, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -10, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": -10, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -43.412044416732584, + "y": 40, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": 10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle12": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 0, + "y": 40, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 0, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": 0, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 0, + "y": 40, + "z": -250 + }, + "rotationOrigin": { + "x": 0, + "y": 0, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle13": { + "advanced": { + "val": false + }, + "angle": { + "x": -10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 43.412044416732584, + "y": 40, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 10, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": 10, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 43.412044416732584, + "y": 40, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": -10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle14": { + "advanced": { + "val": false + }, + "angle": { + "x": -20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 85.50503583141717, + "y": 40, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 20, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": 20, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 85.50503583141717, + "y": 40, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": -20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle15": { + "advanced": { + "val": false + }, + "angle": { + "x": 20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -85.50503583141717, + "y": 0, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -20, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": -20, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -85.50503583141717, + "y": 0, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": 20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle16": { + "advanced": { + "val": false + }, + "angle": { + "x": 10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -43.412044416732584, + "y": 0, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -10, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": -10, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -43.412044416732584, + "y": 0, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": 10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle17": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 0, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": 0, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 0, + "y": 0, + "z": -250 + }, + "rotationOrigin": { + "x": 0, + "y": 0, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle18": { + "advanced": { + "val": false + }, + "angle": { + "x": -10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 43.412044416732584, + "y": 0, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 10, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": 10, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 43.412044416732584, + "y": 0, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": -10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle19": { + "advanced": { + "val": false + }, + "angle": { + "x": -20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 85.50503583141717, + "y": 0, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 20, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": 20, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 85.50503583141717, + "y": 0, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": -20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle20": { + "advanced": { + "val": false + }, + "angle": { + "x": 20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -85.50503583141717, + "y": -40, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -20, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": -20, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -85.50503583141717, + "y": -40, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": 20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle21": { + "advanced": { + "val": false + }, + "angle": { + "x": 10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -43.412044416732584, + "y": -40, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -10, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": -10, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -43.412044416732584, + "y": -40, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": 10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle22": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 0, + "y": -40, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 0, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": 0, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 0, + "y": -40, + "z": -250 + }, + "rotationOrigin": { + "x": 0, + "y": 0, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle23": { + "advanced": { + "val": false + }, + "angle": { + "x": -10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 43.412044416732584, + "y": -40, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 10, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": 10, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 43.412044416732584, + "y": -40, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": -10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle24": { + "advanced": { + "val": false + }, + "angle": { + "x": -20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 85.50503583141717, + "y": -40, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 20, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": 20, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 85.50503583141717, + "y": -40, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": -20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle26": { + "advanced": { + "val": false + }, + "angle": { + "x": 15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -64.70476127563019, + "y": 80, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 15, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -64.70476127563019, + "y": 80, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": 15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle27": { + "advanced": { + "val": false + }, + "angle": { + "x": 15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -64.70476127563019, + "y": 40, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -15, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": -15, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -64.70476127563019, + "y": 40, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": 15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle28": { + "advanced": { + "val": false + }, + "angle": { + "x": 15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -64.70476127563019, + "y": 0, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -15, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": -15, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -64.70476127563019, + "y": 0, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": 15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle29": { + "advanced": { + "val": false + }, + "angle": { + "x": 15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -64.70476127563019, + "y": -40, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -15, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": -15, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -64.70476127563019, + "y": -40, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": 15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle30": { + "advanced": { + "val": false + }, + "angle": { + "x": 15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -64.70476127563019, + "y": -80, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 15, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -64.70476127563019, + "y": -80, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": 15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle31": { + "advanced": { + "val": false + }, + "angle": { + "x": 5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -21.78893568691454, + "y": 80, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 5, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -21.78893568691454, + "y": 80, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": 5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle32": { + "advanced": { + "val": false + }, + "angle": { + "x": 5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -21.78893568691454, + "y": 40, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -5, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": -5, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -21.78893568691454, + "y": 40, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": 5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle33": { + "advanced": { + "val": false + }, + "angle": { + "x": 5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -21.78893568691454, + "y": 0, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -5, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": -5, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -21.78893568691454, + "y": 0, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": 5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle34": { + "advanced": { + "val": false + }, + "angle": { + "x": 5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -21.78893568691454, + "y": -40, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -5, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": -5, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -21.78893568691454, + "y": -40, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": 5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle35": { + "advanced": { + "val": false + }, + "angle": { + "x": 5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -21.78893568691454, + "y": -80, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 5, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -21.78893568691454, + "y": -80, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": 5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle36": { + "advanced": { + "val": false + }, + "angle": { + "x": -5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 21.78893568691454, + "y": 80, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -5, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 21.78893568691454, + "y": 80, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": -5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle37": { + "advanced": { + "val": false + }, + "angle": { + "x": -5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 21.78893568691454, + "y": 40, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 5, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": 5, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 21.78893568691454, + "y": 40, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": -5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle38": { + "advanced": { + "val": false + }, + "angle": { + "x": -5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 21.78893568691454, + "y": 0, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 5, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": 5, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 21.78893568691454, + "y": 0, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": -5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle39": { + "advanced": { + "val": false + }, + "angle": { + "x": -5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 21.78893568691454, + "y": -40, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 5, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": 5, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 21.78893568691454, + "y": -40, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": -5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle40": { + "advanced": { + "val": false + }, + "angle": { + "x": -5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 21.78893568691454, + "y": -80, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -5, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 21.78893568691454, + "y": -80, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": -5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle41": { + "advanced": { + "val": false + }, + "angle": { + "x": -15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 64.70476127563019, + "y": 80, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -15, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 64.70476127563019, + "y": 80, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": -15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle42": { + "advanced": { + "val": false + }, + "angle": { + "x": -15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 64.70476127563019, + "y": 40, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 15, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": 15, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 64.70476127563019, + "y": 40, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": -15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle43": { + "advanced": { + "val": false + }, + "angle": { + "x": -15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 64.70476127563019, + "y": 0, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 15, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": 15, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 64.70476127563019, + "y": 0, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": -15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle44": { + "advanced": { + "val": false + }, + "angle": { + "x": -15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 64.70476127563019, + "y": -40, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 15, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": 15, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 64.70476127563019, + "y": -40, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": -15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle45": { + "advanced": { + "val": false + }, + "angle": { + "x": -15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 64.70476127563019, + "y": -80, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": -15, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 64.70476127563019, + "y": -80, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": -15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [] + }, + "date": "1/19/2024, 12:41:28 PM" +} \ No newline at end of file diff --git a/Custom/patterns/blue.js b/Custom/patterns/blue.js new file mode 100644 index 0000000..7e7fc74 --- /dev/null +++ b/Custom/patterns/blue.js @@ -0,0 +1,47 @@ +var blue = { + "scenes": { + "default": { + "sky": { + "skyColor": "#0000ff" + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/bullseye.JSON b/Custom/patterns/bullseye.JSON index 424cd59..2ad3852 100644 --- a/Custom/patterns/bullseye.JSON +++ b/Custom/patterns/bullseye.JSON @@ -21,15 +21,28 @@ }, "material": { "shader": "flat", - "color": "#ffffff", - "src": "" + "color": "#ffffff" }, "rotation": { "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } + } }, "textures": { diff --git a/Custom/patterns/bullseye.js b/Custom/patterns/bullseye.js new file mode 100644 index 0000000..e01ed31 --- /dev/null +++ b/Custom/patterns/bullseye.js @@ -0,0 +1,86 @@ +var bullseye = { + "scenes": { + "bullseye": { + "sky": { + "skyColor": "#000000" + }, + "bullseye0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "numRings": 30, + "ringPitch": 5, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/checkerboard_b.JSON b/Custom/patterns/checkerboard_b.JSON index 4959ab8..b618c81 100644 --- a/Custom/patterns/checkerboard_b.JSON +++ b/Custom/patterns/checkerboard_b.JSON @@ -25,13 +25,25 @@ }, "material": { "shader": "flat", - "color": "#000000", - "src": "" + "color": "#000000" }, "rotation": { "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } } diff --git a/Custom/patterns/checkerboard_b.js b/Custom/patterns/checkerboard_b.js new file mode 100644 index 0000000..f07ba58 --- /dev/null +++ b/Custom/patterns/checkerboard_b.js @@ -0,0 +1,89 @@ +var checkerboard_b = { + "scenes": { + "checkerboard_b": { + "sky": { + "skyColor": "#000000" + }, + "checkerboard0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "rows": 17, + "cols": 17, + "tileSize": 10, + "color2": { + "val": "#ffffff" + }, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#000000" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/checkerboard_w.JSON b/Custom/patterns/checkerboard_w.JSON index 8070d6f..44bcf16 100644 --- a/Custom/patterns/checkerboard_w.JSON +++ b/Custom/patterns/checkerboard_w.JSON @@ -25,13 +25,25 @@ }, "material": { "shader": "flat", - "color": "#ffffff", - "src": "" + "color": "#ffffff" }, "rotation": { "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } } diff --git a/Custom/patterns/checkerboard_w.js b/Custom/patterns/checkerboard_w.js new file mode 100644 index 0000000..d3fbbc0 --- /dev/null +++ b/Custom/patterns/checkerboard_w.js @@ -0,0 +1,89 @@ +var checkerboard_w = { + "scenes": { + "checkerboard_w": { + "sky": { + "skyColor": "#000000" + }, + "checkerboard0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "rows": 17, + "cols": 17, + "tileSize": 10, + "color2": { + "val": "#000000" + }, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/circularDotArray.JSON b/Custom/patterns/circularDotArray.JSON index 4620b03..09a5aca 100644 --- a/Custom/patterns/circularDotArray.JSON +++ b/Custom/patterns/circularDotArray.JSON @@ -26,13 +26,25 @@ }, "material": { "shader": "flat", - "color": "#ffffff", - "src": "" + "color": "#ffffff" }, "rotation": { "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } } diff --git a/Custom/patterns/circularDotArray.js b/Custom/patterns/circularDotArray.js new file mode 100644 index 0000000..5d9eea5 --- /dev/null +++ b/Custom/patterns/circularDotArray.js @@ -0,0 +1,90 @@ +var circular_dot_array = { + "scenes": { + "circularDotArray": { + "sky": { + "skyColor": "#000000" + }, + "circularDotarray0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "toggleCenterDot": {"val": false}, + "circles": 10, + "dots": 20, + "arraySpacing": { + "val": 15 + }, + "circleSize": 1.5, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/crosshair.JSON b/Custom/patterns/crosshair.JSON index c90171a..8b85553 100644 --- a/Custom/patterns/crosshair.JSON +++ b/Custom/patterns/crosshair.JSON @@ -36,6 +36,19 @@ "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } }, "plane1": { @@ -70,6 +83,19 @@ "x": 0, "y": 0, "z": 90 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":90}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } } diff --git a/Custom/patterns/crosshair.js b/Custom/patterns/crosshair.js new file mode 100644 index 0000000..f54c842 --- /dev/null +++ b/Custom/patterns/crosshair.js @@ -0,0 +1,141 @@ +var crosshair = { + "scenes": { + "crosshair": { + "sky": { + "skyColor": "#000000" + }, + "plane0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "widthReal": 1, + "fill": { + "val": 1, + "isFull": true + }, + "geometry": { + "primitive": "plane", + "width": 0, + "height": 15 + }, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + }, + "plane1": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "widthReal": 1, + "fill": { + "val": 1, + "isFull": true + }, + "geometry": { + "primitive": "plane", + "width": 0, + "height": 15 + }, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 90 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":90}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/dot array.JSON b/Custom/patterns/dot array.JSON index bf4a9b2..1bf6b90 100644 --- a/Custom/patterns/dot array.JSON +++ b/Custom/patterns/dot array.JSON @@ -26,13 +26,25 @@ }, "material": { "shader": "flat", - "color": "#ffffff", - "src": "" + "color": "#ffffff" }, "rotation": { "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } } diff --git a/Custom/patterns/dot array.js b/Custom/patterns/dot array.js new file mode 100644 index 0000000..5709f22 --- /dev/null +++ b/Custom/patterns/dot array.js @@ -0,0 +1,90 @@ +var dot_array = { + "scenes": { + "dot array": { + "sky": { + "skyColor": "#000000" + }, + "dotarray0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "toggleCenterDot": {"val": false}, + "rows": 21, + "cols": 21, + "circleSize": 2, + "spacing": { + "val": 3 + }, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/flying_spot.js b/Custom/patterns/flying_spot.js new file mode 100644 index 0000000..c610555 --- /dev/null +++ b/Custom/patterns/flying_spot.js @@ -0,0 +1,3283 @@ +// this one is a little long + +var flying_spot = { + "scenes": { + "Flying Spot": { + "sky": { + "skyColor": "#000000" + }, + "circle0": { + "advanced": { + "val": false + }, + "angle": { + "x": 20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -85.50503583141717, + "y": -80, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 20, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -85.50503583141717, + "y": -80, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": 20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle1": { + "advanced": { + "val": false + }, + "angle": { + "x": 10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -43.412044416732584, + "y": -80, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 10, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -43.412044416732584, + "y": -80, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": 10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle2": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 0, + "y": -80, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 0, + "y": -80, + "z": -250 + }, + "rotationOrigin": { + "x": 0, + "y": 0, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle3": { + "advanced": { + "val": false + }, + "angle": { + "x": -10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 43.412044416732584, + "y": -80, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -10, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 43.412044416732584, + "y": -80, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": -10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle4": { + "advanced": { + "val": false + }, + "angle": { + "x": -20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 85.50503583141717, + "y": -80, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -20, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 85.50503583141717, + "y": -80, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": -20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle5": { + "advanced": { + "val": false + }, + "angle": { + "x": 20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -85.50503583141717, + "y": 80, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 20, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -85.50503583141717, + "y": 80, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": 20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle6": { + "advanced": { + "val": false + }, + "angle": { + "x": 10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -43.412044416732584, + "y": 80, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 10, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -43.412044416732584, + "y": 80, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": 10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle7": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 0, + "y": 80, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 0, + "y": 80, + "z": -250 + }, + "rotationOrigin": { + "x": 0, + "y": 0, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle8": { + "advanced": { + "val": false + }, + "angle": { + "x": -10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 43.412044416732584, + "y": 80, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -10, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 43.412044416732584, + "y": 80, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": -10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle9": { + "advanced": { + "val": false + }, + "angle": { + "x": -20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 85.50503583141717, + "y": 80, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -20, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 85.50503583141717, + "y": 80, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": -20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle10": { + "advanced": { + "val": false + }, + "angle": { + "x": 20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -85.50503583141717, + "y": 40, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -20, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": -20, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -85.50503583141717, + "y": 40, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": 20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle11": { + "advanced": { + "val": false + }, + "angle": { + "x": 10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -43.412044416732584, + "y": 40, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -10, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": -10, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -43.412044416732584, + "y": 40, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": 10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle12": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 0, + "y": 40, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 0, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": 0, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 0, + "y": 40, + "z": -250 + }, + "rotationOrigin": { + "x": 0, + "y": 0, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle13": { + "advanced": { + "val": false + }, + "angle": { + "x": -10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 43.412044416732584, + "y": 40, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 10, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": 10, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 43.412044416732584, + "y": 40, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": -10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle14": { + "advanced": { + "val": false + }, + "angle": { + "x": -20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 85.50503583141717, + "y": 40, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 20, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": 20, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 85.50503583141717, + "y": 40, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": -20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle15": { + "advanced": { + "val": false + }, + "angle": { + "x": 20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -85.50503583141717, + "y": 0, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -20, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": -20, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -85.50503583141717, + "y": 0, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": 20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle16": { + "advanced": { + "val": false + }, + "angle": { + "x": 10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -43.412044416732584, + "y": 0, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -10, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": -10, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -43.412044416732584, + "y": 0, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": 10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle17": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 0, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": 0, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 0, + "y": 0, + "z": -250 + }, + "rotationOrigin": { + "x": 0, + "y": 0, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle18": { + "advanced": { + "val": false + }, + "angle": { + "x": -10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 43.412044416732584, + "y": 0, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 10, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": 10, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 43.412044416732584, + "y": 0, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": -10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle19": { + "advanced": { + "val": false + }, + "angle": { + "x": -20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 85.50503583141717, + "y": 0, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 20, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": 20, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 85.50503583141717, + "y": 0, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": -20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle20": { + "advanced": { + "val": false + }, + "angle": { + "x": 20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -85.50503583141717, + "y": -40, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -20, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": -20, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -85.50503583141717, + "y": -40, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": 20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle21": { + "advanced": { + "val": false + }, + "angle": { + "x": 10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -43.412044416732584, + "y": -40, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -10, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": -10, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -43.412044416732584, + "y": -40, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": 10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle22": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 0, + "y": -40, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 0, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": 0, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 0, + "y": -40, + "z": -250 + }, + "rotationOrigin": { + "x": 0, + "y": 0, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle23": { + "advanced": { + "val": false + }, + "angle": { + "x": -10, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 43.412044416732584, + "y": -40, + "z": -246.201938253052 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -10, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 10, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": 10, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 43.412044416732584, + "y": -40, + "z": -246.201938253052 + }, + "rotationOrigin": { + "x": 0, + "y": -10, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle24": { + "advanced": { + "val": false + }, + "angle": { + "x": -20, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 85.50503583141717, + "y": -40, + "z": -234.9231551964771 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -20, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 20, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": 20, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 85.50503583141717, + "y": -40, + "z": -234.9231551964771 + }, + "rotationOrigin": { + "x": 0, + "y": -20, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle26": { + "advanced": { + "val": false + }, + "angle": { + "x": 15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -64.70476127563019, + "y": 80, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 15, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -64.70476127563019, + "y": 80, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": 15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle27": { + "advanced": { + "val": false + }, + "angle": { + "x": 15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -64.70476127563019, + "y": 40, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -15, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": -15, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -64.70476127563019, + "y": 40, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": 15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle28": { + "advanced": { + "val": false + }, + "angle": { + "x": 15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -64.70476127563019, + "y": 0, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -15, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": -15, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -64.70476127563019, + "y": 0, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": 15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle29": { + "advanced": { + "val": false + }, + "angle": { + "x": 15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -64.70476127563019, + "y": -40, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -15, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": -15, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -64.70476127563019, + "y": -40, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": 15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle30": { + "advanced": { + "val": false + }, + "angle": { + "x": 15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -64.70476127563019, + "y": -80, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 15, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -64.70476127563019, + "y": -80, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": 15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle31": { + "advanced": { + "val": false + }, + "angle": { + "x": 5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -21.78893568691454, + "y": 80, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 5, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -21.78893568691454, + "y": 80, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": 5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle32": { + "advanced": { + "val": false + }, + "angle": { + "x": 5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -21.78893568691454, + "y": 40, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -5, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": -5, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -21.78893568691454, + "y": 40, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": 5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle33": { + "advanced": { + "val": false + }, + "angle": { + "x": 5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -21.78893568691454, + "y": 0, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -5, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": -5, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -21.78893568691454, + "y": 0, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": 5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle34": { + "advanced": { + "val": false + }, + "angle": { + "x": 5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -21.78893568691454, + "y": -40, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": -5, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": -5, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": -21.78893568691454, + "y": -40, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": 5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle35": { + "advanced": { + "val": false + }, + "angle": { + "x": 5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": -21.78893568691454, + "y": -80, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 5, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": -21.78893568691454, + "y": -80, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": 5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle36": { + "advanced": { + "val": false + }, + "angle": { + "x": -5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 21.78893568691454, + "y": 80, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -5, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 21.78893568691454, + "y": 80, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": -5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle37": { + "advanced": { + "val": false + }, + "angle": { + "x": -5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 21.78893568691454, + "y": 40, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 5, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": 5, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 21.78893568691454, + "y": 40, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": -5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle38": { + "advanced": { + "val": false + }, + "angle": { + "x": -5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 21.78893568691454, + "y": 0, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 5, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": 5, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 21.78893568691454, + "y": 0, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": -5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle39": { + "advanced": { + "val": false + }, + "angle": { + "x": -5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 21.78893568691454, + "y": -40, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -5, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 5, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": 5, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 21.78893568691454, + "y": -40, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": -5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle40": { + "advanced": { + "val": false + }, + "angle": { + "x": -5, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 21.78893568691454, + "y": -80, + "z": -249.04867452293638 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -5, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 21.78893568691454, + "y": -80, + "z": -249.04867452293638 + }, + "rotationOrigin": { + "x": 0, + "y": -5, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle41": { + "advanced": { + "val": false + }, + "angle": { + "x": -15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 64.70476127563019, + "y": 80, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -15, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 64.70476127563019, + "y": 80, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": -15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + }, + "circle42": { + "advanced": { + "val": false + }, + "angle": { + "x": -15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 64.70476127563019, + "y": 40, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 15, + "y": 40, + "r": -250 + }, + { + "theta": -65, + "y": 40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 40, + "r": -250 + }, + { + "theta": 15, + "y": 40, + "r": -250 + } + ], + "initialVelocities": [ + 10, + 10 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 64.70476127563019, + "y": 40, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": -15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle43": { + "advanced": { + "val": false + }, + "angle": { + "x": -15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 64.70476127563019, + "y": 0, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 15, + "y": 0, + "r": -250 + }, + { + "theta": -65, + "y": 0, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": 0, + "r": -250 + }, + { + "theta": 15, + "y": 0, + "r": -250 + } + ], + "initialVelocities": [ + 20, + 20 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 64.70476127563019, + "y": 0, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": -15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle44": { + "advanced": { + "val": false + }, + "angle": { + "x": -15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 64.70476127563019, + "y": -40, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -15, + "z": 0 + }, + "movement": { + "startPoints": [ + { + "theta": 15, + "y": -40, + "r": -250 + }, + { + "theta": -65, + "y": -40, + "r": -250 + } + ], + "endPoints": [ + { + "theta": 65, + "y": -40, + "r": -250 + }, + { + "theta": 15, + "y": -40, + "r": -250 + } + ], + "initialVelocities": [ + 40, + 40 + ], + "accelerations": [ + 0, + 0 + ], + "types": [ + "Start", + "Start" + ], + "origin": { + "x": 64.70476127563019, + "y": -40, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": -15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": 0 + } + }, + "circle45": { + "advanced": { + "val": false + }, + "angle": { + "x": -15, + "z": -250 + }, + "geometry": { + "primitive": "ring", + "radiusOuter": 2, + "radiusInner": 0, + "segmentsTheta": 100 + }, + "fill": { + "val": 2, + "isFull": true + }, + "position": { + "x": 64.70476127563019, + "y": -80, + "z": -241.4814565722671 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": -15, + "z": 0 + }, + "movement": { + "startPoints": [], + "endPoints": [], + "initialVelocities": [], + "accelerations": [], + "types": [], + "origin": { + "x": 64.70476127563019, + "y": -80, + "z": -241.4814565722671 + }, + "rotationOrigin": { + "x": 0, + "y": -15, + "z": 0 + }, + "status": -1, + "index": 0, + "currentVelocity": 0, + "timeElapsed": -1 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [] + }, + "date": "1/19/2024, 12:41:28 PM" +} \ No newline at end of file diff --git a/Custom/patterns/green.js b/Custom/patterns/green.js new file mode 100644 index 0000000..f7e9a09 --- /dev/null +++ b/Custom/patterns/green.js @@ -0,0 +1,47 @@ +var green = { + "scenes": { + "default": { + "sky": { + "skyColor": "#00ff00" + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/grille.JSON b/Custom/patterns/grille.JSON index 56a043d..cc0c14d 100644 --- a/Custom/patterns/grille.JSON +++ b/Custom/patterns/grille.JSON @@ -28,13 +28,25 @@ }, "material": { "shader": "flat", - "color": "#ffffff", - "src": "" + "color": "#ffffff" }, "rotation": { "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } } diff --git a/Custom/patterns/grille.js b/Custom/patterns/grille.js new file mode 100644 index 0000000..42bad2d --- /dev/null +++ b/Custom/patterns/grille.js @@ -0,0 +1,92 @@ +var grille = { + "scenes": { + "default": { + "sky": { + "skyColor": "#000000" + }, + "grille0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "numBars": 100, + "childGeometry": { + "primitive": "plane", + "width": 10, + "height": 450 + }, + "color2": { + "val": "#000000" + }, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/line.JSON b/Custom/patterns/line.JSON index 50029ea..ea65b7d 100644 --- a/Custom/patterns/line.JSON +++ b/Custom/patterns/line.JSON @@ -36,6 +36,19 @@ "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } } diff --git a/Custom/patterns/line.js b/Custom/patterns/line.js new file mode 100644 index 0000000..89ecb3c --- /dev/null +++ b/Custom/patterns/line.js @@ -0,0 +1,94 @@ +var line = { + "scenes": { + "line": { + "sky": { + "skyColor": "#000000" + }, + "plane0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "widthReal": 1, + "fill": { + "val": 1, + "isFull": true + }, + "geometry": { + "primitive": "plane", + "width": 0, + "height": 1000 + }, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff", + "src": "" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/red.js b/Custom/patterns/red.js new file mode 100644 index 0000000..f863415 --- /dev/null +++ b/Custom/patterns/red.js @@ -0,0 +1,47 @@ +var red = { + "scenes": { + "default": { + "sky": { + "skyColor": "#ff0000" + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/ring_w1.JSON b/Custom/patterns/ring_w1.JSON index 4d4d79d..b77b3ef 100644 --- a/Custom/patterns/ring_w1.JSON +++ b/Custom/patterns/ring_w1.JSON @@ -21,13 +21,25 @@ }, "material": { "shader": "flat", - "color": "#ffffff", - "src": "" + "color": "#ffffff" }, "rotation": { "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } } diff --git a/Custom/patterns/ring_w1.js b/Custom/patterns/ring_w1.js new file mode 100644 index 0000000..d347508 --- /dev/null +++ b/Custom/patterns/ring_w1.js @@ -0,0 +1,85 @@ +var ring_w1 = { + "scenes": { + "ring_w1": { + "sky": { + "skyColor": "#000000" + }, + "bullseye0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "numRings": 30, + "ringPitch": 1, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/ring_w2.JSON b/Custom/patterns/ring_w2.JSON index e0995ec..3ca1c58 100644 --- a/Custom/patterns/ring_w2.JSON +++ b/Custom/patterns/ring_w2.JSON @@ -21,13 +21,25 @@ }, "material": { "shader": "flat", - "color": "#ffffff", - "src": "" + "color": "#ffffff" }, "rotation": { "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } } diff --git a/Custom/patterns/ring_w2.js b/Custom/patterns/ring_w2.js new file mode 100644 index 0000000..4e779c5 --- /dev/null +++ b/Custom/patterns/ring_w2.js @@ -0,0 +1,85 @@ +var ring_w2 = { + "scenes": { + "ring_w2": { + "sky": { + "skyColor": "#000000" + }, + "bullseye0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "numRings": 30, + "ringPitch": 2, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/ring_w5.JSON b/Custom/patterns/ring_w5.JSON index 1cbdd04..e5bd642 100644 --- a/Custom/patterns/ring_w5.JSON +++ b/Custom/patterns/ring_w5.JSON @@ -21,13 +21,25 @@ }, "material": { "shader": "flat", - "color": "#ffffff", - "src": "" + "color": "#ffffff" }, "rotation": { "x": 0, "y": 0, "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 } } } diff --git a/Custom/patterns/ring_w5.js b/Custom/patterns/ring_w5.js new file mode 100644 index 0000000..b47faaf --- /dev/null +++ b/Custom/patterns/ring_w5.js @@ -0,0 +1,85 @@ +var ring_w5 = { + "scenes": { + "ring_w5": { + "sky": { + "skyColor": "#000000" + }, + "bullseye0": { + "advanced": { + "val": false + }, + "angle": { + "x": 0, + "z": -250 + }, + "numRings": 30, + "ringPitch": 5, + "position": { + "x": 0, + "y": 0, + "z": -250 + }, + "material": { + "shader": "flat", + "color": "#ffffff" + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "movement":{ + "startPoints":[], + "endPoints":[], + "initialVelocities":[], + "accelerations":[], + "types":[], + "origin":{"x":0,"y":0,"z":-250}, + "rotationOrigin":{"x":0,"y":0,"z":0}, + "status":-1, + "index":0, + "currentVelocity":0, + "timeElapsed":0 + } + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/patterns/white.js b/Custom/patterns/white.js new file mode 100644 index 0000000..4e199af --- /dev/null +++ b/Custom/patterns/white.js @@ -0,0 +1,47 @@ +var white = { + "scenes": { + "default": { + "sky": { + "skyColor": "#ffffff" + } + } + }, + "textures": { + "uploadedTextureFormats": {}, + "textureValues": [ + { + "val": "none", + "text": "none" + }, + { + "val": "QC", + "text": "TG18-QC.2k_12b" + }, + { + "val": "CH", + "text": "TG18-CH.2k" + }, + { + "val": "MM1", + "text": "TG18-MM1.2k" + }, + { + "val": "MM2", + "text": "TG18-MM2.2k" + }, + { + "val": "sQC", + "text": "TG270sQC" + }, + { + "val": "PQC", + "text": "TG18-PQC.2k_12b" + }, + { + "val": "BR", + "text": "TG18-BR.2k_12b" + } + ] + }, + "date": "6/15/2023, 10:54:47 PM" +} \ No newline at end of file diff --git a/Custom/saving.js b/Custom/saving.js index fa927c5..24a1c8d 100644 --- a/Custom/saving.js +++ b/Custom/saving.js @@ -39,7 +39,6 @@ function saveSelected(){ data['textures']['textureValues'] = textures data['textures']['uploadedTextureFormats'] = uploadedTextureFormat } else { - console.log('made it') for (const pattern of Object.keys(scenes[packageSelect.value])){ for (const ent of Object.keys(scenes[packageSelect.value][pattern])){ if(ent.includes('plane')){ diff --git a/Custom/script.js b/Custom/script.js index 187d1e7..28db36d 100644 --- a/Custom/script.js +++ b/Custom/script.js @@ -93,6 +93,10 @@ const widthIn = document.getElementById("widthIn"); /* width input */ const height = document.getElementById("height"); /* height container paragraph */ const heightIn = document.getElementById("heightIn"); /* height input */ +/* Height related */ +const size = document.getElementById("size"); /* height container paragraph */ +const sizeIn = document.getElementById("sizeIn"); /* height input */ + /* Triangle only attributes */ /* Vertex A related */ @@ -146,6 +150,8 @@ const numRingsIn = document.getElementById("numRingsIn"); /* number of rings inp const ringPitch = document.getElementById("ringPitch"); /* ring pitch container paragraph */ const ringPitchIn = document.getElementById("ringPitchIn"); /* ring pitch input */ +const textIn = document.getElementById("text"); + const settingsButtonContainer = document.getElementById("settingsButtonContainer"); const settingsButton = document.getElementById("settingsButton"); const settingsIcon = document.getElementById("settingsIcon"); @@ -187,6 +193,44 @@ const editPatternLayout = document.getElementById('editPatternLayout'); const packageSelect = document.getElementById('packageDisplay') const recentPackages = document.getElementById('recentPackages') +const movement = document.getElementById('movementSettings'); +const movementButton = document.getElementById('movementButton'); +const movementButtonContainer = document.getElementById('movementButtonContainer'); +const stopIndividualButton = document.getElementById('stopIndividualButton'); +const movementIcon = document.getElementById('movementIcon'); +const movementType = document.getElementById('movementType'); +const movementTypeIn = document.getElementById('movementTypeIn'); +const movementHeader = document.getElementById('movementHeader'); +const startHeader = document.getElementById('startHeader'); +const endHeader = document.getElementById('endHeader'); +const startX = document.getElementById('startX'); +const startY = document.getElementById('startY'); +const startZ = document.getElementById('startZ'); +const endX = document.getElementById('endX'); +const endY = document.getElementById('endY'); +const endZ = document.getElementById('endZ'); +const speedHeader = document.getElementById('speedHeader'); +const accelerationHeader = document.getElementById('accelerationHeader'); +//const keyHeader = document.getElementById('keyHeader'); +const speed = document.getElementById('speed'); +const acceleration = document.getElementById('acceleration'); +//const keyBind = document.getElementById('key'); + +const animationButton = document.getElementById('animationButton') +const animationList = document.getElementById('movementAnims-list') +const animationListIcon = document.getElementById('animationListIcon') +const animationListButton = document.getElementById('animationListButton') +const animationListButtonContainer = document.getElementById('animationListButtonContainer') +const settingsLayout = document.getElementById('settingsLayout') + +const startAllButton = document.getElementById('startAllButton') +const pauseAllButton = document.getElementById('pauseAllButton') +const stopAllButton = document.getElementById('stopAllButton') + +const consoleButton = document.getElementById('consoleButton') +const debug = document.getElementById('debug') + + /* Local Variables */ var el = null; /* recently created entity */ var els = new Array(); /* array of all created entities */ @@ -205,6 +249,8 @@ var grilleNum = 0; var dotarrayNum = 0; var circularDotarrayNum = 0; var bullseyeNum = 0; +var textNum = 0; +var timerNum = 0; var textureNum = 0; var numAdded = 0; /* total entities added */ @@ -216,6 +262,9 @@ var fileContent = null; /* contents of uploaded JSON file */ var uploadedTextureFormat = {}; var scenes = {default: {}}; +const packages = {default: ''} // dictionary of packages and their respective links +const names = {default: {}} // list of packages and the names and count of names of each pattern within + patternList.setAttribute('multi-select',false); @@ -234,21 +283,42 @@ items.forEach(item => { let i = 0; let reorderedScene = {} while(i < patternList.children.length){ - reorderedScene[patternList.children[i].innerHTML] = scenes[packageSelect.value][patternList.children[i].innerHTML] + reorderedScene[patternList.children[i].id] = scenes[packageSelect.value][patternList.children[i].id] i++; } scenes[packageSelect.value] = reorderedScene // handles a pattern being selected from the pattern list function selectPattern (e){ - if(e.target.style.background == 'rgb(243, 152, 20)'){ // if selected pattern is highlighted, unselect it + stopAllMovement() + if(e.target.style.background == 'rgb(243, 152, 20)' && patternList.getAttribute("multi-select") == false){ // if selected pattern is highlighted, unselect it e.target.style.background = '#FFF' patternList.setAttribute("selectedIndex","") patternList.setAttribute("multi-select",false); revertChanges() //nameIn.value = packageSelect.value; return; + } else if(e.target.style.background == 'rgb(243, 152, 20)' && patternList.getAttribute("multi-select") == 'true'){ + e.target.style.background = '#FFF'; + first = true; + items.forEach(item => { // changes displayed pattern to selected pattern + if(item.style.background == 'rgb(243, 152, 20)' && first) { + first = false; + patternList.setAttribute("selectedIndex",$(item).index()) + + revertChanges() + addEntitiesFromScene(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id]) + //nameIn.value = patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].textContent; + if(els.length > 0){ + selectedEntity = els[0] + } + } else if(item.style.background == 'rgb(243, 152, 20)' && !first){ + patternList.setAttribute("multi-select",true); + } + }) + return; } + e.target.style.background = '#F39814' // highlights selected pattern items = document.querySelectorAll('#items-list > li'); if(keysPressed['ctrl'] && !isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ // checks for multiselect @@ -262,16 +332,39 @@ function selectPattern (e){ patternList.setAttribute("selectedIndex",$(item).index()) patternList.setAttribute("multi-select",false); revertChanges() - addEntitiesFromScene(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].textContent]) + addEntitiesFromScene(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id]) //nameIn.value = patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].textContent; if(els.length > 0){ selectedEntity = els[0] } } }) + /*let i = 0; + while(i < els.length){ + if(!els[i].getAttribute('movement')){ + els[i].setAttribute("movement",{'startPoints': [],'endPoints': [],'initialVelocities':[],'accelerations':[],'types':[],'origin': els[i].getAttribute('position'), 'rotationOrigin': els[i].getAttribute('rotation'), 'status': -1, 'index': 0, 'currentVelocity': 0, 'timeElapsed': 0}) + } + i++; + }*/ + + setTimeout(() => { + if(els[0] && els[0].getAttribute('movement').types.length > 0){ + updateAnimationList(els[0]) + animationList.setAttribute('selectedIndex',0) + updateStats() + + } else { + updateAnimationList(els[0]) + } + + },200) + + } + + /* Code to make pattern list elements draggable */ function dragStart (e) { var index = $(e.target).index() @@ -301,7 +394,7 @@ function dropped (e) { let i = 0; let reorderedScene = {} while(i < patternList.children.length){ - reorderedScene[patternList.children[i].innerHTML] = scenes[packageSelect.value][patternList.children[i].innerHTML] + reorderedScene[patternList.children[i].id] = scenes[packageSelect.value][patternList.children[i].id] i++; } scenes[packageSelect.value] = reorderedScene @@ -315,6 +408,278 @@ function cancelDefault (e) { } +// animation list functionality + +let animations = document.querySelectorAll('#movementAnims-list > li'); + +animations.forEach(item => { + $(item).prop('draggable', true) + item.addEventListener('dragstart', dragStart) + item.addEventListener('drop', droppedAnim) + item.addEventListener('dragenter', cancelDefault) + item.addEventListener('dragover', cancelDefault) + item.addEventListener('click',selectAnimation) +}) + +let j = 0; + + +// handles a pattern being selected from the pattern list +function selectAnimation (e){ + stopMovement(selectedEntity) + if(e.target.style.background == 'rgb(243, 152, 20)'){ // if selected pattern is highlighted, unselect it + e.target.style.background = '#FFF' + animationList.setAttribute("selectedIndex","") + movementTypeIn.disabled = true + //revertChanges() + //nameIn.value = packageSelect.value; + return; + } + e.target.style.background = '#F39814' // highlights selected pattern + items = document.querySelectorAll('#movementAnims-list > li'); + items.forEach(item => { // changes displayed pattern to selected pattern + if(item != e.target){ + item.style.background = '#FFF' + } else { + animationList.setAttribute("selectedIndex",$(item).index()) + movementTypeIn.disabled = false + updateAnimationUI(selectedEntity,$(item).index()) + + + // add in functionality that will update animation info based on selected + + } + }) +} + +function updateAnimationUI(entity, ind) { + if(ind == -1){ + movementTypeIn.value = "None" + movementTypeIn.disabled = true + updateMovementSettings() + return + } + + let mov = entity.getAttribute('movement') + let i = 0; + let counter = -1; + + // have to take into account rebounds from rubberband + while(i < mov.types.length){ + if(mov.types[i] != 'Rebound'){ + counter++; + if(counter == ind){ + break + } + } + i++; + } + + if(entity.getAttribute('advanced').val){ + startX.value = mov.startPoints[i].x + startY.value = mov.startPoints[i].y + startZ.value = -mov.startPoints[i].z + endX.value = mov.endPoints[i].x + endY.value = mov.endPoints[i].y + endZ.value = -mov.endPoints[i].z + } else { + startX.value = mov.startPoints[i].theta + startY.value = mov.startPoints[i].y + startZ.value = -mov.startPoints[i].r + endX.value = mov.endPoints[i].theta + endY.value = mov.endPoints[i].y + endZ.value = -mov.endPoints[i].r + } + + movementTypeIn.value = mov.types[i] + acceleration.value = mov.accelerations[i] + speed.value = mov.initialVelocities[i] + movementTypeIn.disabled = false + if(mov.types[i] == 'Pause'){ + startY.value = mov.initialVelocities[i] + } + updateMovementSettings() + +} + +function droppedAnim (e) { + cancelDefault(e) + stopMovement(selectedEntity) + + // get new and old index + let oldIndex = e.dataTransfer.getData('text/plain') + let target = $(e.target) + let newIndex = target.index() + + // find oldIndex equivalent and newIndex equivalent in types + let i = 0; + let oldI = -1; + let newI = -1; + let counterOld = -1; + let counterNew = -1; + + let movementComponent = selectedEntity.getAttribute('movement') + // have to take into account rebounds from rubberband + while(i < movementComponent.types.length){ + if(movementComponent.types[i] != 'Rebound'){ + if(counterOld == oldIndex && counterNew == newIndex){ + break + } + + if(counterOld != oldIndex){ + counterOld++; + if(counterOld == oldIndex){ + oldI = i + } + } + if(counterNew != newIndex){ + counterNew++; + if(counterNew == newIndex){ + newI = i + } + } + } + i++; + } + + + // remove dropped items at old place + if(newIndex != oldIndex){ + let dropped = $(this).parent().children().eq(oldIndex).remove() + + // insert the dropped items at new place + if (newIndex < oldIndex) { + target.before(dropped) + } else { + target.after(dropped) + } + if(animationList.getAttribute('selectedIndex') == oldIndex){ + animationList.setAttribute('selectedIndex',newIndex); + } + } + + if(movementComponent.types[newIndex] == 'Rubberband'){ + // insert at newIndex+2 + newIndex += 1; + } + + if(oldI > newI){ + if(movementComponent.types[oldI] == 'Rubberband'){ + // remove both oldI and oldI+1 + let points = movementComponent.startPoints.splice(oldI,2); + let points2 = movementComponent.endPoints.splice(oldI,2); + let vels = movementComponent.initialVelocities.splice(oldI,2); + let accelerations = movementComponent.accelerations.splice(oldI,2); + let types = movementComponent.types.splice(oldI,2); + + movementComponent.startPoints.splice(newI,0,...points) + movementComponent.endPoints.splice(newI,0,...points2) + movementComponent.initialVelocities.splice(newI,0,...vels) + movementComponent.accelerations.splice(newI,0,...accelerations) + movementComponent.types.splice(newI,0,...types) + + } else { + // remove oldI + let points = movementComponent.startPoints.splice(oldI,1); + let points2 = movementComponent.endPoints.splice(oldI,1); + let vels = movementComponent.initialVelocities.splice(oldI,1); + let accelerations = movementComponent.accelerations.splice(oldI,1); + let types = movementComponent.types.splice(oldI,1); + + movementComponent.startPoints.splice(newI,0,...points) + movementComponent.endPoints.splice(newI,0,...points2) + movementComponent.initialVelocities.splice(newI,0,...vels) + movementComponent.accelerations.splice(newI,0,...accelerations) + movementComponent.types.splice(newI,0,...types) + } + + } else if(newI > oldI) { + // remove then insert at newI - 1 or - 2 if newI + if(movementComponent.types[oldI] == 'Rubberband'){ + // remove both oldI and oldI+1 + let points = movementComponent.startPoints.splice(oldI,2); + let points2 = movementComponent.endPoints.splice(oldI,2); + let vels = movementComponent.initialVelocities.splice(oldI,2); + let accelerations = movementComponent.accelerations.splice(oldI,2); + let types = movementComponent.types.splice(oldI,2); + + if(movementComponent.types[newI-1] == 'Rubberband'){ + movementComponent.startPoints.splice(newI,0,...points) + movementComponent.endPoints.splice(newI,0,...points2) + movementComponent.initialVelocities.splice(newI,0,...vels) + movementComponent.accelerations.splice(newI,0,...accelerations) + movementComponent.types.splice(newI,0,...types) + } else { + movementComponent.startPoints.splice(newI-1,0,...points) + movementComponent.endPoints.splice(newI-1,0,...points2) + movementComponent.initialVelocities.splice(newI-1,0,...vels) + movementComponent.accelerations.splice(newI-1,0,...accelerations) + movementComponent.types.splice(newI-1,0,...types) + } + + } else { + // remove oldI + let points = movementComponent.startPoints.splice(oldI,1); + let points2 = movementComponent.endPoints.splice(oldI,1); + let vels = movementComponent.initialVelocities.splice(oldI,1); + let accelerations = movementComponent.accelerations.splice(oldI,1); + let types = movementComponent.types.splice(oldI,1); + + if(movementComponent.types[newI-1] == 'Rubberband'){ + movementComponent.startPoints.splice(newI+1,0,...points) + movementComponent.endPoints.splice(newI+1,0,...points2) + movementComponent.initialVelocities.splice(newI+1,0,...vels) + movementComponent.accelerations.splice(newI+1,0,...accelerations) + movementComponent.types.splice(newI+1,0,...types) + } else { + movementComponent.startPoints.splice(newI,0,...points) + movementComponent.endPoints.splice(newI,0,...points2) + movementComponent.initialVelocities.splice(newI,0,...vels) + movementComponent.accelerations.splice(newI,0,...accelerations) + movementComponent.types.splice(newI,0,...types) + } + } + + } + + /*if(oldIndex < movementComponent.types.length-1 && movementComponent.types[oldIndex+1] == 'Rebound'){ + // remove both oldIndex and oldIndex+1 + let points = movementComponent.startPoints.splice(oldIndex,2); + let points2 = movementComponent.endPoints.splice(oldIndex,2); + let vels = movementComponent.initialVelocities.splice(oldIndex,2); + let accelerations = movementComponent.accelerations.splice(oldIndex,2); + let types = movementComponent.types.splice(oldIndex,2); + + movementComponent.startPoints.splice(newIndex,0,...points) + movementComponent.endPoints.splice(newIndex,0,...points2) + movementComponent.initialVelocities.splice(newIndex,0,...vels) + movementComponent.accelerations.splice(newIndex,0,...accelerations) + movementComponent.types.splice(newIndex,0,...types) + + } else { + let points = movementComponent.startPoints.splice(oldIndex,1); + let points2 = movementComponent.endPoints.splice(oldIndex,1); + let vels = movementComponent.initialVelocities.splice(oldIndex,1); + let accelerations = movementComponent.accelerations.splice(oldIndex,1); + let types = movementComponent.types.splice(oldIndex,1); + + movementComponent.startPoints.splice(newIndex,0,...points) + movementComponent.endPoints.splice(newIndex,0,...points2) + movementComponent.initialVelocities.splice(newIndex,0,...vels) + movementComponent.accelerations.splice(newIndex,0,...accelerations) + movementComponent.types.splice(newIndex,0,...types) + + }*/ + + + + updateJSON() + //scenes[packageSelect.value] = reorderedScene + +} + + + // SOURCE https://stackoverflow.com/questions/16616722/sending-all-javascript-console-output-into-a-dom-element // Writes console information to the debug div which can be opened by pressing ctrl+i diff --git a/Custom/setup.js b/Custom/setup.js new file mode 100644 index 0000000..4cb2cd7 --- /dev/null +++ b/Custom/setup.js @@ -0,0 +1,269 @@ +/* Contains DOM information and code that will run on page load */ + +/* Scene related */ +const scene = document.querySelector("a-scene"); +const entityCanvas = scene.querySelector("#entityCanvas"); + +/* Edit Related */ +var conRight = document.querySelector("#con-right"); +var conLeft = document.querySelector("#con-left"); + +/* Headers */ +const background = document.getElementById("backgroundSettings"); /* header for background settings */ +const ent = document.getElementById("entitySettings"); +const uni = document.getElementById("universalSettings"); +const nonUni = document.getElementById("nonUniversalSettings"); +const utility = document.getElementById("utility"); +const collapse = document.getElementById("collapse"); +const content = document.getElementById("content"); +//content.style.display = "block"; +const addEditContent = document.getElementById("addEditContent") +const addContent = document.getElementById('addContent') +const editContent = document.getElementById('editContent') +const displayEditContent = document.getElementById("displayEditContent") +const nameIn = document.getElementById('name'); +const displayUtility = document.getElementById('displayUtility'); +const advanced = document.getElementById('advancedButton'); + +/* Display scenes */ +const pattern = document.getElementById("pattern"); +const patternList = document.getElementById("items-list"); + +const patternDisplay = document.getElementById("patternDisplay"); +const scene_display_input = document.getElementById("scene-disp-input"); + +/* Selection of entity */ +const entitySelectorText = document.getElementById("editSelector"); /* current entity container paragraph */ +const entitySelector = document.getElementById("entityId"); /* selector */ + +/* Sky related */ +const skyIn = document.getElementById("skyIn"); /* sky color container */ +const skyColor = document.getElementById("skyCol"); /* sky color input */ +//skyIn.style.width = "75%"; + +/* Universal attributes */ +/* Position related */ +const posIn = document.getElementById("posIn"); /* position Container Paragraph */ +var xIn = document.getElementById("x"); /* x input */ +var yIn = document.getElementById("y"); /* y input */ +var zIn = document.getElementById("z"); /* z input */ + +/* Rotation related */ +const rotationZ = document.getElementById("rotationZ"); +const rotationY = document.getElementById("rotationY"); +const rotationX = document.getElementById("rotationX"); + +const pitch = document.getElementById("pitch"); +const roll = document.getElementById("roll"); +const yaw = document.getElementById("yaw"); +/* rotation container paragraph */ +//rotationY.style.display = 'none' +//rotationX.style.display = 'none' +const rotIn = document.getElementById("rotation"); /* rotation input */ + +/* Color related */ +const entColor = document.getElementById("entColor"); +const color = document.getElementById("color"); /* color container paragraph */ +const colIn = document.getElementById("colIn"); /* color input */ +const colIn2 = document.getElementById("colIn2"); /* color input */ +const color2 = document.getElementById("color2"); /* color input */ +//colIn.style.width = "75%"; + +/* Texture related */ +const textureIn = document.getElementById("textureIn"); +const texture = document.getElementById("texture"); +const uploadTextureIn = document.querySelector("#uploadTextureIn"); +const texture_input = document.querySelector("#texture-input"); + +/* Fill related */ +const fillIn = document.getElementById("fillIn"); +const fill = document.getElementById("fill"); + +/* Circle only attributes */ +/* Radius related */ +const radius = document.getElementById("radius"); /* radius container paragraph */ +const radiusIn = document.getElementById("radiusIn"); /* radius input */ + +/* Plane/Gradient only attributes */ +/* Width related */ +const width = document.getElementById("width"); /* width container paragraph */ +const widthIn = document.getElementById("widthIn"); /* width input */ + +/* Height related */ +const height = document.getElementById("height"); /* height container paragraph */ +const heightIn = document.getElementById("heightIn"); /* height input */ + +/* Height related */ +const size = document.getElementById("size"); /* height container paragraph */ +const sizeIn = document.getElementById("sizeIn"); /* height input */ + +/* Triangle only attributes */ +/* Vertex A related */ + +const vertices = document.getElementById("vertices"); +const va = document.getElementById("va"); /* vertex A container paragraph */ +const vax = document.getElementById("vax"); /* x input */ +const vay = document.getElementById("vay"); /* y input */ + +/* vertex B related */ +const vb = document.getElementById("vb"); /* vertex B container paragraph */ +const vbx = document.getElementById("vbx"); /* x input */ +const vby = document.getElementById("vby"); /* y input */ + +/* Vertex C related */ +const vc = document.getElementById("vc"); /* vertex C container paragraph */ +const vcx = document.getElementById("vcx"); /* x input */ +const vcy = document.getElementById("vcy"); /* y input */ + +/* Gradient only attributes */ +/* Number of bars related */ +const numBars = document.getElementById("numBars"); /* number of bars container paragraph */ +const numBarsIn = document.getElementById("numBarsIn"); /* number of bars input */ + +/* Checkerboard only attributes */ +const rows = document.getElementById("rows"); /* rows container paragraph */ +const rowsIn = document.getElementById("rowsIn"); /* rows input */ +const cols = document.getElementById("cols"); /* columns container paragraph */ +const colsIn = document.getElementById("colsIn"); /* columns input */ +const tileSize = document.getElementById("tileSize"); /* tile size container paragraph */ +const tileSizeIn = document.getElementById("tileSizeIn"); /* tile size input */ + +/* dotarray attributes */ +const circleSize = document.getElementById("circleSize"); /* circle size container paragraph */ +const circleSizeIn = document.getElementById("circleSizeIn"); /* circle size input */ +const spacing = document.getElementById("spacing"); /* circle size container paragraph */ +const spacingIn = document.getElementById("spacingIn"); /* circle size input */ +const numDots = document.getElementById("numDots"); /* circle size container paragraph */ +const numDotsIn = document.getElementById("numDotsIn"); /* circle size input */ +const arrayRadius = document.getElementById("arrayRadius"); /* circle size container paragraph */ +const arrayRadiusIn = document.getElementById("arrayRadiusIn"); /* circle size input */ +const numCircles = document.getElementById("numCircles"); /* circle size container paragraph */ +const numCirclesIn = document.getElementById("numCirclesIn"); /* circle size input */ +const arraySpacing = document.getElementById("arraySpacing"); /* circle size container paragraph */ +const arraySpacingIn = document.getElementById("arraySpacingIn"); /* circle size input */ +const toggleCenterDot = document.getElementById("toggleCenterDot"); +const toggleCenterDotIn = document.getElementById("toggleCenterDotIn"); + +/* bullseye attributes */ +const numRings = document.getElementById("numRings"); /* number of rings container paragraph */ +const numRingsIn = document.getElementById("numRingsIn"); /* number of rings input */ +const ringPitch = document.getElementById("ringPitch"); /* ring pitch container paragraph */ +const ringPitchIn = document.getElementById("ringPitchIn"); /* ring pitch input */ + +const textIn = document.getElementById("textIn"); +const text = document.getElementById("text") + +const settingsButtonContainer = document.getElementById("settingsButtonContainer"); +const settingsButton = document.getElementById("settingsButton"); +const settingsIcon = document.getElementById("settingsIcon"); + +const swapContainer = document.getElementById("swapContainer"); + + +/* Send back/forward */ +/*const sendButton = document.getElementById("sendButton");*/ + +/* Save Edits Button */ +/*const editButton = document.getElementById("editButton");*/ + +/* Save Scene button */ +const saveButton = document.getElementById("saveButton"); + +/* Add related */ +const addChoice = document.getElementById("addChoice"); /* add button container paragraph */ +const addButton = document.getElementById("addButton"); /* add button */ +const upload = document.getElementById("upload"); /* upload button container paragraph*/ +const scene_input = document.querySelector("#scene-input"); /* upload button */ + +const removeButton = document.getElementById("removeButton"); /* remove button container paragraph */ +const duplicateButton = document.getElementById("duplicateButton"); + +const specificSettings = document.getElementById("specificSettings"); +const area1 = document.getElementById("area1"); +const area2 = document.getElementById("area2"); +const area3 = document.getElementById("area3"); +const area4 = document.getElementById("area4"); +const area5 = document.getElementById("area5"); + +const hideUniversalIcon = document.getElementById("hideUniversalIcon"); +const hideSpecificIcon = document.getElementById("hideSpecificIcon"); +const universalHeader = document.getElementById("universalHeader"); +const coreLayout = document.getElementById('coreLayout'); +const packageLayout = document.getElementById('packageLayout'); +const editPatternLayout = document.getElementById('editPatternLayout'); +const packageSelect = document.getElementById('packageDisplay') +const recentPackages = document.getElementById('recentPackages') + +const movement = document.getElementById('movementSettings'); +const movementButton = document.getElementById('movementButton'); +const movementButtonContainer = document.getElementById('movementButtonContainer'); +const stopIndividualButton = document.getElementById('stopIndividualButton'); +const movementIcon = document.getElementById('movementIcon'); +const movementType = document.getElementById('movementType'); +const movementTypeIn = document.getElementById('movementTypeIn'); +const movementHeader = document.getElementById('movementHeader'); +const startHeader = document.getElementById('startHeader'); +const endHeader = document.getElementById('endHeader'); +const startX = document.getElementById('startX'); +const startY = document.getElementById('startY'); +const startZ = document.getElementById('startZ'); +const endX = document.getElementById('endX'); +const endY = document.getElementById('endY'); +const endZ = document.getElementById('endZ'); +const speedHeader = document.getElementById('speedHeader'); +const accelerationHeader = document.getElementById('accelerationHeader'); +//const keyHeader = document.getElementById('keyHeader'); +const speed = document.getElementById('speed'); +const acceleration = document.getElementById('acceleration'); +//const keyBind = document.getElementById('key'); + +const animationButton = document.getElementById('animationButton') +const animationList = document.getElementById('movementAnims-list') +const animationListIcon = document.getElementById('animationListIcon') +const animationListButton = document.getElementById('animationListButton') +const animationListButtonContainer = document.getElementById('animationListButtonContainer') +const settingsLayout = document.getElementById('settingsLayout') + +const startAllButton = document.getElementById('startAllButton') +const pauseAllButton = document.getElementById('pauseAllButton') +const stopAllButton = document.getElementById('stopAllButton') + +const consoleButton = document.getElementById('consoleButton') +const debug = document.getElementById('debug') + + +/* Local Variables */ +var el = null; /* recently created entity */ +var els = new Array(); /* array of all created entities */ +var pool = new Array(); + +var sky = scene.querySelector("#sky"); /* sky */ + +var selectedEntity = null; /* selected entity */ + +var circleNum = 0; /* number of circles created */ +var planeNum = 0; /* number of planes created */ +var triangleNum = 0; /* number of triangles created */ +var gradientNum = 0; /* number of gradients created */ +var checkerboardNum = 0; /* number of checkerboards created */ +var grilleNum = 0; +var dotarrayNum = 0; +var circularDotarrayNum = 0; +var bullseyeNum = 0; +var textNum = 0; +var timerNum = 0; +var textureNum = 0; +var numAdded = 0; /* total entities added */ + +var boolAddEdit = false; +var boolDisplayEdit = true; /* toggle for add or edit element */ + +var fileContent = null; /* contents of uploaded JSON file */ + +var uploadedTextureFormat = {}; +var scenes = {default: {}}; + +const packages = {default: ''} // dictionary of packages and their respective links +const names = {default: {}} // list of packages and the names and count of names of each pattern within + +var colorChange = true; \ No newline at end of file diff --git a/Custom/sharing.js b/Custom/sharing.js index a38afc5..71230df 100644 --- a/Custom/sharing.js +++ b/Custom/sharing.js @@ -27,7 +27,6 @@ async function handleImport(){ window.history.pushState('object', document.title, newURL); } - console.log(url.split("?")[1]) alert('Success') return true; } else { @@ -43,12 +42,12 @@ async function handleImport(){ return res; }) if(res2){ - if(url.includes('pastebin')){ + if(url.includes('https://didsr.pythonanywhere.com/webxrtools/get?id=')){ if(!window.location.href.includes('?')){ - let newURL = window.location.href + "?id=" +url.split("https://pastebin.run/")[1].split(".txt")[0] + let newURL = window.location.href + "?id=" +url.split("https://didsr.pythonanywhere.com/webxrtools/get?id=")[1] window.history.pushState('object', document.title, newURL); } else { - let newURL = window.location.href + "," +url.split("https://pastebin.run/")[1].split(".txt")[0] + let newURL = window.location.href + "," +url.split("https://didsr.pythonanywhere.com/webxrtools/get?id=")[1] window.history.pushState('object', document.title, newURL); } } else { @@ -64,7 +63,7 @@ async function handleImport(){ alert('Success') return true; } - console.log('Handling failed') + alert.log('Handling failed') return false; } else { alert('Invalid link'); @@ -82,8 +81,7 @@ async function localLinkImport(url){ ids = url.split('?id=')[1].split(','); let i = 0; while(i < ids.length){ - console.log(ids[i]) - var res = pastebinFetch("https://pastebin.run/"+ids[i]+".txt").then((result) => { + var res = pastebinFetch("https://didsr.pythonanywhere.com/webxrtools/get?id="+ids[i]).then((result) => { return result }); if(!res){ @@ -102,14 +100,9 @@ async function validateLink(url){ return null } else { - console.log(url) return res.json() } }).catch((error) => alert('Failed to fetch from: '+url+' with error '+error)); - if(fileContent == null || !fileContent.hasOwnProperty('filename') || !fileContent.hasOwnProperty('date') || !fileContent.hasOwnProperty('scenes') || !fileContent.hasOwnProperty('textures')){ - return false; - } - return true; + return validateJSON(fileContent) } - diff --git a/Custom/textures/textureInput.js b/Custom/textures/textureInput.js new file mode 100644 index 0000000..9d4f16e --- /dev/null +++ b/Custom/textures/textureInput.js @@ -0,0 +1,82 @@ +/* Contains code related to texture input and change */ + +/* If the textbox for texture value is changed */ +$("#texture").change(function() { + if(texture.value == "none"){ + selectedEntity.setAttribute("material",{color: selectedEntity.getAttribute("material").color, shader: "flat", src: ""}); + fillIn.style.display = "block"; + editEntity() + } else { + selectedEntity.setAttribute("material",{color: selectedEntity.getAttribute("material").color, shader: "flat", src: "#"+texture.value}); + let i = selectedEntity.children.length-1; + while (i >= 0) { + selectedEntity.children[i].parentNode.removeChild(selectedEntity.children[i]); + i--; + } + if(typeof selectedEntity.getAttribute("material").src == "object"){ + selectedEntity.setAttribute("geometry",{primitive: "plane", width: .5*(selectedEntity.getAttribute("material").src.naturalWidth/selectedEntity.getAttribute("material").src.naturalHeight)*250, height: .5*250}); + } else { + selectedEntity.setAttribute("geometry",{primitive: "plane", width: .5*(uploadedTextureFormat[texture.options[texture.selectedIndex].text].width/uploadedTextureFormat[texture.options[texture.selectedIndex].text].height)*250, height: .5*250}); + } + if(selectedEntity.getAttribute("fill").isFull){ + if(selectedEntity.getAttribute("geometry").width > selectedEntity.getAttribute("geometry").height){ + selectedEntity.setAttribute("fill", { val: selectedEntity.getAttribute("geometry").height, isFull: true}); + } else { + selectedEntity.setAttribute("fill", { val: selectedEntity.getAttribute("geometry").width, isFull: true}); + } + } + width.value = selectedEntity.getAttribute("geometry").width; + height.value = selectedEntity.getAttribute("geometry").height; + fill.value = selectedEntity.getAttribute("fill").val; + fillIn.style.display = "none"; + updateJSON(); + } + + + }); + + +/* If the textbox for x value is changed */ +$("#texture-input").change(function() { + let i = 0; + var j = 0; + while(i < texture_input.files.length){ + const reader = new FileReader(); + reader.addEventListener("load", () => { + uploaded_image = reader.result; + }); + + var name = this.files[i].name; + reader.readAsDataURL(this.files[i]); + reader.onload = function (e) { + //Initiate the JavaScript Image object. + var image = new Image(); + + //Set the Base64 string return from FileReader as source. + image.src = e.target.result; + + //Validate the File Height and Width. + image.onload = function () { + let k = 0; + let skip = false; + while(k < texture.options.length){ + if(texture.options[k].text == texture_input.files[j].name){ + skip = true; + } + k++; + } + if(!skip){ + var height = this.height; + var width = this.width; + uploadedTextureFormat[texture_input.files[j].name] = {width: width, height: height}; + var option = document.createElement("option"); + option.text = texture_input.files[j++].name; + option.value = `url(${this.src})`; + texture.add(option); + } + }; + } + i++; + } + + }); \ No newline at end of file diff --git a/Custom/uploading.js b/Custom/uploading.js index 8cb97a9..db2e503 100644 --- a/Custom/uploading.js +++ b/Custom/uploading.js @@ -184,17 +184,38 @@ function addEntitiesFromScene(scene){ } else if (key.includes("bullseye")){ el.setAttribute("id", "bullseye"+bullseyeNum++); drawBullseye(scene[key].ringPitch,scene[key].numRings,scene[key].material.color,el); + } else if (key.includes("text")){ + el.setAttribute("id", "text"+textNum++); + el.setAttribute("text",scene[key].text) + } else if (key.includes("timer")){ + el.setAttribute("id", "timer"+timerNum++); + el.setAttribute("text",scene[key].text) } /* sets stats */ el.setAttribute("angle", scene[key].angle); el.setAttribute("advanced", scene[key].advanced); el.setAttribute("position", {x: scene[key].position.x, y: scene[key].position.y, z: scene[key].position.z}); - el.setAttribute("material", scene[key].material); + let mat = null; + if(scene[key].material){ + mat = JSON.parse(JSON.stringify(scene[key].material)) + + for(let i = 0; i < texture.options.length; i++){ + if(texture.options[i].text == mat.src){ + mat.src = "#"+texture.options[i].value; + break; + } + } + } + + + el.setAttribute("material", mat); el.setAttribute("rotation", scene[key].rotation); + el.setAttribute("movement",JSON.parse(JSON.stringify(scene[key].movement))) el.setAttribute("click-checker",""); numAdded++; entityCanvas.appendChild(el); /* adds entity to scene */ + /* adds option to dropdown */ var option = document.createElement("option"); option.text = el.getAttribute("id"); @@ -204,4 +225,4 @@ function addEntitiesFromScene(scene){ pool.push(el.object3D); } }) -} \ No newline at end of file +} diff --git a/Custom/utility.js b/Custom/utility.js index d3e9430..2d30719 100644 --- a/Custom/utility.js +++ b/Custom/utility.js @@ -11,7 +11,6 @@ function selectNew(clickedEntity){ entitySelector.value = clickedEntity.getAttribute("id"); /* update dropdown */ } else { /* if selected via dropdown */ selectedEntity = scene.querySelector("#"+$("#entityId :selected").text()); /* update selected entity */ - console.log(selectedEntity) } /* Update stats in edit section */ @@ -21,6 +20,12 @@ function selectNew(clickedEntity){ hideEditStats(); /* hide section briefly */ updateStats(); /* update stats */ toggleAddEdit(null); /* re-display section */ + updateAnimationList(selectedEntity) + if(animationList.childElementCount == 0){ + animationList.setAttribute('selectedIndex',"") + } else { + animationList.setAttribute('selectedIndex',0) + } highlightSelection(selectedEntity); } @@ -37,23 +42,6 @@ function removeEntity(){ } els.splice(els.indexOf(selectedEntity),1); pool.splice(pool.indexOf(selectedEntity.object3D),1); - if(selectedEntity.id.includes("plane")){ - planeNum--; - } else if(selectedEntity.id.includes("circle")){ - circleNum--; - } else if(selectedEntity.id.includes("triangle")){ - triangleNum--; - } else if(selectedEntity.id.includes("checkerboard")){ - checkerboardNum--; - } else if(selectedEntity.id.includes("gradient")){ - gradientNum--; - } else if(selectedEntity.id.includes("grille")){ - grilleNum--; - } else if(selectedEntity.id.includes("circularDotarray")){ - circularDotarrayNum--; - } else if(selectedEntity.id.includes("dotarray")){ - dotarrayNum--; - } entityCanvas.removeChild(selectedEntity); for (var i=0; i { selectNew(el) }, 100); } -/* handles changes in selected pattern */ -//var displayOrder = ['default']; -/*function handlePatternSelect(snapshot){ - curr = snapshot.id - if(displayOrder.indexOf(curr) == -1){ - displayOrder.push(curr) - } else { - displayOrder.splice(displayOrder.indexOf(curr),1) - } - while(patternDisplay.options.length != 0){ - patternDisplay.options.remove(patternDisplay.options[0]) - } - patternDisplay.selectedIndex = 0 - i = 0 - len = displayOrder.length - while(i < len){ - if(i == 0){ - patternDisplay.options.add(new Option(displayOrder[i], displayOrder[i], true, true)) - revertChanges() - addEntitiesFromScene(scenes[displayOrder[i]]) - - } else { - patternDisplay.options.add(new Option(displayOrder[i], displayOrder[i])) - } - i++; - } -}*/ - -/* displays current pattern selected */ -/*function displayCurrentPattern(snapshot){ - let len = snapshot.options.length; - let i = 0; - let sum = 0; - while(i < len){ - curr = snapshot.options[i] - if(curr.selected){ - revertChanges() - addEntitiesFromScene(scenes[curr.text]); - } - i++; - } -}*/ - /* resets current scene */ function resetScene(){ let conf = confirm("Reset the selected pattern?"); @@ -199,15 +166,21 @@ function resetScene(){ dotarrayNum = 0; circularDotarrayNum = 0; bullseyeNum = 0; + timerNum = 0; + textNum = 0; + stopAllMovement(); + movementKeyBinds = {} while(entityCanvas.childElementCount != 0){ entityCanvas.removeChild(entityCanvas.children[0]) } while(entitySelector.childElementCount != 0){ entitySelector.remove(entitySelector.children[0]) } + movementIcon.className = "fa-solid fa-play" $('#skyCol').minicolors('value', '#000000'); els = [] updateJSON() + selectedEntity = null } /* used to click through current pattern */ @@ -274,8 +247,12 @@ function addPattern(){ currName = patternName+' ('+names[packageSelect.value][patternName]+')' names[packageSelect.value][patternName] = names[packageSelect.value][patternName] + 1 } + let textContent = currName; + if(currName.length > 20){ + textContent = currName.substring(0,20)+"..." + } scenes[packageSelect.value][currName] = {sky: {skyColor: '#000000'}} - var toggle_button = '
  • '+currName+'
  • '; + var toggle_button = '
  • '+textContent+'
  • '; $('#items-list').append(toggle_button) @@ -296,6 +273,93 @@ function addPattern(){ patternList.children[patternList.children.length-1].dispatchEvent(new Event('click',{target: patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1]})); } +function addMovementAnim(){ + var toggle_button = '
  • Pause
  • '; + let el = document.createElement('li'); + el.innerText = "Pause" + + el.setAttribute('draggable',true) + el.addEventListener('dragstart', dragStart) + el.addEventListener('drop', droppedAnim) + el.addEventListener('dragenter', cancelDefault) + el.addEventListener('dragover', cancelDefault) + el.addEventListener('click',selectAnimation) + + animationList.appendChild(el) + + + + + + animationList.scrollTo({ + top: 1000000000, + left: 0, + behavior: "smooth", + }); + let movementComponent = selectedEntity.getAttribute('movement'); + if(selectedEntity.getAttribute('advanced').val){ + movementComponent.startPoints.push({x: 0, y: 0, z: -250}); + movementComponent.endPoints.push({x: 0, y: 0, z: -250}); + movementComponent.initialVelocities.push(0); + movementComponent.accelerations.push(0); + movementComponent.types.push('Pause'); + } else { + movementComponent.startPoints.push({theta: 0, y: 0, r: -250}); + movementComponent.endPoints.push({theta: 0, y: 0, r: -250}); + movementComponent.initialVelocities.push(0); + movementComponent.accelerations.push(0); + movementComponent.types.push('Pause'); + } + + + +} + +function removeMovementAnim(){ + // remove animation from entity + if(animationList.childElementCount == 0){ + return + } + stopMovement(selectedEntity) + + let index = parseInt(animationList.getAttribute('selectedIndex')) + let i = 0; + let counter = -1; + let movementComponent = selectedEntity.getAttribute('movement'); + // have to take into account rebounds from rubberband + while(i < movementComponent.types.length){ + if(movementComponent.types[i] != 'Rebound'){ + counter++; + if(counter == index){ + break + } + } + i++; + } + + + if(movementComponent.types[i] == 'Rubberband'){ + // need to remove this and also and rebound entry + movementComponent.types.splice(i,2) + movementComponent.startPoints.splice(i,2) + movementComponent.endPoints.splice(i,2) + movementComponent.initialVelocities.splice(i,2) + movementComponent.accelerations.splice(i,2) + } else { + movementComponent.types.splice(i,1) + movementComponent.startPoints.splice(i,1) + movementComponent.endPoints.splice(i,1) + movementComponent.initialVelocities.splice(i,1) + movementComponent.accelerations.splice(i,1) + } + animationList.removeChild(animationList.children[i]) + animationList.setAttribute('selectedIndex',"") + updateAnimationUI(selectedEntity,-1) + movementTypeIn.disabled = true + + updateJSON(); +} + /* removes current pattern from pattern list */ function removePattern(){ if(patternList.childElementCount == 0 || isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ @@ -317,7 +381,7 @@ function removePattern(){ children = [] revertChanges() indices.forEach(ind => { - delete scenes[packageSelect.value][patternList.children[ind].textContent] + delete scenes[packageSelect.value][patternList.children[ind].id] //if(names[packageSelect.value][patternList.children[ind].textContent] == 1){ // delete names[packageSelect.value][patternList.children[ind].textContent] //} @@ -332,6 +396,7 @@ function removePattern(){ /* function used to remove changes made to a scene */ function revertChanges(){ sky.setAttribute('material',{color: '#000000'}) + stopAll() while(entityCanvas.childElementCount != 0){ entityCanvas.removeChild(entityCanvas.children[0]) } @@ -350,11 +415,14 @@ function revertChanges(){ circularDotarrayNum = 0; bullseyeNum = 0; textureNum = 0; + timerNum = 0; + textNum = 0; numAdded = 0; selectedEntity = null; + movementKeyBinds = {} } -names = {'default' : {'red':1,'green':1,'blue':1,'white':1,'grille':1,'crosshair':1,'line':1,'circular dot array':1,'dot array':1,'checkerboard (w)':1,'checkerboard (b)':1,'ring_w5':1,'ring_w10':1,'ring_w20':1,'bullseye':1}} + var colorChange = true; /* handles switches to advanced mode */ @@ -363,18 +431,35 @@ function switchToAdvanced(e){ e.stopPropagation() newVal = !selectedEntity.getAttribute('advanced').val selectedEntity.setAttribute('advanced', {val: newVal}); + mov = selectedEntity.getAttribute('movement') + let i = 0; + while(i < mov.types.length){ + if(newVal){ + if(mov.endPoints[i].r != null){ + mov.endPoints[i] = {x: -mov.endPoints[i].r*Math.sin((mov.endPoints[i].theta*Math.PI)/180), y: mov.endPoints[i].y, z: mov.endPoints[i].r * Math.cos((mov.endPoints[i].theta*Math.PI)/180)} + mov.startPoints[i] = {x: -mov.startPoints[i].r*Math.sin((mov.startPoints[i].theta*Math.PI)/180), y: mov.startPoints[i].y, z: mov.startPoints[i].r * Math.cos((mov.startPoints[i].theta*Math.PI)/180)} + } + } else { + if(mov.endPoints[i].r == null){ + mov.endPoints[i] = {r: Math.sqrt(mov.endPoints[i].x*mov.endPoints[i].x+mov.endPoints[i].z*mov.endPoints[i].z), theta: Math.atan(mov.endPoints[i].z/mov.endPoints[i].x), y: mov.endPoints[i].y} + mov.startPoints[i] = {r: Math.sqrt(mov.startPoints[i].x*mov.startPoints[i].x+mov.startPoints[i].z*mov.startPoints[i].z), theta: Math.atan(mov.startPoints[i].z/mov.startPoints[i].x), y: mov.startPoints[i].y} + } + } + i++; + } + selectedEntity.setAttribute('movement',mov) + endZ.disabled = !endZ.disabled advanced.style.backgroundColor == '' ? advanced.style.backgroundColor = '#00FF00' : advanced.style.backgroundColor ='' updateStats() - editEntity() } /* listens for entering vr and removes restrictions on clicking through patterns */ -scene.addEventListener('enter-vr',function(){ +scene.addEventListener('enter-vr',function(e){ block = false; if(isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ alert('No scene is selected, please exit immersive mode and select a scene.') } - + controlsInterval = setInterval(findControls,10); }); /* listens for exiting vr and restricts clicking through patterns */ @@ -423,7 +508,12 @@ async function highlightSelection(ent){ colNum = ent.children[0].children.length; //tileSizeNum = ent.children[0].children[0].components.geometry.attrValue.width; newGeom = {primitive: 'ring', radiusOuter: ent.children[0].components.geometry.attrValue.radiusOuter*1.5, radiusInner: 0}; - } + } else if(selectedEntity.id.includes("text") || selectedEntity.id.includes("timer")){ + let width = ent.components.text.attrValue.value.length * (ent.components.text.attrValue.width / ent.components.text.attrValue.wrapCount); + let height = ent.components.text.attrValue.height; + newGeom = {primitive: 'plane', width: width, height: height}; + } + tmp = document.createElement('a-entity'); tmp.setAttribute('id','tmp') tmp.setAttribute("geometry",newGeom); @@ -461,6 +551,10 @@ function renamePattern(){ alert('No pattern selected') return } + if(patternList.getAttribute('multi-select') == "true"){ + alert('Multiple patterns selected') + return + } let patternName = prompt("Enter a pattern name: ") if(patternName == null){ return @@ -477,7 +571,7 @@ function renamePattern(){ alert('A pattern with this name already exists'); return; } - oldName = patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].textContent + oldName = patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id currScene = scenes[packageSelect.value][oldName] currName = '' if(Object.keys(names).indexOf(patternName) == -1){ @@ -490,7 +584,13 @@ function renamePattern(){ scenes[packageSelect.value][currName] = currScene delete scenes[packageSelect.value][oldName] - patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].textContent = currName + patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id = currName + + if(currName.length > 20){ + patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].innerText = currName.substring(0,20)+"..." + } else { + patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].innerText = currName + } } let clipboard; @@ -509,7 +609,7 @@ function copyPattern(){ } clipboard = {} indices.forEach(ind => { - clipboard[patternList.children[ind].textContent] = scenes[packageSelect.value][patternList.children[ind].textContent] + clipboard[patternList.children[ind].id] = scenes[packageSelect.value][patternList.children[ind].id] }) } @@ -598,7 +698,12 @@ function changePackage(){ patternList.removeChild(patternList.children[0]) } Object.keys(scenes[packageSelect.value]).forEach(currName =>{ - var toggle_button = '
  • '+currName+'
  • '; + let textContent = currName; + if(currName.length > 20){ + textContent = currName.substring(0,20)+"..." + } + + var toggle_button = '
  • '+textContent+'
  • '; $('#items-list').append(toggle_button) @@ -627,7 +732,6 @@ function renamePackage(){ while(packageName == ""){ packageName = prompt('Please enter a valid package name'); } - console.log(packageName) const re = /^[a-zA-Z0-9-_ ]+$/ if(!re.test(packageName)){ alert('Package name is invalid. '+ packageName +' Limit names to only alphanumerics, -, _, or spaces.') @@ -646,7 +750,7 @@ function renamePackage(){ delete scenes[packageSelect.value] packageSelect.options[packageSelect.selectedIndex].value = packageName packageSelect.options[packageSelect.selectedIndex].text = packageName - //patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].textContent = currName + //patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id = currName } function pastePattern(){ @@ -662,11 +766,18 @@ function pastePattern(){ if(names[packageSelect.value][currName]){ currName = currName + ' ('+names[packageSelect.value][currName]+')' names[packageSelect.value][name.split(' (')[0]] = names[packageSelect.value][name.split(' (')[0]] + 1 + } else { names[packageSelect.value][currName] = 1 } + scenes[packageSelect.value][currName] = clipboard[name] - var toggle_button = '
  • '+currName+'
  • '; + let textContent = currName; + if(currName.length > 20){ + textContent = currName.substring(0,20)+"..." + } + + var toggle_button = '
  • '+textContent+'
  • '; $('#items-list').append(toggle_button) @@ -697,3 +808,49 @@ function cutPattern(){ removePattern() } + +var time; +var timeElapsed = 0; +function startTimer(){ + time = setInterval(() => { + timeElapsed += 10; + let timer = document.getElementById('timer0') + let textVal = timer.getAttribute('text') + let time = Math.floor(timeElapsed) + let minutes = Math.floor(time/1000/60) + time -= minutes*1000*60 + if(minutes < 10){ + minutes = "0"+minutes + } else { + minutes = ""+minutes + } + + let seconds = Math.floor(time/1000) + time -= seconds*1000 + if(seconds < 10){ + seconds = "0"+seconds + } else { + seconds = ""+seconds + } + if(time < 10){ + time = "00"+time + } else if(time < 100){ + time = "0"+time + } else { + time = ""+time + } + + textVal.value = minutes+":"+seconds+":"+time[0]+time[1]+" " + timer.setAttribute('text',textVal) + + },10) +} + +function toggleConsole() { + if(debug.style.display == "block"){ + debug.style.display = "none"; + } else { + debug.style.display = "block"; + + } +} \ No newline at end of file diff --git a/Custom/utils/entity/advanced.js b/Custom/utils/entity/advanced.js new file mode 100644 index 0000000..9edaf75 --- /dev/null +++ b/Custom/utils/entity/advanced.js @@ -0,0 +1,27 @@ +/* handles switches to advanced mode */ +val = false; +function switchToAdvanced(e){ + e.stopPropagation() + newVal = !selectedEntity.getAttribute('advanced').val + selectedEntity.setAttribute('advanced', {val: newVal}); + mov = selectedEntity.getAttribute('movement') + let i = 0; + while(i < mov.types.length){ + if(newVal){ + if(mov.endPoints[i].r != null){ + mov.endPoints[i] = {x: -mov.endPoints[i].r*Math.sin((mov.endPoints[i].theta*Math.PI)/180), y: mov.endPoints[i].y, z: mov.endPoints[i].r * Math.cos((mov.endPoints[i].theta*Math.PI)/180)} + mov.startPoints[i] = {x: -mov.startPoints[i].r*Math.sin((mov.startPoints[i].theta*Math.PI)/180), y: mov.startPoints[i].y, z: mov.startPoints[i].r * Math.cos((mov.startPoints[i].theta*Math.PI)/180)} + } + } else { + if(mov.endPoints[i].r == null){ + mov.endPoints[i] = {r: Math.sqrt(mov.endPoints[i].x*mov.endPoints[i].x+mov.endPoints[i].z*mov.endPoints[i].z), theta: Math.atan(mov.endPoints[i].z/mov.endPoints[i].x), y: mov.endPoints[i].y} + mov.startPoints[i] = {r: Math.sqrt(mov.startPoints[i].x*mov.startPoints[i].x+mov.startPoints[i].z*mov.startPoints[i].z), theta: Math.atan(mov.startPoints[i].z/mov.startPoints[i].x), y: mov.startPoints[i].y} + } + } + i++; + } + selectedEntity.setAttribute('movement',mov) + endZ.disabled = !endZ.disabled + advanced.style.backgroundColor == '' ? advanced.style.backgroundColor = '#00FF00' : advanced.style.backgroundColor ='' + updateStats() + } \ No newline at end of file diff --git a/Custom/utils/entity/duplicateEntity.js b/Custom/utils/entity/duplicateEntity.js new file mode 100644 index 0000000..f9d198d --- /dev/null +++ b/Custom/utils/entity/duplicateEntity.js @@ -0,0 +1,92 @@ +/* Handles entity duplication */ + +function duplicateEntity(){ + if(selectedEntity == null){ + alert('No entities added'); + return + } + // get entity id and increment by 1 + key = selectedEntity.getAttribute("id"); + el = document.createElement("a-entity"); /* create entity */ + if(key.includes("circle")){ /* circle exclusive */ + el.setAttribute("id","circle"+circleNum++); + el.setAttribute("geometry", scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].geometry); + el.setAttribute("fill",scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].fill); + } else if(key.includes("plane")){ /* plane exclusive */ + el.setAttribute("id","plane"+planeNum++); + el.setAttribute("geometry", scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].geometry); + if(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].material.src == ""){ + drawPlaneBorder(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].widthReal,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].geometry.height,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].fill.val,hexToRgb(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].material.color),el); + } + el.setAttribute("fill",scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].fill); + } else if(key.includes("triangle")){ /* triangle exclusive */ + el.setAttribute("id","triangle"+triangleNum++); + el.setAttribute("geometry", scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].geometry); + } else if (key.includes("gradient")){ + el.setAttribute("id", "gradient"+gradientNum++); + drawGradient(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].childGeometry.width,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].childGeometry.height,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].numBars,hexToRgb(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].material.color),hexToRgb(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].color2.val),el); + el.setAttribute('color2',scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].color2) + } else if (key.includes("grille")){ + el.setAttribute("id", "grille"+grilleNum++); + drawGrille(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].childGeometry.width,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].childGeometry.height,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].numBars,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].material.color,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].color2.val,el); + el.setAttribute('color2',scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].color2) + } else if (key.includes("checkerboard")){ + el.setAttribute("id", "checkerboard"+checkerboardNum++); + drawCheckerboard(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].rows,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].cols,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].tileSize,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].material.color,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].color2.val,el); + el.setAttribute('color2',scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].color2) + } else if (key.includes("circularDotarray")){ + el.setAttribute("id", "circularDotarray"+circularDotarrayNum++); + drawCircularDotArray(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].arraySpacing.val,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].circles,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].dots,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].circleSize,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].material.color,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].toggleCenterDot.val,el); + el.setAttribute("arraySpacing",scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].arraySpacing); + el.setAttribute("toggleCenterDot",scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].toggleCenterDot); + } else if (key.includes("dotarray")){ + el.setAttribute("id", "dotarray"+dotarrayNum++); + drawDotArray(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].rows,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].cols,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].circleSize,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].spacing.val,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].material.color,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].toggleCenterDot.val,el); + el.setAttribute("arraySpacing",scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].spacing); + el.setAttribute("toggleCenterDot",scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].toggleCenterDot); + } else if (key.includes("bullseye")){ + el.setAttribute("id", "bullseye"+bullseyeNum++); + drawBullseye(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].ringPitch,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].numRings,scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].material.color,el); + } else if(key.includes("text")){ + el.setAttribute("id", "text"+textNum++); + el.setAttribute("text",scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].text); + } else if(key.includes("timer")){ + el.setAttribute("id", "timer"+timerNum++); + el.setAttribute("text",scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].text); + } + + /* sets stats */ + el.setAttribute("angle", scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].angle); + el.setAttribute("advanced", scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].advanced); + el.setAttribute("position", scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].movement.origin); + let mat = null; + if(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].material){ + mat = JSON.parse(JSON.stringify(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].material)) + + for(let i = 0; i < texture.options.length; i++){ + if(texture.options[i].text == mat.src){ + mat.src = "#"+texture.options[i].value; + break; + } + } + } + + el.setAttribute("material", mat); + el.setAttribute("rotation", scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].rotation); + + el.setAttribute("movement", JSON.parse(JSON.stringify(scenes[packageSelect.value][patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id][key].movement))); + + el.setAttribute("click-checker",""); + numAdded++; + entityCanvas.appendChild(el); /* adds entity to scene */ + + /* adds option to dropdown */ + var option = document.createElement("option"); + option.text = el.getAttribute("id"); + entitySelector.add(option); + + els.push(el);/* adds entity to list of created entities */ + pool.push(el.object3D); + updateJSON(); + setTimeout(() => { selectNew(el) }, 100); +} \ No newline at end of file diff --git a/Custom/utils/entity/removeEntity.js b/Custom/utils/entity/removeEntity.js new file mode 100644 index 0000000..12797fc --- /dev/null +++ b/Custom/utils/entity/removeEntity.js @@ -0,0 +1,27 @@ +/* Removes current entity */ +function removeEntity(){ + if(selectedEntity == null){ + alert('No entities added'); + return + } + let conf = confirm("Delete the selected entity?"); + if(!conf){ + return + } + els.splice(els.indexOf(selectedEntity),1); + pool.splice(pool.indexOf(selectedEntity.object3D),1); + entityCanvas.removeChild(selectedEntity); + for (var i=0; i { + + }); + +} + +function sleep (par1, par2) { + return new Promise((resolve) => { + setTimeout(() => resolve(clearHighlight(par1,par2)), 1000) + }) + } + + async function clearHighlight(ent){ + let i = ent.children.length-1; + while (i >= 0) { + if(ent.children[i].getAttribute('id') == 'tmp'){ + ent.children[i].parentNode.removeChild(ent.children[i]); + i--; + } + i--; + } +} \ No newline at end of file diff --git a/Custom/utils/extra/clipboard.js b/Custom/utils/extra/clipboard.js new file mode 100644 index 0000000..df99782 --- /dev/null +++ b/Custom/utils/extra/clipboard.js @@ -0,0 +1,76 @@ +/* Handles code related to copying, cutting, and pasting */ + +// copies a pattern or group of patterns +let clipboard; +function copyPattern(){ + if(isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ + alert('No pattern selected') + return + } + let i = 0; + indices = [] + while(i < patternList.children.length){ + if(patternList.children[i].style.background == 'rgb(243, 152, 20)'){ + indices.push(i) + } + i++; + } + clipboard = {} + indices.forEach(ind => { + clipboard[patternList.children[ind].id] = scenes[packageSelect.value][patternList.children[ind].id] + }) +} + +// pastes a pattern or group of patterns +function pastePattern(){ + if(clipboard == null){ + return; + } + + Object.keys(clipboard).forEach(name =>{ + currName = name.split(' (')[0]; + if(names[packageSelect.value][currName]){ + currName = currName + ' ('+names[packageSelect.value][currName]+')' + names[packageSelect.value][name.split(' (')[0]] = names[packageSelect.value][name.split(' (')[0]] + 1 + + } else { + names[packageSelect.value][currName] = 1 + } + + scenes[packageSelect.value][currName] = clipboard[name] + let textContent = currName; + if(currName.length > 20){ + textContent = currName.substring(0,20)+"..." + } + + var toggle_button = '
  • '+textContent+'
  • '; + + $('#items-list').append(toggle_button) + + item = document.getElementById(currName) + $(item).prop('draggable', true) + item.addEventListener('dragstart', dragStart) + item.addEventListener('drop', dropped) + item.addEventListener('dragenter', cancelDefault) + item.addEventListener('dragover', cancelDefault) + item.addEventListener('click',selectPattern) + }) + patternList.scrollTo({ + top: 1000000000, + left: 0, + behavior: "smooth", + }); + +} + +// cuts a pattern or group of patterns +function cutPattern(){ + if(isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ + alert('No pattern selected') + return + } + keysPressed["ctrl"] = false; + keysPressed["x"] = false; + copyPattern() + removePattern() +} diff --git a/Custom/utils/extra/consoleDump.js b/Custom/utils/extra/consoleDump.js new file mode 100644 index 0000000..4ab9df6 --- /dev/null +++ b/Custom/utils/extra/consoleDump.js @@ -0,0 +1,36 @@ +// SOURCE https://stackoverflow.com/questions/16616722/sending-all-javascript-console-output-into-a-dom-element +// Writes console information to the debug div which can be opened by pressing ctrl+i + +var baseLogFunction = console.log; +console.log = function(){ + baseLogFunction.apply(console, arguments); + + var args = Array.prototype.slice.call(arguments); + for(var i=0;i { + Object.keys(value[pat]).forEach((el) => { + // if there is an uploaded texture being used + // save it and its formatting information + if(value[pat][el].material && value[pat][el].material.src && value[pat][el].material.src != '' && uploadedTextureFormat[value[pat][el].material.src]){ + let src = textureVals[value[pat][el].material.src]; + let format = uploadedTextureFormat[value[pat][el].material.src] + if(!textures[value[pat][el].material.src]){ + textures[value[pat][el].material.src] = [src,format]; + } + } + }) + }) + + // compress the package content + value = LZString.compressToBase64(JSON.stringify(value)) + + let arr = [value,textures] + + // get the contents of localStorage + let localScenes = JSON.parse(localStorage.getItem('packages')) + + // check if replacing existing package + let ind = null; + let j = 0; + localScenes.forEach((package) => { + let name = Object.keys(package)[0] + if(encodeURIComponent(key.split(' (')[0]) == key.split(' (')[0]){ + // check for exact name match + if(name.split(' (')[0] === key.split(' (')[0]){ + ind = j + } + } else { + // check for link match + if(name.includes(key.split('(')[1].split(')')[0])){ + ind = j + } + } + j++; + }) + + + + sum = 0; + let max = localScenes.length-1 + for(let i = 0; i < max; i++){ + if(ind && i == ind){ + continue; + } + sum += ByteSize(localScenes[i]) + } + + + // create the object + let packageToInsert = {} + packageToInsert[key] = arr + + console.log(packageToInsert) + // strinify the object + let stringified = JSON.stringify(packageToInsert) + + // check if package is larger than localStorage space allocation + // 2 comes from the opening and closing braces [] + if(ByteSize(stringified) >= 5242880-2){ + alert('The package '+key+' is too large to save.') + return false + } + + // if we have space + + // check if we are replacing an existing package with a new one + if(ind != null){ + // prompt if this is ok + let allowDelete = confirm('This process will replace the saved package: \n'+ Object.keys(localScenes[ind])[0]+'\nPress OK to confirm.') + if(!allowDelete){ + return true + } + localScenes.splice(ind,1) + max--; + } else { + let deleteCount = 0 + if(localScenes.length == 20 && sum + 2 + ByteSize(stringified) < 5242880){ + // insert without deleting + deleteCount++; + max--; + } else if(sum + 2 + ByteSize(stringified) >= 5242880){ + // check how many we would need to delete + // not optimal + while(sum + 2 + ByteSize(stringified) >= 5242880){ + sum -= ByteSize(JSON.stringify(localScenes[max])) + deleteCount++; + max--; + } + + } + + // check if user permits delete and then proceed + if(deleteCount > 0){ + let allowDelete = confirm('This process will delete '+ deleteCount +' previously saved packages. Press OK to confirm.') + if(!allowDelete){ + return true + } + localScenes.splice(max+1,deleteCount) + } + + } + + // add package to the beginning of localStorage array + localScenes.unshift(packageToInsert) + localStorage.setItem('packages',JSON.stringify(localScenes)) + + return true; // return success +} + +// Called when a package is selected from one of the local storage options +async function changeUrl(){ + if(recentPackages.value == "none"){ + return + } + // only 10 packages allowed in the browser at once + if(packageSelect.options.length == 10){ + alert('There are already 10 packages.') + return; + } + + // get contents of local storage + let localArr = JSON.parse(localStorage.getItem('packages')) + + // get the desired package from localStorage + let key = Object.keys(localArr[recentPackages.selectedIndex])[0] + + // prompt user for a name for the package and validate the input + let packageName = prompt("Enter a name for this package: ", key) + const re = /^[a-zA-Z0-9-_ ]+$/ + while(packageName == null || packageName == "" || !re.test(packageName) || scenes[packageName] != null){ + if(packageName == null){ + return + } else if(packageName == ""){ + packageName = prompt("Enter a valid package name: ") + } else if(!re.test(packageName)){ + packageName = prompt('Package name is invalid. Limit names to only alphanumerics, - , _ , or spaces.') + } else if(scenes[packageName] != null){ + packageName = prompt('A pattern with this name already exists'); + } + + } + + // add the selected package to the options list + packageSelect.options.add(new Option(packageName,packageName)) + packages[packageName] = '' + scenes[packageName] = JSON.parse(LZString.decompressFromBase64(localArr[recentPackages.selectedIndex][key][0])) + packageSelect.value = packageName + + // read in the uploaded textures and add them to the existing collection + let len2 = texture.options.length; + currTextures = [] + while(i < len2){ + currTextures.push(texture.options[i].text) + i++; + } + i = 0; + + uploadedTextures = [] + newUploadedTextureFormats = [] + Object.keys(localArr[recentPackages.selectedIndex][key][1]).forEach((texture) => { + uploadedTextures.push(texture) + newUploadedTextureFormats.push(texture) + }) + newTextures = [...new Set([...uploadedTextures,...currTextures])] + newTextures.forEach(text => { + + if(uploadedTextures.indexOf(text) != -1 && currTextures.indexOf(text) == -1){ + var option = document.createElement("option"); + option.text = text + option.value = localArr[recentPackages.selectedIndex][key][1][text][0] + texture.add(option); + } + + + }) + + // combines uploaded textures in uploaded file and current file + newUploadedTextureFormat = [...new Set([...Object.keys(uploadedTextureFormat),...newUploadedTextureFormats])] + tmp = {} + newUploadedTextureFormat.forEach(texture => { + if(Object.keys(uploadedTextureFormat).indexOf(texture) != -1){ + tmp[texture] = uploadedTextureFormat[texture] + } else { + tmp[texture] = localArr[recentPackages.selectedIndex][key][1][texture][1] + } + }); + uploadedTextureFormat = tmp + + + // update the link + if(!key.includes('(upload)') && !window.location.href.includes(key.split('(')[1].split(')')[0])){ + if(!window.location.href.includes('?')){ + let newURL = window.location.href + "?id=" +key.split('(')[1].split(')')[0] + window.history.pushState('object', document.title, newURL); + } else { + let newURL = ''; + newURL = window.location.href + "," +key.split('(')[1].split(')')[0] + window.history.pushState('object', document.title, newURL); + } + } + + changePackage(); // invoke the function to change packages to the selected option +} + +// condensed function to get size of a string +function ByteSize(str){ + return new Blob([str]).size +} \ No newline at end of file diff --git a/Custom/utils/localStorage/clearLocal.js b/Custom/utils/localStorage/clearLocal.js new file mode 100644 index 0000000..ad84ac5 --- /dev/null +++ b/Custom/utils/localStorage/clearLocal.js @@ -0,0 +1,13 @@ +// contains code related to clearing local storage + +function clearLocal() { + if(!confirm("This action will clear locally saved packages. Are you sure you would like to continue?")){ + return; + } + localStorage.setItem('packages',JSON.stringify([])); + + recentPackages.innerHTML = ""; + + alert("Local Storage Cleared!") +} + diff --git a/Custom/utils/localStorage/loadExisting.js b/Custom/utils/localStorage/loadExisting.js new file mode 100644 index 0000000..1b4f5b8 --- /dev/null +++ b/Custom/utils/localStorage/loadExisting.js @@ -0,0 +1,61 @@ +/* On page load, fetch all packages that are contained within the link. + Local storage is updated to reflect links. */ + + window.onload = async function() { + // on page load get rid of the aframe cursor + scene.canvas.classList.remove("a-grab-cursor") + + // if there is nothing in localStorage, instantiate an empty packages array + if(localStorage.getItem('packages') == null){ + localStorage.setItem('packages',JSON.stringify([])) + } + + // go through url and fetch desired packages + let thisPage = new URL(window.location); + if(thisPage.searchParams.size != 0){ + // for each search parameter + for (const id of thisPage.searchParams.get('id').split(',')) { + //if the parameter is a pastebin id + + if(encodeURIComponent(id) == id){ + const res = await pastebinFetch('https://didsr.pythonanywhere.com/webxrtools/get?id='+id,true); + } else { + // otherwise the id is an uploaded link + const res = await pastebinFetch(decodeURIComponent(id),true); + } + } + } + + + + + // fetch the packages array from localStorage + let localArr = JSON.parse(localStorage.getItem('packages')) + + // for each package in the array + localArr.forEach((package) => { + // get the name of the package + let key = Object.keys(package)[0] + + // add the package as an option to the recent pacakages dropdown + var option = document.createElement("option"); + option.text = key.split(' (')[0] + option.value = key + recentPackages.add(option); + + }) + recentPackages.selectedIndex = -1 // ensure that there are no selected recent packages + + // update the names dictionary for the default package on load + // done dynamically since we add/remove default patterns + let patternNames = Object.keys(scenes['default']); + patternNames.forEach((patternName) => { + // check if we have some type of name like test (1) and remove the (1) + if(patternName.split(' (').length > 1){ + patternName = patternName.split(' (')[0] + } + names['default'][patternName] = names['default'][patternName] ? names['default'][patternName] + 1 : 1; + }) + + + }; \ No newline at end of file diff --git a/Custom/utils/package/addRemovePacakge.js b/Custom/utils/package/addRemovePacakge.js new file mode 100644 index 0000000..6f2cfcd --- /dev/null +++ b/Custom/utils/package/addRemovePacakge.js @@ -0,0 +1,81 @@ +/* Handles code related to adding or removing packages */ + +// adds a package +function addPackage(){ + flag = false; + let i = 0; + if(packageSelect.options.length == 10){ + alert('There are already 10 packages.') + return; + } + let packageName = prompt("Enter a package name: "); + if(packageName == null){ + return + } + while(packageName == ""){ + packageName = prompt('Please enter a valid package name'); + } + const re = /^[a-zA-Z0-9-_ ]+$/ + if(!re.test(packageName)){ + alert('Package name is invalid. '+ packageName +' Limit names to only alphanumerics, -, _, or spaces.') + return; + } + if(scenes[packageName] != null){ + alert('A package with this name already exists'); + return; + } + while(i < packageSelect.options.length){ + if(packageSelect.options[i].value == packageName){ + alert('A package with this name already exists'); + return + } + i++; + } + + packageSelect.options.add(new Option(packageName,packageName)) + packages[packageName] = '' + scenes[packageName] = {} + names[packageName] = {} + packageSelect.value = packageName + changePackage(); +} + +// removes a package +function removePackage(){ + if(packageSelect.value == ''){ + alert('No package selected') + return + } + let conf = confirm("Delete the selected package: "+packageSelect.value); + packageName = packageSelect.value + if(!conf){ + return + } + i = 0; + while(i < packageSelect.options.length){ + if(packageSelect.options[i].value == packageName){ + packageSelect.remove(i) + } + i++; + } + while(patternList.childElementCount != 0){ + patternList.removeChild(patternList.children[0]) + } + delete scenes[packageName] + delete names[packageName] + revertChanges() + if(packageSelect.value != ''){ + changePackage(); + } + patternList.setAttribute('selectedIndex',null); + if(packages[packageName] != ''){ + let newURL = window.location.href.replace(","+encodeURIComponent(packages[packageName]),'') + newURL = newURL.replace(encodeURIComponent(packages[packageName])+",",'') + newURL = newURL.replace(encodeURIComponent(packages[packageName]),'') + if(newURL.split("?id=")[1] == ''){ + newURL = newURL.split("?id=")[0] + } + window.history.pushState('object', document.title, newURL); + } + delete packages[packageName] +} \ No newline at end of file diff --git a/Custom/utils/package/changePackage.js b/Custom/utils/package/changePackage.js new file mode 100644 index 0000000..b788481 --- /dev/null +++ b/Custom/utils/package/changePackage.js @@ -0,0 +1,29 @@ +/* Selects a new package */ +function changePackage(){ + let i = 0; + patternList.setAttribute('selectedIndex',"") + while(patternList.children.length != 0){ + patternList.removeChild(patternList.children[0]) + } + Object.keys(scenes[packageSelect.value]).forEach(currName =>{ + let textContent = currName; + if(currName.length > 20){ + textContent = currName.substring(0,20)+"..." + } + + var toggle_button = '
  • '+textContent+'
  • '; + + + $('#items-list').append(toggle_button) + + item = document.getElementById(currName) + $(item).prop('draggable', true) + item.addEventListener('dragstart', dragStart) + item.addEventListener('drop', dropped) + item.addEventListener('dragenter', cancelDefault) + item.addEventListener('dragover', cancelDefault) + item.addEventListener('click',selectPattern) + } + ) + revertChanges() +} \ No newline at end of file diff --git a/Custom/utils/package/renamePackage.js b/Custom/utils/package/renamePackage.js new file mode 100644 index 0000000..226ccc2 --- /dev/null +++ b/Custom/utils/package/renamePackage.js @@ -0,0 +1,33 @@ +/* Renames a package */ +function renamePackage(){ + if(packageSelect.options.length == 0){ + alert('No package selected') + return + } + let packageName = prompt("Enter a package name: "); + if(packageName == null){ + return + } + while(packageName == ""){ + packageName = prompt('Please enter a valid package name'); + } + const re = /^[a-zA-Z0-9-_ ]+$/ + if(!re.test(packageName)){ + alert('Package name is invalid. '+ packageName +' Limit names to only alphanumerics, -, _, or spaces.') + return; + } + if(scenes[packageName] != null){ + alert('A package with this name already exists'); + return; + } + + + oldName = packageSelect.value + currPackage = scenes[packageSelect.value] + + scenes[packageName] = currPackage + delete scenes[packageSelect.value] + packageSelect.options[packageSelect.selectedIndex].value = packageName + packageSelect.options[packageSelect.selectedIndex].text = packageName + +} \ No newline at end of file diff --git a/Custom/utils/scene/addRemoveScene.js b/Custom/utils/scene/addRemoveScene.js new file mode 100644 index 0000000..9b24dcc --- /dev/null +++ b/Custom/utils/scene/addRemoveScene.js @@ -0,0 +1,87 @@ +/* Contains code to add or remove patterns */ + +/* adds a pattern to pattern list */ +function addPattern(){ + if(packageSelect.value == ''){ + alert('No package selected') + return + } + let patternName = prompt("Enter a pattern name: ") + if(patternName == null){ + return + } + while(patternName == ""){ + patternName = prompt("Enter a valid pattern name: ") + } + const re = /^[a-zA-Z0-9-_ ]+$/ + if(!re.test(patternName)){ + alert('Pattern name is invalid. '+ patternName +' Limit names to only alphanumerics, - , _ , or spaces.') + return; + } + if(scenes[packageSelect.value][patternName] != null){ + alert('A pattern with this name already exists'); + return; + } + + currName = '' + if(Object.keys(names[packageSelect.value]).indexOf(patternName) == -1){ + names[packageSelect.value][patternName] = 1 + currName = patternName + } else { + currName = patternName+' ('+names[packageSelect.value][patternName]+')' + names[packageSelect.value][patternName] = names[packageSelect.value][patternName] + 1 + } + let textContent = currName; + if(currName.length > 20){ + textContent = currName.substring(0,20)+"..." + } + scenes[packageSelect.value][currName] = {sky: {skyColor: '#000000'}} + var toggle_button = '
  • '+textContent+'
  • '; + + $('#items-list').append(toggle_button) + + item = document.getElementById(currName) + $(item).prop('draggable', true) + item.addEventListener('dragstart', dragStart) + item.addEventListener('drop', dropped) + item.addEventListener('dragenter', cancelDefault) + item.addEventListener('dragover', cancelDefault) + item.addEventListener('click',selectPattern) + + patternList.scrollTo({ + top: 1000000000, + left: 0, + behavior: "smooth", + }); + patternList.children[patternList.children.length-1].dispatchEvent(new Event('click',{target: patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1]})); +} + +/* removes current pattern from pattern list */ +function removePattern(){ + if(patternList.childElementCount == 0 || isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ + alert('No pattern selected') + return + } + let conf = confirm("Delete the selected pattern?"); + if(!conf){ + return + } + let i = 0; + indices = [] + while(i < patternList.children.length){ + if(patternList.children[i].style.background == 'rgb(243, 152, 20)'){ + indices.push(i) + } + i++; + } + children = [] + revertChanges() + indices.forEach(ind => { + delete scenes[packageSelect.value][patternList.children[ind].id] + children.push(patternList.children[ind]) + }) + children.forEach(child =>{ + patternList.removeChild(child) + }) + patternList.setAttribute('selectedIndex',null); +} \ No newline at end of file diff --git a/Custom/utils/scene/displayNext.js b/Custom/utils/scene/displayNext.js new file mode 100644 index 0000000..9bd3600 --- /dev/null +++ b/Custom/utils/scene/displayNext.js @@ -0,0 +1,28 @@ +/* used to click through current pattern */ +function displayNext(direction){ + if(direction){ + // right + if(isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ + return + } + if(parseInt(patternList.getAttribute('selectedIndex')) == patternList.children.length-1){ + patternList.children[0].dispatchEvent(new Event('click',{target: patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1]})); + } else { + patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1].dispatchEvent(new Event('click',{target: patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1]})); + } + + } else { + // left + if(isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ + return + } + if(parseInt(patternList.getAttribute('selectedIndex')) == 0){ + patternList.children[patternList.children.length-1].dispatchEvent(new Event('click',{target: patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1]})); + } else { + patternList.children[parseInt(patternList.getAttribute('selectedIndex'))-1].dispatchEvent(new Event('click',{target: patternList.children[parseInt(patternList.getAttribute('selectedIndex'))+1]})); + } + + } + + $('#patternDisplay').trigger('change'); +} \ No newline at end of file diff --git a/Custom/utils/scene/rename.js b/Custom/utils/scene/rename.js new file mode 100644 index 0000000..268c312 --- /dev/null +++ b/Custom/utils/scene/rename.js @@ -0,0 +1,47 @@ +/* Renames a pattern */ +function renamePattern(){ + if(isNaN(parseInt(patternList.getAttribute('selectedIndex')))){ + alert('No pattern selected') + return + } + if(patternList.getAttribute('multi-select') == "true"){ + alert('Multiple patterns selected') + return + } + let patternName = prompt("Enter a pattern name: ") + if(patternName == null){ + return + } + while(patternName == "" ){ + patternName = prompt("Enter a valid pattern name: ") + } + const re = /^[a-zA-Z0-9-_ ]+$/ + if(!re.test(patternName)){ + alert('Pattern name is invalid. '+ patternName +' Limit names to only alphanumerics, - , _ , or spaces.') + return; + } + if(scenes[packageSelect.value][patternName] != null){ + alert('A pattern with this name already exists'); + return; + } + oldName = patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id + currScene = scenes[packageSelect.value][oldName] + currName = '' + if(Object.keys(names).indexOf(patternName) == -1){ + names[patternName] = 1 + currName = patternName + } else { + names[patternName] = names[patternName] + 1 + currName = patternName+''+names[patternName] + } + scenes[packageSelect.value][currName] = currScene + delete scenes[packageSelect.value][oldName] + + patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].id = currName + + if(currName.length > 20){ + patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].innerText = currName.substring(0,20)+"..." + } else { + patternList.children[parseInt(patternList.getAttribute('selectedIndex'))].innerText = currName + } +} \ No newline at end of file diff --git a/Custom/utils/scene/resetScene.js b/Custom/utils/scene/resetScene.js new file mode 100644 index 0000000..be5c22e --- /dev/null +++ b/Custom/utils/scene/resetScene.js @@ -0,0 +1,60 @@ +/* resets current scene */ +function resetScene(){ + let conf = confirm("Reset the selected pattern?"); + if(!conf){ + return + } + planeNum = 0; + circleNum = 0; + triangleNum = 0; + gradientNum = 0; + checkerboardNum = 0; + grilleNum = 0; + dotarrayNum = 0; + circularDotarrayNum = 0; + bullseyeNum = 0; + timerNum = 0; + textNum = 0; + stopAllMovement(); + movementKeyBinds = {} + while(entityCanvas.childElementCount != 0){ + entityCanvas.removeChild(entityCanvas.children[0]) + } + while(entitySelector.childElementCount != 0){ + entitySelector.remove(entitySelector.children[0]) + } + movementIcon.className = "fa-solid fa-play" + $('#skyCol').minicolors('value', '#000000'); + els = [] + updateJSON() + selectedEntity = null +} + +/* function used to remove changes made to a scene */ +function revertChanges(){ + sky.setAttribute('material',{color: '#000000'}) + stopAll() + while(entityCanvas.childElementCount != 0){ + entityCanvas.removeChild(entityCanvas.children[0]) + } + while(entitySelector.childElementCount != 0){ + entitySelector.remove(entitySelector.children[0]) + } + els = [] + pool = [] + circleNum = 0; /* number of circles created */ + planeNum = 0; /* number of planes created */ + triangleNum = 0; /* number of triangles created */ + gradientNum = 0; /* number of gradients created */ + checkerboardNum = 0; /* number of checkerboards created */ + grilleNum = 0; + dotarrayNum = 0; + circularDotarrayNum = 0; + bullseyeNum = 0; + textureNum = 0; + timerNum = 0; + textNum = 0; + numAdded = 0; + selectedEntity = null; + movementKeyBinds = {} +} \ No newline at end of file diff --git a/Custom/utils/shareableLink/export.js b/Custom/utils/shareableLink/export.js new file mode 100644 index 0000000..60fe901 --- /dev/null +++ b/Custom/utils/shareableLink/export.js @@ -0,0 +1,77 @@ +/* Posts content to pastebin. + Navigates to new link on success and returns false on failure. */ + async function pastebinPost(useTextures){ + // create object to be posted + let code = {filename: packageSelect.value, scenes: JSON.parse(JSON.stringify(scenes[packageSelect.value])), textures: {uploadedTextureFormats: {}, textureValues: []}, date: ""} + + // if we want to save textures + if(useTextures){ + // get all textures + textures = [] + i = 0; + while(i < texture.options.length){ + textures.push({val: texture.options[i].value, text: texture.options[i].text}); + i++; + } + code['textures']['textureValues'] = textures + code['textures']['uploadedTextureFormats'] = uploadedTextureFormat + } else { + // clear all textures + for (const pattern of Object.keys(code['scenes'])){ + for (const ent of Object.keys(code['scenes'][pattern])){ + if(ent.includes('plane')){ + code['scenes'][pattern][ent].material = {shader: code['scenes'][pattern][ent].material.shader, color: code['scenes'][pattern][ent].material.color, src: ''} + } + } + } + } + + // save date + code['date'] = new Date().toLocaleString(); + + // check size of package to ensure it can be posted + const size = new TextEncoder().encode(JSON.stringify(code)).length; + + // attempt to post package + let response = await fetch('https://didsr.pythonanywhere.com/webxrtools/share', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(code) + }).catch((error) => alert('Failed to post with error:\n '+error)); + + if(!response.ok){ + alert('Failed to post with error:\n '+response.status+"\n"+response.statusText) + return + } + + let text = await response.text(); + + try { + // update the link and copy the code to the clipboard + let thisPage = new URL(window.location); + newUrl = thisPage.origin+thisPage.pathname+"?id="+text + // store the package in local storage + manageLocalStorage(packageSelect.value+" ("+text+")", scenes[packageSelect.value]); + tmp = packages[packageSelect.value] + packages[packageSelect.value] = text + await navigator.clipboard.writeText(newUrl); + alert("URL copied to clipboard: "+ newUrl); + if(!window.location.href.includes('?')){ + let newURL = window.location.href + "?id=" +text + window.history.pushState('object', document.title, newURL); + } else { + let newURL = ''; + if(tmp != null && tmp != ''){ + newURL = window.location.href.replace(tmp,text) + } else { + newURL = window.location.href + "," +text + } + window.history.pushState('object', document.title, newURL); + } + } catch (err) { + console.error('Failed to copy: ', err); + return false + } + } \ No newline at end of file diff --git a/Custom/utils/shareableLink/import.js b/Custom/utils/shareableLink/import.js new file mode 100644 index 0000000..49d89a6 --- /dev/null +++ b/Custom/utils/shareableLink/import.js @@ -0,0 +1,277 @@ +// Contains all code relating to the implementation of the sharing feature + +/* Called when a link is provided for import. + Determines if the link is valid and calls appropriate handler. + Returns true on success and false on failure. */ + async function handleImport(){ + let thisPage = new URL(window.location); + if(packageSelect.options.length == 10){ + alert('There are already 10 packages.') + return; + } + let url = prompt('Enter the provided url: '); + if(url == null){ + return null; + } + + // handle links that come from the tool itself + if(url.includes(thisPage.origin) && url.includes('?id=')){ + let res = await localLinkImport(url).then( (res) => { + if(res){ + let ids = url.split("?id=")[1] + if(!window.location.href.includes('?')){ + let newURL = window.location.href + "?id=" +ids + window.history.pushState('object', document.title, newURL); + } else { + let newURL = window.location.href + "," +ids + window.history.pushState('object', document.title, newURL); + } + + alert('Success') + return true; + } else { + alert('Invalid link'); + return false; + } + }) + return res; + } else { // handle links that host valid JSON content + let res = await validateLink(url).then( async (res) => { + if(res){ + let res2 = await pastebinFetch(url).then((res) => { + return res; + }) + if(res2){ + if(url.includes('https://didsr.pythonanywhere.com/webxrtools/get?id=')){ + if(!window.location.href.includes('?')){ + let newURL = window.location.href + "?id=" +url.split("https://didsr.pythonanywhere.com/webxrtools/get?id=")[1] + window.history.pushState('object', document.title, newURL); + } else { + let newURL = window.location.href + "," +url.split("https://didsr.pythonanywhere.com/webxrtools/get?id=")[1] + window.history.pushState('object', document.title, newURL); + } + } else { + if(!window.location.href.includes('?')){ + let newURL = window.location.href + "?id=" +encodeURIComponent(url) + window.history.pushState('object', document.title, newURL); + } else { + let newURL = window.location.href + "," +encodeURIComponent(url) + window.history.pushState('object', document.title, newURL); + } + } + + alert('Success') + return true; + } + alert.log('Handling failed') + return false; + } else { + alert('Invalid link'); + return false; + } + }) + return res; + } + +} + +/* Handles links that originate from the tool. + Returns true on success and false on failure. */ +async function localLinkImport(url){ + ids = url.split('?id=')[1].split(','); + let i = 0; + while(i < ids.length){ + var res = pastebinFetch("https://didsr.pythonanywhere.com/webxrtools/get?id="+ids[i]).then((result) => { + return result + }); + if(!res){ + return res + } + i++; + } + return true +} + +/* Handles links that don't originate from the tool. + Returns true on success and false on failure. */ +async function validateLink(url){ + var fileContent = await fetch(url).then((res) => { + if(res.ok != true){ + return null + + } else { + return res.json() + } + }).catch((error) => alert('Failed to fetch from: '+url+' with error '+error)); + return validateJSON(fileContent) + +} + +/* Fetches content from pastebin or another valid JSON link. + Returns true on success and false on failure. */ + defaultNum = 1; +async function pastebinFetch(url,onload){ + + // fetch contents of JSON at url if it exists + var fileContent = await fetch(url).then((res) => { + if(res.ok != true){ + return null + + } else { + + return res.json() + } + }).catch((error) => alert('Failed to import from: '+url+' with error '+error)); + + // if there is no json object then alert and return + if(fileContent == null){ + alert('No package found') + return false; + } + + if(!validateJSON(fileContent)){ + alert('Invalid Package') + return false; + } + + // Begin name validation + + const re = /^[a-zA-Z0-9-_ ]+$/ // checks that names contain only alphanumerics, dash, underscore, or spaces + + // filename is present for pastebin files, otherwise it is a different link + if(fileContent['filename']){ + + if(!re.test(fileContent['filename'])){ + // failed regex validation indicating the file may have been tampered with + // since this name is not possible in the first place + alert('Package name is invalid. '+fileContent['filename']+' Limit names to only alphanumerics, -, _, or spaces.') + return false; + } + + // check if there is already a package that has this name + if(packages[fileContent['filename']] != null){ + // get a new name and continously check validate it until it passes or user aborts + let packageName = prompt('A package with the name '+ fileContent['filename'] +' already exists. Enter a new name for the package: ') + while(packageName == null || packageName == "" || !re.test(packageName) || scenes[packageName] != null){ + if(packageName == null){ + return + } else if(packageName == ""){ + packageName = prompt("Enter a valid package name: ") + } else if(!re.test(packageName)){ + packageName = prompt('Package name is invalid. '+ packageName +' Limit names to only alphanumerics, - , _ , or spaces.') + } else if(scenes[packageName] != null){ + packageName = prompt('A package with this name already exists. Enter a new name for the package: '); + } + } + fileContent['filename'] = packageName + } + + } else { + // the link provided was not from pastebin + + // get a new name and continously check validate it until it passes or user aborts + let packageName = prompt('Enter a name for this package: ') + while(packageName == null || packageName == "" || !re.test(packageName) || scenes[packageName] != null){ + if(packageName == null){ + return + } else if(packageName == ""){ + packageName = prompt("Enter a valid package name: ") + } else if(!re.test(packageName)){ + packageName = prompt('Package name is invalid. '+ packageName +' Limit names to only alphanumerics, - , _ , or spaces.') + } else if(scenes[packageName] != null){ + packageName = prompt('A package with this name already exists. Enter a new name for the package: '); + } + } + fileContent['filename'] = packageName + } + + names[fileContent['filename']] = {} // create new dictionary in names object to store names of patterns in requested file + + // Validate each pattern name in the package + for (const [name, value] of Object.entries(fileContent['scenes'])) { + const re = /^[a-zA-Z0-9-_ ]+( \([0-9]+\))?$/ // regex to check for a name like test_one-2 (1) + + if(!re.test(name)){ + // failed regex validation indicating the file may have been tampered with + // since this name is not possible in the first place + alert('Pattern name is invalid. '+name+' Limit names to only alphanumerics, -, _, or spaces.') + delete names[fileContent['filename']] + return false; + } + + // update name dictionary + currName = name.split(' (')[0]; + if(names[fileContent['filename']][currName]){ + currName = currName + ' ('+names[fileContent['filename']][currName]+')' + names[fileContent['filename']][name.split(' (')[0]] = names[fileContent['filename']][name.split(' (')[0]] + 1 + } else { + names[fileContent['filename']][currName] = 1 + } + } + + // handle local storage updates + if(url.split("https://didsr.pythonanywhere.com/webxrtools/get?id=").length > 1){ + // if link came from pastebin, then only save pastebin id + let out = manageLocalStorage(fileContent['filename'] + " ("+url.split("https://didsr.pythonanywhere.com/webxrtools/get?id=")[1]+")", fileContent['scenes']) + if(out == false){ + return false; + } + packages[fileContent['filename']] = url.split("https://didsr.pythonanywhere.com/webxrtools/get?id=")[1]// save id in packages + } else { + // if link is not from pastebin, save entire link address + let out = manageLocalStorage(fileContent['filename'] + " ("+encodeURIComponent(decodeURIComponent(url))+")", fileContent['scenes']) + if(out == false){ + return false; + } + packages[fileContent['filename']] = url + } + scenes[fileContent['filename']] = fileContent['scenes'] + + // update list of textures + textures = fileContent['textures']['textureValues'] // texture images + uploadedTextureFormats = fileContent['textures']['uploadedTextureFormats'] // texture aspect ratios + + // merge incoming and existing textures + let i = 0; + let len = texture.options.length; + currTextures = [] + while(i < len){ + currTextures.push(texture.options[i].text) + i++; + } + i = 0; + uploadedTextures = [] + while(i < textures.length){ + uploadedTextures.push(textures[i].text) + i++; + } + newTextures = [...new Set([...uploadedTextures,...currTextures])] + newTextures.forEach(text => { + var option = document.createElement("option"); + if(uploadedTextures.indexOf(text) != -1 && currTextures.indexOf(text) == -1){ + option.text = textures[uploadedTextures.indexOf(text)].text + option.value = textures[uploadedTextures.indexOf(text)].val + texture.add(option); + } + }) + + // merge incoming and existing texture formats + newUploadedTextureFormat = [...new Set([...Object.keys(uploadedTextureFormat),...Object.keys(uploadedTextureFormats)])] + tmp = {} + newUploadedTextureFormat.forEach(texture => { + if(Object.keys(uploadedTextureFormat).indexOf(texture) != -1){ + tmp[texture] = uploadedTextureFormat[texture] + } else { + tmp[texture] = uploadedTextureFormats[texture] + } + }); + uploadedTextureFormat = tmp + flag = false; + + // add option to packageSelect + packageSelect.options.add(new Option(fileContent['filename'],fileContent['filename'])) + packageSelect.value = fileContent['filename'] + + changePackage() // invokes the function change packages to the uploaded one + return true; // returns success +} diff --git a/Custom/utils/shareableLink/validator.js b/Custom/utils/shareableLink/validator.js new file mode 100644 index 0000000..e2619a1 --- /dev/null +++ b/Custom/utils/shareableLink/validator.js @@ -0,0 +1,695 @@ +function validateJSON(file){ + let properties = Object.keys(file); + for(let i = 0; i < properties.length; i++){ + let currProperty = properties[i]; + if(currProperty == 'filename'){ + const re = /^[a-zA-Z0-9-_ ]+$/ + if(!re.test(file[currProperty])){ + // if filename + // check that filename is valid + alert('error: invalid package name') + return false; + } + + } else if(currProperty == 'date'){ + // if date + if(Number.isNaN(Date.parse(file[currProperty]))){ + // check for valid date + alert('error: invalid file date') + return false; + } + } else if(currProperty == 'scenes'){ + continue; + // check if entity contents are valid for given type + + } else if(currProperty == 'textures'){ + let textureProperties = Object.keys(file[currProperty]); + if(textureProperties.length != 2 || !file[currProperty].hasOwnProperty('uploadedTextureFormats') || !file[currProperty].hasOwnProperty('textureValues')){ + console.log('error: invalid texture list') + return false; + } + + let uploadedTextureFormats = Object.keys(file[currProperty]['uploadedTextureFormats']); + for(let j = 0; j < uploadedTextureFormats.length; j++){ + let currTexture = uploadedTextureFormats[j]; + if(Object.keys(file[currProperty]['uploadedTextureFormats'][currTexture]).length != 2 || !file[currProperty]['uploadedTextureFormats'][currTexture].hasOwnProperty('width') || !file[currProperty]['uploadedTextureFormats'][currTexture].hasOwnProperty('width') + || typeof file[currProperty]['uploadedTextureFormats'][currTexture]['width'] != 'number' || typeof file[currProperty]['uploadedTextureFormats'][currTexture]['height'] != 'number' + ){ + console.log('error: invalid texture format') + return false; + + } + } + + let textureValues = file[currProperty]['textureValues']; + for(let j = 0; j < textureValues.length; j++){ + let currTexture = textureValues[j] + if(Object.keys(currTexture).length != 2 || !currTexture.hasOwnProperty('val') || typeof currTexture['val'] != 'string' || !currTexture.hasOwnProperty('text') || typeof currTexture['text'] != 'string'){ + console.log('error: invalid texture list') + return false + } + + if(currTexture['val'] == "none"){ + if(currTexture['text'] != "none"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "QC"){ + if(currTexture['text'] != "TG18-QC.2k_12b"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "CH"){ + if(currTexture['text'] != "TG18-CH.2k"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "MM1"){ + if(currTexture['text'] != "TG18-MM1.2k"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "MM2"){ + if(currTexture['text'] != "TG18-MM2.2k"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "sQC"){ + if(currTexture['text'] != "TG270sQC"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "PQC"){ + if(currTexture['text'] != "TG18-PQC.2k_12b"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "BR"){ + if(currTexture['text'] != "TG18-BR.2k_12b"){ + console.log('error: invalid texture') + return false + } + } else { + let checkForBase64 = currTexture['val'].split(',') + if(checkForBase64.length != 2 || checkForBase64[0] != 'url(data:image/png;base64' || (checkForBase64[1].split(')').length != 2 && checkForBase64[1].split(')').length != '')){ + console.log('error: invalid texture') + return false + } + } + } + + + } else { + console.log('error: invalid property encountered') + return false; + } + } + + + // go through each scene + let currScenes = Object.keys(file['scenes']); + for(let j = 0; j < currScenes.length; j++){ + if(!validateScene(file['scenes'][currScenes[j]], currScenes[j], file['textures']['textureValues'])){ + console.log('error: scene validation failed') + return false + } + } + + + + return true; +} + + + +function validateScene(scene, name, textures){ + // check valid scene name + const re = /^[a-zA-Z0-9-_ ]+( \([0-9]+\))?$/ // regex to check for a name like test_one-2 (1) + + if(!re.test(name)){ + // failed regex validation indicating the file may have been tampered with + console.log('error: invalid pattern name') + return false; + } + let skyEntCount = 0; // check that sky element is there exactly once + let timerEntCount = 0; // check at most one timer + + + + // for each entity in scene + // check if entity is of a valid type and id + // given type, check valid for valid inputs + + + let colorRe = /^\#[a-fA-F0-9]{6}$/ + + + let properties = Object.keys(scene) + for(let i = 0; i < properties.length; i++) { + + let key = properties[i]; + + // handle adding entity similar to drawing entity + + let skyRe = /^sky$/; + + if(key.includes("sky")){ + skyEntCount++; + if(skyEntCount > 1){ + console.log('error: too many sky entities') + return false + } + + // for each property, check if it is valid for this entity + let attributes = Object.keys(scene[key]); + + if(attributes.length != 1 || attributes[0] != 'skyColor' || !colorRe.test(scene[key]['skyColor'] || !skyRe.test(key))){ + console.log('error: sky attributes invalid') + return false + } + + + } else if(key.includes("circle")){ /* validate circle */ + let attributes = Object.keys(scene[key]); + let circleRe = /^circle[0-9]+$/; + if(attributes.length != 8 || !circleRe.test(key)){ + console.log('error: circle1') + return false + } + + if(!scene[key].hasOwnProperty('geometry') || !scene[key].hasOwnProperty('fill')){ + console.log('error: circle2') + return false + } + + let fill = Object.keys(scene[key]['fill']) + if(fill.length != 2 || !scene[key]['fill'].hasOwnProperty('val') || !scene[key]['fill'].hasOwnProperty('isFull') || typeof scene[key]['fill']['val'] != 'number' || typeof scene[key]['fill']['isFull'] != 'boolean'){ + console.log('error: circle3') + return false + } + + let geom = Object.keys(scene[key]['geometry']) + if(geom.length != 4 || !scene[key]['geometry'].hasOwnProperty('primitive') || !scene[key]['geometry'].hasOwnProperty('radiusOuter') || !scene[key]['geometry'].hasOwnProperty('radiusInner') || !scene[key]['geometry'].hasOwnProperty('segmentsTheta') || scene[key]['geometry']['primitive'] != 'ring' || typeof scene[key]['geometry']['radiusOuter'] != 'number' || typeof scene[key]['geometry']['radiusInner'] != 'number' || typeof scene[key]['geometry']['segmentsTheta'] != 'number'){ + console.log('error: circle4') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: circle5') + return false + } + + + + } else if(key.includes("plane")){ /* validate plane */ + + let attributes = Object.keys(scene[key]); + let planeRe = /^plane[0-9]+$/; + if(attributes.length != 9 || !planeRe.test(key)){ + console.log('error: plane1') + return false + } + + if(!scene[key].hasOwnProperty('geometry')){ + console.log('error: plane2') + return false + } else { + let geom = Object.keys(scene[key]['geometry']) + if(geom.length != 3 || !scene[key]['geometry'].hasOwnProperty('primitive') || !scene[key]['geometry'].hasOwnProperty('width') || !scene[key]['geometry'].hasOwnProperty('height') || scene[key]['geometry']['primitive'] != 'plane' || typeof scene[key]['geometry']['width'] != 'number' || typeof scene[key]['geometry']['height'] != 'number'){ + console.log('error: plane3') + return false + } + } + + if(!scene[key].hasOwnProperty('widthReal') || typeof scene[key]['widthReal'] != 'number'){ + console.log('error: plane4') + return false + } + + if(!scene[key].hasOwnProperty('fill')){ + console.log('error: plane5') + return false + } + + let fill = Object.keys(scene[key]['fill']) + if(fill.length != 2 || !scene[key]['fill'].hasOwnProperty('val') || !scene[key]['fill'].hasOwnProperty('isFull') || typeof scene[key]['fill']['val'] != 'number' || typeof scene[key]['fill']['isFull'] != 'boolean'){ + console.log('error: plane6') + return false + } + + if(!scene[key].hasOwnProperty('material')){ + console.log('error: plane7') + return false + } else { + let mat = Object.keys(scene[key]['material']) + if(mat.length == 3){ + if(!scene[key]['material'].hasOwnProperty('shader') || !scene[key]['material'].hasOwnProperty('color') || !scene[key]['material'].hasOwnProperty('src') || scene[key]['material']['shader'] != 'flat' || !colorRe.test(scene[key]['material']['color']) || typeof scene[key]['material']['src'] != 'string'){ + console.log('error: plane8') + return false + } + if(scene[key]['material']['src'] != ''){ + let test = false + for(let j = 0; j < textures.length; j++){ + if(textures[j]['text'] == scene[key]['material']['src']){ + test = true; + break + } + } + if(!test){ + console.log('error: plane9') + return false; + } + } + + } + + } + + if(!validateUniversal(scene, key, attributes, true)){ + console.log('error: plane10') + return false + } + + } else if(key.includes("triangle")){ /* validate triangle */ + let attributes = Object.keys(scene[key]); + let triangleRe = /^triangle[0-9]+$/; + if(attributes.length != 7 || !triangleRe.test(key)){ + console.log('error: triangle1') + return false + } + + if(!scene[key].hasOwnProperty('geometry')){ + console.log('error: triangle2') + return false + } + + let geom = Object.keys(scene[key]['geometry']) + if(geom.length != 4 || !scene[key]['geometry'].hasOwnProperty('primitive') || !scene[key]['geometry'].hasOwnProperty('vertexA') || !scene[key]['geometry'].hasOwnProperty('vertexB') || !scene[key]['geometry'].hasOwnProperty('vertexC') || scene[key]['geometry']['primitive'] != 'triangle' || typeof scene[key]['geometry']['vertexA'] != 'object' || typeof scene[key]['geometry']['vertexB'] != 'object' || typeof scene[key]['geometry']['vertexC'] != 'object'){ + console.log('error: triangle3') + return false + } else { + + let vA = scene[key]['geometry']['vertexA']; + if(Object.keys(vA).length != 3 || !vA.hasOwnProperty('x') || !vA.hasOwnProperty('y') || !vA.hasOwnProperty('z') || typeof vA.x != 'number' || typeof vA.y != 'number' || typeof vA.z != 'number'){ + console.log('error: triangle4') + return false + } + + let vB = scene[key]['geometry']['vertexB']; + if(Object.keys(vB).length != 3 || !vB.hasOwnProperty('x') || !vB.hasOwnProperty('y') || !vB.hasOwnProperty('z') || typeof vB.x != 'number' || typeof vB.y != 'number' || typeof vB.z != 'number'){ + console.log('error: triangle5') + return false + } + + let vC = scene[key]['geometry']['vertexC']; + if(Object.keys(vC).length != 3 || !vC.hasOwnProperty('x') || !vC.hasOwnProperty('y') || !vC.hasOwnProperty('z') || typeof vC.x != 'number' || typeof vC.y != 'number' || typeof vC.z != 'number'){ + console.log('error: triangle6') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: triangle7') + return false + } + } + + + + } else if (key.includes("gradient") || key.includes("grille")){ + let attributes = Object.keys(scene[key]); + if(key.includes("gradient")){ + + let gradientRe = /^gradient[0-9]+$/; + if(attributes.length != 9 || !gradientRe.test(key)){ + console.log('error: gradient1') + return false + } + } else { + let grilleRe = /^grille[0-9]+$/; + if(attributes.length != 9 || !grilleRe.test(key)){ + console.log('error: grille1') + return false + } + } + + + + if(!scene[key].hasOwnProperty('childGeometry')){ + console.log('error: g2') + return false + } + + let geom = Object.keys(scene[key]['childGeometry']) + if(geom.length != 3 || !scene[key]['childGeometry'].hasOwnProperty('primitive') || !scene[key]['childGeometry'].hasOwnProperty('width') || !scene[key]['childGeometry'].hasOwnProperty('height') || scene[key]['childGeometry']['primitive'] != 'plane' || typeof scene[key]['childGeometry']['width'] != 'number' || typeof scene[key]['childGeometry']['height'] != 'number'){ + console.log('error: g3') + return false + } + + if(!scene[key].hasOwnProperty('numBars') || typeof scene[key]['numBars'] != 'number'){ + console.log('error: g4') + return false + } + + if(!scene[key].hasOwnProperty('color2') || Object.keys(scene[key]['color2']).length != 1 || !scene[key]['color2'].hasOwnProperty('val') || typeof scene[key]['color2']['val'] != 'string' || !colorRe.test(scene[key]['color2']['val'])){ + console.log('error: g5') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: g6') + return false + } + + } else if (key.includes("checkerboard")){ + let attributes = Object.keys(scene[key]); + let checkerboardRe = /^checkerboard[0-9]+$/; + if(attributes.length != 10 || !checkerboardRe.test(key)){ + console.log('error: checker1') + return false + } + + if(!scene[key].hasOwnProperty('rows') || typeof scene[key]['rows'] != 'number'){ + console.log('error: checker2') + return false + } + + if(!scene[key].hasOwnProperty('cols') || typeof scene[key]['cols'] != 'number'){ + console.log('error: checker3') + return false + } + + if(!scene[key].hasOwnProperty('tileSize') || typeof scene[key]['tileSize'] != 'number'){ + console.log('error: checker4') + return false + } + + if(!scene[key].hasOwnProperty('color2') || Object.keys(scene[key]['color2']).length != 1 || !scene[key]['color2'].hasOwnProperty('val') || typeof scene[key]['color2']['val'] != 'string' || !colorRe.test(scene[key]['color2']['val'])){ + console.log('error: checker5') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: checker6') + return false + } + + + } else if (key.includes("circularDotarray")){ + let attributes = Object.keys(scene[key]); + let circularDotArrayRe = /^circularDotarray[0-9]+$/; + if(attributes.length != 11 || !circularDotArrayRe.test(key)){ + console.log('error: circular1') + return false + } + + if(!scene[key].hasOwnProperty('circles') || typeof scene[key]['circles'] != 'number'){ + console.log('error: circular2') + return false + } + + if(!scene[key].hasOwnProperty('dots') || typeof scene[key]['dots'] != 'number'){ + console.log('error: circular3') + return false + } + + if(!scene[key].hasOwnProperty('circleSize') || typeof scene[key]['circleSize'] != 'number'){ + console.log('error: circular4') + return false + } + + if(!scene[key].hasOwnProperty('toggleCenterDot') || Object.keys(scene[key]['toggleCenterDot']).length != 1 || !scene[key]['toggleCenterDot'].hasOwnProperty('val') || typeof scene[key]['toggleCenterDot']['val'] != 'boolean'){ + console.log('error: circular5') + return false + } + + if(!scene[key].hasOwnProperty('arraySpacing') || Object.keys(scene[key]['arraySpacing']).length != 1 || !scene[key]['arraySpacing'].hasOwnProperty('val') || typeof scene[key]['arraySpacing']['val'] != 'number'){ + console.log('error: circular6') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: circular7') + return false + } + + + + } else if (key.includes("dotarray")){ + let attributes = Object.keys(scene[key]); + let dotArrayRe = /^dotarray[0-9]+$/; + if(attributes.length != 11 || !dotArrayRe.test(key)){ + console.log('error: dot1') + return false + } + + if(!scene[key].hasOwnProperty('rows') || typeof scene[key]['rows'] != 'number'){ + console.log('error: dot2') + return false + } + + if(!scene[key].hasOwnProperty('cols') || typeof scene[key]['cols'] != 'number'){ + console.log('error: dot3') + return false + } + + if(!scene[key].hasOwnProperty('circleSize') || typeof scene[key]['circleSize'] != 'number'){ + console.log('error: dot4') + return false + } + + if(!scene[key].hasOwnProperty('toggleCenterDot') || Object.keys(scene[key]['toggleCenterDot']).length != 1 || !scene[key]['toggleCenterDot'].hasOwnProperty('val') || typeof scene[key]['toggleCenterDot']['val'] != 'boolean'){ + console.log('error: dot5') + return false + } + + if(!scene[key].hasOwnProperty('spacing') || Object.keys(scene[key]['spacing']).length != 1 || !scene[key]['spacing'].hasOwnProperty('val') || typeof scene[key]['spacing']['val'] != 'number'){ + console.log('error: dot6') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: dot7') + return false + } + + + } else if (key.includes("bullseye")){ + let attributes = Object.keys(scene[key]); + let bullseyeRe = /^bullseye[0-9]+$/; + if(attributes.length != 8 || !bullseyeRe.test(key)){ + console.log('error: bullseye1') + return false + } + + if(!scene[key].hasOwnProperty('numRings') || typeof scene[key]['numRings'] != 'number'){ + console.log('error: bullseye2') + return false + } + + if(!scene[key].hasOwnProperty('ringPitch') || typeof scene[key]['ringPitch'] != 'number'){ + console.log('error: bullseye3') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: bullseye4') + return false + } + + + } else if (key.includes("text")){ + + let attributes = Object.keys(scene[key]); + let textRe = /^text[0-9]+$/; + if(attributes.length != 6 || !textRe.test(key)){ + console.log('error: text1') + return false + } + + if(!scene[key].hasOwnProperty('text') || Object.keys(scene[key]['text']).length != 6 || !scene[key]['text'].hasOwnProperty('value') || !scene[key]['text'].hasOwnProperty('color') || !scene[key]['text'].hasOwnProperty('width') || !scene[key]['text'].hasOwnProperty('height') || !scene[key]['text'].hasOwnProperty('align') || !scene[key]['text'].hasOwnProperty('wrapCount') + || typeof scene[key]['text']['value'] != 'string' || !colorRe.test(scene[key]['text']['color']) || typeof scene[key]['text']['width'] != 'number' || typeof scene[key]['text']['height'] != 'number' || scene[key]['text']['align'] != 'center' || typeof scene[key]['text']['wrapCount'] != 'number' + ){ + console.log('error: text2') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: text3') + return false + } + + + } else if (key.includes("timer")){ + timerEntCount++; + if(timerEntCount > 1){ + console.log('error: timer1') + return false + } + + let attributes = Object.keys(scene[key]); + let timerRe = /^timer[0-9]+$/; + if(attributes.length != 6 || !timerRe.test(key)){ + console.log('error: timer2') + return false + } + + if(!scene[key].hasOwnProperty('text') || Object.keys(scene[key]['text']).length != 24 + || !scene[key]['text'].hasOwnProperty('value') || !scene[key]['text'].hasOwnProperty('color') || !scene[key]['text'].hasOwnProperty('width') || !scene[key]['text'].hasOwnProperty('height') || !scene[key]['text'].hasOwnProperty('align') || !scene[key]['text'].hasOwnProperty('wrapCount') + || !scene[key]['text'].hasOwnProperty('alphaTest') || !scene[key]['text'].hasOwnProperty('anchor') || !scene[key]['text'].hasOwnProperty('baseline') || !scene[key]['text'].hasOwnProperty('font') || !scene[key]['text'].hasOwnProperty('fontImage') || !scene[key]['text'].hasOwnProperty('letterSpacing') + || !scene[key]['text'].hasOwnProperty('lineHeight') || !scene[key]['text'].hasOwnProperty('negate') || !scene[key]['text'].hasOwnProperty('opacity') || !scene[key]['text'].hasOwnProperty('shader') || !scene[key]['text'].hasOwnProperty('side') || !scene[key]['text'].hasOwnProperty('tabSize') + || typeof scene[key]['text']['value'] != 'string' || !colorRe.test(scene[key]['text']['color']) || typeof scene[key]['text']['width'] != 'number' || typeof scene[key]['text']['height'] != 'number' || scene[key]['text']['align'] != 'center' || typeof scene[key]['text']['wrapCount'] != 'number' + || scene[key]['text']['alphaTest'] != 0.5 || scene[key]['text']['anchor'] != 'center' || scene[key]['text']['baseline'] != 'center' || scene[key]['text']['font'] != 'roboto' || scene[key]['text']['fontImage'] != '' || scene[key]['text']['letterSpacing'] != 0 + || scene[key]['text']['lineHeight'] != 0 || scene[key]['text']['negate'] != true || scene[key]['text']['opacity'] != 1 || scene[key]['text']['shader'] != 'sdf' || scene[key]['text']['side'] != 'front' || scene[key]['text']['tabSize'] != 4 + || scene[key]['text']['transparent'] != true || scene[key]['text']['whiteSpace'] != 'normal' || scene[key]['text']['wrapPixels'] != 0 || scene[key]['text']['xOffset'] != 0 || scene[key]['text']['yOffset'] != 0 || scene[key]['text']['zOffset'] != 0.001 + ){ + console.log('error: timer3') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: timer4') + return false + } + + + + } else { + return false; + } + } + + + + + + return true; +} + + + + +function validateUniversal(scene, key, attributes, skipMat){ + let colorRe = /^\#[a-fA-F0-9]{6}$/; + let uniqueAttrs = ['geometry', 'widthReal','fill', 'childGeometry', 'numBars', 'color2', 'rows', 'cols', 'tileSize', 'circles', 'dots', 'circleSize', 'arraySpacing','toggleCenterDot', 'spacing', 'numRings', 'ringPitch', 'text'] + for(let j = 0; j < attributes.length; j++) { + let attr = attributes[j]; + + if(attr == "advanced"){ + let advanced = Object.keys(scene[key][attr]) + if(advanced.length != 1 || !scene[key][attr].hasOwnProperty('val') || typeof scene[key][attr]['val'] != 'boolean'){ + console.log('test1 hello') + return false + } + } else if(attr == "angle"){ + let angle = Object.keys(scene[key][attr]) + if(angle.length != 2 || !scene[key][attr].hasOwnProperty('x') || !scene[key][attr].hasOwnProperty('z') || typeof scene[key][attr]['x'] != 'number' || typeof scene[key][attr]['z'] != 'number'){ + console.log('test2') + return false + } + } else if(attr == "position"){ + let pos = Object.keys(scene[key][attr]) + if(pos.length != 3 || !scene[key][attr].hasOwnProperty('x') || !scene[key][attr].hasOwnProperty('y') || !scene[key][attr].hasOwnProperty('z') || typeof scene[key][attr]['x'] != 'number' || typeof scene[key][attr]['y'] != 'number' || typeof scene[key][attr]['z'] != 'number'){ + console.log('test4') + return false + } + + } else if(attr == "material"){ + if(skipMat){ + continue; + } + let mat = Object.keys(scene[key][attr]) + if((mat.length != 2 || !scene[key][attr].hasOwnProperty('shader') || !scene[key][attr].hasOwnProperty('color') || scene[key][attr]['shader'] != 'flat' || !colorRe.test(scene[key][attr]['color'])) && (mat.length != 3 || !scene[key][attr].hasOwnProperty('shader') || !scene[key][attr].hasOwnProperty('color') || !scene[key][attr].hasOwnProperty('src') || scene[key][attr]['shader'] != 'flat' || !colorRe.test(scene[key][attr]['color']) || scene[key][attr]['src'] != '')){ + console.log('test5') + return false + } + } else if(attr == "rotation"){ + let rot = Object.keys(scene[key][attr]) + if(rot.length != 3 || !scene[key][attr].hasOwnProperty('x') || !scene[key][attr].hasOwnProperty('y') || !scene[key][attr].hasOwnProperty('z') || typeof scene[key][attr]['x'] != 'number' || typeof scene[key][attr]['y'] != 'number' || typeof scene[key][attr]['z'] != 'number'){ + console.log('test6') + return false + } + } else if(attr == "movement"){ + let mov = Object.keys(scene[key][attr]) + if(mov.length != 11 || !scene[key][attr].hasOwnProperty('startPoints') || !scene[key][attr].hasOwnProperty('endPoints') || !scene[key][attr].hasOwnProperty('initialVelocities') || !scene[key][attr].hasOwnProperty('accelerations') || !scene[key][attr].hasOwnProperty('types') || !scene[key][attr].hasOwnProperty('origin') || !scene[key][attr].hasOwnProperty('rotationOrigin') || !scene[key][attr].hasOwnProperty('status') || !scene[key][attr].hasOwnProperty('index') || !scene[key][attr].hasOwnProperty('currentVelocity') || !scene[key][attr].hasOwnProperty('timeElapsed') + || !(scene[key][attr]['startPoints'].length == scene[key][attr]['endPoints'].length && scene[key][attr]['initialVelocities'].length == scene[key][attr]['accelerations'].length && scene[key][attr]['types'].length == scene[key][attr]['startPoints'].length && scene[key][attr]['startPoints'].length == scene[key][attr]['accelerations'].length + || typeof scene[key][attr]['status'] != 'number' || scene[key][attr]['status'] != -1 + || typeof scene[key][attr]['origin'] != 'object' + || typeof scene[key][attr]['rotationOrigin'] != 'object' + || typeof scene[key][attr]['index'] != 'number' || scene[key][attr]['index'] != 0 + || typeof scene[key][attr]['currentVelocity'] != 'number' || scene[key][attr]['currentVelocity'] != 0 + || typeof scene[key][attr]['timeElapsed'] != 'number' || scene[key][attr]['timeElapsed'] != 0 + )){ + console.log('test7') + return false; + + } else { + // need to validate each object stored in movement + + for(let k = 0; k < scene[key][attr]['startPoints'].length; k++){ + if(scene[key][attr]['startPoints'][k].hasOwnProperty('x') && scene[key][attr]['startPoints'][k].hasOwnProperty('y') && scene[key][attr]['startPoints'][k].hasOwnProperty('z')){ + if(typeof scene[key][attr]['startPoints'][k].x != 'number' && typeof scene[key][attr]['startPoints'][k].y != 'number' && typeof scene[key][attr]['startPoints'][k].z != 'number'){ + console.log('test8') + return false; + } + } else if(scene[key][attr]['startPoints'][k].hasOwnProperty('r') && scene[key][attr]['startPoints'][k].hasOwnProperty('y') && scene[key][attr]['startPoints'][k].hasOwnProperty('theta')){ + if(typeof scene[key][attr]['startPoints'][k].r != 'number' && typeof scene[key][attr]['startPoints'][k].theta != 'number' && typeof scene[key][attr]['startPoints'][k].y != 'number'){ + console.log('test9') + return false + } + } else { + console.log('test10') + return false; + } + } + + + for(let k = 0; k < scene[key][attr]['endPoints'].length; k++){ + if(scene[key][attr]['endPoints'][k].hasOwnProperty('x') && scene[key][attr]['endPoints'][k].hasOwnProperty('y') && scene[key][attr]['endPoints'][k].hasOwnProperty('z')){ + if(typeof scene[key][attr]['endPoints'][k].x != 'number' && typeof scene[key][attr]['endPoints'][k].y != 'number' && typeof scene[key][attr]['endPoints'][k].z != 'number'){ + console.log('test11') + return false; + } + } else if(scene[key][attr]['endPoints'][k].hasOwnProperty('r') && scene[key][attr]['endPoints'][k].hasOwnProperty('y') && scene[key][attr]['endPoints'][k].hasOwnProperty('theta')){ + if(typeof scene[key][attr]['endPoints'][k].r != 'number' && typeof scene[key][attr]['endPoints'][k].theta != 'number' && typeof scene[key][attr]['endPoints'][k].y != 'number'){ + console.log('test12') + return false + } + } else { + console.log('test13') + return false; + } + } + + + for(let k = 0; k < scene[key][attr]['initialVelocities'].length; k++){ + if(typeof scene[key][attr]['initialVelocities'][k] != 'number'){ + console.log('test14') + return false + } + } + + for(let k = 0; k < scene[key][attr]['accelerations'].length; k++){ + if(typeof scene[key][attr]['accelerations'][k] != 'number'){ + console.log('test15') + return false + } + } + + for(let k = 0; k < scene[key][attr]['types'].length; k++){ + if(typeof scene[key][attr]['types'][k] != 'string' || (scene[key][attr]['types'][k] != 'Discontinous' && scene[key][attr]['types'][k] != 'Pause' && scene[key][attr]['types'][k] != 'Start' && scene[key][attr]['types'][k] != 'Rubberband' && scene[key][attr]['types'][k] != 'Rebound')){ + console.log('test16') + return false + } + } + + } + } else if(uniqueAttrs.includes(attr)) { + continue; + } else { + console.log('test17') + return false; + } + + } + return true; +} diff --git a/Custom/validator.js b/Custom/validator.js new file mode 100644 index 0000000..7e238c6 --- /dev/null +++ b/Custom/validator.js @@ -0,0 +1,692 @@ +function validateJSON(file){ + let properties = Object.keys(file); + for(let i = 0; i < properties.length; i++){ + let currProperty = properties[i]; + if(currProperty == 'filename'){ + const re = /^[a-zA-Z0-9-_ ]+$/ + if(!re.test(file[currProperty])){ + // if filename + // check that filename is valid + alert('error: invalid package name') + return false; + } + + } else if(currProperty == 'date'){ + // if date + if(Number.isNaN(Date.parse(file[currProperty]))){ + // check for valid date + alert('error: invalid file date') + return false; + } + } else if(currProperty == 'scenes'){ + continue; + // check if entity contents are valid for given type + + } else if(currProperty == 'textures'){ + let textureProperties = Object.keys(file[currProperty]); + if(textureProperties.length != 2 || !file[currProperty].hasOwnProperty('uploadedTextureFormats') || !file[currProperty].hasOwnProperty('textureValues')){ + console.log('error: invalid texture list') + return false; + } + + let uploadedTextureFormats = Object.keys(file[currProperty]['uploadedTextureFormats']); + for(let j = 0; j < uploadedTextureFormats.length; j++){ + let currTexture = uploadedTextureFormats[j]; + if(Object.keys(file[currProperty]['uploadedTextureFormats'][currTexture]).length != 2 || !file[currProperty]['uploadedTextureFormats'][currTexture].hasOwnProperty('width') || !file[currProperty]['uploadedTextureFormats'][currTexture].hasOwnProperty('width') + || typeof file[currProperty]['uploadedTextureFormats'][currTexture]['width'] != 'number' || typeof file[currProperty]['uploadedTextureFormats'][currTexture]['height'] != 'number' + ){ + console.log('error: invalid texture format') + return false; + + } + } + + let textureValues = file[currProperty]['textureValues']; + for(let j = 0; j < textureValues.length; j++){ + let currTexture = textureValues[j] + if(Object.keys(currTexture).length != 2 || !currTexture.hasOwnProperty('val') || typeof currTexture['val'] != 'string' || !currTexture.hasOwnProperty('text') || typeof currTexture['text'] != 'string'){ + console.log('error: invalid texture list') + return false + } + + if(currTexture['val'] == "none"){ + if(currTexture['text'] != "none"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "QC"){ + if(currTexture['text'] != "TG18-QC.2k_12b"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "CH"){ + if(currTexture['text'] != "TG18-CH.2k"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "MM1"){ + if(currTexture['text'] != "TG18-MM1.2k"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "MM2"){ + if(currTexture['text'] != "TG18-MM2.2k"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "sQC"){ + if(currTexture['text'] != "TG270sQC"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "PQC"){ + if(currTexture['text'] != "TG18-PQC.2k_12b"){ + console.log('error: invalid texture') + return false + } + } else if(currTexture['val'] == "BR"){ + if(currTexture['text'] != "TG18-BR.2k_12b"){ + console.log('error: invalid texture') + return false + } + } else { + let checkForBase64 = currTexture['val'].split(',') + if(checkForBase64.length != 2 || checkForBase64[0] != 'url(data:image/png;base64' || (checkForBase64[1].split(')').length != 2 && checkForBase64[1].split(')').length != '')){ + console.log('error: invalid texture') + return false + } + } + } + + + } else { + console.log('error: invalid property encountered') + return false; + } + } + + + // go through each scene + let currScenes = Object.keys(file['scenes']); + for(let j = 0; j < currScenes.length; j++){ + if(!validateScene(file['scenes'][currScenes[j]], currScenes[j], file['textures']['textureValues'])){ + console.log('error: scene validation failed') + return false + } + } + + + + return true; +} + + + +function validateScene(scene, name, textures){ + // check valid scene name + const re = /^[a-zA-Z0-9-_ ]+( \([0-9]+\))?$/ // regex to check for a name like test_one-2 (1) + + if(!re.test(name)){ + // failed regex validation indicating the file may have been tampered with + console.log('error: invalid pattern name') + return false; + } + let skyEntCount = 0; // check that sky element is there exactly once + let timerEntCount = 0; // check at most one timer + + + + // for each entity in scene + // check if entity is of a valid type and id + // given type, check valid for valid inputs + + + let colorRe = /^\#[a-fA-F0-9]{6}$/ + + + let properties = Object.keys(scene) + for(let i = 0; i < properties.length; i++) { + + let key = properties[i]; + + // handle adding entity similar to drawing entity + + let skyRe = /^sky$/; + + if(key.includes("sky")){ + skyEntCount++; + if(skyEntCount > 1){ + console.log('error: too many sky entities') + return false + } + + // for each property, check if it is valid for this entity + let attributes = Object.keys(scene[key]); + + if(attributes.length != 1 || attributes[0] != 'skyColor' || !colorRe.test(scene[key]['skyColor'] || !skyRe.test(key))){ + console.log('error: sky attributes invalid') + return false + } + + + } else if(key.includes("circle")){ /* validate circle */ + let attributes = Object.keys(scene[key]); + let circleRe = /^circle[0-9]+$/; + if(attributes.length != 8 || !circleRe.test(key)){ + console.log('error: circle1') + return false + } + + if(!scene[key].hasOwnProperty('geometry') || !scene[key].hasOwnProperty('fill')){ + console.log('error: circle2') + return false + } + + let fill = Object.keys(scene[key]['fill']) + if(fill.length != 2 || !scene[key]['fill'].hasOwnProperty('val') || !scene[key]['fill'].hasOwnProperty('isFull') || typeof scene[key]['fill']['val'] != 'number' || typeof scene[key]['fill']['isFull'] != 'boolean'){ + console.log('error: circle3') + return false + } + + let geom = Object.keys(scene[key]['geometry']) + if(geom.length != 4 || !scene[key]['geometry'].hasOwnProperty('primitive') || !scene[key]['geometry'].hasOwnProperty('radiusOuter') || !scene[key]['geometry'].hasOwnProperty('radiusInner') || !scene[key]['geometry'].hasOwnProperty('segmentsTheta') || scene[key]['geometry']['primitive'] != 'ring' || typeof scene[key]['geometry']['radiusOuter'] != 'number' || typeof scene[key]['geometry']['radiusInner'] != 'number' || typeof scene[key]['geometry']['segmentsTheta'] != 'number'){ + console.log('error: circle4') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: circle5') + return false + } + + + + } else if(key.includes("plane")){ /* validate plane */ + + let attributes = Object.keys(scene[key]); + let planeRe = /^plane[0-9]+$/; + if(attributes.length != 9 || !planeRe.test(key)){ + console.log('error: plane1') + return false + } + + if(!scene[key].hasOwnProperty('geometry')){ + console.log('error: plane2') + return false + } else { + let geom = Object.keys(scene[key]['geometry']) + if(geom.length != 3 || !scene[key]['geometry'].hasOwnProperty('primitive') || !scene[key]['geometry'].hasOwnProperty('width') || !scene[key]['geometry'].hasOwnProperty('height') || scene[key]['geometry']['primitive'] != 'plane' || typeof scene[key]['geometry']['width'] != 'number' || typeof scene[key]['geometry']['height'] != 'number'){ + console.log('error: plane3') + return false + } + } + + if(!scene[key].hasOwnProperty('widthReal') || typeof scene[key]['widthReal'] != 'number'){ + console.log('error: plane4') + return false + } + + if(!scene[key].hasOwnProperty('fill')){ + console.log('error: plane5') + return false + } + + let fill = Object.keys(scene[key]['fill']) + if(fill.length != 2 || !scene[key]['fill'].hasOwnProperty('val') || !scene[key]['fill'].hasOwnProperty('isFull') || typeof scene[key]['fill']['val'] != 'number' || typeof scene[key]['fill']['isFull'] != 'boolean'){ + console.log('error: plane6') + return false + } + + if(!scene[key].hasOwnProperty('material')){ + console.log('error: plane7') + return false + } else { + let mat = Object.keys(scene[key]['material']) + if(mat.length == 3){ + if(!scene[key]['material'].hasOwnProperty('shader') || !scene[key]['material'].hasOwnProperty('color') || !scene[key]['material'].hasOwnProperty('src') || scene[key]['material']['shader'] != 'flat' || !colorRe.test(scene[key]['material']['color']) || typeof scene[key]['material']['src'] != 'string'){ + console.log('error: plane8') + return false + } + let test = false + for(let j = 0; j < textures.length; j++){ + if(textures[j]['text'] == scene[key]['material']['src']){ + test = true; + break + } + } + if(!test){ + console.log('error: plane9') + return false; + } + } + + } + + if(!validateUniversal(scene, key, attributes, true)){ + console.log('error: plane10') + return false + } + + } else if(key.includes("triangle")){ /* validate triangle */ + let attributes = Object.keys(scene[key]); + let triangleRe = /^triangle[0-9]+$/; + if(attributes.length != 7 || !triangleRe.test(key)){ + console.log('error: triangle1') + return false + } + + if(!scene[key].hasOwnProperty('geometry')){ + console.log('error: triangle2') + return false + } + + let geom = Object.keys(scene[key]['geometry']) + if(geom.length != 4 || !scene[key]['geometry'].hasOwnProperty('primitive') || !scene[key]['geometry'].hasOwnProperty('vertexA') || !scene[key]['geometry'].hasOwnProperty('vertexB') || !scene[key]['geometry'].hasOwnProperty('vertexC') || scene[key]['geometry']['primitive'] != 'triangle' || typeof scene[key]['geometry']['vertexA'] != 'object' || typeof scene[key]['geometry']['vertexB'] != 'object' || typeof scene[key]['geometry']['vertexC'] != 'object'){ + console.log('error: triangle3') + return false + } else { + + let vA = scene[key]['geometry']['vertexA']; + if(Object.keys(vA).length != 3 || !vA.hasOwnProperty('x') || !vA.hasOwnProperty('y') || !vA.hasOwnProperty('z') || typeof vA.x != 'number' || typeof vA.y != 'number' || typeof vA.z != 'number'){ + console.log('error: triangle4') + return false + } + + let vB = scene[key]['geometry']['vertexB']; + if(Object.keys(vB).length != 3 || !vB.hasOwnProperty('x') || !vB.hasOwnProperty('y') || !vB.hasOwnProperty('z') || typeof vB.x != 'number' || typeof vB.y != 'number' || typeof vB.z != 'number'){ + console.log('error: triangle5') + return false + } + + let vC = scene[key]['geometry']['vertexC']; + if(Object.keys(vC).length != 3 || !vC.hasOwnProperty('x') || !vC.hasOwnProperty('y') || !vC.hasOwnProperty('z') || typeof vC.x != 'number' || typeof vC.y != 'number' || typeof vC.z != 'number'){ + console.log('error: triangle6') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: triangle7') + return false + } + } + + + + } else if (key.includes("gradient") || key.includes("grille")){ + let attributes = Object.keys(scene[key]); + if(key.includes("gradient")){ + + let gradientRe = /^gradient[0-9]+$/; + if(attributes.length != 9 || !gradientRe.test(key)){ + console.log('error: gradient1') + return false + } + } else { + let grilleRe = /^grille[0-9]+$/; + if(attributes.length != 9 || !grilleRe.test(key)){ + console.log('error: grille1') + return false + } + } + + + + if(!scene[key].hasOwnProperty('childGeometry')){ + console.log('error: g2') + return false + } + + let geom = Object.keys(scene[key]['childGeometry']) + if(geom.length != 3 || !scene[key]['childGeometry'].hasOwnProperty('primitive') || !scene[key]['childGeometry'].hasOwnProperty('width') || !scene[key]['childGeometry'].hasOwnProperty('height') || scene[key]['childGeometry']['primitive'] != 'plane' || typeof scene[key]['childGeometry']['width'] != 'number' || typeof scene[key]['childGeometry']['height'] != 'number'){ + console.log('error: g3') + return false + } + + if(!scene[key].hasOwnProperty('numBars') || typeof scene[key]['numBars'] != 'number'){ + console.log('error: g4') + return false + } + + if(!scene[key].hasOwnProperty('color2') || Object.keys(scene[key]['color2']).length != 1 || !scene[key]['color2'].hasOwnProperty('val') || typeof scene[key]['color2']['val'] != 'string' || !colorRe.test(scene[key]['color2']['val'])){ + console.log('error: g5') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: g6') + return false + } + + } else if (key.includes("checkerboard")){ + let attributes = Object.keys(scene[key]); + let checkerboardRe = /^checkerboard[0-9]+$/; + if(attributes.length != 10 || !checkerboardRe.test(key)){ + console.log('error: checker1') + return false + } + + if(!scene[key].hasOwnProperty('rows') || typeof scene[key]['rows'] != 'number'){ + console.log('error: checker2') + return false + } + + if(!scene[key].hasOwnProperty('cols') || typeof scene[key]['cols'] != 'number'){ + console.log('error: checker3') + return false + } + + if(!scene[key].hasOwnProperty('tileSize') || typeof scene[key]['tileSize'] != 'number'){ + console.log('error: checker4') + return false + } + + if(!scene[key].hasOwnProperty('color2') || Object.keys(scene[key]['color2']).length != 1 || !scene[key]['color2'].hasOwnProperty('val') || typeof scene[key]['color2']['val'] != 'string' || !colorRe.test(scene[key]['color2']['val'])){ + console.log('error: checker5') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: checker6') + return false + } + + + } else if (key.includes("circularDotarray")){ + let attributes = Object.keys(scene[key]); + let circularDotArrayRe = /^circularDotarray[0-9]+$/; + if(attributes.length != 11 || !circularDotArrayRe.test(key)){ + console.log('error: circular1') + return false + } + + if(!scene[key].hasOwnProperty('circles') || typeof scene[key]['circles'] != 'number'){ + console.log('error: circular2') + return false + } + + if(!scene[key].hasOwnProperty('dots') || typeof scene[key]['dots'] != 'number'){ + console.log('error: circular3') + return false + } + + if(!scene[key].hasOwnProperty('circleSize') || typeof scene[key]['circleSize'] != 'number'){ + console.log('error: circular4') + return false + } + + if(!scene[key].hasOwnProperty('toggleCenterDot') || Object.keys(scene[key]['toggleCenterDot']).length != 1 || !scene[key]['toggleCenterDot'].hasOwnProperty('val') || typeof scene[key]['toggleCenterDot']['val'] != 'boolean'){ + console.log('error: circular5') + return false + } + + if(!scene[key].hasOwnProperty('arraySpacing') || Object.keys(scene[key]['arraySpacing']).length != 1 || !scene[key]['arraySpacing'].hasOwnProperty('val') || typeof scene[key]['arraySpacing']['val'] != 'number'){ + console.log('error: circular6') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: circular7') + return false + } + + + + } else if (key.includes("dotarray")){ + let attributes = Object.keys(scene[key]); + let dotArrayRe = /^dotarray[0-9]+$/; + if(attributes.length != 11 || !dotArrayRe.test(key)){ + console.log('error: dot1') + return false + } + + if(!scene[key].hasOwnProperty('rows') || typeof scene[key]['rows'] != 'number'){ + console.log('error: dot2') + return false + } + + if(!scene[key].hasOwnProperty('cols') || typeof scene[key]['cols'] != 'number'){ + console.log('error: dot3') + return false + } + + if(!scene[key].hasOwnProperty('circleSize') || typeof scene[key]['circleSize'] != 'number'){ + console.log('error: dot4') + return false + } + + if(!scene[key].hasOwnProperty('toggleCenterDot') || Object.keys(scene[key]['toggleCenterDot']).length != 1 || !scene[key]['toggleCenterDot'].hasOwnProperty('val') || typeof scene[key]['toggleCenterDot']['val'] != 'boolean'){ + console.log('error: dot5') + return false + } + + if(!scene[key].hasOwnProperty('spacing') || Object.keys(scene[key]['spacing']).length != 1 || !scene[key]['spacing'].hasOwnProperty('val') || typeof scene[key]['spacing']['val'] != 'number'){ + console.log('error: dot6') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: dot7') + return false + } + + + } else if (key.includes("bullseye")){ + let attributes = Object.keys(scene[key]); + let bullseyeRe = /^bullseye[0-9]+$/; + if(attributes.length != 8 || !bullseyeRe.test(key)){ + console.log('error: bullseye1') + return false + } + + if(!scene[key].hasOwnProperty('numRings') || typeof scene[key]['numRings'] != 'number'){ + console.log('error: bullseye2') + return false + } + + if(!scene[key].hasOwnProperty('ringPitch') || typeof scene[key]['ringPitch'] != 'number'){ + console.log('error: bullseye3') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: bullseye4') + return false + } + + + } else if (key.includes("text")){ + + let attributes = Object.keys(scene[key]); + let textRe = /^text[0-9]+$/; + if(attributes.length != 6 || !textRe.test(key)){ + console.log('error: text1') + return false + } + + if(!scene[key].hasOwnProperty('text') || Object.keys(scene[key]['text']).length != 6 || !scene[key]['text'].hasOwnProperty('value') || !scene[key]['text'].hasOwnProperty('color') || !scene[key]['text'].hasOwnProperty('width') || !scene[key]['text'].hasOwnProperty('height') || !scene[key]['text'].hasOwnProperty('align') || !scene[key]['text'].hasOwnProperty('wrapCount') + || typeof scene[key]['text']['value'] != 'string' || !colorRe.test(scene[key]['text']['color']) || typeof scene[key]['text']['width'] != 'number' || typeof scene[key]['text']['height'] != 'number' || scene[key]['text']['align'] != 'center' || typeof scene[key]['text']['wrapCount'] != 'number' + ){ + console.log('error: text2') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: text3') + return false + } + + + } else if (key.includes("timer")){ + timerEntCount++; + if(timerEntCount > 1){ + console.log('error: timer1') + return false + } + + let attributes = Object.keys(scene[key]); + let timerRe = /^timer[0-9]+$/; + if(attributes.length != 6 || !timerRe.test(key)){ + console.log('error: timer2') + return false + } + + if(!scene[key].hasOwnProperty('text') || Object.keys(scene[key]['text']).length != 24 + || !scene[key]['text'].hasOwnProperty('value') || !scene[key]['text'].hasOwnProperty('color') || !scene[key]['text'].hasOwnProperty('width') || !scene[key]['text'].hasOwnProperty('height') || !scene[key]['text'].hasOwnProperty('align') || !scene[key]['text'].hasOwnProperty('wrapCount') + || !scene[key]['text'].hasOwnProperty('alphaTest') || !scene[key]['text'].hasOwnProperty('anchor') || !scene[key]['text'].hasOwnProperty('baseline') || !scene[key]['text'].hasOwnProperty('font') || !scene[key]['text'].hasOwnProperty('fontImage') || !scene[key]['text'].hasOwnProperty('letterSpacing') + || !scene[key]['text'].hasOwnProperty('lineHeight') || !scene[key]['text'].hasOwnProperty('negate') || !scene[key]['text'].hasOwnProperty('opacity') || !scene[key]['text'].hasOwnProperty('shader') || !scene[key]['text'].hasOwnProperty('side') || !scene[key]['text'].hasOwnProperty('tabSize') + || typeof scene[key]['text']['value'] != 'string' || !colorRe.test(scene[key]['text']['color']) || typeof scene[key]['text']['width'] != 'number' || typeof scene[key]['text']['height'] != 'number' || scene[key]['text']['align'] != 'center' || typeof scene[key]['text']['wrapCount'] != 'number' + || scene[key]['text']['alphaTest'] != 0.5 || scene[key]['text']['anchor'] != 'center' || scene[key]['text']['baseline'] != 'center' || scene[key]['text']['font'] != 'roboto' || scene[key]['text']['fontImage'] != '' || scene[key]['text']['letterSpacing'] != 0 + || scene[key]['text']['lineHeight'] != 0 || scene[key]['text']['negate'] != true || scene[key]['text']['opacity'] != 1 || scene[key]['text']['shader'] != 'sdf' || scene[key]['text']['side'] != 'front' || scene[key]['text']['tabSize'] != 4 + || scene[key]['text']['transparent'] != true || scene[key]['text']['whiteSpace'] != 'normal' || scene[key]['text']['wrapPixels'] != 0 || scene[key]['text']['xOffset'] != 0 || scene[key]['text']['yOffset'] != 0 || scene[key]['text']['zOffset'] != 0.001 + ){ + console.log('error: timer3') + return false + } + + if(!validateUniversal(scene, key, attributes)){ + console.log('error: timer4') + return false + } + + + + } else { + return false; + } + } + + + + + + return true; +} + + + + +function validateUniversal(scene, key, attributes, skipMat){ + let colorRe = /^\#[a-fA-F0-9]{6}$/; + let uniqueAttrs = ['geometry', 'widthReal','fill', 'childGeometry', 'numBars', 'color2', 'rows', 'cols', 'tileSize', 'circles', 'dots', 'circleSize', 'arraySpacing','toggleCenterDot', 'spacing', 'numRings', 'ringPitch', 'text'] + for(let j = 0; j < attributes.length; j++) { + let attr = attributes[j]; + + if(attr == "advanced"){ + let advanced = Object.keys(scene[key][attr]) + if(advanced.length != 1 || !scene[key][attr].hasOwnProperty('val') || typeof scene[key][attr]['val'] != 'boolean'){ + console.log('test1 hello') + return false + } + } else if(attr == "angle"){ + let angle = Object.keys(scene[key][attr]) + if(angle.length != 2 || !scene[key][attr].hasOwnProperty('x') || !scene[key][attr].hasOwnProperty('z') || typeof scene[key][attr]['x'] != 'number' || typeof scene[key][attr]['z'] != 'number'){ + console.log('test2') + return false + } + } else if(attr == "position"){ + let pos = Object.keys(scene[key][attr]) + if(pos.length != 3 || !scene[key][attr].hasOwnProperty('x') || !scene[key][attr].hasOwnProperty('y') || !scene[key][attr].hasOwnProperty('z') || typeof scene[key][attr]['x'] != 'number' || typeof scene[key][attr]['y'] != 'number' || typeof scene[key][attr]['z'] != 'number'){ + console.log('test4') + return false + } + + } else if(attr == "material"){ + if(skipMat){ + continue; + } + let mat = Object.keys(scene[key][attr]) + if((mat.length != 2 || !scene[key][attr].hasOwnProperty('shader') || !scene[key][attr].hasOwnProperty('color') || scene[key][attr]['shader'] != 'flat' || !colorRe.test(scene[key][attr]['color'])) && (mat.length != 3 || !scene[key][attr].hasOwnProperty('shader') || !scene[key][attr].hasOwnProperty('color') || !scene[key][attr].hasOwnProperty('src') || scene[key][attr]['shader'] != 'flat' || !colorRe.test(scene[key][attr]['color']) || scene[key][attr]['src'] != '')){ + console.log('test5') + return false + } + } else if(attr == "rotation"){ + let rot = Object.keys(scene[key][attr]) + if(rot.length != 3 || !scene[key][attr].hasOwnProperty('x') || !scene[key][attr].hasOwnProperty('y') || !scene[key][attr].hasOwnProperty('z') || typeof scene[key][attr]['x'] != 'number' || typeof scene[key][attr]['y'] != 'number' || typeof scene[key][attr]['z'] != 'number'){ + console.log('test6') + return false + } + } else if(attr == "movement"){ + let mov = Object.keys(scene[key][attr]) + if(mov.length != 11 || !scene[key][attr].hasOwnProperty('startPoints') || !scene[key][attr].hasOwnProperty('endPoints') || !scene[key][attr].hasOwnProperty('initialVelocities') || !scene[key][attr].hasOwnProperty('accelerations') || !scene[key][attr].hasOwnProperty('types') || !scene[key][attr].hasOwnProperty('origin') || !scene[key][attr].hasOwnProperty('rotationOrigin') || !scene[key][attr].hasOwnProperty('status') || !scene[key][attr].hasOwnProperty('index') || !scene[key][attr].hasOwnProperty('currentVelocity') || !scene[key][attr].hasOwnProperty('timeElapsed') + || !(scene[key][attr]['startPoints'].length == scene[key][attr]['endPoints'].length && scene[key][attr]['initialVelocities'].length == scene[key][attr]['accelerations'].length && scene[key][attr]['types'].length == scene[key][attr]['startPoints'].length && scene[key][attr]['startPoints'].length == scene[key][attr]['accelerations'].length + || typeof scene[key][attr]['status'] != 'number' || scene[key][attr]['status'] != -1 + || typeof scene[key][attr]['origin'] != 'object' + || typeof scene[key][attr]['rotationOrigin'] != 'object' + || typeof scene[key][attr]['index'] != 'number' || scene[key][attr]['index'] != 0 + || typeof scene[key][attr]['currentVelocity'] != 'number' || scene[key][attr]['currentVelocity'] != 0 + || typeof scene[key][attr]['timeElapsed'] != 'number' || scene[key][attr]['timeElapsed'] != 0 + )){ + console.log('test7') + return false; + + } else { + // need to validate each object stored in movement + + for(let k = 0; k < scene[key][attr]['startPoints'].length; k++){ + if(scene[key][attr]['startPoints'][k].hasOwnProperty('x') && scene[key][attr]['startPoints'][k].hasOwnProperty('y') && scene[key][attr]['startPoints'][k].hasOwnProperty('z')){ + if(typeof scene[key][attr]['startPoints'][k].x != 'number' && typeof scene[key][attr]['startPoints'][k].y != 'number' && typeof scene[key][attr]['startPoints'][k].z != 'number'){ + console.log('test8') + return false; + } + } else if(scene[key][attr]['startPoints'][k].hasOwnProperty('r') && scene[key][attr]['startPoints'][k].hasOwnProperty('y') && scene[key][attr]['startPoints'][k].hasOwnProperty('theta')){ + if(typeof scene[key][attr]['startPoints'][k].r != 'number' && typeof scene[key][attr]['startPoints'][k].theta != 'number' && typeof scene[key][attr]['startPoints'][k].y != 'number'){ + console.log('test9') + return false + } + } else { + console.log('test10') + return false; + } + } + + + for(let k = 0; k < scene[key][attr]['endPoints'].length; k++){ + if(scene[key][attr]['endPoints'][k].hasOwnProperty('x') && scene[key][attr]['endPoints'][k].hasOwnProperty('y') && scene[key][attr]['endPoints'][k].hasOwnProperty('z')){ + if(typeof scene[key][attr]['endPoints'][k].x != 'number' && typeof scene[key][attr]['endPoints'][k].y != 'number' && typeof scene[key][attr]['endPoints'][k].z != 'number'){ + console.log('test11') + return false; + } + } else if(scene[key][attr]['endPoints'][k].hasOwnProperty('r') && scene[key][attr]['endPoints'][k].hasOwnProperty('y') && scene[key][attr]['endPoints'][k].hasOwnProperty('theta')){ + if(typeof scene[key][attr]['endPoints'][k].r != 'number' && typeof scene[key][attr]['endPoints'][k].theta != 'number' && typeof scene[key][attr]['endPoints'][k].y != 'number'){ + console.log('test12') + return false + } + } else { + console.log('test13') + return false; + } + } + + + for(let k = 0; k < scene[key][attr]['initialVelocities'].length; k++){ + if(typeof scene[key][attr]['initialVelocities'][k] != 'number'){ + console.log('test14') + return false + } + } + + for(let k = 0; k < scene[key][attr]['accelerations'].length; k++){ + if(typeof scene[key][attr]['accelerations'][k] != 'number'){ + console.log('test15') + return false + } + } + + for(let k = 0; k < scene[key][attr]['types'].length; k++){ + if(typeof scene[key][attr]['types'][k] != 'string' || (scene[key][attr]['types'][k] != 'Discontinous' && scene[key][attr]['types'][k] != 'Pause' && scene[key][attr]['types'][k] != 'Start' && scene[key][attr]['types'][k] != 'Rubberband' && scene[key][attr]['types'][k] != 'Rebound')){ + console.log('test16') + return false + } + } + + } + } else if(uniqueAttrs.includes(attr)) { + continue; + } else { + console.log('test17') + return false; + } + + } + return true; +} diff --git a/lib/controller_profile.js b/lib/controller_profile.js new file mode 100644 index 0000000..949f2d5 --- /dev/null +++ b/lib/controller_profile.js @@ -0,0 +1,65 @@ +const data = [{ + "windows" : { + "axis": { + "0": "trackpad", "1": "trackpad", "2": "thumbstick", "3": "thumbstick"}, + "left": { + "0": "trigger", "1": "grip", "2": "trackpad", "3": "thumbstick", "4": null, "5": null, "6": null}, + "right": { + "0": "trigger", "1": "grip", "2": "trackpad", "3": "thumbstick", "4": null, "5": null, "6": null} + }, + + "oc_touch" : { + "axis": { + "0": null, "1": null, "2": "thumbstick", "3": "thumbstick"}, + "left": { + "0": "trigger", "1": "grip", "2": null, "3": "thumbstick", "4": "xbutton", "5": "ybutton", "6": "surface"}, + "right": { + "0": "trigger", "1": "grip", "2": null, "3": "thumbstick", "4": "abutton", "5": "bbutton", "6": "surface"} + }, + + "oc_go" : { + "axis": { + "0": "trackpad", "1": "trackpad", "2": null, "3": null}, + "left": { + "0": "trigger", "1": null, "2": "trackpad", "3": null, "4": null, "5": null, "6": null}, + "right": { + "0": "trigger", "1": null, "2": "trackpad", "3": null, "4": null, "5": null, "6": null } + }, + + "vive" : { + "axis": { + "0": "trackpad", "1": "trackpad", "2": null, "3": null}, + "left": { + "0": "trigger", "1": "grip", "2": "trackpad", "3": null, "4": null, "5": null, "6": null}, + "right": { + "0": "trigger", "1": "grip", "2": "trackpad", "3": null, "4": null, "5": null, "6": null} + }, + + "vive_focus" : { + "axis": { + "0": "trackpad", "1": "trackpad", "2": null, "3": null}, + "left": { + "0": "trigger", "1": null, "2": "trackpad", "3": null, "4": "menu", "5": null, "6": null}, + "right": { + "0": "trigger", "1": null, "2": "trackpad", "3": null, "4": "menu", "5": null, "6": null} + }, + + "magic" : { + "axis": { + "0": "trackpad", "1": "trackpad", "2": null, "3": null}, + "left": { + "0": "trigger", "1": null, "2": "trackpad", "3": null, "4": "menu", "5": "trackpad", "6": null}, + "right": { + "0": "trigger", "1": null, "2": "trackpad", "3": null, "4": "menu", "5": "trackpad", "6": null} + }, + + "generic" : { + "axis": { + "0": "trackpad", "1": "trackpad", "2": "thumbstick", "3": "thumbstick"}, + "left": { + "0": "trigger", "1": "grip", "2": "trackpad", "3": "thumbstick", "4": "xbutton", "5": "ybutton", "6": "surface"}, + "right": { + "0": "trigger", "1": "grip", "2": "trackpad", "3": "thumbstick", "4": "abutton", "5": "bbutton", "6": "surface"} + } + +}] \ No newline at end of file