phetsims / ratio-and-proportion

"Ratio and Proportion" is an educational simulation in HTML5, by PhET Interactive Simulations.
GNU General Public License v3.0
1 stars 4 forks source link

Brainstorming movement and sim state data output #142

Closed brettfiedler closed 3 years ago

brettfiedler commented 4 years ago

Note: This issue is not tied to RaPs publication. It's just the most relevant sim we have currently in design. For use during meeting scheduled for 8/17

We'd like to be able to extract positional data for sim elements (e.g., draggable), positional data of related tangibles (e.g., fiducial markers [mechamarkers]), or other sim state/trigger information (e.g., goal states, auditory events, etc) for further statistical and qualitative analysis.

Frequency of data would be tied to whatever the limits of each mode are (e.g., mechamarkers might be 50-60Hz). Ultimate result requires synchronization across any obtained data. There are some synchronization issues that may be addressed at this stage (universal time stamps?), but much of it may need to happen in post-process.

I don't have too much more insight how exactly we get this information, but ideally, the end product that we do our analysis looks something like this (I imagine this is a bit simplified or there are better alternatives for specified units, but hopefully gets the idea across):

image

zepumph commented 4 years ago

I think that all this data is available in the step function in which we convert marker location into model values:

https://github.com/phetsims/ratio-and-proportion/blob/77f0c610f1c41928c87f166460a1ec64da24fd73/js/common/view/ProportionMarkerInput.js#L45-L62

We should be able to log everything needed there

This step function is run at 60hz (ideal).

zepumph commented 4 years ago

I have written code that spits out a csv also before, and could add an options dialog entry to do that. My guess is that this would take an hour to get a one-off version snapshot that could do this for you. I will hold off until our meeting though.

https://github.com/phetsims/interaction-dashboard/blob/705fa1f14e8231fbc5861ec9e508c0c3c0c01366/js/preprocessor/preprocessor.js#L153

zepumph commented 4 years ago

From today's meeting, I will try to get something to @BLFiedler for him to poke around with by the end of next week.

brettfiedler commented 4 years ago

I'll be talking to EM tomorrow (8/18) and can confirm if anything here should be prioritized for the short-term.

zepumph commented 3 years ago

Today @BLFiedler said we can "push this out for a while." Marking as deferred.

brettfiedler commented 3 years ago

This is being revived for this quarter. Reassigning to put on radar, but we may want to move it to a new issue that discusses the specific implementation and/or PhET-iO usage.

zepumph commented 3 years ago

@BLFiedler, would you please put out any deadlines for the next quarter or so, even general ones, into this issue?

brettfiedler commented 3 years ago

Looking like week of March 9th for a usable prototype.

zepumph commented 3 years ago

Sounds good! Looks like that is about 3 weeks left for this deadline. I will get started now on an MVP.

zepumph commented 3 years ago

After instrumenting the sim over in https://github.com/phetsims/ratio-and-proportion/issues/351, here is an example of the data stream as you change one of the hands.

```json [ { "index": 167, "time": 1613424215275, "type": "MODEL", "phetioID": "ratioAndProportion.createScreen.model.ratio.tupleProperty", "name": "changed", "componentType": "PropertyIO", "data": { "oldValue": { "antecedent": 0.2, "consequent": 0.5055520808315553 }, "newValue": { "antecedent": 0.2, "consequent": 0.5067515362955503 } }, "children": [ { "index": 168, "time": 1613424215275, "type": "MODEL", "phetioID": "ratioAndProportion.createScreen.model.unclampedFitnessProperty", "name": "changed", "componentType": "DerivedPropertyIO", "data": { "oldValue": 0.4722395958422235, "newValue": 0.46624231852224907 } } ] }, { "index": 169, "time": 1613424215279, "type": "MODEL", "phetioID": "ratioAndProportion.createScreen.model.ratio.tupleProperty", "name": "changed", "componentType": "PropertyIO", "data": { "oldValue": { "antecedent": 0.2, "consequent": 0.5067515362955503 }, "newValue": { "antecedent": 0.2, "consequent": 0.5079509917595452 } }, "children": [ { "index": 170, "time": 1613424215279, "type": "MODEL", "phetioID": "ratioAndProportion.createScreen.model.unclampedFitnessProperty", "name": "changed", "componentType": "DerivedPropertyIO", "data": { "oldValue": 0.46624231852224907, "newValue": 0.4602450412022745 } } ] }, { "index": 171, "time": 1613424215283, "type": "MODEL", "phetioID": "ratioAndProportion.createScreen.model.ratio.tupleProperty", "name": "changed", "componentType": "PropertyIO", "data": { "oldValue": { "antecedent": 0.2, "consequent": 0.5079509917595452 }, "newValue": { "antecedent": 0.2, "consequent": 0.5103499026875351 } }, "children": [ { "index": 172, "time": 1613424215283, "type": "MODEL", "phetioID": "ratioAndProportion.createScreen.model.unclampedFitnessProperty", "name": "changed", "componentType": "DerivedPropertyIO", "data": { "oldValue": 0.4602450412022745, "newValue": 0.4482504865623247 } } ] }, { "index": 173, "time": 1613424215295, "type": "MODEL", "phetioID": "ratioAndProportion.createScreen.model.ratio.tupleProperty", "name": "changed", "componentType": "PropertyIO", "data": { "oldValue": { "antecedent": 0.2, "consequent": 0.5103499026875351 }, "newValue": { "antecedent": 0.2, "consequent": 0.5115493581515301 } }, "children": [ { "index": 174, "time": 1613424215295, "type": "MODEL", "phetioID": "ratioAndProportion.createScreen.model.unclampedFitnessProperty", "name": "changed", "componentType": "DerivedPropertyIO", "data": { "oldValue": 0.4482504865623247, "newValue": 0.4422532092423497 } } ] }, { "index": 175, "time": 1613424215304, "type": "MODEL", "phetioID": "ratioAndProportion.createScreen.model.ratio.tupleProperty", "name": "changed", "componentType": "PropertyIO", "data": { "oldValue": { "antecedent": 0.2, "consequent": 0.5115493581515301 }, "newValue": { "antecedent": 0.2, "consequent": 0.512748813615525 } }, "children": [ { "index": 176, "time": 1613424215304, "type": "MODEL", "phetioID": "ratioAndProportion.createScreen.model.unclampedFitnessProperty", "name": "changed", "componentType": "DerivedPropertyIO", "data": { "oldValue": 0.4422532092423497, "newValue": 0.436255931922375 } } ] } ] ```

And the model.targetRatioProperty will be in the data stream whenever it changes. Its initial value is given (along with everything else) in the initialState event that is emitted on startup. You can play with it further by looking at the console output of

JSON output: https://phet-dev.colorado.edu/html/ratio-and-proportion/1.1.0-dev.2/phet-io/ratio-and-proportion_all_phet-io.html?postMessageOnError&phetioStandalone&phetioConsoleLog=json&phetioEmitHighFrequencyEvents=false

A bit nicer and colorized: https://phet-dev.colorado.edu/html/ratio-and-proportion/1.1.0-dev.2/phet-io/ratio-and-proportion_all_phet-io.html?postMessageOnError&phetioStandalone&phetioConsoleLog=colorized&phetioEmitHighFrequencyEvents=false

I'll head over to https://github.com/phetsims/tangible/issues/4 now.

zepumph commented 3 years ago

https://github.com/phetsims/tangible/issues/4 is going to take a bit more work. I'll keep posted.

zepumph commented 3 years ago

Here is my working progress:

```diff Index: sherpa/lib/license.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sherpa/lib/license.json b/sherpa/lib/license.json --- a/sherpa/lib/license.json (revision be80d522167d828fc905e5ef695447a498ade921) +++ b/sherpa/lib/license.json (date 1613437903777) @@ -26,6 +26,14 @@ "BigInteger-cda5bcc.js" ] }, + "beholder-detection-1.1.5.js": { + "text": [ + "Copyright (c) 2020 Peter Gyory & Clement Zheng" + ], + "projectURL": "https://github.com/atlas-acme-lab/beholder-detection", + "license": "The MIT License", + "notes": "A computer vision library for tangible input into phetsims." + }, "benchmark-1.0.0.js": { "text": [ "Copyright (c) 2010-2012 Mathias Bynens http://mths.be", Index: ratio-and-proportion/package.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/ratio-and-proportion/package.json b/ratio-and-proportion/package.json --- a/ratio-and-proportion/package.json (revision a5a455e4c79963a4151be0b62dcb48e8a8944ca5) +++ b/ratio-and-proportion/package.json (date 1613426508027) @@ -12,7 +12,11 @@ "phet": { "requirejsNamespace": "RATIO_AND_PROPORTION", "phetLibs": [ - "griddle" + "griddle", + "tangible" + ], + "preload": [ + "../sherpa/lib/beholder-detection-1.1.4.js" ], "simulation": true, "runnable": true, Index: ratio-and-proportion/ratio-and-proportion_en.html IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/ratio-and-proportion/ratio-and-proportion_en.html b/ratio-and-proportion/ratio-and-proportion_en.html --- a/ratio-and-proportion/ratio-and-proportion_en.html (revision a5a455e4c79963a4151be0b62dcb48e8a8944ca5) +++ b/ratio-and-proportion/ratio-and-proportion_en.html (date 1613426707980) @@ -34,7 +34,11 @@ "phet": { "requirejsNamespace": "RATIO_AND_PROPORTION", "phetLibs": [ - "griddle" + "griddle", + "tangible" + ], + "preload": [ + "../sherpa/lib/beholder-detection-1.1.4.js" ], "simulation": true, "runnable": true, @@ -103,7 +107,8 @@ '../sherpa/lib/base64-js-1.2.0.js', '../sherpa/lib/TextEncoderLite-3c9f6f0.js', '../tandem/js/PhetioIDUtils.js', - '../chipper/js/SimVersion.js' + '../chipper/js/SimVersion.js', + '../sherpa/lib/beholder-detection-1.1.4.js' ]; if ( brand === 'phet-io' ) { Index: tangible/package.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tangible/package.json b/tangible/package.json --- a/tangible/package.json (revision e8ba2f6d5aa010d3c70ae7aa6174946de9bcde55) +++ b/tangible/package.json (date 1613426382126) @@ -10,7 +10,10 @@ "grunt": "~1.1.0" }, "phet": { - "requirejsNamespace": "TANGIBLE" + "requirejsNamespace": "TANGIBLE", + "preload": [ + "../sherpa/lib/beholder-detection-1.1.4.js" + ] }, "eslintConfig": { "extends": "../chipper/eslint/sim_es6_eslintrc.js" Index: tangible/js/MarkerInput.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tangible/js/MarkerInput.js b/tangible/js/MarkerInput.js --- a/tangible/js/MarkerInput.js (revision e8ba2f6d5aa010d3c70ae7aa6174946de9bcde55) +++ b/tangible/js/MarkerInput.js (date 1613435087946) @@ -13,19 +13,38 @@ // modules import stepTimer from '../../axon/js/stepTimer.js'; -import Features from '../../scenery/js/util/Features.js'; import '../../sherpa/lib/mechamarkers-21f16221e414ec2dca68bbfbb866369eea7abd70.js'; import tangible from './tangible.js'; // This flag keeps Mechamarkers from being implemented more than once. let mechamarkersInitialized = false; +const beholderInitParameters = { + camera_params: { + videoSize: 1, // The video size values map to the following [320 x 240, 640 x 480, 1280 x 720, 1920 x 1080] + rearCamera: false, // Boolean value for defaulting to the rear facing camera. Only works on mobile + torch: false // Boolean value for if torch/flashlight is on. Only works for rear facing mobile cameras. Can only be set from init + }, + detection_params: { + minMarkerDistance: 10, + minMarkerPerimeter: 0.2, + maxMarkerPerimeter: 0.8, + sizeAfterPerspectiveRemoval: 49 + }, + feed_params: { + contrast: 0, + brightness: 0, + grayscale: 0, + flip: false + } +}; + class MarkerInput { constructor() { - // @protected (read-only) {Mechamarkers} - instead of using the global, this allows to keep encapsulation better. - this.Mechamarkers = window.Mechamarkers; + // @protected (read-only) {Beholder} - instead of using the global, this allows to keep encapsulation better. + this.Beholder = window[ 'beholder-detection' ]; // the Mechamarkers library only needs to be initialized once. If another instance has already done this, then don't // do it again. @@ -34,21 +53,20 @@ stepTimer.addListener( () => { // Mechamarkers stuff - this.Mechamarkers.update( Date.now() ); + this.Beholder.update( Date.now() ); } ); - const canvas = document.createElement( 'canvas' ); + const div = document.createElement( 'div' ); // push it off screen and disable user input so that it cannot be selected on Safari, see // https://github.com/phetsims/ratio-and-proportion/issues/39 - canvas.style.position = 'absolute'; - canvas.style.left = '-10000px'; - canvas.style.right = '-10000px'; - canvas.style[ Features.userSelect ] = 'none'; + // div.style.position = 'absolute'; + // div.style.left = '-10000px'; + // div.style.right = '-10000px'; + // div.style[ Features.userSelect ] = 'none'; - const ctx = canvas.getContext( '2d' ); - document.body.appendChild( canvas ); - this.Mechamarkers.init( canvas, ctx ); + document.body.appendChild( div ); + this.Beholder.init( div, beholderInitParameters ); mechamarkersInitialized = true; } Index: ratio-and-proportion/README.md IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/ratio-and-proportion/README.md b/ratio-and-proportion/README.md --- a/ratio-and-proportion/README.md (revision a5a455e4c79963a4151be0b62dcb48e8a8944ca5) +++ b/ratio-and-proportion/README.md (date 1613426706543) @@ -41,6 +41,7 @@ git clone https://github.com/phetsims/sun.git git clone https://github.com/phetsims/tambo.git git clone https://github.com/phetsims/tandem.git +git clone https://github.com/phetsims/tangible.git git clone https://github.com/phetsims/twixt.git git clone https://github.com/phetsims/utterance-queue.git ``` Index: ratio-and-proportion/js/common/view/ProportionMarkerInput.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/ratio-and-proportion/js/common/view/ProportionMarkerInput.js b/ratio-and-proportion/js/common/view/RAPMarkerInput.js rename from ratio-and-proportion/js/common/view/ProportionMarkerInput.js rename to ratio-and-proportion/js/common/view/RAPMarkerInput.js --- a/ratio-and-proportion/js/common/view/ProportionMarkerInput.js (revision a5a455e4c79963a4151be0b62dcb48e8a8944ca5) +++ b/ratio-and-proportion/js/common/view/RAPMarkerInput.js (date 1613434766219) @@ -21,7 +21,7 @@ // "one" here refers to the max value of each ratio half. Their range is from 0 to 1 const HEIGHT_OF_ONE = RAPQueryParameters.heightInPixels; -class ProportionMarkerInput extends MarkerInput { +class RAPMarkerInput extends MarkerInput { /** * @param {Property.} ratioTupleProperty @@ -48,16 +48,17 @@ */ step() { + console.log( this.Beholder.getMarker( 2 ).present ); + // This controller needs all three markers - this.isBeingInteractedWithProperty.value = this.Mechamarkers.getMarker( RATIO_MARKER_LEFT ).present && - this.Mechamarkers.getMarker( BASE_MARKER ).present && - this.Mechamarkers.getMarker( RATIO_MARKER_RIGHT ).present; + this.isBeingInteractedWithProperty.value = this.Beholder.getMarker( RATIO_MARKER_LEFT ).present && + this.Beholder.getMarker( BASE_MARKER ).present && + this.Beholder.getMarker( RATIO_MARKER_RIGHT ).present; if ( this.isBeingInteractedWithProperty.value ) { - - const baseMarker = this.Mechamarkers.getMarker( BASE_MARKER ); - const leftMarker = this.Mechamarkers.getMarker( RATIO_MARKER_LEFT ); - const rightMarker = this.Mechamarkers.getMarker( RATIO_MARKER_RIGHT ); + const baseMarker = this.Beholder.getMarker( BASE_MARKER ); + const leftMarker = this.Beholder.getMarker( RATIO_MARKER_LEFT ); + const rightMarker = this.Beholder.getMarker( RATIO_MARKER_RIGHT ); assert && assert( leftMarker.present && baseMarker.present && rightMarker.present, 'all markers must be present' ); @@ -68,5 +69,5 @@ } } -ratioAndProportion.register( 'ProportionMarkerInput', ProportionMarkerInput ); -export default ProportionMarkerInput; \ No newline at end of file +ratioAndProportion.register( 'RAPMarkerInput', RAPMarkerInput ); +export default RAPMarkerInput; \ No newline at end of file Index: ratio-and-proportion/ratio-and-proportion-tests.html IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/ratio-and-proportion/ratio-and-proportion-tests.html b/ratio-and-proportion/ratio-and-proportion-tests.html --- a/ratio-and-proportion/ratio-and-proportion-tests.html (revision a5a455e4c79963a4151be0b62dcb48e8a8944ca5) +++ b/ratio-and-proportion/ratio-and-proportion-tests.html (date 1613426708864) @@ -35,7 +35,11 @@ "phet": { "requirejsNamespace": "RATIO_AND_PROPORTION", "phetLibs": [ - "griddle" + "griddle", + "tangible" + ], + "preload": [ + "../sherpa/lib/beholder-detection-1.1.4.js" ], "simulation": true, "runnable": true, @@ -104,6 +108,7 @@ '../sherpa/lib/TextEncoderLite-3c9f6f0.js', '../tandem/js/PhetioIDUtils.js', '../chipper/js/SimVersion.js', + '../sherpa/lib/beholder-detection-1.1.4.js', '../sherpa/lib/qunit-2.10.0.js', '../chipper/js/sim-tests/qunit-connector.js' ]; Index: sherpa/licenses/beholder-detection-1.1.5.js.txt IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sherpa/licenses/beholder-detection-1.1.5.js.txt b/sherpa/licenses/beholder-detection-1.1.5.js.txt new file mode 100644 --- /dev/null (date 1613426214312) +++ b/sherpa/licenses/beholder-detection-1.1.5.js.txt (date 1613426214312) @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Peter Gyory & Clement Zheng + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file Index: ratio-and-proportion/js/common/view/RAPScreenView.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/ratio-and-proportion/js/common/view/RAPScreenView.js b/ratio-and-proportion/js/common/view/RAPScreenView.js --- a/ratio-and-proportion/js/common/view/RAPScreenView.js (revision a5a455e4c79963a4151be0b62dcb48e8a8944ca5) +++ b/ratio-and-proportion/js/common/view/RAPScreenView.js (date 1613426667627) @@ -39,6 +39,7 @@ import HandPositionsDescriber from './describers/HandPositionsDescriber.js'; import RatioDescriber from './describers/RatioDescriber.js'; import TickMarkDescriber from './describers/TickMarkDescriber.js'; +import RAPMarkerInput from './RAPMarkerInput.js'; import RAPColorProfile from './RAPColorProfile.js'; import RAPTickMarkLabelsNode from './RAPTickMarkLabelsNode.js'; import RatioHalf from './RatioHalf.js'; @@ -229,12 +230,12 @@ ); // @private TODO: add support for mechamarker input again https://github.com/phetsims/ratio-and-proportion/issues/89 - // this.markerInput = new ProportionMarkerInput( ratio.tupleProperty ); + this.markerInput = new RAPMarkerInput( ratio.tupleProperty ); const soundGeneratorEnabledProperty = DerivedProperty.or( [ this.antecedentRatioHalf.isBeingInteractedWithProperty, this.consequentRatioHalf.isBeingInteractedWithProperty, - // this.markerInput.isBeingInteractedWithProperty, // TODO: add support for mechamarker input again https://github.com/phetsims/ratio-and-proportion/issues/89 + this.markerInput.isBeingInteractedWithProperty, // TODO: add support for mechamarker input again https://github.com/phetsims/ratio-and-proportion/issues/89 bothHandsPDOMNode.isBeingInteractedWithProperty ] ); @@ -424,7 +425,7 @@ step( dt ) { // TODO: add support for mechamarker input, https://github.com/phetsims/ratio-and-proportion/issues/89 - // this.markerInput.step( dt ); + this.markerInput.step( dt ); this.inProportionSoundGenerator.step( dt ); this.staccatoFrequencySoundGenerator.step( dt ); } Index: sherpa/lib/beholder-detection-1.1.5.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sherpa/lib/beholder-detection-1.1.5.js b/sherpa/lib/beholder-detection-1.1.5.js new file mode 100644 --- /dev/null (date 1613437903768) +++ b/sherpa/lib/beholder-detection-1.1.5.js (date 1613437903768) @@ -0,0 +1,25 @@ +/* + MIT License + + Copyright (c) 2020 Peter Gyory & Clement Zheng + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +module.exports=function(t){var e={};function r(n){if(e[n])return e[n].exports;var i=e[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)r.d(n,i,function(e){return t[e]}.bind(null,i));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=33)}([function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0}),e.NO_IL=e.NO=e.MemoryStream=e.Stream=void 0;var o=r(23),s=r(25),a=o.default(s.getPolyfill()),u={};function c(){}function l(t){for(var e=t.length,r=Array(e),n=0;n=this.max&&e._n(t)},t.prototype._e=function(t){var e=this.out;e!==u&&e._e(t)},t.prototype._c=function(){var t=this.out;t!==u&&t._c()},t}(),E=function(){function t(t,e){this.out=t,this.op=e}return t.prototype._n=function(){this.op.end()},t.prototype._e=function(t){this.out._e(t)},t.prototype._c=function(){this.op.end()},t}(),S=function(){function t(t,e){this.type="endWhen",this.ins=e,this.out=u,this.o=t,this.oil=h}return t.prototype._start=function(t){this.out=t,this.o._add(this.oil=new E(t,this)),this.ins._add(this)},t.prototype._stop=function(){this.ins._remove(this),this.o._remove(this.oil),this.out=u,this.oil=h},t.prototype.end=function(){var t=this.out;t!==u&&t._c()},t.prototype._n=function(t){var e=this.out;e!==u&&e._n(t)},t.prototype._e=function(t){var e=this.out;e!==u&&e._e(t)},t.prototype._c=function(){this.end()},t}(),j=function(){function t(t,e){this.type="filter",this.ins=e,this.out=u,this.f=t}return t.prototype._start=function(t){this.out=t,this.ins._add(this)},t.prototype._stop=function(){this.ins._remove(this),this.out=u},t.prototype._n=function(t){var e=this.out;if(e!==u){var r=f(this,t,e);r!==u&&r&&e._n(t)}},t.prototype._e=function(t){var e=this.out;e!==u&&e._e(t)},t.prototype._c=function(){var t=this.out;t!==u&&t._c()},t}(),k=function(){function t(t,e){this.out=t,this.op=e}return t.prototype._n=function(t){this.out._n(t)},t.prototype._e=function(t){this.out._e(t)},t.prototype._c=function(){this.op.inner=u,this.op.less()},t}(),A=function(){function t(t){this.type="flatten",this.ins=t,this.out=u,this.open=!0,this.inner=u,this.il=h}return t.prototype._start=function(t){this.out=t,this.open=!0,this.inner=u,this.il=h,this.ins._add(this)},t.prototype._stop=function(){this.ins._remove(this),this.inner!==u&&this.inner._remove(this.il),this.out=u,this.open=!0,this.inner=u,this.il=h},t.prototype.less=function(){var t=this.out;t!==u&&(this.open||this.inner!==u||t._c())},t.prototype._n=function(t){var e=this.out;if(e!==u){var r=this.inner,n=this.il;r!==u&&n!==h&&r._remove(n),(this.inner=t)._add(this.il=new k(e,this))}},t.prototype._e=function(t){var e=this.out;e!==u&&e._e(t)},t.prototype._c=function(){this.open=!1,this.less()},t}(),T=function(){function t(t,e,r){var n=this;this.type="fold",this.ins=r,this.out=u,this.f=function(e){return t(n.acc,e)},this.acc=this.seed=e}return t.prototype._start=function(t){this.out=t,this.acc=this.seed,t._n(this.acc),this.ins._add(this)},t.prototype._stop=function(){this.ins._remove(this),this.out=u,this.acc=this.seed},t.prototype._n=function(t){var e=this.out;if(e!==u){var r=f(this,t,e);r!==u&&e._n(this.acc=r)}},t.prototype._e=function(t){var e=this.out;e!==u&&e._e(t)},t.prototype._c=function(){var t=this.out;t!==u&&t._c()},t}(),C=function(){function t(t){this.type="last",this.ins=t,this.out=u,this.has=!1,this.val=u}return t.prototype._start=function(t){this.out=t,this.has=!1,this.ins._add(this)},t.prototype._stop=function(){this.ins._remove(this),this.out=u,this.val=u},t.prototype._n=function(t){this.has=!0,this.val=t},t.prototype._e=function(t){var e=this.out;e!==u&&e._e(t)},t.prototype._c=function(){var t=this.out;t!==u&&(this.has?(t._n(this.val),t._c()):t._e(new Error("last() failed because input stream completed")))},t}(),D=function(){function t(t,e){this.type="map",this.ins=e,this.out=u,this.f=t}return t.prototype._start=function(t){this.out=t,this.ins._add(this)},t.prototype._stop=function(){this.ins._remove(this),this.out=u},t.prototype._n=function(t){var e=this.out;if(e!==u){var r=f(this,t,e);r!==u&&e._n(r)}},t.prototype._e=function(t){var e=this.out;e!==u&&e._e(t)},t.prototype._c=function(){var t=this.out;t!==u&&t._c()},t}(),P=function(){function t(t){this.type="remember",this.ins=t,this.out=u}return t.prototype._start=function(t){this.out=t,this.ins._add(t)},t.prototype._stop=function(){this.ins._remove(this.out),this.out=u},t}(),N=function(){function t(t,e){this.type="replaceError",this.ins=e,this.out=u,this.f=t}return t.prototype._start=function(t){this.out=t,this.ins._add(this)},t.prototype._stop=function(){this.ins._remove(this),this.out=u},t.prototype._n=function(t){var e=this.out;e!==u&&e._n(t)},t.prototype._e=function(t){var e=this.out;if(e!==u)try{this.ins._remove(this),(this.ins=this.f(t))._add(this)}catch(t){e._e(t)}},t.prototype._c=function(){var t=this.out;t!==u&&t._c()},t}(),I=function(){function t(t,e){this.type="startWith",this.ins=t,this.out=u,this.val=e}return t.prototype._start=function(t){this.out=t,this.out._n(this.val),this.ins._add(t)},t.prototype._stop=function(){this.ins._remove(this.out),this.out=u},t}(),L=function(){function t(t,e){this.type="take",this.ins=e,this.out=u,this.max=t,this.taken=0}return t.prototype._start=function(t){this.out=t,this.taken=0,this.max<=0?t._c():this.ins._add(this)},t.prototype._stop=function(){this.ins._remove(this),this.out=u},t.prototype._n=function(t){var e=this.out;if(e!==u){var r=++this.taken;r1))if(this._stopID!==u)clearTimeout(this._stopID),this._stopID=u;else{var n=this._prod;n!==u&&n._start(this)}},t.prototype._remove=function(t){var e=this,r=this._target;if(r)return r._remove(t);var n=this._ils,i=n.indexOf(t);i>-1&&(n.splice(i,1),this._prod!==u&&n.length<=0?(this._err=u,this._stopID=setTimeout((function(){return e._stopNow()}))):1===n.length&&this._pruneCycles())},t.prototype._pruneCycles=function(){this._hasNoSinks(this,[])&&this._remove(this._ils[0])},t.prototype._hasNoSinks=function(t,e){if(-1!==e.indexOf(t))return!0;if(t.out===this)return!0;if(t.out&&t.out!==u)return this._hasNoSinks(t.out,e.concat(t));if(t._ils){for(var r=0,n=t._ils.length;r1)this._has&&t._n(this._v);else if(this._stopID!==u)this._has&&t._n(this._v),clearTimeout(this._stopID),this._stopID=u;else if(this._has)t._n(this._v);else{var n=this._prod;n!==u&&n._start(this)}},e.prototype._stopNow=function(){this._has=!1,t.prototype._stopNow.call(this)},e.prototype._x=function(){this._has=!1,t.prototype._x.call(this)},e.prototype.map=function(t){return this._map(t)},e.prototype.mapTo=function(e){return t.prototype.mapTo.call(this,e)},e.prototype.take=function(e){return t.prototype.take.call(this,e)},e.prototype.endWhen=function(e){return t.prototype.endWhen.call(this,e)},e.prototype.replaceError=function(e){return t.prototype.replaceError.call(this,e)},e.prototype.remember=function(){return this},e.prototype.debug=function(e){return t.prototype.debug.call(this,e)},e}(B);e.MemoryStream=R;var $=B;e.default=$},function(t,e){t.exports=require("mathjs")},function(t,e,r){"use strict";(function(t){function r(){var e;return(e="undefined"!=typeof window?window:void 0!==t?t:this).Cyclejs=e.Cyclejs||{},(e=e.Cyclejs).adaptStream=e.adaptStream||function(t){return t},e}Object.defineProperty(e,"__esModule",{value:!0}),e.setAdapt=function(t){r().adaptStream=t},e.adapt=function(t){return r().adaptStream(t)}}).call(this,r(4))},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(6),i=r(31);function o(t,e,r){var o,s,a,u={};if(void 0!==r?(u=e,i.array(r)?o=r:i.primitive(r)?s=r:r&&r.sel&&(o=[r])):void 0!==e&&(i.array(e)?o=e:i.primitive(e)?s=e:e&&e.sel?o=[e]:u=e),void 0!==o)for(a=0;a0||e._n(n([t],this.vals)))},t.prototype._e=function(t){var e=this.out;e!==o&&e._e(t)},t.prototype._c=function(){var t=this.out;t!==o&&t._c()},t.prototype.up=function(t,e){var r=this.vals[e];this.Nn>0&&r===o&&this.Nn--,this.vals[e]=t},t.prototype.down=function(t,e){this.others[t]._remove(e)},t}();e.SampleCombineOperator=u,a=function(){for(var t=[],e=0;e2?arguments[2]:{},o=n(e);i&&(o=s.call(o,Object.getOwnPropertySymbols(e)));for(var a=0;a=0&&"[object Function]"===n.call(t.callee)),r}},function(t,e,r){"use strict";"undefined"!=typeof self?t.exports=self:"undefined"!=typeof window?t.exports=window:t.exports=Function("return this")()},function(t,e,r){"use strict";(function(e){var n=r(17);t.exports=function(){return"object"==typeof e&&e&&e.Math===Math&&e.Array===Array?e:n}}).call(this,r(4))},function(t,e){var r,n,i=t.exports={};function o(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function a(t){if(r===setTimeout)return setTimeout(t,0);if((r===o||!r)&&setTimeout)return r=setTimeout,setTimeout(t,0);try{return r(t,0)}catch(e){try{return r.call(null,t,0)}catch(e){return r.call(this,t,0)}}}!function(){try{r="function"==typeof setTimeout?setTimeout:o}catch(t){r=o}try{n="function"==typeof clearTimeout?clearTimeout:s}catch(t){n=s}}();var u,c=[],l=!1,f=-1;function h(){l&&u&&(l=!1,u.length?c=u.concat(c):f=-1,c.length&&p())}function p(){if(!l){var t=a(h);l=!0;for(var e=c.length;e;){for(u=c,c=[];++f1)for(var r=1;r0&&!i.call(t,0))for(var m=0;m0)for(var y=0;y=0&&(t._idleTimeoutId=setTimeout((function(){t._onTimeout&&t._onTimeout()}),e))},r(30),e.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==t&&t.setImmediate||this&&this.setImmediate,e.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==t&&t.clearImmediate||this&&this.clearImmediate}).call(this,r(4))},function(t,e,r){(function(t,e){!function(t,r){"use strict";if(!t.setImmediate){var n,i,o,s,a,u=1,c={},l=!1,f=t.document,h=Object.getPrototypeOf&&Object.getPrototypeOf(t);h=h&&h.setTimeout?h:t,"[object process]"==={}.toString.call(t.process)?n=function(t){e.nextTick((function(){d(t)}))}:!function(){if(t.postMessage&&!t.importScripts){var e=!0,r=t.onmessage;return t.onmessage=function(){e=!1},t.postMessage("","*"),t.onmessage=r,e}}()?t.MessageChannel?((o=new MessageChannel).port1.onmessage=function(t){d(t.data)},n=function(t){o.port2.postMessage(t)}):f&&"onreadystatechange"in f.createElement("script")?(i=f.documentElement,n=function(t){var e=f.createElement("script");e.onreadystatechange=function(){d(t),e.onreadystatechange=null,i.removeChild(e),e=null},i.appendChild(e)}):n=function(t){setTimeout(d,0,t)}:(s="setImmediate$"+Math.random()+"$",a=function(e){e.source===t&&"string"==typeof e.data&&0===e.data.indexOf(s)&&d(+e.data.slice(s.length))},t.addEventListener?t.addEventListener("message",a,!1):t.attachEvent("onmessage",a),n=function(e){t.postMessage(s+e,"*")}),h.setImmediate=function(t){"function"!=typeof t&&(t=new Function(""+t));for(var e=new Array(arguments.length-1),r=0;r=0&&"selector"===t[r].type;r--)e=t[r].scope+" "+e;return e.trim()}function x(t,e){if(!Array.isArray(t)||!Array.isArray(e)||t.length!==e.length)return!1;for(var r=0;re.length||!x(this._namespace,e.slice(0,this._namespace.length)))return!1;for(var r=this._namespace.length;r1&&("."===e[0]||"#"===e[0])?"sibling":"total"),scope:t};var e}var S=function(){function t(e,r,n,i,o,s){var a;void 0===n&&(n=[]),this._rootElement$=e,this._sanitation$=r,this._namespace=n,this._isolateModule=i,this._eventDelegator=o,this._name=s,this.isolateSource=function(e,r){return new t(e._rootElement$,e._sanitation$,e._namespace.concat(E(r)),e._isolateModule,e._eventDelegator,e._name)},this.isolateSink=(a=this._namespace,function(t,e){return":root"===e?t:t.map((function(t){if(!t)return t;var r=E(e),n=M({},t,{data:M({},t.data,{isolate:t.data&&Array.isArray(t.data.isolate)?t.data.isolate:a.concat([r])})});return M({},n,{key:void 0!==n.key?n.key:JSON.stringify(n.data.isolate)})}))})}return t.prototype._elements=function(){if(0===this._namespace.length)return this._rootElement$.map((function(t){return[t]}));var t=new O(this._namespace,this._isolateModule);return this._rootElement$.map((function(){return t.call()}))},t.prototype.elements=function(){var t=Object(d.adapt)(this._elements().remember());return t._isCycleSource=this._name,t},t.prototype.element=function(){var t=Object(d.adapt)(this._elements().filter((function(t){return t.length>0})).map((function(t){return t[0]})).remember());return t._isCycleSource=this._name,t},Object.defineProperty(t.prototype,"namespace",{get:function(){return this._namespace},enumerable:!0,configurable:!0}),t.prototype.select=function(e){if("string"!=typeof e)throw new Error("DOM driver's select() expects the argument to be a string as a CSS selector");if("document"===e)return new v(this._name);if("body"===e)return new g(this._name);var r=":root"===e?[]:this._namespace.concat({type:"selector",scope:e.trim()});return new t(this._rootElement$,this._sanitation$,r,this._isolateModule,this._eventDelegator,this._name)},t.prototype.events=function(t,e,r){if(void 0===e&&(e={}),"string"!=typeof t)throw new Error("DOM driver's events() expects argument to be a string representing the event type to listen for.");var n=this._eventDelegator.addEventListener(t,this._namespace,e,r),i=Object(d.adapt)(n);return i._isCycleSource=this._name,i},t.prototype.dispose=function(){this._sanitation$.shamefullySendNext(null)},t}();function j(t,e,r,n,i){return{sel:t,data:e,children:r,text:n,elm:i,key:void 0===e?void 0:e.key}}var k=j,A=Array.isArray;function T(t){return"string"==typeof t||"number"==typeof t}var C={createElement:function(t){return document.createElement(t)},createElementNS:function(t,e){return document.createElementNS(t,e)},createTextNode:function(t){return document.createTextNode(t)},createComment:function(t){return document.createComment(t)},insertBefore:function(t,e,r){t.insertBefore(e,r)},removeChild:function(t,e){t.removeChild(e)},appendChild:function(t,e){t.appendChild(e)},parentNode:function(t){return t.parentNode},nextSibling:function(t){return t.nextSibling},tagName:function(t){return t.tagName},setTextContent:function(t,e){t.textContent=e},getTextContent:function(t){return t.textContent},isElement:function(t){return 1===t.nodeType},isText:function(t){return 3===t.nodeType},isComment:function(t){return 8===t.nodeType}};function D(t){return void 0===t}function P(t){return void 0!==t}var N=k("",{},[],void 0,void 0);function I(t,e){return t.key===e.key&&t.sel===e.sel}function L(t,e,r){var n,i,o,s={};for(n=e;n<=r;++n)null!=(o=t[n])&&void 0!==(i=o.key)&&(s[i]=n);return s}var B=["create","update","remove","destroy","pre","post"];function R(t,e){var r,n,i={},o=void 0!==e?e:C;for(r=0;r0?c:a.length,h=l>0?l:a.length,p=-1!==c||-1!==l?a.slice(0,Math.min(f,h)):a,d=t.elm=P(n)&&P(r=n.ns)?o.createElementNS(r,p):o.createElement(p);for(f0&&d.setAttribute("class",a.slice(h+1).replace(/\./g," ")),r=0;rd?c(t,null==r[v+1]?null:r[v+1].elm,r,p,v,n):f(t,e,l,d))}(a,l,p,r):P(p)?(P(t.text)&&o.setTextContent(a,""),c(a,null,p,0,p.length-1,r)):P(l)?f(a,l,0,l.length-1):P(t.text)&&o.setTextContent(a,""):t.text!==e.text&&(P(l)&&f(a,l,0,l.length-1),o.setTextContent(a,e.text)),P(s)&&P(n=s.postpatch)&&n(t,e)}}return function(t,e){var r,n,s,a=[];for(r=0;r)[ \t]*)|(?:[ \t]*(\\+)[ \t]*)|(?:[ \t]*(~)[ \t]*))',"y");i.lastIndex=r.lastIndex;for(var o=[],s=void 0,a=void 0,u=-1;i.lastIndex1)throw new Error("Invalid selector, only one id is allowed");var h=new RegExp("([\\w-]+)[ \t]*("+H+")?"+V+"([^\\]]+)?"),p=o.filter((function(t){return t.startsWith("[")})).map((function(t){return h.exec(t).slice(1,4)})).map((function(t){var e,r=t[0],n=t[1],i=t[2];return(e={})[r]=[J(n),i?Y(i):i],e})).reduce((function(t,e){return q({},t,e)}),{}),d=o.filter((function(t){return t.startsWith(":")})).map((function(t){return function(t){if("first-child"===t||"last-child"===t||"root"===t||"empty"===t)return[t,void 0];if(t.startsWith("contains")){return["contains",t.slice(10,-2)]}var e=t.slice(10,-1);"even"===e&&(e="2n");"odd"===e&&(e="2n+1");return["nth-child",e]}(t.substring(1))}));return{id:f[0]||"",tag:n,classList:l,attributes:p,nextSelector:s,pseudos:d}}function Y(t){if(t.startsWith('"'))return t.slice(1,-1);if("true"===t)return!0;if("false"===t)return!1;var e=parseFloat(t);return isNaN(e)?t:e}function J(t){switch(t){case"=":return"exact";case"^=":return"startsWith";case"$=":return"endsWith";case"*=":return"contains";case"~=":return"whitespace";case"|=":return"dash";default:return"has"}}function Q(t){switch(t.trim()){case">":return"child";case"+":return"nextSibling";case"~":return"sibling";default:return"subtree"}}function tt(t){return function(e,r){var n="object"==typeof e?e:Z(e),i=n.tag,o=n.id,s=n.classList,a=n.attributes,u=n.nextSelector,c=n.pseudos;if(void 0!==u)throw new Error("matches can only process selectors that target a single element");if(!r)return!1;if(i&&i.toLowerCase()!==t.tag(r).toLowerCase())return!1;if(o&&o!==t.id(r))return!1;for(var l=t.className(r).split(" "),f=0;f=0))return!1;if(!b[2]&&x&&_!==x-1)return!1}}}return!0}}function et(t){if(!t.sel)return{tagName:"",id:"",className:""};var e=t.sel,r=e.indexOf("#"),n=e.indexOf(".",r),i=r>0?r:e.length,o=n>0?n:e.length;return{tagName:-1!==r||-1!==n?e.slice(0,Math.min(i,o)):e,id:i0?e.slice(o+1).replace(/\./g," "):void 0}}function rt(t){var e=et(t).className,r=void 0===e?"":e;if(!t.data)return r;var n=t.data,i=n.class,o=n.props;i&&(r+=" "+Object.keys(i).filter((function(t){return i[t]})).join(" "));return o&&o.className&&(r+=" "+o.className),r&&r.trim()}var nt=r(5),it={tag:function(t){return et(t).tagName},className:function(t){return rt(t)},id:function(t){return et(t).id||""},children:function(t){return t.children||[]},parent:function(t){return t.data[nt.a]||t},contents:function(t){return t.text||""},attr:function(t,e){if(t.data){var r=t.data,n=r.attrs,i=void 0===n?{}:n,o=r.props,s=void 0===o?{}:o,a=r.dataset,u=void 0===a?{}:a;if(i[e])return i[e];if(s[e])return s[e];if(0===e.indexOf("data-")&&u[e.slice(5)])return u[e.slice(5)]}}},ot=tt(it);var st,at;st=it,at=function(t,e){var r=e.data,n=ot.bind(null,t);if(r&&r.fn){var i=void 0;return!!n(i=Array.isArray(r.args)?r.fn.apply(null,r.args):r.args?r.fn.call(null,r.args):r.fn())&&i}return n(e)}||tt(st);var ut=function(){function t(t){this.rootElement=t}return t.prototype.call=function(t){if(11===this.rootElement.nodeType)return this.wrapDocFrag(null===t?[]:[t]);if(null===t)return this.wrap([]);var e=et(t),r=e.tagName,n=e.id,i=rt(t),o=((t.data||{}).props||{}).id,s=void 0===o?n:o;return"string"==typeof s&&s.toUpperCase()===this.rootElement.id.toUpperCase()&&r.toUpperCase()===this.rootElement.tagName.toUpperCase()&&i.toUpperCase()===this.rootElement.className.toUpperCase()?t:this.wrap([t])},t.prototype.wrapDocFrag=function(t){return Object(U.vnode)("",{isolate:[]},t,void 0,this.rootElement)},t.prototype.wrap=function(t){var e=this.rootElement,r=e.tagName,n=e.id,i=e.className,o=n?"#"+n:"",s=i?"."+i.split(" ").join("."):"",a=Object(p.h)(""+r.toLowerCase()+o+s,{},t);return a.data=a.data||{},a.data.isolate=a.data.isolate||[],a},t}(),ct=r(8),lt=r.n(ct),ft=r(9),ht=r.n(ft),pt=r(10),dt=r.n(pt),mt=r(11),yt=r.n(mt),vt=r(12),gt=r.n(vt),bt=[yt.a,lt.a,ht.a,dt.a,gt.a],_t=function(){function t(t){this.mapper=t,this.tree=[void 0,{}]}return t.prototype.set=function(t,e,r){for(var n=this.tree,i=void 0!==r?r:t.length,o=0;o=0;r--){var n=e[r],i=void 0!==n.data?n.data.isolation:void 0;void 0!==i&&t.removeElement(i),t.eventDelegator.removeElement(n.elm,i)}t.vnodesBeingRemoved=[]}}},t}(),wt=function(){function t(){this.arr=[],this.prios=[]}return t.prototype.add=function(t,e){for(var r=0;r=0&&"total"!==o[s].type);for(var a=Ot({},n,{scopeChecker:e,subject:t,bubbles:!!n.bubbles,useCapture:!!n.useCapture,passive:!!n.passive}),u=0;u=0;o--){if("total"===e[o].type){i=o+1;break}i=o}var s=this.virtualListeners.getDefault(e,(function(){return new Map}),i);return s.has(t)||s.set(t,new wt),s.get(t)},t.prototype.setupDOMListener=function(t,e){var r=this;if(this.origin){var n=m(this.origin,t,!1,!1,e).subscribe({next:function(n){return r.onEvent(t,n,e)},error:function(){},complete:function(){}});this.domListeners.set(t,{sub:n,passive:e})}else this.domListenersToAdd.set(t,e)},t.prototype.setupNonBubblingListener=function(t){t[0];var e=t[1],r=t[2],n=t[3];if(this.origin){var i=r.call();if(i.length){var o=this;i.forEach((function(t){var r,i=t.subs;if(!i||!i[e]){var s=m(t,e,!1,!1,n.passive).subscribe({next:function(t){return o.onEvent(e,t,!!n.passive,!1)},error:function(){},complete:function(){}});o.nonBubblingListeners.has(e)||o.nonBubblingListeners.set(e,new Map);var a=o.nonBubblingListeners.get(e);if(!a)return;a.set(t,{sub:s,destination:n}),t.subs=Ot({},i,((r={})[e]=s,r))}}))}}},t.prototype.resetEventListeners=function(){for(var t=this.domListeners.entries(),e=t.next();!e.done;){var r=e.value,n=r[0],i=r[1],o=i.sub,s=i.passive;o.unsubscribe(),this.setupDOMListener(n,s),e=t.next()}},t.prototype.putNonBubblingListener=function(t,e,r,n){var i=this.nonBubblingListeners.get(t);if(i){var o=i.get(e);o&&o.destination.passive===n&&o.destination.useCapture===r&&(this.virtualNonBubblingListener[0]=o.destination)}},t.prototype.onEvent=function(t,e,r,n){void 0===n&&(n=!0);var i=this.patchEvent(e),o=this.isolateModule.getRootElement(e.target);if(n){var s=this.isolateModule.getNamespace(e.target);if(!s)return;var a=this.getVirtualListeners(t,s);this.bubble(t,e.target,o,i,a,s,s.length-1,!0,r),this.bubble(t,e.target,o,i,a,s,s.length-1,!1,r)}else this.putNonBubblingListener(t,e.target,!0,r),this.doBubbleStep(t,e.target,o,i,this.virtualNonBubblingListener,!0,r),this.putNonBubblingListener(t,e.target,!1,r),this.doBubbleStep(t,e.target,o,i,this.virtualNonBubblingListener,!1,r),e.stopPropagation()},t.prototype.bubble=function(t,e,r,n,i,o,s,a,u){a||n.propagationHasBeenStopped||this.doBubbleStep(t,e,r,n,i,a,u);var c=r,l=s;if(e===r){if(!(s>=0&&"sibling"===o[s].type))return;c=this.isolateModule.getElement(o,s),l--}e.parentNode&&c&&this.bubble(t,e.parentNode,c,n,i,o,l,a,u),a&&!n.propagationHasBeenStopped&&this.doBubbleStep(t,e,r,n,i,a,u)},t.prototype.doBubbleStep=function(t,e,r,n,i,o,s){r&&(this.mutateEventCurrentTarget(n,e),i.forEach((function(t){if(t.passive===s&&t.useCapture===o){var i=_(t.scopeChecker.namespace);!n.propagationHasBeenStopped&&t.scopeChecker.isDirectlyInScope(e)&&(""!==i&&e.matches(i)||""===i&&e===r)&&(y(n,t.preventDefault),t.subject.shamefullySendNext(n))}})))},t.prototype.patchEvent=function(t){var e=t;e.propagationHasBeenStopped=!1;var r=e.stopPropagation;return e.stopPropagation=function(){r.call(this),this.propagationHasBeenStopped=!0},e},t.prototype.mutateEventCurrentTarget=function(t,e){try{Object.defineProperty(t,"currentTarget",{value:e,configurable:!0})}catch(t){console.log("please use event.ownerTarget")}t.ownerTarget=e},t}();function St(t){return i.a.merge(t,i.a.never())}function jt(t){return t.elm}function kt(t){(console.error||console.log)(t)}function At(t,e){void 0===e&&(e={}),b(t);var r=e.modules||bt;!function(t){if(!Array.isArray(t))throw new Error("Optional modules option must be an array for snabbdom modules")}(r);var n,o,s=new xt,a=R([s.createModule()].concat(r)),u=i.a.create({start:function(t){"loading"===document.readyState?document.addEventListener("readystatechange",(function(){var e=document.readyState;"interactive"!==e&&"complete"!==e||(t.next(null),t.complete())})):(t.next(null),t.complete())},stop:function(){}}),c=i.a.create({start:function(t){o=new MutationObserver((function(){return t.next(null)}))},stop:function(){o.disconnect()}});return function(r,l){void 0===l&&(l="DOM"),function(t){if(!t||"function"!=typeof t.addListener||"function"!=typeof t.fold)throw new Error("The DOM driver function expects as input a Stream of virtual DOM elements")}(r);var f=i.a.create(),h=u.map((function(){var e=function(t){var e="string"==typeof t?document.querySelector(t):t;if("string"==typeof t&&null===e)throw new Error("Cannot render into unknown element `"+t+"`");return e}(t)||document.body;return n=new ut(e),e})),p=r.remember();p.addListener({}),c.addListener({});var d=h.map((function(t){return i.a.merge(p.endWhen(f),f).map((function(t){return n.call(t)})).startWith((e=Object(G.toVNode)(t),e.data=e.data||{},e.data.isolate=[],e)).fold(a,Object(G.toVNode)(t)).drop(1).map(jt).startWith(t).map((function(t){return o.observe(t,{childList:!0,attributes:!0,characterData:!0,subtree:!0,attributeOldValue:!0,characterDataOldValue:!0}),t})).compose(St);var e})).flatten(),m=F()(u,c).endWhen(f).compose(z()(d)).map((function(t){return t[1]})).remember();m.addListener({error:e.reportSnabbdomError||kt});var y=new Et(m,s);return new S(m,f,[],s,y,l)}}!function(){function t(t){this._mockConfig=t,t.elements?this._elements=t.elements:this._elements=Object(d.adapt)(i.a.empty())}t.prototype.elements=function(){var t=this._elements;return t._isCycleSource="MockedDOM",t},t.prototype.element=function(){var t=this.elements().filter((function(t){return t.length>0})).map((function(t){return t[0]})).remember(),e=Object(d.adapt)(t);return e._isCycleSource="MockedDOM",e},t.prototype.events=function(t,e,r){var n=this._mockConfig[t],o=Object(d.adapt)(n||i.a.empty());return o._isCycleSource="MockedDOM",o},t.prototype.select=function(e){return new t(this._mockConfig[e]||{})},t.prototype.isolateSource=function(t,e){return t.select(".___"+e)},t.prototype.isolateSink=function(t,e){return Object(d.adapt)(i.a.fromObservable(t).map((function(t){return t.sel&&-1!==t.sel.indexOf("___"+e)||(t.sel+=".___"+e),t})))}}();function Tt(t){return function(t){return"string"==typeof t&&t.length>0}(t)&&("."===t[0]||"#"===t[0])}function Ct(t){return function(e,r,n){var i=void 0!==e,o=void 0!==r,s=void 0!==n;return Tt(e)?o&&s?Object(p.h)(t+e,r,n):o?Object(p.h)(t+e,r):Object(p.h)(t+e,{}):s?Object(p.h)(t+e,r,n):o?Object(p.h)(t,e,r):i?Object(p.h)(t,e):Object(p.h)(t,{})}}var Dt=["a","altGlyph","altGlyphDef","altGlyphItem","animate","animateColor","animateMotion","animateTransform","circle","clipPath","colorProfile","cursor","defs","desc","ellipse","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotlight","feTile","feTurbulence","filter","font","fontFace","fontFaceFormat","fontFaceName","fontFaceSrc","fontFaceUri","foreignObject","g","glyph","glyphRef","hkern","image","line","linearGradient","marker","mask","metadata","missingGlyph","mpath","path","pattern","polygon","polyline","radialGradient","rect","script","set","stop","style","switch","symbol","text","textPath","title","tref","tspan","use","view","vkern"],Pt=Ct("svg");Dt.forEach((function(t){Pt[t]=Ct(t)}));var Nt=["a","abbr","address","area","article","aside","audio","b","base","bdi","bdo","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","dd","del","details","dfn","dir","div","dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","kbd","keygen","label","legend","li","link","main","map","mark","menu","meta","nav","noscript","object","ol","optgroup","option","p","param","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","title","tr","u","ul","video"],It={SVG_TAG_NAMES:Dt,TAG_NAMES:Nt,svg:Pt,isSelector:Tt,createTagFunction:Ct};Nt.forEach((function(t){It[t]=Ct(t)}));var Lt=It,Bt=(Lt.svg,Lt.a,Lt.abbr,Lt.address,Lt.area,Lt.article,Lt.aside,Lt.audio,Lt.b,Lt.base,Lt.bdi,Lt.bdo,Lt.blockquote,Lt.body,Lt.br,Lt.button,Lt.canvas),Rt=(Lt.caption,Lt.cite,Lt.code,Lt.col,Lt.colgroup,Lt.dd,Lt.del,Lt.dfn,Lt.dir,Lt.div),$t=(Lt.dl,Lt.dt,Lt.em,Lt.embed,Lt.fieldset,Lt.figcaption,Lt.figure,Lt.footer,Lt.form,Lt.h1,Lt.h2,Lt.h3,Lt.h4,Lt.h5,Lt.h6,Lt.head,Lt.header,Lt.hgroup,Lt.hr,Lt.html,Lt.i,Lt.iframe,Lt.img,Lt.input),Ft=(Lt.ins,Lt.kbd,Lt.keygen,Lt.label,Lt.legend,Lt.li,Lt.link,Lt.main,Lt.map,Lt.mark,Lt.menu,Lt.meta,Lt.nav,Lt.noscript,Lt.object,Lt.ol,Lt.optgroup,Lt.option),Wt=(Lt.p,Lt.param,Lt.pre,Lt.progress,Lt.q,Lt.rp,Lt.rt,Lt.ruby,Lt.s,Lt.samp,Lt.script,Lt.section,Lt.select),zt=(Lt.small,Lt.source,Lt.span),Gt=(Lt.strong,Lt.style,Lt.sub,Lt.sup,Lt.table,Lt.tbody,Lt.td,Lt.textarea,Lt.tfoot,Lt.th,Lt.thead,Lt.title,Lt.tr,Lt.u,Lt.ul,Lt.video);const Ut={main:{position:"absolute",left:0,top:0,width:"50px",height:"50px",background:"#000",zIndex:9999,color:"white",fontSize:"20px",fontWeight:700,padding:"6px",borderRadius:"0 0 50px 0",boxSizing:"border-box",cursor:"pointer",transition:"all 100ms ease-in",padding:"0 0 0 0"},active:{position:"absolute",left:0,top:0,width:"50px",height:"50px",background:"#000",zIndex:9999,color:"white",fontSize:"20px",fontWeight:700,padding:"6px",borderRadius:"0 0 50px 0",boxSizing:"border-box",cursor:"pointer",transition:"all 100ms ease-in",padding:"6px 0 0 0"}},qt={position:"absolute",top:0,left:0,zIndex:100},Vt={main:{position:"absolute",top:0,left:"-110vw",transition:"all 500ms ease-in-out",zIndex:999},active:{position:"absolute",top:0,left:0,transition:"all 500ms ease-in-out",zIndex:999}},Ht={position:"relative",float:"left",clear:"both",borderRadius:"0 0 0.5em 0",borderBottom:"4px solid #FFF"},Xt={position:"absolute",top:0,left:0,borderRadius:"0 0 0.5em 0"},Kt={position:"relative",float:"left",clear:"both",margin:"1em 0 1em 0em",padding:"1em 1.5em 1em 1.5em",background:"black",borderRadius:"0 0.5em 0.5em 0",borderBottom:"4px solid #FFF",width:"23em"},Zt={position:"relative",float:"left",clear:"both",margin:"0.25em 0 0.25em 0",width:"100%"},Yt={position:"relative",float:"left",fontWeight:600,color:"white"},Jt={position:"relative",float:"right"};function Qt(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(t)))return;var r=[],n=!0,i=!1,o=void 0;try{for(var s,a=t[Symbol.iterator]();!(n=(s=a.next()).done)&&(r.push(s.value),!e||r.length!==e);n=!0);}catch(t){i=!0,o=t}finally{try{n||null==a.return||a.return()}finally{if(i)throw o}}return r}(t,e)||function(t,e){if(!t)return;if("string"==typeof t)return te(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);"Object"===r&&t.constructor&&(r=t.constructor.name);if("Map"===r||"Set"===r)return Array.from(t);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return te(t,e)}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function te(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);rt.camera_params.rearCamera).take(2),n=i.a.combine(e.videoSize$,e.camID$,r).map(t=>i.a.fromPromise(function(t){let e=Qt(t,3),r=e[0],n=e[1],i=e[2];const o=ee[r];return re&&re.getTracks().forEach(t=>{t.stop()}),void 0===navigator.mediaDevices.getUserMedia&&(navigator.mediaDevices.getUserMedia=function(t){var e=navigator.webkitGetUserMedia||navigator.mozGetUserMedia;return e?new Promise((function(r,n){e.call(navigator,t,r,n)})):Promise.reject(new Error("getUserMedia is not implemented in this browser"))}),navigator.mediaDevices.getUserMedia({video:{width:o.width,height:o.height,deviceId:0===n||i?{}:{exact:n},facingMode:i?{exact:"environment"}:{}}})}(t))).flatten(),o=i.a.of(Gt("#beholder-video",{attrs:{autoplay:!0},style:{display:"none"}}));return i.a.combine(t.DOM.select("#beholder-video").element(),n,e.torch$).subscribe({next:t=>{let e=Qt(t,3),r=e[0],n=e[1],i=e[2];"srcObject"in r?r.srcObject=n:r.src=window.URL.createObjectURL(n),re=n;const o=n.getVideoTracks()[0];new ImageCapture(o).getPhotoCapabilities().then(()=>{void 0!==o.getCapabilities().torch&&o.applyConstraints({advanced:[{torch:i}]})})},error:t=>console.log(t)}),{vdom$:o}},ie=ie||{};ie.Image=function(t,e,r){this.width=t||0,this.height=e||0,this.data=r||[]},ie.grayscale=function(t,e){for(var r=t.data,n=e.data,i=r.length,o=0,s=0;oh&&(h=r,a=n)}return a},ie.stackBoxBlurMult=[1,171,205,293,57,373,79,137,241,27,391,357,41,19,283,265],ie.stackBoxBlurShift=[0,9,10,11,9,12,10,11,12,9,13,13,10,9,13,13],ie.BlurStack=function(){this.color=0,this.next=null},ie.stackBoxBlur=function(t,e,r){var n,i,o,s,a,u,c,l,f,h,p=t.data,d=e.data,m=t.height,y=t.width,v=m-1,g=y-1,b=r+r+1,_=r+1,x=ie.stackBoxBlurMult[r],w=ie.stackBoxBlurShift[r];for(n=i=new ie.BlurStack,h=1;h>>w,c=u+((c=l+_)>>w,c=l+((c=f+_)=1&&0===r[i+1]&&(u=!0),(a||u)&&(++s,p.push(ie.borderFollowing(r,i,s,{x:l,y:c},u,n))));return p},ie.borderFollowing=function(t,e,r,n,i,o){var s,a,u,c,l,f=[];f.hole=i,c=l=i?0:4;do{if(0!==t[s=e+o[c=c-1&7]])break}while(c!==l);if(c===l)t[e]=-r,f.push({x:n.x,y:n.y});else for(a=e,4^c;;){l=c;do{u=a+o[++c]}while(0===t[u]);if((c&=7)-1>>>0>>0?t[a]=-r:1===t[a]&&(t[a]=r),f.push({x:n.x,y:n.y}),c,n.x+=ie.neighborhood[c][0],n.y+=ie.neighborhood[c][1],u===e&&a===s)break;a=u,c=c+4&7}return f},ie.neighborhood=[[1,0],[1,-1],[0,-1],[-1,-1],[-1,0],[-1,1],[0,1],[1,1]],ie.neighborhoodDeltas=function(t){for(var e=[],r=ie.neighborhood.length,n=0;ns&&(s=o,d.start_index=f);for(s<=e?m.push({x:n.x,y:n.y}):(p.start_index=h,p.end_index=d.start_index+=p.start_index,d.start_index-=d.start_index>=v?v:0,d.end_index=p.start_index,d.end_indexs&&(s=o,d.start_index=l);a=s*s<=e*(u*u+c*c)}a?m.push({x:n.x,y:n.y}):(d.end_index=p.end_index,p.end_index=d.start_index,y.push({start_index:d.start_index,end_index:d.end_index}),y.push({start_index:p.start_index,end_index:p.end_index}))}return m},ie.warp=function(t,e,r,n){var i,o,s,a,u,c,l,f,h,p,d,m,y,v,g,b,_,x,w,O,M,E,S=t.data,j=e.data,k=t.width,A=t.height,T=0;for(y=(m=ie.getPerspectiveTransform(r,n-1))[8],v=m[2],g=m[5],M=0;M>>0)===k-1?i:i+1,a=1-(s=w-i),l=1-(c=(O=(x+=m[3])/b)-(u=O>>>0)),f=h=u*k,p=d=(u===A-1?u:u+1)*k,j[T++]=l*(a*S[f+i]+s*S[h+o])+c*(a*S[p+i]+s*S[d+o])&255;return e.width=n,e.height=n,e},ie.getPerspectiveTransform=function(t,e){var r=ie.square2quad(t);return r[0]/=e,r[1]/=e,r[3]/=e,r[4]/=e,r[6]/=e,r[7]/=e,r},ie.square2quad=function(t){var e,r,n,i,o,s,a,u=[];return e=t[0].x-t[1].x+t[2].x-t[3].x,r=t[0].y-t[1].y+t[2].y-t[3].y,0===e&&0===r?(u[0]=t[1].x-t[0].x,u[1]=t[2].x-t[1].x,u[2]=t[0].x,u[3]=t[1].y-t[0].y,u[4]=t[2].y-t[1].y,u[5]=t[0].y,u[6]=0,u[7]=0,u[8]=1):(n=t[1].x-t[2].x,i=t[3].x-t[2].x,o=t[1].y-t[2].y,a=n*(s=t[3].y-t[2].y)-i*o,u[6]=(e*s-i*r)/a,u[7]=(n*r-e*o)/a,u[8]=1,u[0]=t[1].x-t[0].x+u[6]*t[1].x,u[1]=t[3].x-t[0].x+u[7]*t[3].x,u[2]=t[0].x,u[3]=t[1].y-t[0].y+u[6]*t[1].y,u[4]=t[3].y-t[0].y+u[7]*t[3].y,u[5]=t[0].y),u},ie.isContourConvex=function(t){var e,r,n,i,o,s,a,u,c=0,l=!0,f=t.length,h=0,p=0;for(r=t[f-1],o=(e=t[0]).x-r.x,s=e.y-r.y;h(n=a*s)?1:i{le=ae(ae({},le),t)},ce.Marker=(t,e)=>{const r=(e[0].y-e[2].y)/(e[0].x-e[2].x),n=(e[1].y-e[3].y)/(e[1].x-e[3].x),i={x:0,y:0};return i.x=function(t,e,r,n){return(r.y-n.y+e*n.x-t*r.x)/(e-t)}(r,n,e[0],e[1]),i.y=function(t,e,r){return e*(r-t.x)+t.y}(e[0],r,i.x),{id:t,corners:e,center:i}},ce.Detector=function(){this.grey=new oe.Image,this.thres=new oe.Image,this.homography=new oe.Image,this.binary=[],this.contours=[],this.polys=[],this.candidates=[]},ce.Detector.prototype.detect=function(t,e){return oe.grayscale(t,this.grey),oe.adaptiveThreshold(this.grey,this.thres,2,7),this.contours=oe.findContours(this.thres,this.binary),this.candidates=this.findCandidates(this.contours,t.width*e.minMarkerPerimeter,t.width*e.maxMarkerPerimeter,.05,10),this.candidates=this.clockwiseCorners(this.candidates),this.candidates=this.notTooNear(this.candidates,e.minMarkerDistance),this.findMarkers(this.grey,this.candidates,e.sizeAfterPerspectiveRemoval)},ce.Detector.prototype.findCandidates=function(t,e,r,n,i){var o,s,a,u=[],c=t.length;for(this.polys=[],a=0;a=e&&o.length<=r&&(s=oe.approxPolyDP(o,o.length*n),this.polys.push(s),4===s.length&&oe.isContourConvex(s)&&oe.minEdgeLength(s)>=i&&u.push(s));return u},ce.Detector.prototype.clockwiseCorners=function(t){var e,r,n,i,o,s=t.length;for(o=0;o>>0,u=a*a>>1,c=[],l=[],f=[];for(o=0;o<7;++o)for(i=0===o||6===o?1:6,s=0;s<7;s+=i)if(r={x:s*a,y:o*a,width:a,height:a},oe.countNonZero(t,r)>u)return null;for(o=0;o<5;++o)for(c[o]=[],s=0;s<5;++s)r={x:(s+1)*a,y:(o+1)*a,width:a,height:a},c[o][s]=oe.countNonZero(t,r)>u?1:0;for(l[0]=c,f[0]=this.hammingDistance(l[0]),n={first:f[0],second:0},o=1;o<4;++o)l[o]=this.rotate(l[o-1]),f[o]=this.hammingDistance(l[o]),f[o]t.length)&&(e=t.length);for(var r=0,n=new Array(e);rt.getContext("2d")),n=t.DOM.select("#beholder-video").element(),o=t.update,s=new fe.Detector;return t.config.subscribe({next:t=>{ve=me(me({},ve),t.detection_params)}}),{marker$:i.a.combine(e,r,o,n).compose(z()(t.config)).map(t=>{let e=he(t,2),r=e[0],n=e[1].feed_params;const i=he(r,4),o=i[0],a=i[1],u=i[2],c=i[3];if(c.readyState===c.HAVE_ENOUGH_DATA){a.filter=`contrast(${(100+Math.floor(n.contrast))/100})\n brightness(${(100+Math.floor(n.brightness))/100})\n grayscale(${Math.floor(n.grayscale)/100})`,n.flip?(a.save(),a.translate(o.width,0),a.scale(-1,1),a.drawImage(c,0,0,o.width,o.height),a.restore()):a.drawImage(c,0,0,o.width,o.height);let t=a.getImageData(0,0,o.width,o.height);return[s.detect(t,ve),u]}return[[],u]})}};class be{static sub(t,e){return new be(e.x-t.x,e.y-t.y)}static fromAngle(t){return new be(Math.cos(t),Math.sin(t))}static add(t,e){return new be(e.x+t.x,e.y+t.y)}static addScalar(t,e,r){const n=t.x+e.x*r,i=t.y+e.y*r;return new be(n,i)}static mag(t){return Math.sqrt(t.x*t.x+t.y*t.y)}static angleBetween(t,e){return Math.atan2(t.x*e.y-t.y*e.x,t.x*e.x+t.y*e.y)}static rotate(t,e){const r=t.x*Math.cos(e)-t.y*Math.sin(e),n=t.x*Math.sin(e)+t.y*Math.cos(e);return new be(r,n)}static scale(t,e){return new be(t.x*e,t.y*e)}static dist(t,e){return new be(e.x-t.x,e.y-t.y).mag()}static dist2(t,e){return new be(e.x-t.x,e.y-t.y).mag2()}static normalize(t){const e=t.mag();return new be(t.x/e,t.y/e)}static copy(t){return new be(t.x,t.y)}constructor(t,e){this.x=t,this.y=e}clone(){return new be(this.x,this.y)}copy(t){return this.x=t.x,this.y=t.y,this}add(t){return this.x+=t.x,this.y+=t.y,this}addScalar(t,e){return this.x+=t.x*e,this.y+=t.y*e,this}set(t,e){return this.x=t,this.y=e,this}sub(t){return this.x-=t.x,this.y-=t.y,this}scale(t){return this.x*=t,this.y*=t,this}mag(){return Math.sqrt(this.x*this.x+this.y*this.y)}mag2(){return this.x*this.x+this.y*this.y}dist(t){return be.sub(this,t).mag()}dist2(t){return be.sub(this,t).mag2()}normalize(){const t=this.mag();return this.x/=t,this.y/=t,this}getAngle(){return Math.atan2(this.y,this.x)}rotate(t){const e=this.x*Math.cos(t)-this.y*Math.sin(t),r=this.x*Math.sin(t)+this.y*Math.cos(t);return this.x=e,this.y=r,this}dot(t){return this.x*t.x+this.y*t.y}}var _e=class{constructor(t){this.timeout=50,this.timestamp=this.timeout,this.present=!1,this.center={x:0,y:0},this.corners=[],this.rotation=0,this.scale=29/640,this.enable3D=!1,this.avgPerim=0,this.id=t}setScale(t,e){this.scale=t/e}update(t){this.timestamp=0,this.present=!0,this.center=t.center,this.corners=t.corners.map(t=>t),this.rotation=be.angleBetween(be.sub(this.corners[0],this.corners[1]),new be(1,0));const e=this.corners.map((t,e,r)=>{const n=t.x-r[(e+1)%r.length].x,i=t.y-r[(e+1)%r.length].y;return Math.sqrt(n*n+i*i)});this.avgPerim=(e[0]+e[1]+e[2]+e[3])/4,this.enable3D&&(this.center.z=this.avgPerim/this.scale)}updatePresence(t){this.timestamp+=t,this.present=!(this.timestamp>=this.timeout)}},xe=r(1);var we=class{constructor(t,e){this.markerA=t,this.markerB=e}get isPresent(){return this.markerA.present&&this.markerB.present}get angleBetween(){return this.markerA.rotation-this.markerB.rotation}get distance(){return be.sub(this.markerA.center,this.markerB.center).mag()}getRelativePosition(t){if(this.isPresent()){const e=[{x:-t/2,y:-t/2},{x:t/2,y:-t/2},{x:t/2,y:t/2},{x:-t/2,y:t/2}],r=function(t,e,r,n,i,o,s,a){const u=xe.matrix([[i.x,i.y,1,0,0,0,-t.x*i.x,-t.x*i.y],[0,0,0,i.x,i.y,1,-t.y*i.x,-t.y*i.y],[o.x,o.y,1,0,0,0,-e.x*o.x,-e.x*o.y],[0,0,0,o.x,o.y,1,-e.y*o.x,-e.y*o.y],[s.x,s.y,1,0,0,0,-r.x*s.x,-r.x*s.y],[0,0,0,s.x,s.y,1,-r.y*s.x,-r.y*s.y],[a.x,a.y,1,0,0,0,-n.x*a.x,-n.x*a.y],[0,0,0,a.x,a.y,1,-n.y*a.x,-n.y*a.y]]),c=xe.matrix([[t.x],[t.y],[e.x],[e.y],[r.x],[r.y],[n.x],[n.y]]),l=xe.lusolve(u,c);return xe.matrix([[xe.subset(l,xe.index(0,0)),xe.subset(l,xe.index(1,0)),xe.subset(l,xe.index(2,0))],[xe.subset(l,xe.index(3,0)),xe.subset(l,xe.index(4,0)),xe.subset(l,xe.index(5,0))],[xe.subset(l,xe.index(6,0)),xe.subset(l,xe.index(7,0)),1]])}(this.markerA.corners[0],this.markerA.corners[1],this.markerA.corners[2],this.markerA.corners[3],e[0],e[1],e[2],e[3]),n=xe.inv(r),i=t=>function(t,e){const r=xe.matrix([[e.x],[e.y],[1]]),n=xe.multiply(t,r);return{x:xe.subset(n,xe.index(0,0))/xe.subset(n,xe.index(2,0)),y:xe.subset(n,xe.index(1,0))/xe.subset(n,xe.index(2,0))}}(n,t),o=i(this.markerA.center),s=i(this.markerB.center),a=i(this.markerA.corners[0]),u=i(this.markerA.corners[1]),c=i(this.markerB.corners[0]),l=i(this.markerB.corners[1]),f=be.sub(o,s),h=be.sub(a,u);return{distance:be.mag(f),heading:be.angleBetween(h,f),rotation:be.angleBetween(h,be.sub(c,l))}}return{distance:void 0,heading:void 0,rotation:void 0}}};function Oe(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(t)))return;var r=[],n=!0,i=!1,o=void 0;try{for(var s,a=t[Symbol.iterator]();!(n=(s=a.next()).done)&&(r.push(s.value),!e||r.length!==e);n=!0);}catch(t){i=!0,o=t}finally{try{n||null==a.return||a.return()}finally{if(i)throw o}}return r}(t,e)||function(t,e){if(!t)return;if("string"==typeof t)return Me(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);"Object"===r&&t.constructor&&(r=t.constructor.name);if("Map"===r||"Set"===r)return Array.from(t);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return Me(t,e)}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function Me(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);r[t.target.id,t.target.classList.contains("isCheck")?t.target.checked:t.target.value]),r=i.a.fromPromise(navigator.mediaDevices.enumerateDevices()).map(t=>t.filter(t=>"videoinput"===t.kind)),n=i.a.combine(r,t.config).map(t=>{let e=Oe(t,2),r=e[0],n=e[1];return Rt("#parameters-menu",{style:Kt},[Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Camera Feed ID"),Wt("#CAMERA_INDEX.param-input",{style:Jt},r.map(Ee))]),Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Video Size"),Wt("#camera_params-videoSize.param-input",{style:Jt},[Ft({attrs:{value:0,label:"320 x 240",selected:0===n.camera_params.videoSize}}),Ft({attrs:{value:1,label:"640 x 480",selected:1===n.camera_params.videoSize}}),Ft({attrs:{value:2,label:"1280 x 720",selected:2===n.camera_params.videoSize}}),Ft({attrs:{value:3,label:"1920 x 1080",selected:3===n.camera_params.videoSize}})])]),Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Min Marker Distance"),$t("#detection_params-minMarkerDistance.param-input",{style:Jt,attrs:{type:"number",name:"MIN_MARKER_DISTANCE",min:1,max:50,value:n.detection_params.minMarkerDistance,step:1}})]),Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Min Marker Perimeter"),$t("#detection_params-minMarkerPerimeter.param-input",{style:Jt,attrs:{type:"number",name:"MIN_MARKER_PERIMETER",min:.01,max:.99,value:n.detection_params.minMarkerPerimeter,step:.01}})]),Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Max Marker Perimeter"),$t("#detection_params-maxMarkerPerimeter.param-input",{style:Jt,attrs:{type:"number",name:"MAX_MARKER_PERIMETER",min:.01,max:.99,value:n.detection_params.maxMarkerPerimeter,step:.01}})]),Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Size After Perspective Removal"),$t("#detection_params-sizeAfterPerspectiveRemoval.param-input",{style:Jt,attrs:{type:"number",name:"SIZE_AFTER_PERSPECTIVE_REMOVAL",min:1,max:200,value:n.detection_params.sizeAfterPerspectiveRemoval,step:1}})]),Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Contrast"),$t("#feed_params-contrast.param-input",{style:Jt,attrs:{type:"number",name:"IMAGE_CONTRAST",min:-100,max:100,value:n.feed_params.contrast,step:1}})]),Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Brightness"),$t("#feed_params-brightness.param-input",{style:Jt,attrs:{type:"number",name:"IMAGE_BRIGHTNESS",min:-100,max:100,value:n.feed_params.brightness,step:1}})]),Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Grayscale"),$t("#feed_params-grayscale.param-input",{style:Jt,attrs:{type:"number",name:"IMAGE_GRAYSCALE",min:0,max:100,value:n.feed_params.grayscale,step:1}})]),Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Flip Camera"),$t("#feed_params-flip.param-input.isCheck",{style:Jt,attrs:{type:"checkbox",name:"IMAGE_FLIP",checked:n.feed_params.flip}})]),Rt(".parameter-item",{style:Zt},[zt({style:Yt},"Torch Active"),$t("#camera_params-torch.param-input.isCheck",{style:Jt,attrs:{type:"checkbox",name:"TORCH",checked:n.camera_params.torch}})])])}),o=e.filter(t=>"CAMERA_INDEX"===t[0]).map(t=>t[1]).startWith(0);return{vdom$:n,configUpdate$:e.filter(t=>"CAMERA_INDEX"!==t[0]).map(t=>{let e=Oe(t,2),r=e[0],n=e[1];return t=>{const e=Oe(r.split("-"),2),i=e[0],o=e[1];return t[i][o]="boolean"!=typeof n?parseFloat(n):n,t}}),camID$:o}};function je(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function ke(t){for(var e=1;et.length)&&(e=t.length);for(var r=0,n=new Array(e);r{let e=Te(t,3),r=e[0],n=e[1],i=e[2];const o=Te(r,2),s=o[0],a=o[1];i.clearRect(0,0,n.width,n.height),s.forEach(t=>{const e=Pe.find(e=>e.id===t.id);if(void 0===e)return;e.update(t);const r=t.center,n=t.corners,o=e.rotation;i.strokeStyle="#FF00AA",i.beginPath(),n.forEach((t,e)=>{i.moveTo(t.x,t.y);let r=n[(e+1)%n.length];i.lineTo(r.x,r.y)}),i.stroke(),i.closePath(),i.strokeStyle="blue",i.strokeRect(n[0].x-2,n[0].y-2,4,4),i.strokeStyle="#FF00AA",i.strokeRect(r.x-1,r.y-1,2,2),i.font="12px monospace",i.textAlign="center",i.fillStyle="#FF55AA",i.fillText("ID="+e.id,r.x,r.y-7),i.fillText(o.toFixed(2),r.x,r.y+15)}),Pe.forEach(t=>t.updatePresence(a))};function Ie(t){const e=i.a.merge(t.DOM.select("#toggle-screen").events("mouseover").mapTo(!0),t.DOM.select("#toggle-screen").events("mouseout").mapTo(!1)).startWith(!1),r=t.DOM.select("#toggle-screen").events("click").fold(t=>!t,!0),n=Se(t),o=n.camID$,a=t.config.map(t=>t.camera_params.videoSize).startWith(-1).compose(s.a).filter(t=>{let e=Te(t,2);return e[0]!==e[1]}).map(t=>{let e=Te(t,2);e[0];return e[1]}).startWith(0),u=t.config.map(t=>t.camera_params.torch).startWith(!1).compose(s.a).filter(t=>{let e=Te(t,2);return e[0]!==e[1]}).map(t=>{let e=Te(t,2);e[0];return e[1]}).startWith(!1),c=a.map(t=>{const e=De[t];return Rt([Bt("#detection-canvas",{style:Ht,attrs:ke({},e)}),Bt("#detection-canvas-overlay",{style:Xt,attrs:ke({},e)})])}),l=ne(t,{videoSize$:a,camID$:o,torch$:u}),f=ge(t),h=i.a.combine(e,r),p=i.a.combine(c,n.vdom$,l.vdom$),d=i.a.combine(h,p).map(t=>{let e=Te(t,2),r=Te(e[0],2),n=(r[0],r[1]),i=e[1];return Rt("#beholder-overlay",{style:qt},[Rt("#toggle-screen",{style:Ut.main},"⥂"),Rt("#detection-panel",{style:n?Vt.main:Vt.active},i)])}),m=t.DOM.select("#detection-canvas-overlay").element(),y=m.map(t=>t.getContext("2d"));i.a.combine(f.marker$,m,y).subscribe({next:Ne});return{DOM:d,config:n.configUpdate$}}let Le,Be=Date.now();const Re=()=>i.a.create({start:t=>{Le=()=>{const e=Date.now(),r=e-Be;Be=e,t.next(r)}},stop:()=>{}}),$e=()=>{Le()};const Fe={camera_params:{videoSize:1,torch:!1},detection_params:{minMarkerDistance:10,minMarkerPerimeter:.02,maxMarkerPerimeter:.8,sizeAfterPerspectiveRemoval:49},feed_params:{contrast:0,brightness:0,grayscale:0,flip:!1}},We=(t,e,r)=>{if(r)r.length<=0&&console.warn("BEHOLDER WARNING: your provided list of markers is empty, no markers will be tracked"),r.forEach(t=>Pe.push(new _e(t)));else for(let t=0;t<100;t++)Pe.push(new _e(t));let n=Fe;e&&(e.camera_params&&(n.camera_params=ke(ke({},n.camera_params),e.camera_params)),e.detection_params&&(n.detection_params=ke(ke({},n.detection_params),e.detection_params)),e.feed_params&&(n.feed_params=ke(ke({},n.feed_params),e.feed_params)));var o;h(Ie,{DOM:At(t),update:Re,config:(o=n,t=>i.a.merge(i.a.of(t=>t),t).fold((t,e)=>e(t),o))})},ze=()=>Pe,Ge=t=>Pe.find(e=>e.id===t),Ue=(t,e)=>{new we(Pe[t],Pe[e])};e.default={init:We,update:$e,getMarker:Ge,getMarkerPair:Ue,getAllMarkers:ze}}]); \ No newline at end of file ```
zepumph commented 3 years ago

I have been able to commit for this issue, over in https://github.com/phetsims/tangible/issues/4.

Master now supports marker input.

To use:

  1. you need to use the "Original" AruCo markers that can be found at https://chev.me/arucogen/
  2. See https://github.com/phetsims/ratio-and-proportion/blob/5e6aa461687b711d0b6673ff2833b325ef80b69f/js/common/view/RAPMarkerInput.js#L18-L20 for the markers that control input. I was taping the "BASE" marker to my shirt, and then moving the left/right ones with my hands while looking at the webcam. That will probably work to start prototyping.
  3. This feature is hidden behind the ?tangible query parameter.
  4. Marker input is REALLY delicate. Peter was telling me that this is because the blurring is in the webcam quality, and so by the time the image gets to the browser, the detection software is looking at a blurry image. With that in mind, first please move quite slowly until the the hands react to your movement.

You can use this version to test: https://phet-dev.colorado.edu/html/ratio-and-proportion/1.1.0-dev.4/phet/ratio-and-proportion_en_phet.html?tangible

Good luck!

brettfiedler commented 3 years ago

Okay, magically started working (edit: magic = effort by MK). Did manage to get detection and movement in 1.1.0dev.5 (!), did not try going back to dev.4 though. It definitely loses the marker quite frequently when in motion and messing with configs did not seem to help it visually on the visual webcam demo.

zepumph commented 3 years ago

To catch up the thread, @BLFiedler and I both found this morning that marker input wasn't working on 1.1.0-dev.4, I created https://github.com/project-beholder/beholder-detection/issues/4 as a best guess of what the problem is. I'm glad it is working for you though!

zepumph commented 3 years ago

@BLFiedler, here is an updated version that should track much better than it did before.

Still to do for this issue:

It is highly recommended to use https://bayes.colorado.edu/dev/phettest/tangible/demo.html to calibrate your system, since it has a video for you to use. Perhaps we will give this out to participants also if we can't embed the video into the sim in time.

Take a look on master or with something like

https://phet-dev.colorado.edu/html/ratio-and-proportion/1.1.0-dev.8/phet/ratio-and-proportion_all_phet.html?tangible&screens=1&heightInPixels=600

brettfiedler commented 3 years ago

Quick answers:

zepumph commented 3 years ago

Primary though, given our desire for some data, it might be good and good enough to consider how to get the data export for RaP for things like hand position, toggle states, success states, sound states, etc through phet-io so we can have folks play around on say a tablet and still get that data out.

From design meeting today, @BLFiedler will take a look at the actual items that he wants in the data stream.

To test master with tangible: https://bayes.colorado.edu/dev/phettest/ratio-and-proportion/ratio-and-proportion_en.html?brand=phet&tangible

Highly recommended to have the demo open in a different tab so you can check to see if the markers are working https://bayes.colorado.edu/dev/phettest/tangible/demo.html

And to test master of the PhET-iO data stream:

https://bayes.colorado.edu/dev/phettest/ratio-and-proportion/ratio-and-proportion_en.html?brand=phet-io&phetioStandalone&ea&phetioConsoleLog=colorized

Current marker numbers:

https://github.com/phetsims/ratio-and-proportion/blob/ff007072d91a83ece8ce3e6c320192e787e732d6/js/common/view/RAPMarkerInput.js#L18-L20

brettfiedler commented 3 years ago

Alright - these are probably not exact, and I think in most cases we care about the "newValue" for each, but I think we'd want an output of the following:

If possible, it might also be nice to have a reference for the thresholds as well, so we can recreate what sounds are being played when? Might be fine just to have the values, but may be important to log dyanmically when they change? Not sure what value that would be.

brettfiedler commented 3 years ago

Nope, forgot to assign. Sorry! @ you @zepumph

zepumph commented 3 years ago

Version needed by next Friday, so I should get a working, recording version by this Friday.

zepumph commented 3 years ago

If possible, it might also be nice to have a reference for the thresholds as well, so we can recreate what sounds are being played when?

Would there be any values that aren't included in this set of constants?

https://github.com/phetsims/ratio-and-proportion/blob/5a4faa76cc16406a312ba77429dd3f8a764d4b18/js/common/RAPConstants.js#L18-L38

zepumph commented 3 years ago

So long as those values are all we need, here is how you access everything you asked for.

In general, any phetioID below that is on the discoverScreen is also on the createScreen. Unless otherwise stated, the event will have a key phetioID which will match the provided phetioID when a change occurs.

We can also capture a complete phetioState every so often (for other data studies we often do every second or so. This way we have a snapshot of all these values every second in addition. Would that be helpful?

You can look at the current values and customize the above phetioIDs by playing with studio here: https://phet-dev.colorado.edu/html/ratio-and-proportion/1.1.0-dev.11/phet-io/wrappers/studio/

Then from there I can create a link to record with. I am presuming that we can use phet-dev to do the study.

zepumph commented 3 years ago

https://phet-io.colorado.edu/devguide/ can be a good guide for general PhET-iO understanding.

brettfiedler commented 3 years ago

?phetioConsoleLog=json

zepumph commented 3 years ago

Add instrumentation for movingInDirectionProperty and inProportionProperty

zepumph commented 3 years ago

I created a parsing engine to give @BLFiedler a table-like format to interpret. I started with a test:

I started with a small data log in which I manipulate every control in the sim

ration-and-proportion-test.json.log

Then I wrote a Node script to turn this into a format that the R package jsonlite can stream in (just remove the .txt:

parsePhetio.mjs.txt

Then I wrote a small R script to make it into a csv:


library(jsonlite)
myData = read.delim2("test.json", header = FALSE)

# Trivial example
mydata <- stream_in("test.json")

print(mydata)

test1 <- stream_in(file("parsedJSONOutput.json"))
print(test1)

write.csv(test1, "data.csv", row.names=FALSE)

It created a csv like this:

data.csv

@BLFiedler, I hope that this helps get you started.

brettfiedler commented 3 years ago

Great! First glance, it looks good to me. I think the data parsing is probably something we can refine later if I'm having any trouble, but for now let's go ahead and get a test link I can try out before deployment on Friday?

zepumph commented 3 years ago

Just a couple minutes out on that!

zepumph commented 3 years ago

Please see https://github.com/phetsims/special-ops/issues/202.

brettfiedler commented 3 years ago

For the purposes of this study, this is complete and I will close. Further exploration of movement related data output can happen in new issues.