Compare commits
104 Commits
Author | SHA1 | Date | |
---|---|---|---|
f27fb4d93c | |||
11fb82eba9 | |||
170e6b700b | |||
387ad763f8 | |||
0e61f162bb | |||
400f01a6be | |||
afcbe69c44 | |||
9165fe09fc | |||
08f36fbb94 | |||
afca2aa4d1 | |||
3c6c766412 | |||
329a1e15ea | |||
16a025f3a0 | |||
26b975b5f2 | |||
79b9bce904 | |||
d3d9fa6ca0 | |||
8f5c930f76 | |||
a082807455 | |||
3469f51b7b | |||
e8e31e6aa7 | |||
2df90152fe | |||
e25590539e | |||
a7795302fc | |||
480d89ba8e | |||
9487d324fd | |||
bfc95e075e | |||
7d4ba9b2b2 | |||
71b83040e6 | |||
1976534da0 | |||
ebbbe6e86c | |||
c4a9b4a381 | |||
c7d36a5e82 | |||
c8c45be057 | |||
a8a7267851 | |||
9cdb4081fd | |||
dcb398d3d5 | |||
f6e350cf9f | |||
c085094394 | |||
7c2ea7c406 | |||
84b98a2788 | |||
4e99cd3ef1 | |||
57647318c4 | |||
2720698864 | |||
dfe2fe3cff | |||
18fefa2a44 | |||
c0bab4877a | |||
d2a1d10ada | |||
bcb68d937d | |||
05a8395028 | |||
3a1601a419 | |||
712c2178d2 | |||
12cdad72c8 | |||
95076f8f24 | |||
6391620373 | |||
86212e2153 | |||
98e430f3c1 | |||
5c7e856e7b | |||
a47988913e | |||
ea72dba6c7 | |||
215e846a54 | |||
21b34cb459 | |||
282134f5dc | |||
6b44352839 | |||
9a883d5672 | |||
59ce4338d3 | |||
90560050b8 | |||
2c72173bfe | |||
1a5992408b | |||
0e1a3b7e6b | |||
ea6bfa03bd | |||
20d584db2b | |||
0e4e0d8899 | |||
a18a690417 | |||
c9d57c5e8d | |||
7e9d42a571 | |||
1b51320edb | |||
4f44238237 | |||
c8ffa0ded7 | |||
6b07d5f716 | |||
8784e23b60 | |||
4b8409fbc0 | |||
3237978cba | |||
33c8902050 | |||
fa6c81cf80 | |||
46175fc7db | |||
e6ef86101f | |||
c4d3d7634e | |||
b0380fbef8 | |||
c85c68ab68 | |||
334d5aca9a | |||
1330a0fb82 | |||
005447ce44 | |||
60d5271de2 | |||
e3ab6ace55 | |||
5f4488dea7 | |||
07dc3bdae1 | |||
5d5d78ced0 | |||
fe46461c4c | |||
ccda971fd1 | |||
8fa6cf41ca | |||
aa3c6d5fc0 | |||
6a719e7718 | |||
b18b8333d0 | |||
84d80131d4 |
1
.gitignore
vendored
@ -31,3 +31,4 @@ videos/**
|
|||||||
lib/
|
lib/
|
||||||
.vscode/
|
.vscode/
|
||||||
bin
|
bin
|
||||||
|
scripts/out.*
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
# LEGO Mindstorms EV3 target for PXT
|
# LEGO Mindstorms EV3 target for PXT
|
||||||
|
|
||||||
[](https://ci2.dot.net/job/Private/job/pxt_project_pink/job/master/job/pxt-ev3_Push/)
|
[](https://ci2.dot.net/job/Private/job/pxt_project_rainbow/job/master/job/pxt-ev3_Push/)
|
||||||
|
|
||||||
This repo contains the editor target hosted at https://d541eec2-1e96-4b7b-a223-da9d01d0337a.pxt.io/
|
This repo contains the editor target hosted at https://d541eec2-1e96-4b7b-a223-da9d01d0337a.pxt.io/
|
||||||
|
|
||||||
|
Issue tracker: https://src.education.lego.com/groups/ev3-makecode
|
||||||
|
|
||||||
## Local Dev setup
|
## Local Dev setup
|
||||||
|
|
||||||
These instructions assume familiarity with dev tools and languages.
|
These instructions assume familiarity with dev tools and languages.
|
||||||
@ -14,7 +16,7 @@ These instructions assume familiarity with dev tools and languages.
|
|||||||
|
|
||||||
In a common folder,
|
In a common folder,
|
||||||
|
|
||||||
* clone https://github.com/Microsoft/pxt to ``pxt`` folder (currently, build against `freshcoat` branch)
|
* clone https://github.com/Microsoft/pxt to ``pxt`` folder
|
||||||
* clone https://github.com/Microsoft/pxt-common-packages to ``pxt-common-packages`` folder
|
* clone https://github.com/Microsoft/pxt-common-packages to ``pxt-common-packages`` folder
|
||||||
* clone https://github.com/Microsoft/pxt-ev3 to ``pxt-ev3`` folder
|
* clone https://github.com/Microsoft/pxt-ev3 to ``pxt-ev3`` folder
|
||||||
* go to ``pxt`` and run
|
* go to ``pxt`` and run
|
||||||
|
@ -3,9 +3,3 @@
|
|||||||
## Reference #reference
|
## Reference #reference
|
||||||
|
|
||||||
* [Reference](/reference)
|
* [Reference](/reference)
|
||||||
* [input](/reference/input)
|
|
||||||
* [light](/reference/light)
|
|
||||||
* [music](/reference/music)
|
|
||||||
* [pins](/reference/pins)
|
|
||||||
* [control](/reference/control)
|
|
||||||
* [serial](/reference/serial)
|
|
||||||
|
47
docs/maker.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Maker Activites
|
||||||
|
|
||||||
|
These six activities require the LEGO® MINDSTORMS® Education EV3 Core Set (45544). Supporting materials for teachers and middle school students are provided, offering everything teachers and students need to explore their inner makers as they follow the design process to solve open-ended, themed challenges...
|
||||||
|
|
||||||
|
* [Download Curriculum Materials](https://education.lego.com/en-us/downloads/mindstorms-ev3)
|
||||||
|
|
||||||
|
## Activites
|
||||||
|
|
||||||
|
```codecard
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Sound Machine",
|
||||||
|
"description": "Create instruments with your EV3 Brick!",
|
||||||
|
"url":"/maker/sound-machine",
|
||||||
|
"cardType": "example",
|
||||||
|
"imageUrl": "/static/maker/sound-machine.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sound Of Color",
|
||||||
|
"description": "Play different sounds based on the color",
|
||||||
|
"url":"/maker/sound-of-color",
|
||||||
|
"cardType": "example",
|
||||||
|
"imageUrl": "/static/maker/sound-of-color.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Security Gadget",
|
||||||
|
"description": "Raise the alarm when your brick is lifted!",
|
||||||
|
"url":"/maker/security-gadget",
|
||||||
|
"cardType": "example",
|
||||||
|
"imageUrl": "/static/maker/security-gadget.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Intruder Detector",
|
||||||
|
"description": "Raise the alarm when an intruder sneaks in",
|
||||||
|
"url":"/maker/intruder-detector",
|
||||||
|
"cardType": "example",
|
||||||
|
"imageUrl": "/static/maker/intruder-detector.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Puppet",
|
||||||
|
"description": "Build an automated puppet",
|
||||||
|
"url":"/maker/puppet",
|
||||||
|
"cardType": "example",
|
||||||
|
"imageUrl": "/static/maker/puppet.png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
11
docs/maker/intruder-detector.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Intruder Detector
|
||||||
|
|
||||||
|
This program will activate an alarm when an object moves in front of the Ultrasonic Sensor.
|
||||||
|
|
||||||
|
TODO support for event when value changes
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
input.ultrasonic4.onObjectNear(function () {
|
||||||
|
music.playSoundUntilDone(music.sounds(Sounds.PowerUp))
|
||||||
|
})
|
||||||
|
```
|
17
docs/maker/puppet.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Puppet
|
||||||
|
|
||||||
|
Use this program with the Programmable Brick and Large Motor.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
loops.forever(function () {
|
||||||
|
output.largeMotorA.setPower(30)
|
||||||
|
output.largeMotorA.on(true)
|
||||||
|
loops.pause(100)
|
||||||
|
output.largeMotorA.on(false)
|
||||||
|
music.playSoundUntilDone(music.sounds(Sounds.PowerUp))
|
||||||
|
output.largeMotorA.setPower(-30)
|
||||||
|
output.largeMotorA.on(true)
|
||||||
|
loops.pause(100)
|
||||||
|
output.largeMotorA.on(false)
|
||||||
|
})
|
||||||
|
```
|
9
docs/maker/security-gadget.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Security Gadget
|
||||||
|
|
||||||
|
This program will activate an alarm when an object is lifted from the Touch Sensor.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
input.touchSensor1.onEvent(TouchSensorEvent.Released, function () {
|
||||||
|
music.playSoundUntilDone(music.sounds(Sounds.PowerUp))
|
||||||
|
})
|
||||||
|
```
|
12
docs/maker/sound-machine.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Sound Machine
|
||||||
|
|
||||||
|
This example program combined with the small model will make a beat and rhythm on any surface when the program is run.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
loops.forever(function () {
|
||||||
|
output.motorA.on(50)
|
||||||
|
loops.pause(200)
|
||||||
|
output.motorA.on(100)
|
||||||
|
loops.pause(200)
|
||||||
|
})
|
||||||
|
```
|
15
docs/maker/sound-of-color.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Sound Of Color
|
||||||
|
|
||||||
|
This program will play different sounds when the wheel is rotated. The sound is determined by which color is placed in front of the color Sensor.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
input.color3.onColorDetected(ColorSensorColor.Blue, function () {
|
||||||
|
music.playTone(Note.G4, music.beat(BeatFraction.Half))
|
||||||
|
})
|
||||||
|
input.color3.onColorDetected(ColorSensorColor.Red, function () {
|
||||||
|
music.playTone(Note.C5, music.beat(BeatFraction.Half))
|
||||||
|
})
|
||||||
|
input.color3.onColorDetected(ColorSensorColor.Green, function () {
|
||||||
|
music.playTone(Note.D5, music.beat(BeatFraction.Half))
|
||||||
|
})
|
||||||
|
```
|
@ -1,19 +1,3 @@
|
|||||||
# Reference
|
# Reference
|
||||||
|
|
||||||
```namespaces
|
TODO
|
||||||
input.onGesture(Gesture.Shake, () => {})
|
|
||||||
light.showRing('red red red red red red red red red red')
|
|
||||||
music.playTone(0, 0)
|
|
||||||
pins.pulseDuration()
|
|
||||||
control.runInBackground(() => {})
|
|
||||||
serial.writeLine("");
|
|
||||||
```
|
|
||||||
|
|
||||||
## See Also
|
|
||||||
|
|
||||||
[blocks](/blocks), [JavaScript](/javascript), [input](/reference/input), [light](/reference/light), [music](/reference/music),
|
|
||||||
[control](/reference/control), [pins](/reference/pins), [serial](/reference/serial)
|
|
||||||
|
|
||||||
```package
|
|
||||||
circuit-playground
|
|
||||||
```
|
|
BIN
docs/static/hero.png
vendored
Normal file
After Width: | Height: | Size: 40 KiB |
74
docs/static/lego-logo.svg
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="Layer_1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="141.73"
|
||||||
|
height="141.731"
|
||||||
|
viewBox="0 0 141.73 141.73101"
|
||||||
|
enable-background="new 0 0 265.365 141.732"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="lego-logo.svg"><metadata
|
||||||
|
id="metadata26"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs24" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1199"
|
||||||
|
inkscape:window-height="604"
|
||||||
|
id="namedview22"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:zoom="1.6520876"
|
||||||
|
inkscape:cx="101.03204"
|
||||||
|
inkscape:cy="98.819336"
|
||||||
|
inkscape:window-x="372"
|
||||||
|
inkscape:window-y="149"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="g6" /><g
|
||||||
|
id="product_logo" /><g
|
||||||
|
id="guides" /><g
|
||||||
|
id="LEGO_LOGO_SMALL_RGB"><g
|
||||||
|
id="g6"><path
|
||||||
|
d="m 0.961,0.948 0,139.839 139.84,0 0,-139.839 -139.84,0 z"
|
||||||
|
id="path8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#ffffff" /><path
|
||||||
|
d="m 0.961,0.948 0,139.839 139.84,0 0,-139.839 -139.84,0 z m 130.536,58.127 c -0.23,4.356 -1.617,9.786 -2.839,13.414 -4.917,14.609 -10.618,23.666 -23.821,23.666 -3.887,0 -10.783,-1.031 -13.324,-7.947 l -0.602,-1.641 -1.082,1.371 C 85.7,93.168 79.564,96.186 72.998,96.217 67.96,96.24 63.912,94.553 61.293,91.334 l -0.718,-0.883 -0.765,0.844 c -2.797,3.088 -7.668,4.859 -13.364,4.859 -4.473,0 -8.335,-1.521 -10.876,-4.277 l -0.692,-0.752 -0.72,0.727 c -2.825,2.852 -7.276,4.289 -12.873,4.158 -6.79,-0.162 -10.924,-3.951 -11.058,-10.141 -0.205,-9.543 9.031,-29.622 12.854,-35.727 2.482,-4.078 5.895,-6.055 10.438,-6.055 3.092,0 5.207,0.646 6.463,1.977 1.144,1.211 1.37,2.232 1.472,4.688 l 0.131,3.175 1.676,-2.7 c 4.054,-6.532 10.773,-7.434 17.43,-7.434 4.628,0 8.667,1.702 10.289,4.336 l 0.599,0.971 0.866,-0.737 c 3.431,-2.916 8.271,-4.521 13.629,-4.521 5.849,0 10.099,1.454 12.634,4.322 0.576,0.651 0.957,1.106 1.486,2.219 l 0.651,1.366 0.977,-1.155 c 3.712,-4.39 8.779,-6.615 15.067,-6.615 4.98,0 8.761,1.324 11.235,3.935 3.104,3.271 3.547,7.875 3.373,11.161 z"
|
||||||
|
id="path10"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#f6ec36" /><path
|
||||||
|
d="m 0.961,0.948 0,139.839 139.84,0 0,-139.839 -139.84,0 z M 135.435,64.44 c -0.633,5.089 -4.072,14.518 -5.899,18.286 -4.903,10.102 -11.453,17.863 -24.074,17.863 -6.742,0 -12.288,-2.15 -15.531,-6.283 -4.749,4.162 -10.627,6.342 -16.956,6.342 -4.876,0 -9.185,-1.408 -12.471,-4.012 -3.626,2.555 -8.52,3.98 -13.943,3.98 -4.53,0 -8.606,-1.244 -11.796,-3.543 -3.529,2.342 -8.154,3.506 -13.583,3.377 C 12.149,100.237 5.912,94.507 5.733,86.188 5.497,75.137 14.803,54.911 19.189,47.907 c 3.282,-5.392 8.292,-8.293 14.431,-8.293 6.663,0 9.078,1.903 10.267,4.326 5.141,-4.366 11.956,-4.617 16.808,-4.617 5.356,0 8.717,1.422 11.575,3.697 3.948,-2.368 8.477,-3.599 13.871,-3.599 6.897,0 12.021,1.604 15.595,5.137 4.168,-3.469 9.388,-5.05 15.23,-5.05 7.725,0 12.808,2.786 15.793,6.94 4.203,5.859 3.451,11.757 2.676,17.992 z"
|
||||||
|
id="path12"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#d01012" /><path
|
||||||
|
d="m 0,0 0,141.731 141.73,0 L 141.73,0 0,0 Z m 139.781,1.952 0,137.832 -137.83,0 0,-137.832 137.83,0 z"
|
||||||
|
id="path14"
|
||||||
|
inkscape:connector-curvature="0" /><path
|
||||||
|
d="m 134.752,42.53 0,-0.965 0.655,0 c 0.479,0 0.728,0.168 0.728,0.479 0,0.247 -0.16,0.486 -0.647,0.486 l -0.736,0 z m 2.351,2.002 -0.408,-0.708 c -0.354,-0.619 -0.452,-0.717 -0.755,-0.823 l 0,-0.019 c 0.596,-0.07 0.95,-0.453 0.95,-0.983 0,-0.56 -0.354,-0.984 -1.109,-0.984 l -1.754,0 0,3.519 0.727,0 0,-1.441 0.133,0 c 0.311,0 0.435,0.035 0.576,0.176 0.142,0.144 0.354,0.443 0.479,0.69 l 0.284,0.575 0.877,-0.002 0,0 z m -1.731,-4.497 c 1.51,0 2.737,1.218 2.737,2.726 0,1.508 -1.229,2.731 -2.737,2.731 -1.506,0 -2.72,-1.225 -2.72,-2.731 0,-1.506 1.214,-2.726 2.72,-2.726 z m 0,-0.67 c -1.877,0 -3.392,1.516 -3.392,3.396 0,1.879 1.515,3.394 3.392,3.394 1.883,0 3.397,-1.515 3.397,-3.394 0.002,-1.882 -1.514,-3.396 -3.397,-3.396 z"
|
||||||
|
id="path16"
|
||||||
|
inkscape:connector-curvature="0" /><path
|
||||||
|
d="m 116.889,43.002 c -7.325,0 -12.386,2.904 -15.812,6.961 -0.579,-1.214 -1.019,-1.746 -1.638,-2.447 -2.805,-3.17 -7.313,-4.649 -13.364,-4.649 -5.912,0 -10.841,1.846 -14.263,4.754 -1.777,-2.89 -6.013,-4.803 -11.121,-4.803 -6.674,0 -13.946,0.944 -18.261,7.896 -0.104,-2.503 -0.33,-3.828 -1.737,-5.317 -1.71,-1.81 -4.477,-2.282 -7.173,-2.282 -4.912,0 -8.605,2.146 -11.266,6.515 -3.859,6.163 -13.212,26.441 -13.001,36.263 0.137,6.344 4.393,10.916 12.01,11.096 5.952,0.143 10.62,-1.449 13.59,-4.447 2.665,2.896 6.731,4.594 11.596,4.594 5.417,0 10.868,-1.627 14.088,-5.182 2.712,3.332 6.979,5.271 12.468,5.244 7.175,-0.035 13.477,-3.438 17.593,-8.652 2.259,6.148 7.985,8.59 14.241,8.59 13.709,0 19.729,-9.428 24.748,-24.332 1.13,-3.357 2.639,-8.963 2.889,-13.672 0.413,-7.839 -2.903,-16.13 -15.587,-16.13 z M 25.672,80.477 c 7.093,-1.232 8.876,1.332 8.653,3.707 -0.669,7.109 -7.191,8.699 -12.854,8.566 -4.107,-0.1 -7.8,-1.98 -7.903,-6.748 -0.18,-8.342 8.328,-27.641 12.333,-34.038 1.85,-3.037 4.104,-4.539 7.698,-4.539 3.406,0 4.25,1.749 4.2,3.844 -0.134,5.589 -9.164,22.574 -12.127,29.208 z M 48.77,73.925 c -0.469,1.27 -1.357,3.93 -2.076,6.75 2.328,-0.582 4.074,-0.986 7.065,-0.914 3.408,0.084 5.586,1.496 5.586,4.314 0,6.832 -7.551,8.838 -12.789,8.838 -5.751,0 -10.803,-3.275 -10.803,-9.564 0,-7.373 3.997,-18.553 7.748,-26.001 4.606,-9.146 9.312,-10.362 17.419,-10.362 3.562,0 7.667,1.524 7.667,4.888 0,4.662 -3.95,6.44 -7.866,6.665 -1.672,0.096 -4.246,0.188 -5.786,0.079 0,0 -1.303,1.985 -2.688,5.523 7.252,-1.021 10.327,0.625 9.103,4.82 -1.657,5.667 -6.567,6.04 -12.58,4.964 z M 84.438,56.293 c -1.961,0 -3.244,1.242 -4.198,2.583 -2.102,2.952 -6.656,14.781 -7.365,19.64 -0.486,3.328 0.951,4.006 2.522,4.006 2.52,0 5.345,-2.666 6.309,-7.064 0,0 -4.797,-0.117 -3.475,-4.371 1.285,-4.128 3.723,-5.022 7.764,-5.188 7.961,-0.325 7.175,5.553 6.538,8.688 -2.069,10.18 -9.314,18.355 -19.562,18.355 -7.016,0 -11.371,-3.881 -11.371,-11.035 0,-5.098 2.529,-13.101 4.534,-17.784 4.267,-9.968 8.742,-16.944 20.143,-16.944 6.84,0 12.235,2.458 11.444,8.866 -0.58,4.703 -2.934,7.465 -7.21,7.799 -1.196,0.093 -6.089,-0.031 -4.466,-4.66 0.565,-1.619 0.799,-2.891 -1.607,-2.891 z m 43.057,8.413 c -1.205,6.002 -3.916,13.101 -6.859,18.392 -4.801,8.633 -10.633,9.842 -15.723,9.781 -5.093,-0.061 -10.83,-1.941 -10.874,-9.795 -0.031,-5.639 2.401,-13.598 4.466,-18.815 3.599,-9.458 7.277,-17.17 18.904,-17.031 13.559,0.161 11.141,12.219 10.086,17.468 z M 115.993,56.49 c -1.164,-0.016 -2.18,0.212 -3.093,1.62 -2.051,2.657 -8.047,19.042 -7.941,22.31 0.038,1.174 0.687,2.201 2.133,2.201 1.659,0.002 2.581,-1.145 3.396,-2.529 1.887,-3.193 7.193,-17.676 7.339,-21.297 0.043,-1.055 -0.133,-2.284 -1.834,-2.305 z"
|
||||||
|
id="path18"
|
||||||
|
inkscape:connector-curvature="0" /></g></g></svg>
|
After Width: | Height: | Size: 7.7 KiB |
BIN
docs/static/lego_education_logo.png
vendored
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
docs/static/maker/intruder-detector.png
vendored
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
docs/static/maker/puppet.png
vendored
Normal file
After Width: | Height: | Size: 377 KiB |
BIN
docs/static/maker/security-gadget.png
vendored
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
docs/static/maker/sound-machine.png
vendored
Normal file
After Width: | Height: | Size: 141 KiB |
BIN
docs/static/maker/sound-of-color.png
vendored
Normal file
After Width: | Height: | Size: 159 KiB |
73
legoresources/Blocks_Implemented_In_Swift_Playgrounds.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# Functions for measuring in Swift Playgrounds "EV3 Animal Rescue" (Miranda)
|
||||||
|
|
||||||
|
## Sensors
|
||||||
|
|
||||||
|
* Ultrasonic (cm, inches)
|
||||||
|
* Gyro (angle, rate)
|
||||||
|
* Reset Gyro
|
||||||
|
* Touch (count, on/off)
|
||||||
|
* Light (color, reflection, ambience)
|
||||||
|
* IR (proximity, seek)
|
||||||
|
|
||||||
|
## Motor Sensors
|
||||||
|
|
||||||
|
* Motor (degrees, rotations, power)
|
||||||
|
|
||||||
|
#### Wait for
|
||||||
|
|
||||||
|
All sensor methods have a `waitFor` version that waits for the sensor value to be >= or <=. We don't use '=' equality because it is non-sensical for floating point data.
|
||||||
|
|
||||||
|
All sensors also have a `waitForIncrease`, `waitForDecrease` version.
|
||||||
|
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
# Output Functions
|
||||||
|
|
||||||
|
* Move tank ( for seconds, for degrees, for rotations, indefinitely )
|
||||||
|
* Stop Move (stops 2 motors)
|
||||||
|
* Motor off
|
||||||
|
* Motor on (for seconds, for degrees, for rotations, indefinitely)
|
||||||
|
* Reset motor (resets all counters related to the motor)
|
||||||
|
|
||||||
|
|
||||||
|
# Brick Functions
|
||||||
|
|
||||||
|
* Brick light on (color + mode: flashing, on, or pulsating)
|
||||||
|
* Brick light off
|
||||||
|
* Play sound (wait for completion, play once, play repeating)
|
||||||
|
- Parameters:
|
||||||
|
- file: Takes an input conforming to enum `SoundFile`. One of the following:
|
||||||
|
- `.hello`
|
||||||
|
- `.goodbye`
|
||||||
|
- `.fanfare`
|
||||||
|
- `.errorAlarm`
|
||||||
|
- `.start`
|
||||||
|
- `.stop`
|
||||||
|
- `.object`
|
||||||
|
- `.ouch`
|
||||||
|
- `.blip`
|
||||||
|
- `.arm`
|
||||||
|
- `.snap`
|
||||||
|
- `.laser`
|
||||||
|
* Play sound frequency (for seconds, can either wait for completion or continue while playing)
|
||||||
|
* Play sound note (in range `C4-D#9`) (for seconds, can either wait for completion or continue while playing)
|
||||||
|
* Stopsound (stops all sounds playing)
|
||||||
|
* Wait for seconds
|
||||||
|
* Display text
|
||||||
|
* Display text at `(x,y)` with color`(black, white)`, font`(bold, normal, large)` with option to clear screen
|
||||||
|
* Display line from `(x1,y1)` to `(x2,y2)` with color `(black, white)` option to clear screen
|
||||||
|
* Display rectangle at `(x,y)` with `(width, height)`, with fill or no fill, with color `(black, white)`, option to clear screen
|
||||||
|
* Display image at `(x,y)`, option to clear screen.
|
||||||
|
takes an input conforming to enum `ImageName`. One of the following:
|
||||||
|
- `.neutral`
|
||||||
|
- `.pinchRight`
|
||||||
|
- `.awake`
|
||||||
|
- `.hurt`
|
||||||
|
- `.accept`
|
||||||
|
- `.decline`
|
||||||
|
- `.questionMark`
|
||||||
|
- `.warning`
|
||||||
|
- `.stop`
|
||||||
|
- `.pirate`
|
||||||
|
- `.boom`
|
||||||
|
- `.ev3Icon`
|
1370
legoresources/RobotAPI.swift
Normal file
@ -31,6 +31,22 @@
|
|||||||
"Array.splice|param|deleteCount": "The number of elements to remove. eg: 0",
|
"Array.splice|param|deleteCount": "The number of elements to remove. eg: 0",
|
||||||
"Array.splice|param|start": "The zero-based location in the array from which to start removing elements. eg: 0",
|
"Array.splice|param|start": "The zero-based location in the array from which to start removing elements. eg: 0",
|
||||||
"Array.unshift": "Add one element to the beginning of an array and return the new length of the array.",
|
"Array.unshift": "Add one element to the beginning of an array and return the new length of the array.",
|
||||||
|
"Boolean.toString": "Returns a string representation of an object.",
|
||||||
|
"Buffer.fill": "Fill (a fragment) of the buffer with given value.",
|
||||||
|
"Buffer.getNumber": "Read a number in specified format from the buffer.",
|
||||||
|
"Buffer.length": "Returns the length of a Buffer object.",
|
||||||
|
"Buffer.rotate": "Rotate buffer left in place.\n\n\n\nstart. eg: -1",
|
||||||
|
"Buffer.rotate|param|length": "number of elements in buffer. If negative, length is set as the buffer length minus",
|
||||||
|
"Buffer.rotate|param|offset": "number of bytes to shift; use negative value to shift right",
|
||||||
|
"Buffer.rotate|param|start": "start offset in buffer. Default is 0.",
|
||||||
|
"Buffer.setNumber": "Write a number in specified format in the buffer.",
|
||||||
|
"Buffer.shift": "Shift buffer left in place, with zero padding.\n\n\n\nstart. eg: -1",
|
||||||
|
"Buffer.shift|param|length": "number of elements in buffer. If negative, length is set as the buffer length minus",
|
||||||
|
"Buffer.shift|param|offset": "number of bytes to shift; use negative value to shift right",
|
||||||
|
"Buffer.shift|param|start": "start offset in buffer. Default is 0.",
|
||||||
|
"Buffer.slice": "Return a copy of a fragment of a buffer.",
|
||||||
|
"Buffer.toHex": "Convert a buffer to its hexadecimal representation.",
|
||||||
|
"Buffer.write": "Write contents of `src` at `dstOffset` in current buffer.",
|
||||||
"Math": "More complex operations with numbers.",
|
"Math": "More complex operations with numbers.",
|
||||||
"Math.abs": "Returns the absolute value of a number (the value without regard to whether it is positive or negative).\nFor example, the absolute value of -5 is the same as the absolute value of 5.",
|
"Math.abs": "Returns the absolute value of a number (the value without regard to whether it is positive or negative).\nFor example, the absolute value of -5 is the same as the absolute value of 5.",
|
||||||
"Math.abs|param|x": "A numeric expression for which the absolute value is needed.",
|
"Math.abs|param|x": "A numeric expression for which the absolute value is needed.",
|
||||||
@ -91,6 +107,7 @@
|
|||||||
"Math.tan|param|x": "An angle in radians",
|
"Math.tan|param|x": "An angle in radians",
|
||||||
"Math.trunc": "Returns the number with the decimal part truncated.",
|
"Math.trunc": "Returns the number with the decimal part truncated.",
|
||||||
"Math.trunc|param|x": "A numeric expression.",
|
"Math.trunc|param|x": "A numeric expression.",
|
||||||
|
"Number.toString": "Returns a string representation of a number.",
|
||||||
"String": "Combine, split, and search text strings.\n\nCombine, split, and search text strings.",
|
"String": "Combine, split, and search text strings.\n\nCombine, split, and search text strings.",
|
||||||
"String.charAt": "Return the character at the specified index.",
|
"String.charAt": "Return the character at the specified index.",
|
||||||
"String.charAt|param|index": "The zero-based index of the desired character.",
|
"String.charAt|param|index": "The zero-based index of the desired character.",
|
||||||
@ -107,6 +124,8 @@
|
|||||||
"String.substr|param|length": "number of characters to extract",
|
"String.substr|param|length": "number of characters to extract",
|
||||||
"String.substr|param|start": "first character index; can be negative from counting from the end, eg:0",
|
"String.substr|param|start": "first character index; can be negative from counting from the end, eg:0",
|
||||||
"control": "Program controls and events.",
|
"control": "Program controls and events.",
|
||||||
|
"control.AnimationQueue.cancel": "Cancels the current running animation and clears the queue",
|
||||||
|
"control.AnimationQueue.runUntilDone": "Runs 'render' in a loop until it returns false or the 'stop' function is called",
|
||||||
"control.assert": "Display an error code and stop the program when the assertion is `false`.",
|
"control.assert": "Display an error code and stop the program when the assertion is `false`.",
|
||||||
"control.deviceSerialNumber": "Derive a unique, consistent serial number of this device from internal data.",
|
"control.deviceSerialNumber": "Derive a unique, consistent serial number of this device from internal data.",
|
||||||
"control.millis": "Gets the number of milliseconds elapsed since power on.",
|
"control.millis": "Gets the number of milliseconds elapsed since power on.",
|
||||||
@ -119,15 +138,19 @@
|
|||||||
"control.waitForEvent": "Blocks the calling thread until the specified event is raised.",
|
"control.waitForEvent": "Blocks the calling thread until the specified event is raised.",
|
||||||
"control.waitMicros": "Block the current fiber for the given microseconds",
|
"control.waitMicros": "Block the current fiber for the given microseconds",
|
||||||
"control.waitMicros|param|micros": "number of micro-seconds to wait. eg: 4",
|
"control.waitMicros|param|micros": "number of micro-seconds to wait. eg: 4",
|
||||||
|
"hex": "Tagged hex literal converter",
|
||||||
"loops.forever": "Repeats the code forever in the background. On each iteration, allows other codes to run.",
|
"loops.forever": "Repeats the code forever in the background. On each iteration, allows other codes to run.",
|
||||||
"loops.pause": "Pause for the specified time in milliseconds",
|
"loops.pause": "Pause for the specified time in milliseconds",
|
||||||
"loops.pause|param|ms": "how long to pause for, eg: 100, 200, 500, 1000, 2000",
|
"loops.pause|param|ms": "how long to pause for, eg: 100, 200, 500, 1000, 2000",
|
||||||
|
"loops.timePicker": "Get the time field editor",
|
||||||
|
"loops.timePicker|param|ms": "time duration in milliseconds, eg: 500, 1000",
|
||||||
|
"parseInt": "Convert a string to an integer.",
|
||||||
"serial": "Reading and writing data over a serial connection.",
|
"serial": "Reading and writing data over a serial connection.",
|
||||||
"serial.writeBuffer": "Send a buffer across the serial connection.",
|
"serial.writeBuffer": "Send a buffer across the serial connection.",
|
||||||
"serial.writeLine": "Write a line of text to the serial port.",
|
"serial.writeLine": "Write a line of text to the serial port.",
|
||||||
"serial.writeNumber": "Write a number to the serial port.",
|
"serial.writeNumber": "Write a number to the serial port.",
|
||||||
"serial.writeString": "Write some text to the serial port.",
|
"serial.writeString": "Write some text to the serial port.",
|
||||||
"serial.writeValue": "Write a name:value pair as a line of text to the serial port.",
|
"serial.writeValue": "Write a name:value pair as a line of text to the serial port.",
|
||||||
"serial.writeValue|param|name": "name of the value stream, eg: x",
|
"serial.writeValue|param|name": "name of the value stream, eg: \"x\"",
|
||||||
"serial.writeValue|param|value": "to write"
|
"serial.writeValue|param|value": "to write"
|
||||||
}
|
}
|
@ -10,7 +10,7 @@
|
|||||||
"Array.unshift|block": "%list| insert %value| at beginning",
|
"Array.unshift|block": "%list| insert %value| at beginning",
|
||||||
"Array|block": "Array",
|
"Array|block": "Array",
|
||||||
"Math.constrain|block": "constrain %value|between %low|and %high",
|
"Math.constrain|block": "constrain %value|between %low|and %high",
|
||||||
"Math.map|block": "map %value|from %fromLow|%fromHigh|to %toLow|%toHigh",
|
"Math.map|block": "map %value|from low %fromLow|from high %fromHigh|to low %toLow|to high %toHigh",
|
||||||
"Math.randomRange|block": "pick random %min|to %limit",
|
"Math.randomRange|block": "pick random %min|to %limit",
|
||||||
"Math|block": "Math",
|
"Math|block": "Math",
|
||||||
"String.charAt|block": "char from %this=text|at %pos",
|
"String.charAt|block": "char from %this=text|at %pos",
|
||||||
@ -30,8 +30,10 @@
|
|||||||
"control.waitMicros|block": "wait (µs)%micros",
|
"control.waitMicros|block": "wait (µs)%micros",
|
||||||
"control|block": "control",
|
"control|block": "control",
|
||||||
"loops.forever|block": "forever",
|
"loops.forever|block": "forever",
|
||||||
"loops.pause|block": "pause (ms) %pause",
|
"loops.pause|block": "pause %pause=timePicker|ms",
|
||||||
|
"loops.timePicker|block": "%ms",
|
||||||
"loops|block": "loops",
|
"loops|block": "loops",
|
||||||
|
"parseInt|block": "parse to integer %text",
|
||||||
"serial.writeBuffer|block": "serial|write buffer %buffer",
|
"serial.writeBuffer|block": "serial|write buffer %buffer",
|
||||||
"serial.writeLine|block": "serial|write line %text",
|
"serial.writeLine|block": "serial|write line %text",
|
||||||
"serial.writeNumber|block": "serial|write number %value",
|
"serial.writeNumber|block": "serial|write number %value",
|
||||||
@ -40,9 +42,13 @@
|
|||||||
"serial|block": "serial",
|
"serial|block": "serial",
|
||||||
"{id:category}Array": "Array",
|
"{id:category}Array": "Array",
|
||||||
"{id:category}Arrays": "Arrays",
|
"{id:category}Arrays": "Arrays",
|
||||||
|
"{id:category}Boolean": "Boolean",
|
||||||
|
"{id:category}Buffer": "Buffer",
|
||||||
"{id:category}Control": "Control",
|
"{id:category}Control": "Control",
|
||||||
|
"{id:category}Helpers": "Helpers",
|
||||||
"{id:category}Loops": "Loops",
|
"{id:category}Loops": "Loops",
|
||||||
"{id:category}Math": "Math",
|
"{id:category}Math": "Math",
|
||||||
|
"{id:category}Number": "Number",
|
||||||
"{id:category}Serial": "Serial",
|
"{id:category}Serial": "Serial",
|
||||||
"{id:category}String": "String",
|
"{id:category}String": "String",
|
||||||
"{id:category}Text": "Text"
|
"{id:category}Text": "Text"
|
||||||
|
14
libs/base/shims.d.ts
vendored
@ -70,7 +70,7 @@ declare namespace loops {
|
|||||||
* Repeats the code forever in the background. On each iteration, allows other codes to run.
|
* Repeats the code forever in the background. On each iteration, allows other codes to run.
|
||||||
* @param body code to execute
|
* @param body code to execute
|
||||||
*/
|
*/
|
||||||
//% help=loops/forever weight=100 blockGap=8
|
//% help=loops/forever weight=100 afterOnStart=true
|
||||||
//% blockId=forever block="forever" blockAllowMultiple=1 shim=loops::forever
|
//% blockId=forever block="forever" blockAllowMultiple=1 shim=loops::forever
|
||||||
function forever(a: () => void): void;
|
function forever(a: () => void): void;
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ declare namespace loops {
|
|||||||
* @param ms how long to pause for, eg: 100, 200, 500, 1000, 2000
|
* @param ms how long to pause for, eg: 100, 200, 500, 1000, 2000
|
||||||
*/
|
*/
|
||||||
//% help=loops/pause weight=99
|
//% help=loops/pause weight=99
|
||||||
//% async block="pause (ms) %pause"
|
//% async block="pause %pause=timePicker|ms"
|
||||||
//% blockId=device_pause shim=loops::pause
|
//% blockId=device_pause shim=loops::pause
|
||||||
function pause(ms: int32): void;
|
function pause(ms: int32): void;
|
||||||
}
|
}
|
||||||
@ -99,8 +99,8 @@ declare namespace control {
|
|||||||
*/
|
*/
|
||||||
//% weight=20 blockGap=8 blockId="control_on_event" block="on event|from %src|with value %value"
|
//% weight=20 blockGap=8 blockId="control_on_event" block="on event|from %src|with value %value"
|
||||||
//% blockExternalInputs=1
|
//% blockExternalInputs=1
|
||||||
//% help="control/on-event" shim=control::onEvent
|
//% help="control/on-event" flags.defl=16 shim=control::onEvent
|
||||||
function onEvent(src: int32, value: int32, handler: () => void): void;
|
function onEvent(src: int32, value: int32, handler: () => void, flags?: int32): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the device.
|
* Reset the device.
|
||||||
@ -120,7 +120,7 @@ declare namespace control {
|
|||||||
/**
|
/**
|
||||||
* Run other code in the background.
|
* Run other code in the background.
|
||||||
*/
|
*/
|
||||||
//% help=control/run-in-background blockAllowMultiple=1
|
//% help=control/run-in-background blockAllowMultiple=1 afterOnStart=true
|
||||||
//% blockId="control_run_in_background" block="run in background" blockGap=8 shim=control::runInBackground
|
//% blockId="control_run_in_background" block="run in background" blockGap=8 shim=control::runInBackground
|
||||||
function runInBackground(a: () => void): void;
|
function runInBackground(a: () => void): void;
|
||||||
|
|
||||||
@ -144,14 +144,14 @@ declare namespace serial {
|
|||||||
* Write some text to the serial port.
|
* Write some text to the serial port.
|
||||||
*/
|
*/
|
||||||
//% help=serial/write-string
|
//% help=serial/write-string
|
||||||
//% weight=87
|
//% weight=87 blockHidden=true
|
||||||
//% blockId=serial_writestring block="serial|write string %text" shim=serial::writeString
|
//% blockId=serial_writestring block="serial|write string %text" shim=serial::writeString
|
||||||
function writeString(text: string): void;
|
function writeString(text: string): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a buffer across the serial connection.
|
* Send a buffer across the serial connection.
|
||||||
*/
|
*/
|
||||||
//% help=serial/write-buffer advanced=true weight=6
|
//% help=serial/write-buffer weight=6 blockHidden=true
|
||||||
//% blockId=serial_writebuffer block="serial|write buffer %buffer" shim=serial::writeBuffer
|
//% blockId=serial_writebuffer block="serial|write buffer %buffer" shim=serial::writeBuffer
|
||||||
function writeBuffer(buffer: Buffer): void;
|
function writeBuffer(buffer: Buffer): void;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ DEPS = $(PXT_HEADERS) package.json Makefile Makefile.inc
|
|||||||
all: $(EXE)
|
all: $(EXE)
|
||||||
|
|
||||||
$(EXE): $(PXT_OBJS)
|
$(EXE): $(PXT_OBJS)
|
||||||
$(LD) -o $(EXE) $(LDFLAGS) -Wl,-Map,$(EXE:.elf=.map) $(PXT_OBJS) $(LIBSTDCPP) -lm -lpthread $(NPM_LIBS)
|
$(LD) -o $(EXE) $(LDFLAGS) -Wl,-Map,$(EXE:.elf=.map) $(PXT_OBJS) $(LIBSTDCPP) -lm -lpthread -lz $(NPM_LIBS)
|
||||||
cp $(EXE) $(EXE:.elf=.full)
|
cp $(EXE) $(EXE:.elf=.full)
|
||||||
$(PREF)strip $(EXE)
|
$(PREF)strip $(EXE)
|
||||||
node -p 'require("fs").readFileSync("$(EXE)").toString("hex")' > $(HEX)
|
node -p 'require("fs").readFileSync("$(EXE)").toString("hex")' > $(HEX)
|
||||||
|
@ -1,4 +1,42 @@
|
|||||||
{
|
{
|
||||||
|
"ButtonEvent": "User interaction on buttons",
|
||||||
|
"Draw": "Drawing modes",
|
||||||
|
"Image.buffer": "Returns the underlaying Buffer object.",
|
||||||
|
"Image.doubled": "Double size of an image.",
|
||||||
|
"Image.draw": "Draw an image on the screen.",
|
||||||
|
"Image.height": "Returns the height of an image.",
|
||||||
|
"Image.width": "Returns the width of an image.",
|
||||||
|
"LightsPattern": "Patterns for lights under the buttons.",
|
||||||
|
"MMap.getNumber": "Read a number in specified format from the buffer.",
|
||||||
|
"MMap.ioctl": "Perform ioctl(2) on the underlaying file",
|
||||||
|
"MMap.length": "Returns the length of a Buffer object.",
|
||||||
|
"MMap.read": "Perform read(2) on the underlaying file",
|
||||||
|
"MMap.setNumber": "Write a number in specified format in the buffer.",
|
||||||
|
"MMap.slice": "Read a range of bytes into a buffer.",
|
||||||
|
"MMap.write": "Perform write(2) on the underlaying file",
|
||||||
|
"TouchSensorEvent": "Touch sensor interactions",
|
||||||
|
"brick.Button": "Generic button class, for device buttons and sensors.",
|
||||||
|
"brick.Button.isPressed": "Check if button is currently pressed or not.",
|
||||||
|
"brick.Button.onEvent": "Do something when a button or sensor is clicked, up or down.",
|
||||||
|
"brick.Button.onEvent|param|body": "code to run when the event is raised",
|
||||||
|
"brick.Button.wasPressed": "See if the button was pressed again since the last time you checked.",
|
||||||
|
"brick.buttonDown": "Down button on the EV3 Brick.",
|
||||||
|
"brick.buttonEnter": "Enter button on the EV3 Brick.",
|
||||||
|
"brick.buttonLeft": "Left button on the EV3 Brick.",
|
||||||
|
"brick.buttonRight": "Right button on the EV3 Brick.",
|
||||||
|
"brick.buttonUp": "Up button on the EV3 Brick.",
|
||||||
|
"brick.pattern": "Pattern block.",
|
||||||
|
"brick.pattern|param|pattern": "the lights pattern to use. eg: LightsPattern.Green",
|
||||||
|
"brick.print": "Show text on the screen.",
|
||||||
|
"brick.print|param|text": "the text to print on the screen, eg: \"Hello world\"",
|
||||||
|
"brick.print|param|x": "the starting position's x coordinate, eg: 0",
|
||||||
|
"brick.print|param|y": "the starting position's x coordinate, eg: 0",
|
||||||
|
"brick.setPixel": "Sets a pixel on or off",
|
||||||
|
"brick.setPixel|param|on": "a value indicating if the pixel should be on or off",
|
||||||
|
"brick.setPixel|param|x": "the starting position's x coordinate, eg: 0",
|
||||||
|
"brick.setPixel|param|y": "the starting position's x coordinate, eg: 0",
|
||||||
|
"brick.setStatusLight": "Set lights.",
|
||||||
|
"brick.setStatusLight|param|pattern": "the lights pattern to use.",
|
||||||
"control": "Program controls and events.",
|
"control": "Program controls and events.",
|
||||||
"control.allocateNotifyEvent": "Allocates the next user notification event",
|
"control.allocateNotifyEvent": "Allocates the next user notification event",
|
||||||
"control.deviceFirmwareVersion": "Determine the version of system software currently running.",
|
"control.deviceFirmwareVersion": "Determine the version of system software currently running.",
|
||||||
@ -7,47 +45,55 @@
|
|||||||
"control.raiseEvent": "Announce that an event happened to registered handlers.",
|
"control.raiseEvent": "Announce that an event happened to registered handlers.",
|
||||||
"control.raiseEvent|param|src": "ID of the Component that generated the event",
|
"control.raiseEvent|param|src": "ID of the Component that generated the event",
|
||||||
"control.raiseEvent|param|value": "Component specific code indicating the cause of the event.",
|
"control.raiseEvent|param|value": "Component specific code indicating the cause of the event.",
|
||||||
"input": "Respond to and read data from buttons and sensors.",
|
"motors.Motor.clearCount": "Clears the motor count",
|
||||||
"input.Button": "Generic button class, for device buttons and sensors.",
|
"motors.Motor.count": "Gets motor step count.",
|
||||||
"input.buttonDown": "Down button.",
|
"motors.Motor.move": "Moves the motor by a number of degrees",
|
||||||
"input.buttonEnter": "Enter button.",
|
"motors.Motor.move|param|angle": "the degrees to rotate, eg: 360",
|
||||||
"input.buttonLeft": "Left button.",
|
"motors.Motor.move|param|power": "the power from ``100`` full forward to ``-100`` full backward, eg: 50",
|
||||||
"input.buttonRight": "Right button.",
|
"motors.Motor.power": "Sets the motor power level from ``-100`` to ``100``.",
|
||||||
"input.buttonUp": "Up button.",
|
"motors.Motor.power|param|power": "the power from ``100`` full forward to ``-100`` full backward, eg: 50",
|
||||||
"input.remoteBottomLeft": "Remote bottom-left button.",
|
"motors.Motor.reset": "Resets the motor.",
|
||||||
"input.remoteBottomRight": "Remote bottom-right button.",
|
"motors.Motor.setBrake": "Sets the automatic brake on or off when the motor is off",
|
||||||
"input.remoteCenter": "Remote beacon (center) button.",
|
"motors.Motor.setBrake|param|brake": "a value indicating if the motor should break when off",
|
||||||
"input.remoteTopLeft": "Remote top-left button.",
|
"motors.Motor.setReversed": "Reverses the motor polarity",
|
||||||
"input.remoteTopRight": "Remote top-right button.",
|
"motors.Motor.speed": "Gets motor actual speed.",
|
||||||
|
"motors.Motor.stop": "Stops the motor",
|
||||||
|
"motors.Motor.tachoCount": "Gets motor tacho count.",
|
||||||
|
"motors.stopAllMotors": "Stops all motors",
|
||||||
"output.createBuffer": "Create a new zero-initialized buffer.",
|
"output.createBuffer": "Create a new zero-initialized buffer.",
|
||||||
"output.createBuffer|param|size": "number of bytes in the buffer",
|
"output.createBuffer|param|size": "number of bytes in the buffer",
|
||||||
"output.getCurrentSpeed": "Get motor speed.",
|
|
||||||
"output.getCurrentSpeed|param|out": "the output connection that the motor is connected to",
|
|
||||||
"output.getPattern": "Pattern block.",
|
|
||||||
"output.getPattern|param|pattern": "the lights pattern to use. eg: LightsPattern.Green",
|
|
||||||
"output.setLights": "Set lights.",
|
|
||||||
"output.setLights|param|pattern": "the lights pattern to use.",
|
|
||||||
"output.setPower": "Set motor power.",
|
|
||||||
"output.setPower|param|out": "the output connection that the motor is connected to",
|
|
||||||
"output.setPower|param|power": "the desired power to use. eg: 100",
|
|
||||||
"output.setSpeed": "Set motor speed.",
|
|
||||||
"output.setSpeed|param|out": "the output connection that the motor is connected to",
|
|
||||||
"output.setSpeed|param|speed": "the desired speed to use. eg: 100",
|
|
||||||
"output.start": "Turn motor on.",
|
|
||||||
"output.start|param|out": "the output connection that the motor is connected to",
|
|
||||||
"output.stop": "Turn motor off.",
|
|
||||||
"output.stop|param|out": "the output connection that the motor is connected to",
|
|
||||||
"output.turn": "Turn a motor on for a specified number of milliseconds.",
|
|
||||||
"output.turn|param|ms": "the number of milliseconds to turn the motor on, eg: 500",
|
|
||||||
"output.turn|param|out": "the output connection that the motor is connected to",
|
|
||||||
"output.turn|param|useBrake": "whether or not to use the brake, defaults to false",
|
|
||||||
"screen.clear": "Clear screen and reset font to normal.",
|
"screen.clear": "Clear screen and reset font to normal.",
|
||||||
"screen.doubleIcon": "Double size of an icon.",
|
"screen.imageOf": "Makes an image bound to a buffer.",
|
||||||
"screen.drawIcon": "Draw an icon on the screen.",
|
"screen.unpackPNG": "Decompresses a 1-bit gray scale PNG image to image format.",
|
||||||
"screen.drawText": "Show text on the screen.",
|
"sensors.ColorSensor.ambientLight": "Get current ambient light value from the color sensor.",
|
||||||
"screen.drawText|param|text": "the text to print on the screen, eg: \"Hello world\"",
|
"sensors.ColorSensor.color": "Get the current color from the color sensor.",
|
||||||
"screen.drawText|param|x": "the starting position's x coordinate, eg: 0",
|
"sensors.ColorSensor.onColorDetected": "Registers code to run when the given color is detected",
|
||||||
"screen.drawText|param|y": "the starting position's x coordinate, eg: 0",
|
"sensors.ColorSensor.onColorDetected|param|color": "the color to dtect",
|
||||||
|
"sensors.ColorSensor.onColorDetected|param|handler": "the code to run when detected",
|
||||||
|
"sensors.ColorSensor.reflectedLight": "Get current reflected light value from the color sensor.",
|
||||||
|
"sensors.GyroSensor.angle": "Get the current angle from the gyroscope.",
|
||||||
|
"sensors.GyroSensor.rate": "Get the current rotation rate from the gyroscope.",
|
||||||
|
"sensors.InfraredSensor.on": "Registers code to run when an object is getting near.",
|
||||||
|
"sensors.InfraredSensor.on|param|handler": "the code to run when detected",
|
||||||
|
"sensors.InfraredSensor.proximity": "Get the promixity measured by the infrared sensor, from ``0`` (close) to ``100`` (far)",
|
||||||
|
"sensors.InfraredSensor.remoteCommand": "Get the remote commandreceived the infrared sensor.",
|
||||||
|
"sensors.InfraredSensor.wait": "Waits for the event to occur",
|
||||||
|
"sensors.RemoteInfraredBeaconButton.isPressed": "Check if a remote button is currently pressed or not.",
|
||||||
|
"sensors.RemoteInfraredBeaconButton.onEvent": "Do something when a button or sensor is clicked, up or down",
|
||||||
|
"sensors.RemoteInfraredBeaconButton.onEvent|param|body": "code to run when the event is raised",
|
||||||
|
"sensors.RemoteInfraredBeaconButton.wasPressed": "See if the remote button was pressed again since the last time you checked.",
|
||||||
|
"sensors.TouchSensor.isTouched": "Check if touch sensor is touched.",
|
||||||
|
"sensors.TouchSensor.onEvent": "Do something when a touch sensor is touched...",
|
||||||
|
"sensors.TouchSensor.onEvent|param|body": "code to run when the event is raised",
|
||||||
|
"sensors.UltraSonicSensor.distance": "Gets the distance from the sonar in millimeters",
|
||||||
|
"sensors.UltraSonicSensor.on": "Registers code to run when the given color is close",
|
||||||
|
"sensors.UltraSonicSensor.on|param|handler": "the code to run when detected",
|
||||||
|
"sensors.UltraSonicSensor.wait": "Waits for the event to occur",
|
||||||
|
"sensors.remoteButtonBottomLeft": "Remote bottom-left button.",
|
||||||
|
"sensors.remoteButtonBottomRight": "Remote bottom-right button.",
|
||||||
|
"sensors.remoteButtonCenter": "Remote beacon (center) button.",
|
||||||
|
"sensors.remoteButtonTopLeft": "Remote top-left button.",
|
||||||
|
"sensors.remoteButtonTopRight": "Remote top-right button.",
|
||||||
"serial": "Reading and writing data over a serial connection.",
|
"serial": "Reading and writing data over a serial connection.",
|
||||||
"serial.writeDmesg": "Send DMESG debug buffer over serial."
|
"serial.writeDmesg": "Send DMESG debug buffer over serial."
|
||||||
}
|
}
|
@ -1,8 +1,17 @@
|
|||||||
{
|
{
|
||||||
"ButtonEvent.Click|block": "click",
|
"ButtonEvent.Click|block": "click",
|
||||||
"ButtonEvent.Down|block": "down",
|
"ButtonEvent.Down|block": "down",
|
||||||
"ButtonEvent.LongClick|block": "long click",
|
|
||||||
"ButtonEvent.Up|block": "up",
|
"ButtonEvent.Up|block": "up",
|
||||||
|
"ColorSensorColor.Black|block": "black",
|
||||||
|
"ColorSensorColor.Blue|block": "blue",
|
||||||
|
"ColorSensorColor.Brown|block": "brown",
|
||||||
|
"ColorSensorColor.Green|block": "green",
|
||||||
|
"ColorSensorColor.None|block": "none",
|
||||||
|
"ColorSensorColor.Red|block": "red",
|
||||||
|
"ColorSensorColor.White|block": "white",
|
||||||
|
"ColorSensorColor.Yellow|block": "yellow",
|
||||||
|
"InfraredSensorEvent.ObjectDetected|block": "object detected",
|
||||||
|
"InfraredSensorEvent.ObjectNear|block": "object near",
|
||||||
"LightsPattern.GreenFlash|block": "Flashing Green",
|
"LightsPattern.GreenFlash|block": "Flashing Green",
|
||||||
"LightsPattern.GreenPulse|block": "Pulsing Green",
|
"LightsPattern.GreenPulse|block": "Pulsing Green",
|
||||||
"LightsPattern.Green|block": "Green",
|
"LightsPattern.Green|block": "Green",
|
||||||
@ -13,37 +22,114 @@
|
|||||||
"LightsPattern.RedFlash|block": "Flashing Red",
|
"LightsPattern.RedFlash|block": "Flashing Red",
|
||||||
"LightsPattern.RedPulse|block": "Pulsing Red",
|
"LightsPattern.RedPulse|block": "Pulsing Red",
|
||||||
"LightsPattern.Red|block": "Red",
|
"LightsPattern.Red|block": "Red",
|
||||||
|
"Output.ALL|block": "All",
|
||||||
|
"Output.A|block": "A",
|
||||||
|
"Output.B|block": "B",
|
||||||
|
"Output.C|block": "C",
|
||||||
|
"Output.D|block": "D",
|
||||||
|
"TouchSensorEvent.Bumped|block": "bumped",
|
||||||
|
"TouchSensorEvent.Pressed|block": "pressed",
|
||||||
|
"TouchSensorEvent.Released|block": "released",
|
||||||
|
"UltrasonicSensorEvent.ObjectDetected|block": "object detected",
|
||||||
|
"UltrasonicSensorEvent.ObjectNear|block": "object near",
|
||||||
|
"brick.Button.isPressed|block": "%button|is pressed",
|
||||||
|
"brick.Button.onEvent|block": "on %button|%event",
|
||||||
|
"brick.Button.wasPressed|block": "%button|was pressed",
|
||||||
|
"brick.buttonDown|block": "button down",
|
||||||
|
"brick.buttonEnter|block": "button enter",
|
||||||
|
"brick.buttonLeft|block": "button left",
|
||||||
|
"brick.buttonRight|block": "button right",
|
||||||
|
"brick.buttonUp|block": "button up",
|
||||||
|
"brick.pattern|block": "%pattern",
|
||||||
|
"brick.print|block": "print %text| at x: %x| y: %y",
|
||||||
|
"brick.setPixel|block": "set pixel %on| at x: %x| y: %y",
|
||||||
|
"brick.setStatusLight|block": "set status light %pattern=led_pattern",
|
||||||
|
"brick|block": "brick",
|
||||||
"control.raiseEvent|block": "raise event|from %src|with value %value",
|
"control.raiseEvent|block": "raise event|from %src|with value %value",
|
||||||
"control|block": "control",
|
"control|block": "control",
|
||||||
"input.buttonDown|block": "button down",
|
"motors.Motor.count|block": "%motor|count",
|
||||||
"input.buttonEnter|block": "button enter",
|
"motors.Motor.move|block": "move %motor|by %angle|degrees at %power|%",
|
||||||
"input.buttonLeft|block": "button left",
|
"motors.Motor.power|block": "power %motor|to %power|%",
|
||||||
"input.buttonRight|block": "button right",
|
"motors.Motor.setBrake|block": "set %motor|brake %brake",
|
||||||
"input.buttonUp|block": "button up",
|
"motors.Motor.setReversed|block": "set %motor|reversed %reversed",
|
||||||
"input.remoteBottomLeft|block": "remote bottom-left",
|
"motors.Motor.speed|block": "%motor|speed",
|
||||||
"input.remoteBottomRight|block": "remote bottom-right",
|
"motors.Motor.stop|block": "stop %motor",
|
||||||
"input.remoteCenter|block": "remote center",
|
"motors.Motor.tachoCount|block": "%motor|tacho count",
|
||||||
"input.remoteTopLeft|block": "remote top-left",
|
"motors.largeMotorA|block": "large motor A",
|
||||||
"input.remoteTopRight|block": "remote top-right",
|
"motors.largeMotorB|block": "large motor B",
|
||||||
"input|block": "input",
|
"motors.largeMotorC|block": "large motor C",
|
||||||
"output.getCurrentSpeed|block": "motor %out|speed",
|
"motors.largeMotorD|block": "large motor D",
|
||||||
"output.getPattern|block": "%pattern",
|
"motors.mediumMotorA|block": "medium motor A",
|
||||||
"output.setLights|block": "set status light %pattern=led_pattern",
|
"motors.mediumMotorB|block": "medium motor B",
|
||||||
"output.setPower|block": "set motor %out| power to %power",
|
"motors.mediumMotorC|block": "medium motor C",
|
||||||
"output.setSpeed|block": "set motor %out| speed to %speed",
|
"motors.mediumMotorD|block": "medium motor D",
|
||||||
"output.start|block": "turn motor %out|on",
|
"motors.stopAllMotors|block": "stop all motors",
|
||||||
"output.stop|block": "turn motor %out|off",
|
"motors|block": "motors",
|
||||||
"output.turn|block": "turn motor %out| on for %ms|milliseconds",
|
|
||||||
"output|block": "output",
|
"output|block": "output",
|
||||||
"screen.drawText|block": "print %text| at x: %x| y: %y",
|
|
||||||
"screen|block": "screen",
|
"screen|block": "screen",
|
||||||
|
"sensors.ColorSensor.ambientLight|block": "%color| ambient light",
|
||||||
|
"sensors.ColorSensor.color|block": "%color| color",
|
||||||
|
"sensors.ColorSensor.onColorDetected|block": "on %sensor|detected %color",
|
||||||
|
"sensors.ColorSensor.reflectedLight|block": "%color| reflected light",
|
||||||
|
"sensors.GyroSensor.angle|block": "%sensor|angle",
|
||||||
|
"sensors.GyroSensor.rate|block": "%sensor|rotation rate",
|
||||||
|
"sensors.InfraredSensor.on|block": "on %sensor|%event",
|
||||||
|
"sensors.InfraredSensor.proximity|block": "%infrared|proximity",
|
||||||
|
"sensors.InfraredSensor.remoteCommand|block": "%infrared|remote command",
|
||||||
|
"sensors.InfraredSensor.wait|block": "wait %sensor|for %event",
|
||||||
|
"sensors.RemoteInfraredBeaconButton.isPressed|block": "%button|is pressed",
|
||||||
|
"sensors.RemoteInfraredBeaconButton.onEvent|block": "on %button|%event",
|
||||||
|
"sensors.RemoteInfraredBeaconButton.wasPressed|block": "%button|was pressed",
|
||||||
|
"sensors.TouchSensor.isTouched|block": "%sensor|is touched",
|
||||||
|
"sensors.TouchSensor.onEvent|block": "on %sensor|%event",
|
||||||
|
"sensors.UltraSonicSensor.distance|block": "%sensor|distance",
|
||||||
|
"sensors.UltraSonicSensor.on|block": "on %sensor|%event",
|
||||||
|
"sensors.UltraSonicSensor.wait|block": "wait %sensor|for %event",
|
||||||
|
"sensors.color1|block": "color sensor 1",
|
||||||
|
"sensors.color2|block": "color sensor 2",
|
||||||
|
"sensors.color3|block": "color sensor 3",
|
||||||
|
"sensors.color4|block": "color sensor 4",
|
||||||
|
"sensors.gyro1|block": "gyro sensor 1",
|
||||||
|
"sensors.gyro2|block": "gyro sensor 2",
|
||||||
|
"sensors.gyro3|block": "gyro sensor 3",
|
||||||
|
"sensors.gyro4|block": "gyro sensor 4",
|
||||||
|
"sensors.infraredSensor1|block": "infrared sensor 1",
|
||||||
|
"sensors.infraredSensor2|block": "infrared sensor 2",
|
||||||
|
"sensors.infraredSensor3|block": "infrared sensor 3",
|
||||||
|
"sensors.infraredSensor4|block": "infrared sensor 4",
|
||||||
|
"sensors.remoteButtonBottomLeft|block": "remote button bottom-left",
|
||||||
|
"sensors.remoteButtonBottomRight|block": "remote button bottom-right",
|
||||||
|
"sensors.remoteButtonCenter|block": "remote button center",
|
||||||
|
"sensors.remoteButtonTopLeft|block": "remote button top-left",
|
||||||
|
"sensors.remoteButtonTopRight|block": "remote button top-right",
|
||||||
|
"sensors.touchSensor1|block": "touch sensor 1",
|
||||||
|
"sensors.touchSensor2|block": "touch sensor 2",
|
||||||
|
"sensors.touchSensor3|block": "touch sensor 3",
|
||||||
|
"sensors.touchSensor4|block": "touch sensor 4",
|
||||||
|
"sensors.ultrasonic1|block": "ultrasonic sensor 1",
|
||||||
|
"sensors.ultrasonic2|block": "ultrasonic sensor 2",
|
||||||
|
"sensors.ultrasonic3|block": "ultrasonic sensor 3",
|
||||||
|
"sensors.ultrasonic4|block": "ultrasonic sensor 4",
|
||||||
|
"sensors|block": "sensors",
|
||||||
"serial|block": "serial",
|
"serial|block": "serial",
|
||||||
|
"{id:category}Brick": "Brick",
|
||||||
"{id:category}Control": "Control",
|
"{id:category}Control": "Control",
|
||||||
"{id:category}Input": "Input",
|
"{id:category}Image": "Image",
|
||||||
|
"{id:category}Images": "Images",
|
||||||
|
"{id:category}MMap": "MMap",
|
||||||
|
"{id:category}Motors": "Motors",
|
||||||
"{id:category}Output": "Output",
|
"{id:category}Output": "Output",
|
||||||
"{id:category}Screen": "Screen",
|
"{id:category}Screen": "Screen",
|
||||||
|
"{id:category}Sensors": "Sensors",
|
||||||
"{id:category}Serial": "Serial",
|
"{id:category}Serial": "Serial",
|
||||||
"{id:group}Lights": "Lights",
|
"{id:group}Buttons": "Buttons",
|
||||||
|
"{id:group}Color Sensor": "Color Sensor",
|
||||||
|
"{id:group}Gyro Sensor": "Gyro Sensor",
|
||||||
|
"{id:group}Infrared Sensor": "Infrared Sensor",
|
||||||
|
"{id:group}Light": "Light",
|
||||||
"{id:group}Motors": "Motors",
|
"{id:group}Motors": "Motors",
|
||||||
"{id:group}Screen": "Screen"
|
"{id:group}Remote Infrared Beacon": "Remote Infrared Beacon",
|
||||||
|
"{id:group}Screen": "Screen",
|
||||||
|
"{id:group}Touch Sensor": "Touch Sensor",
|
||||||
|
"{id:group}Ultrasonic Sensor": "Ultrasonic Sensor"
|
||||||
}
|
}
|
@ -4,34 +4,34 @@
|
|||||||
*/
|
*/
|
||||||
const enum LightsPattern {
|
const enum LightsPattern {
|
||||||
//% block=Off enumval=0
|
//% block=Off enumval=0
|
||||||
//% blockIdentity=output.getPattern
|
//% blockIdentity=output.pattern
|
||||||
Off = 0,
|
Off = 0,
|
||||||
//% block=Green enumval=1
|
//% block=Green enumval=1
|
||||||
//% blockIdentity=output.getPattern
|
//% blockIdentity=output.pattern
|
||||||
Green = 1,
|
Green = 1,
|
||||||
//% block=Red enumval=2
|
//% block=Red enumval=2
|
||||||
//% blockIdentity=output.getPattern
|
//% blockIdentity=output.pattern
|
||||||
Red = 2,
|
Red = 2,
|
||||||
//% block=Orange enumval=3
|
//% block=Orange enumval=3
|
||||||
//% blockIdentity=output.getPattern
|
//% blockIdentity=output.pattern
|
||||||
Orange = 3,
|
Orange = 3,
|
||||||
//% block="Flashing Green" enumval=4
|
//% block="Flashing Green" enumval=4
|
||||||
//% blockIdentity=output.getPattern
|
//% blockIdentity=output.pattern
|
||||||
GreenFlash = 4,
|
GreenFlash = 4,
|
||||||
//% block="Flashing Red" enumval=5
|
//% block="Flashing Red" enumval=5
|
||||||
//% blockIdentity=output.getPattern
|
//% blockIdentity=output.pattern
|
||||||
RedFlash = 5,
|
RedFlash = 5,
|
||||||
//% block="Flashing Orange" enumval=6
|
//% block="Flashing Orange" enumval=6
|
||||||
//% blockIdentity=output.getPattern
|
//% blockIdentity=output.pattern
|
||||||
OrangeFlash = 6,
|
OrangeFlash = 6,
|
||||||
//% block="Pulsing Green" enumval=7
|
//% block="Pulsing Green" enumval=7
|
||||||
//% blockIdentity=output.getPattern
|
//% blockIdentity=output.pattern
|
||||||
GreenPulse = 7,
|
GreenPulse = 7,
|
||||||
//% block="Pulsing Red" enumval=8
|
//% block="Pulsing Red" enumval=8
|
||||||
//% blockIdentity=output.getPattern
|
//% blockIdentity=output.pattern
|
||||||
RedPulse = 8,
|
RedPulse = 8,
|
||||||
//% block="Pulsing Orange" enumval=9
|
//% block="Pulsing Orange" enumval=9
|
||||||
//% blockIdentity=output.getPattern
|
//% blockIdentity=output.pattern
|
||||||
OrangePulse = 9,
|
OrangePulse = 9,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,15 +41,13 @@ const enum LightsPattern {
|
|||||||
const enum ButtonEvent {
|
const enum ButtonEvent {
|
||||||
//% block="click"
|
//% block="click"
|
||||||
Click = 1,
|
Click = 1,
|
||||||
//% block="long click"
|
|
||||||
LongClick = 2,
|
|
||||||
//% block="up"
|
//% block="up"
|
||||||
Up = 3,
|
Up = 3,
|
||||||
//% block="down"
|
//% block="down"
|
||||||
Down = 4,
|
Down = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace input {
|
namespace brick {
|
||||||
/**
|
/**
|
||||||
* Generic button class, for device buttons and sensors.
|
* Generic button class, for device buttons and sensors.
|
||||||
*/
|
*/
|
||||||
@ -67,7 +65,7 @@ namespace input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//% hidden
|
//% hidden
|
||||||
update(curr: boolean) {
|
_update(curr: boolean) {
|
||||||
if (this._isPressed == curr) return
|
if (this._isPressed == curr) return
|
||||||
this._isPressed = curr
|
this._isPressed = curr
|
||||||
if (curr) {
|
if (curr) {
|
||||||
@ -76,7 +74,8 @@ namespace input {
|
|||||||
} else {
|
} else {
|
||||||
control.raiseEvent(this._id, ButtonEvent.Up)
|
control.raiseEvent(this._id, ButtonEvent.Up)
|
||||||
let delta = control.millis() - this.downTime
|
let delta = control.millis() - this.downTime
|
||||||
control.raiseEvent(this._id, delta > 500 ? ButtonEvent.LongClick : ButtonEvent.Click)
|
control.raiseEvent(this._id, ButtonEvent.Click)
|
||||||
|
//control.raiseEvent(this._id, delta > 500 ? ButtonEvent.LongClick : ButtonEvent.Click)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,16 +83,13 @@ namespace input {
|
|||||||
* Check if button is currently pressed or not.
|
* Check if button is currently pressed or not.
|
||||||
* @param button the button to query the request
|
* @param button the button to query the request
|
||||||
*/
|
*/
|
||||||
//% help=input/button/is-pressed weight=79
|
//% help=input/button/is-pressed
|
||||||
//% block="%button|is pressed"
|
//% block="%button|is pressed"
|
||||||
//% blockId=buttonIsPressed
|
//% blockId=buttonIsPressed
|
||||||
//% blockGap=8
|
//% parts="brick"
|
||||||
//% parts="buttonpair"
|
//% blockNamespace=brick
|
||||||
//% blockNamespace=input
|
//% weight=81 blockGap=8
|
||||||
//% group="Brick buttons"
|
//% group="Buttons"
|
||||||
//% button.fieldEditor="gridpicker"
|
|
||||||
//% button.fieldOptions.width=220
|
|
||||||
//% button.fieldOptions.columns=3
|
|
||||||
isPressed() {
|
isPressed() {
|
||||||
return this._isPressed
|
return this._isPressed
|
||||||
}
|
}
|
||||||
@ -102,15 +98,13 @@ namespace input {
|
|||||||
* See if the button was pressed again since the last time you checked.
|
* See if the button was pressed again since the last time you checked.
|
||||||
* @param button the button to query the request
|
* @param button the button to query the request
|
||||||
*/
|
*/
|
||||||
//% help=input/button/was-pressed weight=78
|
//% help=input/button/was-pressed
|
||||||
//% block="%button|was pressed"
|
//% block="%button|was pressed"
|
||||||
//% blockId=buttonWasPressed
|
//% blockId=buttonWasPressed
|
||||||
//% parts="buttonpair" blockGap=8
|
//% parts="brick"
|
||||||
//% blockNamespace=input advanced=true
|
//% blockNamespace=brick
|
||||||
//% group="Brick buttons"
|
//% weight=80 blockGap=8
|
||||||
//% button.fieldEditor="gridpicker"
|
//% group="Buttons"
|
||||||
//% button.fieldOptions.width=220
|
|
||||||
//% button.fieldOptions.columns=3
|
|
||||||
wasPressed() {
|
wasPressed() {
|
||||||
const r = this._wasPressed
|
const r = this._wasPressed
|
||||||
this._wasPressed = false
|
this._wasPressed = false
|
||||||
@ -118,26 +112,24 @@ namespace input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do something when a button or sensor is clicked, double clicked, etc...
|
* Do something when a button or sensor is clicked, up or down.
|
||||||
* @param button the button that needs to be clicked or used
|
* @param button the button that needs to be clicked or used
|
||||||
* @param event the kind of button gesture that needs to be detected
|
* @param event the kind of button gesture that needs to be detected
|
||||||
* @param body code to run when the event is raised
|
* @param body code to run when the event is raised
|
||||||
*/
|
*/
|
||||||
//% help=input/button/on-event weight=99 blockGap=8
|
//% help=input/button/on-event
|
||||||
//% blockId=buttonEvent block="on %button|%event"
|
//% blockId=buttonEvent block="on %button|%event"
|
||||||
//% parts="buttonpair"
|
//% parts="brick"
|
||||||
//% blockNamespace=input
|
//% blockNamespace=brick
|
||||||
//% group="Brick buttons"
|
//% weight=99 blockGap=8
|
||||||
//% button.fieldEditor="gridpicker"
|
//% group="Buttons"
|
||||||
//% button.fieldOptions.width=220
|
|
||||||
//% button.fieldOptions.columns=3
|
|
||||||
onEvent(ev: ButtonEvent, body: () => void) {
|
onEvent(ev: ButtonEvent, body: () => void) {
|
||||||
control.onEvent(this._id, ev, body)
|
control.onEvent(this._id, ev, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace input {
|
namespace brick {
|
||||||
let btnsMM: MMap
|
let btnsMM: MMap
|
||||||
let buttons: DevButton[]
|
let buttons: DevButton[]
|
||||||
|
|
||||||
@ -163,11 +155,11 @@ namespace input {
|
|||||||
btnsMM = control.mmap("/dev/lms_ui", DAL.NUM_BUTTONS, 0)
|
btnsMM = control.mmap("/dev/lms_ui", DAL.NUM_BUTTONS, 0)
|
||||||
if (!btnsMM) control.fail("no buttons?")
|
if (!btnsMM) control.fail("no buttons?")
|
||||||
buttons = []
|
buttons = []
|
||||||
input.internal.unsafePollForChanges(50, readButtons, (prev, curr) => {
|
sensors.internal.unsafePollForChanges(50, readButtons, (prev, curr) => {
|
||||||
if (curr & DAL.BUTTON_ID_ESCAPE)
|
if (curr & DAL.BUTTON_ID_ESCAPE)
|
||||||
control.reset()
|
control.reset()
|
||||||
for (let b of buttons)
|
for (let b of buttons)
|
||||||
b.update(!!(curr & b.mask))
|
b._update(!!(curr & b.mask))
|
||||||
})
|
})
|
||||||
control.dmesg("runtime started, " + control.deviceFirmwareVersion())
|
control.dmesg("runtime started, " + control.deviceFirmwareVersion())
|
||||||
}
|
}
|
||||||
@ -184,35 +176,36 @@ namespace input {
|
|||||||
|
|
||||||
initBtns() // always ON as it handles ESCAPE button
|
initBtns() // always ON as it handles ESCAPE button
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Left button.
|
* Enter button on the EV3 Brick.
|
||||||
|
*/
|
||||||
|
//% whenUsed block="button enter" weight=95 fixedInstance
|
||||||
|
export const buttonEnter: Button = new DevButton(DAL.BUTTON_ID_ENTER)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Left button on the EV3 Brick.
|
||||||
*/
|
*/
|
||||||
//% whenUsed block="button left" weight=95 fixedInstance
|
//% whenUsed block="button left" weight=95 fixedInstance
|
||||||
export const buttonLeft: Button = new DevButton(DAL.BUTTON_ID_LEFT)
|
export const buttonLeft: Button = new DevButton(DAL.BUTTON_ID_LEFT)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Right button.
|
* Right button on the EV3 Brick.
|
||||||
*/
|
*/
|
||||||
//% whenUsed block="button right" weight=94 fixedInstance
|
//% whenUsed block="button right" weight=94 fixedInstance
|
||||||
export const buttonRight: Button = new DevButton(DAL.BUTTON_ID_RIGHT)
|
export const buttonRight: Button = new DevButton(DAL.BUTTON_ID_RIGHT)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Up button.
|
* Up button on the EV3 Brick.
|
||||||
*/
|
*/
|
||||||
//% whenUsed block="button up" weight=95 fixedInstance
|
//% whenUsed block="button up" weight=95 fixedInstance
|
||||||
export const buttonUp: Button = new DevButton(DAL.BUTTON_ID_UP)
|
export const buttonUp: Button = new DevButton(DAL.BUTTON_ID_UP)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Down button.
|
* Down button on the EV3 Brick.
|
||||||
*/
|
*/
|
||||||
//% whenUsed block="button down" weight=95 fixedInstance
|
//% whenUsed block="button down" weight=95 fixedInstance
|
||||||
export const buttonDown: Button = new DevButton(DAL.BUTTON_ID_DOWN)
|
export const buttonDown: Button = new DevButton(DAL.BUTTON_ID_DOWN)
|
||||||
|
|
||||||
/**
|
|
||||||
* Enter button.
|
|
||||||
*/
|
|
||||||
//% whenUsed block="button enter" weight=95 fixedInstance
|
|
||||||
export const buttonEnter: Button = new DevButton(DAL.BUTTON_ID_ENTER)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -222,7 +215,7 @@ namespace control {
|
|||||||
*/
|
*/
|
||||||
export function deviceFirmwareVersion(): string {
|
export function deviceFirmwareVersion(): string {
|
||||||
let buf = output.createBuffer(6)
|
let buf = output.createBuffer(6)
|
||||||
input.internal.getBtnsMM().read(buf)
|
brick.internal.getBtnsMM().read(buf)
|
||||||
let r = ""
|
let r = ""
|
||||||
for (let i = 0; i < buf.length; ++i) {
|
for (let i = 0; i < buf.length; ++i) {
|
||||||
let c = buf[i]
|
let c = buf[i]
|
||||||
@ -233,7 +226,7 @@ namespace control {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace output {
|
namespace brick {
|
||||||
let currPattern: LightsPattern
|
let currPattern: LightsPattern
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,14 +234,14 @@ namespace output {
|
|||||||
* @param pattern the lights pattern to use.
|
* @param pattern the lights pattern to use.
|
||||||
*/
|
*/
|
||||||
//% blockId=setLights block="set status light %pattern=led_pattern"
|
//% blockId=setLights block="set status light %pattern=led_pattern"
|
||||||
//% weight=100 group="Lights"
|
//% weight=100 group="Light"
|
||||||
export function setLights(pattern: number): void {
|
export function setStatusLight(pattern: number): void {
|
||||||
if (currPattern === pattern)
|
if (currPattern === pattern)
|
||||||
return
|
return
|
||||||
currPattern = pattern
|
currPattern = pattern
|
||||||
let cmd = output.createBuffer(2)
|
let cmd = output.createBuffer(2)
|
||||||
cmd[0] = pattern + 48
|
cmd[0] = pattern + 48
|
||||||
input.internal.getBtnsMM().write(cmd)
|
brick.internal.getBtnsMM().write(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -257,9 +250,9 @@ namespace output {
|
|||||||
* @param pattern the lights pattern to use. eg: LightsPattern.Green
|
* @param pattern the lights pattern to use. eg: LightsPattern.Green
|
||||||
*/
|
*/
|
||||||
//% blockId=led_pattern block="%pattern"
|
//% blockId=led_pattern block="%pattern"
|
||||||
//% shim=TD_ID colorSecondary="#6e9a36"
|
//% shim=TD_ID colorSecondary="#6e9a36" group="Light"
|
||||||
//% blockHidden=true useEnumVal=1 pattern.fieldOptions.decompileLiterals=1
|
//% blockHidden=true useEnumVal=1 pattern.fieldOptions.decompileLiterals=1
|
||||||
export function getPattern(pattern: LightsPattern): number {
|
export function pattern(pattern: LightsPattern): number {
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,21 +9,30 @@ const enum ColorSensorMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const enum ColorSensorColor {
|
const enum ColorSensorColor {
|
||||||
|
//% block="none"
|
||||||
None,
|
None,
|
||||||
|
//% block="black"
|
||||||
Black,
|
Black,
|
||||||
|
//% block="blue"
|
||||||
Blue,
|
Blue,
|
||||||
|
//% block="green"
|
||||||
Green,
|
Green,
|
||||||
|
//% block="yellow"
|
||||||
Yellow,
|
Yellow,
|
||||||
|
//% block="red"
|
||||||
Red,
|
Red,
|
||||||
|
//% block="white"
|
||||||
White,
|
White,
|
||||||
|
//% block="brown"
|
||||||
Brown,
|
Brown,
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace input {
|
namespace sensors {
|
||||||
|
|
||||||
|
//% fixedInstances
|
||||||
export class ColorSensor extends internal.UartSensor {
|
export class ColorSensor extends internal.UartSensor {
|
||||||
constructor() {
|
constructor(port: number) {
|
||||||
super()
|
super(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
_deviceType() {
|
_deviceType() {
|
||||||
@ -34,22 +43,93 @@ namespace input {
|
|||||||
this._setMode(m)
|
this._setMode(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
getAmbientLight() {
|
_query() {
|
||||||
|
if (this.mode == ColorSensorMode.Color)
|
||||||
|
return this.getNumber(NumberFormat.UInt8LE, 0)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_update(prev: number, curr: number) {
|
||||||
|
control.raiseEvent(this._id, curr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers code to run when the given color is detected
|
||||||
|
* @param color the color to dtect
|
||||||
|
* @param handler the code to run when detected
|
||||||
|
*/
|
||||||
|
//% help=input/color/on-color-detected
|
||||||
|
//% block="on %sensor|detected %color"
|
||||||
|
//% blockId=colorOnColorDetected
|
||||||
|
//% parts="colorsensor"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=100 blockGap=8
|
||||||
|
//% group="Color Sensor"
|
||||||
|
onColorDetected(color: ColorSensorColor, handler: () => void) {
|
||||||
|
control.onEvent(this._id, <number>color, handler);
|
||||||
|
this.setMode(ColorSensorMode.Color)
|
||||||
|
if (this.color() == color)
|
||||||
|
control.runInBackground(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current ambient light value from the color sensor.
|
||||||
|
* @param color the color sensor to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/color/ambient-light
|
||||||
|
//% block="%color| ambient light"
|
||||||
|
//% blockId=colorGetAmbient
|
||||||
|
//% parts="colorsensor"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=65 blockGap=8
|
||||||
|
//% group="Color Sensor"
|
||||||
|
ambientLight() {
|
||||||
this.setMode(ColorSensorMode.Ambient)
|
this.setMode(ColorSensorMode.Ambient)
|
||||||
return this.getNumber(NumberFormat.UInt8LE, 0)
|
return this.getNumber(NumberFormat.UInt8LE, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
getReflectedLight() {
|
/**
|
||||||
|
* Get current reflected light value from the color sensor.
|
||||||
|
* @param color the color sensor to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/color/refelected-light
|
||||||
|
//% block="%color| reflected light"
|
||||||
|
//% blockId=colorGetReflected
|
||||||
|
//% parts="colorsensor"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=64 blockGap=8
|
||||||
|
//% group="Color Sensor"
|
||||||
|
reflectedLight(): number {
|
||||||
this.setMode(ColorSensorMode.Reflect)
|
this.setMode(ColorSensorMode.Reflect)
|
||||||
return this.getNumber(NumberFormat.UInt8LE, 0)
|
return this.getNumber(NumberFormat.UInt8LE, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
getColor(): ColorSensorColor {
|
/**
|
||||||
|
* Get the current color from the color sensor.
|
||||||
|
* @param color the color sensor to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/color/color
|
||||||
|
//% block="%color| color"
|
||||||
|
//% blockId=colorGetColor
|
||||||
|
//% parts="colorsensor"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=66 blockGap=8
|
||||||
|
//% group="Color Sensor"
|
||||||
|
color(): ColorSensorColor {
|
||||||
this.setMode(ColorSensorMode.Color)
|
this.setMode(ColorSensorMode.Color)
|
||||||
return this.getNumber(NumberFormat.UInt8LE, 0)
|
return this.getNumber(NumberFormat.UInt8LE, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//% whenUsed
|
//% whenUsed block="color sensor 3" weight=95 fixedInstance
|
||||||
export const color: ColorSensor = new ColorSensor()
|
export const color3: ColorSensor = new ColorSensor(3)
|
||||||
|
|
||||||
|
//% whenUsed block="color sensor 1" weight=95 fixedInstance
|
||||||
|
export const color1: ColorSensor = new ColorSensor(1)
|
||||||
|
|
||||||
|
//% whenUsed block="color sensor 2" weight=95 fixedInstance
|
||||||
|
export const color2: ColorSensor = new ColorSensor(2)
|
||||||
|
|
||||||
|
//% whenUsed block="color sensor 4" weight=95 fixedInstance
|
||||||
|
export const color4: ColorSensor = new ColorSensor(4)
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,11 @@ const enum GyroSensorMode {
|
|||||||
Rate = 1,
|
Rate = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace input {
|
namespace sensors {
|
||||||
|
//% fixedInstances
|
||||||
export class GyroSensor extends internal.UartSensor {
|
export class GyroSensor extends internal.UartSensor {
|
||||||
constructor() {
|
constructor(port: number) {
|
||||||
super()
|
super(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
_deviceType() {
|
_deviceType() {
|
||||||
@ -18,17 +19,48 @@ namespace input {
|
|||||||
this._setMode(m)
|
this._setMode(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
getAngle() {
|
/**
|
||||||
|
* Get the current angle from the gyroscope.
|
||||||
|
* @param sensor the gyroscope to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/gyro/angle
|
||||||
|
//% block="%sensor|angle"
|
||||||
|
//% blockId=gyroGetAngle
|
||||||
|
//% parts="gyroscope"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=65 blockGap=8
|
||||||
|
//% group="Gyro Sensor"
|
||||||
|
angle() {
|
||||||
this.setMode(GyroSensorMode.Angle)
|
this.setMode(GyroSensorMode.Angle)
|
||||||
return this.getNumber(NumberFormat.Int16LE, 0)
|
return this.getNumber(NumberFormat.Int16LE, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
getRate() {
|
/**
|
||||||
|
* Get the current rotation rate from the gyroscope.
|
||||||
|
* @param sensor the gyroscope to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/gyro/rate
|
||||||
|
//% block="%sensor|rotation rate"
|
||||||
|
//% blockId=gyroGetRate
|
||||||
|
//% parts="gyroscope"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=65 blockGap=8
|
||||||
|
//% group="Gyro Sensor"
|
||||||
|
rate() {
|
||||||
this.setMode(GyroSensorMode.Rate)
|
this.setMode(GyroSensorMode.Rate)
|
||||||
return this.getNumber(NumberFormat.Int16LE, 0)
|
return this.getNumber(NumberFormat.Int16LE, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//% whenUsed
|
//% fixedInstance whenUsed block="gyro sensor 2"
|
||||||
export const gyro: GyroSensor = new GyroSensor()
|
export const gyro2: GyroSensor = new GyroSensor(2)
|
||||||
|
|
||||||
|
//% fixedInstance whenUsed block="gyro sensor 1"
|
||||||
|
export const gyro1: GyroSensor = new GyroSensor(1)
|
||||||
|
|
||||||
|
//% fixedInstance whenUsed block="gyro sensor 3"
|
||||||
|
export const gyro3: GyroSensor = new GyroSensor(3)
|
||||||
|
|
||||||
|
//% fixedInstance whenUsed block="gyro sensor 4"
|
||||||
|
export const gyro4: GyroSensor = new GyroSensor(4)
|
||||||
}
|
}
|
||||||
|
136
libs/core/images.jres
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
{
|
||||||
|
"*": {
|
||||||
|
"namespace": "images",
|
||||||
|
"dataEncoding": "base64",
|
||||||
|
"mimeType": "image/png"
|
||||||
|
},
|
||||||
|
"expressionsBigSmile": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAt0lEQVR42u3UsQ0DIQwFUEcUlIzAKIwG2YxRGIGSApmcuVy4AwqkSxEppvjFEwIJG0OZLg/s7Ox/50G0PLuDlidHAHPk2TOAPnLJZSAXsXPUSOebbLhe7L/oUJfpHXfXvefd1X1X7+julZRJ3/VSJ0A0y/6sc8Muu/9E57TVjR7oysfoNDFQjJ7ofeToZEmNjtvZQU/qDrY4M3EnE9iJ+61cs/6JAGLmuVX32m+tq64eBf879i/5C1nd6IT3Lpx4AAAAAElFTkSuQmCC",
|
||||||
|
"expressionsHeartLarge": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAB/UlEQVR42u3WPW7cMBAF4BEEmI1hXSCwrpAyRWDlSCndicEWuZZ8Ex6BpQqBDsUfzXtcOQsHcapso+UHLeftaJekvJ6+Fvm77sd8DT27LWMvM/oqZWzFoC8iaaIgIuhxOOzXLb6Z1OPwvs/Tlxuyx+lHW+b7bNRdvMtPeb75h3q8TbYxl52duo3jJ5Pmk9HPh8s+vuSyZp2q7x8X85LqSLdPmH1PLb3LdeRpqL5PKxLn3evIo6nukz9PqY4MPfvXMdURc6nu0vhT8f6leionD0OqL51jNybXl8Nt/nz17zP7JdeX5+p52FX/MpHHgvl6XzwU/5bry8PILsXvim/CLzOce/9O74qvrf88d3mvm//+Ed4+Rxn+jYfWxz/z19anG24bn2/40jivD/o7v+W++R/d8vXqcf3et6s2w/pJbYP1ltoD6zO1AdZzaoOu//x1db/gmLq/cMzioY0J+xTFrG6bmLDfUczqrolZ3XP3cT+l+NVDE/PYl5uYul9zzMMXjnm445h6HuCYh68c8/CNuqweOL6eQzi++kIx1R3FVPcUU32lmOobxVQPFBPOSxQT3EKX0ReMD+4xJviKMcE3jAkeMCae6zAmuoWY6AvERHfaZXIP8dE3iIkeICadPyEmudWY5G+9d925+/7cV3Pu23DuYXzj/Dx93Dn8yn8BfHcW3cEFXEQAAAAASUVORK5CYII=",
|
||||||
|
"expressionsHeartSmall": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABXUlEQVR42u3WMW7DIBgF4Gd5YKvVC5QzRN1r5Sy9RiRbykF6FTJ17BU4AiMDsgtWZN6PbSVRutUMlvMR4QcxP8G42gx23/0B99ONaUrv0cYrUDoQvxow9ZIH1OdxdFMvu4e6jKMFlHQH5dInHKUbVL6LD4eTHuXUxofDL/xND9FPnfAoL02MiY+2dKV8vGrhaYT6HOOj0exphGry+qt0XGzq/WFPI+OYHN8Lh1m4w9w+H/R3dpv9sOGv97jJjv/gtA7VM06/S/2M++zqHg/Zmw3X5Xt+bYt9ccO7ct/xctL+Fcszu5XTpTogpkV1Q0yL6oyIOfsgY3K94pjZjYiZ3YqY2Z2IyfWTY2YPImb2QcSk+txzTHLDMcktxyR3HJPcc0zykF8SeV5wfPaeYrIbisluKSa7o5jsgWKyDxRTnGsUU3g/r7L0rXtbr7tX6x6adR/0xvnb7v83dv8j/wXoD/U2XjE34wAAAABJRU5ErkJggg==",
|
||||||
|
"expressionsMouth1open": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACQklEQVR42u3WvW7TUBQH8L+xFC+AH4DBPAIL6sBgNp6g0IlkR2LuUtmoQ0bew+nQsRuOPPAEqKurbGHgRlXEFXHv4Vxfxx/XNgIJhCrlSlHkn/7+kO1zfECDK8V98c0Qr1IIp8+5yw4EXVWAcSBqcYzG632U2Wq8u9i/yz57O3ZzxPaKyPhctTWQtRMVtRJ1POQfm/oDV6HlH7te/I5Hxum/++2Q35buWx7+Td8M+Uq78st7a9wvn0Pphe1++X5S4XVd+pRpl7Z72mUoXhGJyDxf0plz8tiDPOM3e//cXc4sFXsBZ8n1sXeuhjyHz/cT7ozoQWC8eBrRUiBMsYsnl4GY7P2Zp9wrvEuxpbsTwDfXR/KU3+YpXaf4kSQXwAvPuHgPPE6SBeePj9/E7vYDn++cz3+d44ylzCekkjRQiMPCST7Rep/XawY4KReQ2aryvK74fKI8tl5NfhrjbArc2fmEaL0gSux8d7XynXXIH/L/KL+j3h4L+qrr8ZGdfo2jFAoTO3+BJ7quXTt/outahks7fxnp/hDlX6z8jOa6z4hv7UtakLqp+s8p/Cb8Fm6mfUNyBrfJc2FmVX/j0iWqypK4WL3KHUpzZ12mBfJIVK48HXpONKVtzLuaflj2Vf52BuC+E3An0N+e2j35EMgxcYuOc+vlb+NL/XmcNx7o9hyHPAHEIX3W2y1Pdd/No57f6P4tqOcr/S8PPuL7mSZqe3eeqd0ei0oXQ/NSNj5f/XIeG57fhue9kflwbJ6873PyiP8E/XjE+2xX1DsAAAAASUVORK5CYII=",
|
||||||
|
"expressionsMouth1shut": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACG0lEQVR42u3WMW7bMBQG4F8lYC5tdAT1CN2DQt1yAiEZgqK9gbcGHUoVGTz2CpkzxBmzWYaGnsBzWXj0IiNIQ8QKXx5FW7ZsuWiBBl1EQDb1gRJp8vHRoNaSofPOn8PnbTzNUAS7rAU7EDXVAt4BtcEp1l4/Y/3d2puF/d7sslyw+zduFkXeB3ZTI1M7UVkrUcNjvtjsX7iNt/xb08s/ceWd/rvftvlt5eGWx//S520+dW7Dam69h9U6VF5ue1jFJ5Wy6Sak3LnZduncxMURUaH8+pJrc06SPdI5R/Zq3QW3GVv2EsGY98fKeTdojZD3EYQmehF5L18rGl/jMMNdKvtR0Vv5G2nFCT5leCB7A4R+fGTOgN6QZtyeHk+Bt9J78R54dUz3GRY4GKZi9pX7O+f+Zxqfr9z7f0EmREkWWaRxGUwUTU4RuXGKSy4aCDL4+o0bp4nHkyRJuD/Z5/65mvSV+71KuzbDFF9GwIOrX9DAzVvxgx4T7mNSXcdEH5fzeYaDy7pcQeTO52Q+QCR1OQHy5XrxUEbkW4/omgew9IAyHdxVrQtoVSzdSuJkcEh2SIsUwXJ9qzjhXBBBGP6AcHupdmleAho9UTacQ4n3+ju33Qdrj1y4pTFntDSm7+5+wzMXR1rt+E8XjwXt+NR9m873+CpHq01v5ufat9N85UVb/s/3nxe/PV/az6P282vPebfvfOz+P3T+DP4EGY4rBjL+iVYAAAAASUVORK5CYII=",
|
||||||
|
"expressionsMouth2open": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAEj0lEQVR42uXWz2sjZRgH8CnBBkrT8VKQsOkchXpRJAhVnEgPuQhboW+SQmFXCgrxklIiKOjMbm0ClTZ/gJQs7sHum6HbImJlYUkI20Fx24MMKHhIGtxCBJlsWZyu4/v4vPMjZKaJeFPwufU7n74zTOZ9n0eAoVUX/q18bFjcEoRhF3iMJQVTJvRLGYhVYbCksLWEIRWtC7khF6IbKWFXCa3I71ZShRzec2YwfRZv9YzjQez1/0eRWphPqcKyAixqgqWAKcMFgNTEPJYSNBlYxISnCjyWwAQmrUvAJlRhUQZ7zFTOZTjDXGFSFPOE4+2Uqfwug16Fx4o9I0pgx1Uhg/n1gvJLFbYq8EixryQwn04JVAKr8KL8WxU2T+BYtp57VQR7Dj3mPwrywwokrkJFttRiBaxkSqhJcKEJ0ncnENfhM9lUuxUw86pAROhlJqWdNEuuQFLqdQzMCXrMazNivMNmb7MXqp37FPOu68nXYnLNLq6xYlUnpAJtA30FevTX8dkDO39gd6slir5B0ZehQ0ipuGQZb1lGdYv7Jq5PdTiltJ7XLPrNBa1McH+T+zT3143MqpE7J8cJ7st9f4vSl6h2So8/5H6c+xU4I2TVyEbIok7eNLiPcX8bjii9R2slqk3Rbyl6Fud+DTYIuUbIA5JJkHmCnk1zfwD4zFiM0jl6zL09x/0S905ln5CPuLeT3O96vl8VK899ru+9qpiEew22Q97scr+obIV823C8MhHyDcp9Ro6F/LqzPpXjIX/T8VlpOuSjjq9JQV8TpxxPxJCfirm+EvKTcdefhPxk0n0ePeSFvOvfSQZ4FiPHfz4bfH6h6/pC0GfGDNfvh3yEun4h5CPe+ochP+755ZCPeV4L+TnPZ0K+6Pla0Ne6nidPyKXiflg5nvwv/NMRfmeE3x7h10f4G32fNQa9avh+Lx949LzvlwPrq/7vSw8DvuR/D6QQ8KWE71sBX972fT3gyxv+9/NJwOtHvp8sDvp00/dXAh+c3va/z9cCH2i65+0X7fWgN739lfs44Octb/19FtjATdvbvwUW2PDzzNvvbd/fp3e5Z3Oun3d91lghiwb/M+H6JvddquXbVMPjvAkTri/j+U2+IrliylhEOg9b7vlThk1ai+O2/z6vbXNf8s4rBLhpV5LHSewA6JuuF9HvjdOzeGVOw3ejQ9M9PyX0uOdPdqo7OXw3aWg4fk/ChnH4fq36aXXzjtVF33Y8NuwH1xbWiPRQeriE7yYNLfd8luHo3v4fNVmXH92x3+vq4J7nyzI0V1sXGflM7iyxV5JpsJz1d2U4bTX+1JRT5fwAZrALe/1CgU59zP4Zm/7FDyDE0Bcdj7kaYbs4JFhfgjqugz3r+fNIhP2EI4fdgRvraWBOPzrESSMaZR+ABexdaDR0cPvdAoApRmENc3geGq0TcPvjPuZSFF7meRTa5lXAHwB9AXNZhC94LkLbEj1/C3NFhDLPq9CzMS9x/zbmIPlz0SrDfIP7NuaDc5Tv37g8d/H+frd+eU6LMHXkXDdqDvybuXH4nDn2j8dV58J/bN7+C/XpdkrjO2oQAAAAAElFTkSuQmCC",
|
||||||
|
"expressionsMouth2shut": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAD20lEQVR42u2V72sjRRjHpwS7UJqsbwoSTAZ8I8Q3lSMo9cXkKDRvhDshk6ZQuJOAQnzTEiJ4qLt37aVQaecPkLIH98LeZPFaRDw5OBLqJSg2fSELCr5IGmwhgmxaxM257uPMbu9IeznxrdLvy898dn7PswiGporO+Tn/L/KRYbiF0LAGiUXwaeqhJ9EGsI4Gg8+6DhoSpYpyQxqUmym0qZ3pUY5W1lFOjBkfpM+LoZ7zfVB7T77RcEvwiI7mNfAUGxwNbAJ9ALwjeDiFTAJeyIZHGhxhsMHDSxi8MR1lCLgjtnZM4FBwzcOK4DHfd1O29geBhgFHmhtXMbhRHWUFv7qg/WLAGoMDzX0xJvhECnEMzsIk+c2A1T1oEueFN1Rwp4Qv+I+I7DKIXQJGHL3EwEmmUAVD30T4uz2INuBTYuuPGNgFHVEVetlxvJH2knlI4l5nQ3AqfMErcTXa8RK3vVeMzoN1wbuBT79Sk0W3VPRKRoMuMWhbwmfQ47+OJrbdwrbbNcr8OoMaF/4KdCxaLs051luOZaxRncGO6J83oFPg1YLp8K/7nI1xwW9IPw37lF61sotW7pg2Y9JfCfz9Ar/F+avc3OfNa7zMYFT6eTigdNGaDdFMg74phmIQlv5t+KbA7/NKmZsR/i3nK8yLSr8Iy5ReofQhzcboNKWCT0h/G5YLXMTjfIo3OW8wd0r6c7Bcon5mf6cfUZpmblL6m7Cc4ANpMKcg/RwsJ+lA0sym0jdh/Yxvd6Wf0dZP+dOsbfm+FokO+jusxqWfJZGJ0/6S3z8n8RP/Ab/r+zd8fxbHfX/WytOMJX3F9ytY+l1uFtrcvCZ8NeL7VMVhSr+kuVLKysSEHwkHPsOrvBLlW4nvC+a68Mfjgb+H12g2TPPJZlKcAJ0evxD4aeF/PsoPo2zKbIv1onwwn3fIGp1P0r0NYyPXs2gaXQzm/xKp83vvV4xPjNU7TpfXUTtY7yR5eOVykeJdvDvnUDoz0gr2Z5LU72/9WSENcnDHfa9bD53s5yTZWWz1s+SQdOa815IzISfY/9fJfqv2l6nta8fbEI/WleC85j8kneqI+7N4dP0fAIVnVKfkn6/gesjbFI/U+QL00TpxE/75fkyOQyHvJ/Hk3Q5cX5rRPP8+3NPIkaJ4H4AD3rtQqzUguG+XAWxVgaLg8DLUWnsQ3M8twbECFyRXoG1fAhiT/oLgRIXPJFeh7agn/i3BNRVWJDeg5wpelv7bggN+XJcWPcFvSr8t+GAde+xffLruyfd1t/p0nQx5+jPr6rPq8D/U7eF1fuRf/y78hvP/7Dn/H/G/AcCdgHhcio2gAAAAAElFTkSuQmCC",
|
||||||
|
"expressionsSad": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAkElEQVR42u3TzQ2AIAwG0I9w8MgIjuJosoErMQojeORg1BSD4c8oJzUphwYeCTSlYKsOA3Z29tfc9RQXlbvtKM4ydy2S5a3DTzTGZ77CCzB8w1vzb67PVZ2v3oX7lj1zI6q+ns2cugFEzQGEbo7dkavSLZSDLF1L2iudznDnBZGPIaR+/JfSJ58Uvzs7+498B3cE819ZIE/TAAAAAElFTkSuQmCC",
|
||||||
|
"expressionsSick": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAApklEQVR42u3TsRHDIAwF0O+joPQIbBKNZkZjFI9AScFBkJucET7bcZEUouDgFSCEhDocAerq6j/z5HjOc++r5Tma3v202546toXHcs0LNgHoP/xu/Lfzc5Tno3/RulV/5p6GXjAPPaHVZiHhkTsgO+GBeyhJ99QuiNJRg6mrOL+YdkP1S+/Z1owXRJzcKcAk3fHGCucXxc+Td3nLoHE+ofWjrv6NvwHAjOwKGeyV+gAAAABJRU5ErkJggg==",
|
||||||
|
"expressionsSmile": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAkklEQVR42u3TvQ2AIBAF4EcsLBnBURwNNnAlRmEESwqD8pOYAGeUSk2O4gJfQeB4YCeHATs7+2vuplg3WbsdY12H2rUolreONNFQz9wjCTB/w3vP392fqz5fvQvnlr3wJaWl9Zw31XjOOeEhao5wHaJp0e5vIR2G1h3CkMT5oyvCDSCo+/rzM1Z9MILfnZ39V34A6BLzXyhnhjoAAAAASUVORK5CYII=",
|
||||||
|
"expressionsSwearing": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAEtklEQVR42r3WQW/cRBQA4HGN5B4Q7rEHlLlx5hikKMM/oagSV1S4pNKS2apIERKq/0BgfwiHTBQUH4Buf0DReuWqPq6ND3G0s/N4783MepvsFVZq4/3stWfee34zAvZ+jPjP3dHf5Y7deP+Tjl/r0fs1e0XHf+/40LO3RG933DbsRuHx/GbLZ7dz9ssL/NJsvc36gr2jb10TXajG3+eWvrl8eznMvW8+mFClwaz2zDfD8cyjj4Ok+20WwYdkHDteso5eTca5UhxiPKdCRe9gqpvoQsyi162Qq+BOiDyEFxpzjMeO3QqR0W0l/lemcIbCPnhv6SGvJJQokrwVIiWnWZwrqGFl1Y4L8sdPdA9N9IQmZsgPK4W+4ftXIr+k2eH93RdO9ninXW9xPO45zOrR5TL6CcxKKproTnFY3ImWJVUI+wN56XyE3ORAFlAl3vPiytHMQLpToaTDabLLs5eOZmBzp6YO55p7Vya1HIkcFFhVBW/V23Q4o5nlIDEzg5DBf03bOUUihxwH6YIPcpZznaOXcIX5C57L/Cr4JQ3soQx5+TQ7a8klxhBnfZDHPGZ55d1itJ3Koh9LrD9ynjQ+xddJcq6msHXM/rbeqOTcWEbeN3+B2OewwhTvc6y4R/u8FXkXvLBp9DUIPutw7DYx/CuOW8Wlw461x6P19ZNB9Er4Iua4hdclehadg+DdCF+U7PoyXA/sYusvtz7NKz7h3T/XYm8Rn9MPlJ9XaALoRnwpeKTsJngSPQ/xl3c8C+7jhZHxfpgG16PT55Nkv6fBB2oW3GO8J3e85PCMTp9b362EePChd767IU7vuuWWIJ7gudErbnnYQsQjCpwRjpPI75SiwhAP8Z82wnLeHMVE8gPIsT+0igsDY1hJHtFHFFAjOr8mYIzYrfiYAmpEDS0XkvKNCXhmmRFNNYCP0cA+jW4mmts49lRKEIcoNaL88TQ4XpvyiESCfn1wrGHtjumh0vgpixdGvP6OHABbq1P46Db49VPvC2raVkJNPkVPj9h900Y33uv8CLPSBJ9BWXlfuiNweuVzU8yguMZhCoyPPQRQC5+DfAayMGKCPkye488v6Ba9UxKUpBFhnB9zahf4yN5p6Y4VJZPyQjyR6J07VXaiekMOLwD+oOaOjjgMun5H8aGr33AQ4NZa3bWQXVM8fdoxdAUM9gT6pcvmoy/I7eQZ1Ffu2/k0D75YsZ+mUBZOF34dwc9vN+TUwMoSBxv7hltj4OZAUC+dVt57rMBN9G5wJyq+p+A3ERoX8KH95yS+17jWOY3NTtPStWy/D471ejPI4F3dPo19SbFjFjA7fdk1O/sW77SgFru+qvwihe+Y7Oqd66fYOtzXeFCorh3dUTNy9MCDrW/Yqa/iufdHuk28W8dt0Ht1yOse+wl5or0/2/bbzWdcxBfYHy7gfTf6K37P0b9R0NSjO/avwL7DIDXR3Q/R6d0er3eJ3fXx/sq79E7rIWxW6HOrg/+O8+LWaKkeGlyTvf/MueCGit5jJXg/j/tBl7Gvgv+y3R2lHLeb4OMO7CfyKqM4qF2fkTf8Mt/z2xW7vrsvXRe8Nbq3X13TfkUk93zgdpLt3/caud83//9+e7//C0qd0Owbh6PaAAAAAElFTkSuQmCC",
|
||||||
|
"expressionsTalking": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACDklEQVR42r3Wzc3kIAwGYEesxJESKIXSyGgby3ZCCRw5IGb5S7DBiRTp05fjowkwxPgFvuxzwK855EetfhQHO3uoDNvs0B9F3Z++Ud8BTJtEY499gAQgsLvz/dCX1L2N0kAPj/3t+oIc7vtsbQXD8S75vrA6vBye6sqqx2vWOpA43ZPNdnC6E9hj+QfVd0W+SRm1Ohk+m2oep2/pZfMgqMetuZPUEzQ/9FQMeUHFdzMXiam+lJDTxdM2u1fFo5w9S/agZk+iuNdLdW7FnVl8r27Xai5+rFXubPadcZP9s7ovLlYP+t6TXD2qe49q9STvPWjm+IoHN4z/fe+e88+DW8b//aC/XM9P7cPb/X/7fd/Wz219bkw964c6vzsvB3O+7MN5vDu/d+c9rP2helz+WJBPfeauL619bG/97ZgX1PuhnyZOvX+GaYLSZ1sfphM4efZtOsHe+/b3ELQNGzYvwpULdAK3jTzCE9Rw6vkl8DAa5Z1FcWRRPo6BWrJeecrm5pmuI3G7+/OGcAZ09zO/c55LkvsHQL8VGOKxDBTWe0KOaYN+PjxB+dhXQI8Ez+W4A6z3mai/41aAPGhcYNhxQaLxDS5s7PggDHcWHxzsu+X8IHWNPG6sf0j8I/eSdUHiHzkpurHPf4C9NybYWEdXr+neqFj39DBd7oC/r9KzhOpE8T7fpeBqRaxH+8v38P/FUimZWI/+PwAAAABJRU5ErkJggg==",
|
||||||
|
"expressionsWink": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAqElEQVR42u3TsQ3DIBAF0B9RUGYERvFokMplVmIDr8AILlNYJEARKbl/kd0kUnRXwSsQ+nxwp5Nhbm7+Y9/O3FfH/bnd6QnxzYvjnsYCmF69AoH5dtR9oV5DnZir9//sR/PRctbexXr7HV8UnxW/KJ4VL1HpwyT8OjwIH80p0kczIfvTaSWe2kHp5IUXtPGz8Fv3sAiv3VmeGXDM21eNNP/krIfm5n/hD5hN/hkkU+lAAAAAAElFTkSuQmCC",
|
||||||
|
"expressionsZzz": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACK0lEQVR42sXWPXLVMBAH8NV7zLhhxsNQpHRBQZuOUiXnYCg4Q5pIVJQ5Ategi6m4hjtad7gQUvQt7Xo1QxryquT3/Oz/rtaWwbGfFV7Q54HDPHCYOLfgPyOHkYvnufg6OP7nwD8P/N0/u/kvfj33eR14/IJzt9Glyb4DWct2/MK5v4DkXAMoztn1jR0VnBsaP/txmqzkp/jZT/FbfyTneuBAy0pu+XkO8fuydpn9QGUZKL6jsmLm6FtflhZ2aetey9rgXhTv44dbYan3Y4vfOYofRmmp8yaQz8lR/M5RfN3Og1al87U/f3BZvQWiXirQ6c/msrRnRX5bXGlwnb/O/uh/0ftF2pTQAXGTKrWx+Ooi+HXqXVeHX6GLE3IV/P3JL8WP2CzOl96h+D50iZ4PKuffmscvOA8TpyzcppVS3b5gwn/xkhrQfmFcvqT3v2Qf2cKPQFhBPA2l0HfEdR7KV8TDigS/YI+dN2loeo8dZn2OQ0z9GPqSXSGPHXuGx06ynp4JIW7vcTDzI7D3uF4GwJ39Y3qaIA8d++LMTPf30JkPdN9/SH5DXUvefQoD3X3ffBq4GPingb8ZueVcw1v2fcYO3K1X3vn90Z+H3R99E9j90S8ruz/6W4XdH90m+PexFvMR11tiHhL3p8Rc0fG2xgR0vKkxsR81JvWS84fCXi58KPxeIR5y4cSh+B/i37N/I54L2CYyD8Wv/LpsZP734orcF6lf7vcLv28/AaqW51qc+IlYAAAAAElFTkSuQmCC",
|
||||||
|
"eyesAngry": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACX0lEQVR42u3Wv27UMBwH8K8bRAakhhEJUF6BCRhQI96kb8BtIDHETLxORwakXsXAyAtUOksMbJCJC7qrw++fnYNgBpYy9Ja6n7P9dezWv2D642eNG79u34M+Tn8P3G7UpY1OHNaH3cM62VCgZ4/artgHbbfs1sfl6Xks+UjDuKcsQ0b5mn2QdpRvfSuzVexBEifPC5Ivp71jX+sKA3WN9hRg99JnGmjYvlL3Pbu2R/badoHdxrKNje1GR25jI3ur7YE8JqefQ/KW3ObkcUNnWX/xvWVNbyivtzU0/+C2humC3Jrxxq/f64K7gqMveFvweuGjuFt4YL9rAQfu2R9YwOwR8lwWMPuovna/eVAfNGB2fyRuT5A94lj3TQOyjzhR14DsAbbPGpDdV+YakJxWn85FApKP6JJLQPKAfI4SkNxX2SXAnKafz50DzGn6uT8HmNP02XeBAsxp+uxbTwHqEXdm/woKUB/xcPZLvlbVA45nf8d3srrH2z77GZ6iFY9wH2Zf4Rlq8RHu83n2U7yCEw+oLg799BGes3tULntcw9/jP7tAteAWNgeO++Ij3/6/Om9RCEsf2P3Rwvn/JuD2wifxx0vn9eP7odP68US82tEO2vobc75XG/V0366Al+Ldjh549jPghd7DV+S9ON/ztP/f9J6/WqPu8/1/CbdRj3Je4hWfb3VudWTF58t1hPttfdNb3XmP11p3+Dl34STVqY3bWp2iuraffqS69mX6ZHWN62D7MdXBMXZWB7luhi7XTZfqZqnOlupyqY6X6n7pPaH0XlF8Dym9t9y81/2H/hNF0RpVT4pRTAAAAABJRU5ErkJggg==",
|
||||||
|
"eyesAwake": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACCUlEQVR42u3WvU7cQBAH8L9lCVNA7gGOxC+Qgo7iohyPkkdIB50dIdHyCHmFK6nAVClpr4jESbQITMNZwngzMzvjPT4WRIREFLiG4efd9dq741m4B38V3v21vQX9Ev//jOOBd4kxFoe2YS+hjbQrULB3Pk7Zax/n7Nom6YfnvuQNdeOWMg3pVWbstcSdXC1zGS1ln8kdXckTkouuTdgrP8MZNe30KcBeShtXU7c29V4W7D5u2DN9C+zal60Z6NsYk2vfjj33cU3emdPf2jwn1zG5Xz3Wez3ird7L/aD7FTqHwV+4zsEdkWvYvUXHfa/Es7veQFy3eHCoJ7d9Zu73vrkAz0cSITilyVfxhi4Ep9TIdP4VZ4g6Z489l8Tq0saed0J9vdOYadH7d85TcQqyw34cTreEXYIT85tKE9Kn44X5dZmsmH/G8lbvSHcb79kUaxvmc6S/ikrc7ePTyPwK2U7BuUg5OcGXYfAlmrR/rgnWez/DcnKo/g2bq8Gx4Hh1v5KXbvPH8Cmf02KY7wOj8D6xZOs7BTbC+8cH89/A1sJ6fTQ/X1gvWt+R+Xxhfd025ubXVdgP7iC9ML+p87B/jt3Pfh8Wbcivy7bo9+3eaXD59tl+zv+T78YLfYef+/2P1ZFY3YnWqVhdi9XBWN2M1dlYXY7V8Vjdj50TYueK6Dkkdm55P9f9g/4H3XgcZrwl+lgAAAAASUVORK5CYII=",
|
||||||
|
"eyesBlackEye": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAB50lEQVR42u3WMU/CQBQH8Hc2UkKinZhQuzO5MWACfgMH2P0WjK0xcfUrMJpOjmzUya/gQGITV6OdgITSeu/u2rsebWyCRmK4geGXR++4Pu5/kBQOH/a+Sx4AHdamIwPZ8Ig5OLqH3G3dA+6W7r6Bn67JPBJzodjsaQZz/uUeW47DZifMXfHMJImJWC16zOfCJ0cGd9ehLtaMpZEppkdfQtvzvDEubGmJ5faoh2RIxwCnXNri56EHDQ+HSxcUpm5T95tYP+zbWMd9ie62Wf3Y0p2VD6+oB47YVos6sHLvwdSc8PqB5vEBr/fobw3Etsboh7x+qHtJfZTWE80bov5my/qmqL/83/XYE8wBsoZDv38r9NiJCn0d2k6Rr3xzWuQLIK/c7WTtw2mSPDH/APhMfeWS49RnUB9lDsZd6i9w0kl9AcazI3wCZ93U52Depv4IFy3pNUP6eebvUCdT4dfQP5IOisOf+5yeGnL90PrOF4pPALpyP6Em9xM6Re9rBjAqcvm+yt5vtX6Q/ZPvT9lvWj9n/VnWz7/9//qh82er87DCeVt6PivneaXzX80LNUeUfMnljppHak4p+ZXLNTXv1BxU8jGXm0qe5nNWyd9cLit5ncvxCrlfdk8ou1eU3UNK7y37e92O+BcKz5DpA7xJVAAAAABJRU5ErkJggg==",
|
||||||
|
"eyesBottomLeft": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABtUlEQVR42u3WsU7DMBAG4N8KUjfCjiCPAQMir9I3oBtIDDETr8PIgNRUDIy8AFIjMbBBJhrUEOOzz04Gjkos7ZAuvX7xJa5z8hnm10+J0bftLexH+d8Vxal3FyN3Dh5DrsGDOBUoyDsfJ+S1jzNyHqPi7SnXemPTaKSbhsvSE/LaxZ27qjN3t4S8ck80mibkLppWkZd+hpUd2vG/ALl2Y0xt09rEuy7IfdyQT3gVyDmXrEl5NXLrnNuRZz6urXfB7XcdPLPO96S8Oudn/eEtP8vc2OcVPIf0H85zMAvrHHajj777XuW9q4FnT9GbLo/emq/ob+Y5+ro6j75Uq+grnRbBH3Ad/R3JPPgMR9FfoJbsXYX96Pc4+GD/LnFbBL/D6UVwrR6jz3B2yb7W6nUefIqrk+BIFgOfDlwFPy6hD9k/sYdl7+gdW3c7f/Tzh5pv8hnQr497F2E90a8nkqJffwzWf1Jsel/S+xXqIeX6cfuqrx+/33K9Off1xvuwr0/nvj7J7T7v69nv82rT/i/1EanviH1K6mtSH5T6ptRnpb4s9XGp70vnBOlcIZ5DpHPLeK7bQf8By0loyHh24uwAAAAASUVORK5CYII=",
|
||||||
|
"eyesBottomRight": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABt0lEQVR42u3WsU7DMBAG4N8KUjfCjlAeAwZEXqVvQDeQkFoz8TqMDKi0YmDkBRCNxMAGmWhRg8OdfbYz1FRiKUjt0usXX+I6J5/RrvxMsPVNewP6KPe74jh3bmOU1iFj2DVkkKQCI3bj4oy9dnHBLmNUuD3nki8ojUfaadgs3WOvbWzsVV3Yu2XslX1iq3lC9mLbKPaJm2FFQ438C7BrO6atKa3JnOsRu4sX7D1ZBXbJZVvksholueQa9sLFNbnxTt+194Jc7sl5dSnP+sEbeVZ7Sc8byRzyX7jMoZ2SS2i2vvV/4VUZXXW8eAi+MGXwpv0M/to+Bl9WJ8Fnah58rvOR91tcBH9Ddud9gIPgT1AzcVNhN/gN9t7Fvya4Gnq/xtGpd63ugw9wfCa+1Opl7L2P80PvyKYd73dceTcT6H3xD+zgOTqiY+NO80ecP9R4nQ+AuD7IxnE9EdcT2TCuPzrr3xuue1+p97uqHnKpH7+vUv2E/ZbrzTvVW9yHqT69U32yu32e6jns8+rH/T/VR1J9J9mnUn0t1QdTfTPVZ1N9OdXHU30/dU5InSuS55DUuWV7rvuD/g1mT2jIOjnq9AAAAABJRU5ErkJggg==",
|
||||||
|
"eyesCrazy1": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABwUlEQVR42u3WsU7DMBAG4D+K1HShfYACeQW2DkUqj8JblM1BSKw8Aq/QsRMNEyMrAxKRWBENS1OpqYPPvqIEnZGCkOjQLHW/+GLXvuaMSrxS7P2/vYS5Avc9o3bfuW1jbB3chzwBd+JQQJFr1w7Jc9eOyblP8PV4ijW+MmHU007DRiUReW7b2t5NYvu0kDyzI1YJTcjerMqAPHUzzExXzb8C5IntU+UmrAydJ4rctVfkEa8COceSrfq8GmPjHKvJY9fOjeutm89867FxfibF5WMe6wcveazq0oyneA79X3hcuSndG+cu2vlHqUR/rG5FvwsXol+gkFxnGEm+SXEk+ToJeqIjvJa8QPigBF8iupK9E0r+hm4wFx075UvzJ1ItvAAiJa4nOvL6o9dmv77tr0l2MR9uXsX80aoU822Tx0rKz3UazbWQzwWCF3Z3sb8DC8mf0Z1I/oTDoeQzHI8kn+J0IPuJ6Oc4O5AdO+VTYNDGZ8BIXk8M5fXHpM1++fa3lg8Nr+VPw2v51vBafjZ9m89/9B5u+/731RFf3fHWKV9d89VBX9301VlfXfbVcV/d950TfOcK7znEd27Zn+t20D8BnntiOLdBg8cAAAAASUVORK5CYII=",
|
||||||
|
"eyesCrazy2": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABxElEQVR42u3WMU7DMBQG4D+K1HSBHqBArsDWoUjhKNyibA5CYuUIXKFjJxomRlYGJCKxIhqWplJTGz/bLUl5BhUh0aFd6n7xs137Kc9Q7CfDzv/bK+hPYH/n1O5YN20kxuH6kKdwnVwoIMilbYfkhW3H5K5PsBqeYrXPdBj1NMswUWlEXpi2NE/T2IwWkudmRpXSgsxDVQXkmV1hrrtK9y9Anpo+qtBhVWg9FeS2PSOP3C6Qu1iyWcftRqLdxUry2LYL7XLp+rtYeqzdjUlxReLm+sYrN5e60PMJt4bOL9ytQd1pt4tRkvPrF9alqFhfFLHgfJ5FY85LBM+cvwETzp/QHnD+iIMe5yMc9Tkf4qTL+zHrZzjd4x1b5UOgu4mPgD6/n+jx+4/BD+dlU/3r+b5Xgs2HB3XD5s9tOGHz7Rwll58yR5/L50WGw5rTWMbnabDPOsIrzkuE94LxKaJL3lsh569oB2PWsVU+1S9nsYGXQCTY/USL33+w++87r7XzXflaPnx6I39q3si3mjfys+aNfF75H72HN33/++qIr+5465SvrvnqoK9u+uqsry776riv7vvuCb57hfce4ru37O51W+gf2n5iOAicusQAAAAASUVORK5CYII=",
|
||||||
|
"eyesDisappointed": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACl0lEQVR42u3WvW7UQBAH8DGLblOccpSRCPErUEERhHkUHoEKqGIjJCokHoFXoKQipqKkpUDCEj1Zqlg6x2b+83H2GQ6JKhR3xZ39u/2Ytcc7puGPn5r2ft3eEX8yPW9wvFKXYyrEydrAK7JG1pWohPd6HOBJj3O4tck2w6Mve8vd0EnDyPm7ivAUdOASItOwsDc4bEOFgOhJRKMMXgNSbHiEPmtleoJXGKBZJe7WhU6mr0q4TJi38Njr9HBtUXQ8Mv8n0zcFu45Y9vB8kOkTew/n7vhNvFAsIOXsFhn6pcKidkd46imad7JCPnvB85XSYuhW5uitLgvYOGZTlwWIyxXk6D6yS8QcnjtOzNHEXTqbY0h3mcwcIbhLcOYI2T1NPYzeLMd5X2ej1/lb9/6MRq/ChftV4pjdiS7d1zVnpDknz6n7JbLNnJPnjvsPZJt5S9mh+1ei6J4ovHL/QkfBvaHwqTR/T8eZe03xpfs7ekDuFS3C6Hc5Z9SJDrJz88f0iAp1PDAT5wWod3Nfqbdzj+rp5rYfBPVmue3LTL2eO6lX9/jpHeOn23QmTk9nXj2EnxBf8zheHzqtT+DH2ZpoMV5Put8cwo/CuqLp9X+WFvBb8areul8X7Q04L7uZ3t/sGxM8H55P8yGe9+rF8GGaP3k5qJfD52HMt7LjPBMfhp/dmJ9vvnNewpGn03xGHsODbZnmss3B4+/e+nM6881zOvN+79fvfr90Xx3v1z967/mg+7zmQ/4Xt7yyOqJ5lXvdYde6o154nULtkjqlVbvwuqbmOSl1rSotV1EH1aUO1hqE1k112tRZrRNaBa3OyibPe67UZQlI67LUcd5DZUJpo3V8V93f9Z6w671i53vIrveW/Xvdf+i/ANCjCZtu1XwRAAAAAElFTkSuQmCC",
|
||||||
|
"eyesDizzy": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACP0lEQVR42u3WvY7cIBAAYCwXdMsLnOw8xhar+FXyCFuelNOZjaVsmTeKbF2R18BvQOnCgswwM+AiJEoUKT+6bY77FuxZGBhU/OZnVq/+p31X8Gnof4dtQ57aakiuuA+6VdyJhyo1ogdqt+ie2j0692ny43Es+AbDsGcKI42yGt2ndkjf2j49rUV36Y3RYkDpy7g36DNF6KBr4F+h0G3qEz0M21tyO6JTe0PXPAvoPBZtMzwbAziPDeg9tT14EIe/XrwH52fiOD/wu77jO78r3uB9I8dgfsE5hriAczP8Y74d/NPBXfFgDm6LbwcPqrg7OC03+TwWp2Ulvx3i8QNkMHvrm+zuCbM8edAz7BBxj1lObjD72RfXQ8Im37vGa/Fphgwj3y4GEon9PmFI5I8DhMSutYPVT+6vz2YWN8Z24n5rnPjJKKPZITY/kodTp/Sd3K1OPYo/PPTtC/m6LP4yiJ+HRfxl2h+Kv1+z3z8f/N26in88dX3xa3F9+rG/Kf7h4BdVfC7edcqJr+4kDvPQSJwwD53ECfP2RX6X9/tTdt3GSfw6bDIP8X6L7STr0vvzkNfxrLWsY2uvY173PP97p/L8R7ept0byxLZ5vdxzGkV55cxS8jBug+RhjFPOW4MnALnFA0ryv8GdwfnfQy/ZLyrnJ57QOT/htNTiO57m4r7kf1Ql/2mDseMGC//D+fObzuGfPf9rdaRWd6p1qlbXanWwVjdrdbZWl2t1vFb3a/eE2r2ieg+p3Vte73V/oX8Fk+c0j6ggguQAAAAASUVORK5CYII=",
|
||||||
|
"eyesDown": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAB1ElEQVR42u3WsU7CQBgH8H/ThLJIHwC1r+DGgAk+im+BW8+YuPoIvoIjk+Dk6MpAYhNXI3UBEkrrfd99X+vgkeiCQ1k4fty1R+/j/ofqx9cMrR/aC9hX4D5n1I6dcxsjdkgfcgPpJEOBlLx07ZA8d+2EXPoE9eVprPWNHUY9eRo8ykTkObdL/tYkfLWQPOM7VoYmxF9WRUA+czPMbNdSfgXIDfepcjusCJ2blNy1N+SRPAVyGUu2ieVpjKzL2JI8ce3ceqlu33P1xLpck8blI7nXHi/kXtW1vV8qc4j/4DKH6sm6NMvWW2/9187/bXH6b6t/Fmntd2+Nv1T36mVaNP4YLtV3eZLWfoW1+nYWTdXLDEP1NYJX9d0MJ+ofwFJ9a4Ke+gLdce0Ib9XnOB6orxE+p+ITnA7VV4hu1B9w3m+8EzZ+Vvs7usFU/BIXR43jm+PgvrJp1Mwf/f0e2+eDKJV9dQK7FrLfboGO7rdzYFC7QU99AYzZ7T7v1svt87xesv+79XXO68seunqQHOF6oByhflQ/kjtcP5Q7lFNUb5JTXG+cU8bVp+Ya1Sfnms1BvrbkINUz56AvN30568tlX477ct93TvCdK7znEN+5pT3X/UP/Ag/3YjiGSiORAAAAAElFTkSuQmCC",
|
||||||
|
"eyesEvil": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACf0lEQVR42u3WP27VMBwH8J+xVHcpOUAL4QjdOjyklJv0CGztFnMCjtArMDIVcwFYGZCwxIqoWfoi1Y35/bHjNJAODMDwnlTV7/Ns5xvHyS+QfvtxsPN/7RHwo+S7p3Yjzm3o2CH3IbeQO+WhAD35KG1NHqTdkuc+apqexqIPOIx6cgweZQ154PbIv9qWZ9Pknj1ZCsQ/pqDInUT32HXMTSC3csoBg0adT7Yntzx4IDcy5Sm5cnyqZEMjJ+07dB04xEguLR3QRy29RpxbekQTWnSTj0beSYKBPaeb3LfksUkS9BUKJ3NdbNgl6OS2zy5BJ8clJm9zjPfoOdgoPpi547fsErQ4LlR2CVocFza7BC2OKYpz0OL4pbjvZo6TFueg2Wn9inPQ7NSe+uvqYdb/g6ruj6pfQXXX1jwXtG+yW/NuOl9Puzw7qC/F7xztcnHcxdfFby2Y4gPsn0/Ou188wNFJ8S08UsU9PN0UvwEDxR08P6y+h0HFLRxP/g32Mag4wOlBdbql2Olmu+dGfFi6Fg9LV+J+4QrE3bOF257dns38Bqc/a8lH9QbgcO4vG/Ko577FmBeGfDBvATaz9dyLmjw0nwBOZuv/eFTkvv0McD67Xk+SJXf999n1wuu7Se4FusVj1euL+2GbPF4a/Lt1dT+kK31NDxd65NyF2f75mC7TQI63UB/rfvsR+xTJcbu8/lqd97aSZxPt+eL37/eFl/tl53/Xm1+vS/wDH0326TlPdeMB1znDVEfoP9Wd7FPdoXFUp/Kxap1KXKdsnrPWteS43vXLOohEdbNb1s3EddM3yzob1UN1ea2Or9X9tfeEtfeK1feQtfeW3Xvdf+g/AbKCHZJ1IQy5AAAAAElFTkSuQmCC",
|
||||||
|
"eyesHurt": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACWklEQVR42u3Wv27UQBAG8G8xwl2cFvHHjwEFuhNvcm+QdEFCwps3oqSIFFNR0lIgzhV0ZCtiFMdmvp1Zm/jiFDQgcdfc+ne7O3u7dzOL4dZXjb3/be8gL6fPDduFemxjHR3Wh+5hnWwoUNF7bWf0oO2Sbn3cOD3HircyjD3jMuIon9NDbPfxU1/G2TJ6EyMOnguKHw6do9e6wka69vYtQPexzxBkWJep+4qu7Zae2y7QbSytLWw31uI2tqeX2g7ifXJ5D8lLcZuT48LaYt3hncUaTiVeZWso/sBtDcN7cWv2e/9/3PH3uusdvdj1QM92vaZj1xF9PfdOvZh7wEtxn829dly/BfjNkdNbDTB5h5Lea4DJA6q4Dxpgcll9dA0wOXJ1DTC6TK+uAUaX6W2fY4DRuTnqMcDoMr15DJC8w8PkXxkgecAq+ZYJM3mNIsU9AyaXrHpufsyMbC6Z1W1t/Q2zqrlk38ML9euaGdlc2s+PzL1jylaXjPziRP3KOzlkdSbuV8/Mkcni1AM22EzufKZeo4Z/pP4D9+VJHQ8k8OjAJ6zoj/H0pn/BAf0Qb2761t+jw/Uzl58dPZ97q17OvVevhg0wrh/ufPB0OdroMa+qN/Q8bvqJuedRtJaH3wJH6tfiVczDzPPvgAvN87L/eTXm/888r+hyXgdWF+T5u0xqdeQYT7SOsN+lLyqrO2d4rXWH//OrZpXq1NZdWp2SutYNP1Nd+zZ8tLrGOlh+SHWw7ddWB1k3JWGkuulS3Vyqs0t1eamOL9X9pXvC0r1i8R6ydG/Z3+v+Qf8FVocQ2WNhih8AAAAASUVORK5CYII=",
|
||||||
|
"eyesKnockedOut": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABBUlEQVR42u2WMQ6DMAxFjRgYOUKOkqOR3ozehCNkZEBJ/W3TLnWlVlVLq7BgXvwTk8HfVO8+MzX+bb4RP51+L4hH5RJTFE6WA57IkkxKNIEXjXvwrHEAt5zuuj20zFeWIVPKEFUawLPERVZTkN168EVOrAkFyWLdOvBZK1w4tdhfEHiSnJpZtvXK0wSu8Qo+2C2AmxZsHe02InPTFvCgcWZeds7vvPPA3PaELkc76wHf7Kx64vMmq2F8gVsN9czcwvLvfLjxNTZ+DF60DQiXRhA+w9v9H5b/Qj95Ux9+tv97PuL5jutTnq95Puj5puezni97Pu75vjcneHOFO4d4c0ub6w7IL0FMhO6cZXtmAAAAAElFTkSuQmCC",
|
||||||
|
"eyesLove": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAB+klEQVR42u3WPU7DMBQH8BeMmgXUgaliKFsP0IExx2EEiQPEnIAj9CrhBByBHMGjB8vB//eeGw+4EhVSK9QudX/1V2L7PdP042egi5/aA6VPI79HlJfiXKaOnbQO3JJW0qZEPTxK2cCdlNdwrdPsu0fb5D41Q02eBreyLdxxOfK/ds29GfjII04WE+I/p9DAB5nhmKpGfQqCW64zudQsGHHbw6Xs4a2+Bbi2hfmlvo0uubaN8LWUXfKYPX277Ovk2ifauU7HOuBBx5re0ni9zmF5hOscpo/kWoz/29OSp8UXJyy++tBOaeuxR+o8ZbevMW099mB2jnp1WgXaiHv6HJtOPNKNp624o6eh2YkHWrzQSnykraXP7OaBbrOviJ7FPbb8tfiA8qbwReGP4nx4rnp2Pmt3hZvC7wunws1pnZ+Liudqj3ONS/o+ETYOe5Two+uF8JPXF33m9UVIy/uBQ5HsB36UvK847Om+wlDqjkOduMVQ6ug0788BQ6kHjnriI4ZSjxwlxR2iZN7naKvuMYXsFtFWPGAK2UdEZz0XGCq7Q3xUt2Z2T/M5Sodk76GZHWE2O8c4dcS4/XlsZ/dd4e+zh77wr9njucaTP4rDv43/tTxSyzvVPFXLa7U8WMubtTxby8u1PF7L+7V7Qu1eUb2H1O4tl3vdGfo3+S3L7VKZWUcAAAAASUVORK5CYII=",
|
||||||
|
"eyesMiddleLeft": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABpUlEQVR42u3WvU7DMBAH8L8VpG6EHUEeAwZEXqVvQDeQGGImXoeRAampGBh5AaRGYmCDTDSowcbnnJ0OHBUfUhmSpddffLbruDnDfnqVGHzT3sJdqvteUZx27mPk3sFtyDW4EacCBbnp4oS87uKMnNuo2D3lOm9cGrX00/BZekRe+9j4uzrzvSXklR/RapqQv2lbRV52M6xcU8O/AuTat7G1S2uTznVB3sUN+YhXgZxzyZqUVyN3zrmGPOvi2rkJ7j7r4Jlz7pPy6pzH+sJbHsteuPEKnkP6A+c52JlzDs3gv/Eq712teHYXvTF59Na+RX+y99GX1XH0uVpEX+i0CH6D8+jPSKbBJ9iL/gA1ZzcVtqNfY+eF/b3EZRH8CocnwbW6jT7B0Sn7UqvHafAxzg6CI5mt+HjFVfD9EnqX/RVbmPeO3rFxd/NHP3+o6TqfAP36+GcR1hP9eiIp+vXHyvqPinXPS3q+0n6Q9o+036T9Ke7n4f+e/t17+Lvvf6mOSHVHrFNSXZPqoFQ3pTor1WWpjkt1XzonSOcK8RwinVuGc90/9A8jSWjIB4EzpQAAAABJRU5ErkJggg==",
|
||||||
|
"eyesMiddleRight": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABpklEQVR42u3WvU7DMBAH8L8VpG6EHaE8BgyIvErfgG4gITVm4nUYGVBJxcDICyAaiYENMtGgBgefc3YycFR8SDAkS6+/+GzXcXNG8+GVY/C/9hr2Uu33guK4dRcjdQ5uQ67BjTgVyMhNG0fkZRsn5NxGhe4p13pl06ilm4bL0iPy0sXG3dWJ6y0iL9yIjaYJuZtNrcjzdoaFbWr4V4BcuzZNadPqqHWdkbdxRT7iVSDnXLIq5tVIrXOuIU/auLRuvNvP0ntinfukvDLlsT7xmsdqTu14Gc8h/obzHJq5dQ7N4D/0Iu1c9Ty5CV6ZNHjdvAZ/bG6Dr4qD4Au1DL7Uceb9EifBnxBdeZ9gJ/gd1ILdFNgMfoGtZ/a3HGdT7+fYO/Su1XXwCfaP2FdaPcy8j3G86x3RvOfjnivvJofeZn/BBu47R+f4c7fzRzd/qNk6nwDd+iCadeuJbj0RTbv1R2/9R9N1z0t6vtJ+kPaPtN+k/Snu5+H//lvv4a++/6U6ItUdsU5JdU2qg1LdlOqsVJelOi7VfemcIJ0rxHOIdG4ZznX/0N8BvkBoyHDCksQAAAAASUVORK5CYII=",
|
||||||
|
"eyesNeutral": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABrElEQVR42u3WvU7DMBAH8H8UqekCfYACeQW2DkUqj8JblC1BSKw8Aq/AyETDxMjKgEQkVkTD0lRq6uBz7uIOHBUfUhmapddffLFruzmj/vTKsPVNewV7Bc33nOJe4y7GyDm4DXkKbsSpQEJumjgkL5o4Juc2Qft4yrU+t2nU0g3DZaUReeFi4+6msXtaSJ67HuuUBuRu1lVAnjUjzG1Tw78C5KlrUxc2rQobTxPyJp6TRzwL5JxLNu/xbIysc64hj5u4sG7E7WchHlvnZ1JeMeK+vvCK+6rPbH8Jj6H3A+cx1HfWOTRb/5W7NWanNRZ/r5LWL1+8P9RX4iapvN+GU/FlESetn6IUX2TRRNzkGIqXCJ7Flxn2xd+AqfgiDXbFn9Adt47wQvwRewPxEuF9wn6Dg6H4DNG5+DWO+t47offD1l/RDSbsJzje8Y4Vx8Z9Zt9Kfvzor/MSiPz8YOjnEx0/nxj4+cfK/GO8br209dX2g7p/tP2m7U9tP2//73/3Hv7u+1+rI1rdUeuUVte0OqjVTa3OanVZq+Na3dfOCdq5Qj2HaOeW7bnuH/oHa6JiOJLpgNsAAAAASUVORK5CYII=",
|
||||||
|
"eyesNuclear": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACgklEQVR42u3WsW7bMBAA0BMMVEthrR6M6hu6OUv5Mf2BBlk6FCGLDF2K+geK5lfUJd7iX5ChwasMLzRA8HrHO9pLGaNB0GSIYSTUEynS5B0pwL9+Onj15/YA9KnkuudyI57KYJKD1mF3oJW0KYBlj1KesI9Sbtm1TnV8PLcl99SMa6ZhpFauZh9TOaa7rk1Pm7D3qUd0PKB0E0PF3skIe6oa9VcAu0t1cKRmYSLuLLuUPXuts8Cubdl8o7NhyLVtZG+lPJLH7PR/zN6S6zO53Wi0rwc8aF/4lfqzOobmEa5jwN/kWoz/ydP8qvN8Z+84etQBTl7xBIuHujfZ6XuTfbSxyT5aFnH6e5N9QzNo1b8hXaqvqCejTr357Evk6+R0H4NVn1scluLhEvGHEY8LeshK3NOj6lY8fDLYDeK7jhZ0qU4h6K7E987Gai3uKXLhUnxwJlQr8UNHfiG+BRNgK75zdYSZ+B20Hj6K79nn4vfsV+IDTAK8tcm/QzPCe/Et+1T8J/vF4/wO6l/wxiSfwbvPMBO/h3oKtfgC5vTNPq1PvphnX6c4ETccJ+Gcc85kp9w7OjYnDyY7LfstLWzyFqNFr78rhar8rlsuH8459Xt06ld94HEG8SWPc6frgr7CIOuyxr7Gva4juxdfsQ+67rzpHXTdeevTdffsO40Tdo2TQNum3WtcoatQ4ypY8hyH7BqHkXYnk+OW807jFlsHbY7za4Brk/OCPOfFB4AvOS9WPdXJeeSqYx5twuSUd2OzOZenpbwu7QPFfaO0zzzJPvZE+/C/7v+lc6R07hTPqdK5VjoHS+dm6Zwtnculc7x07pfeE0rvFcX3kNJ7y+t73Qv0PwhMG/HSVEiBAAAAAElFTkSuQmCC",
|
||||||
|
"eyesPinchLeft": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABe0lEQVR42u3WvU7DMBAH8H8UpG7kARDkMdjIq/QN6MbAEPNGjAxIhImRF0BqNjbIRIMabO7ss8PAgUAVBZEuvf7is6/5OsO9+2kw+bZ9AH2y8LvluAjuY1TeIWPYDWSQpAI1uw1xzt6FuGSXMVmannPJe0rjkb4Mn2Vm7J2PrT9qSj9bzt76FZ3hgvxBN2TsTaiwpaFW/gXYjR/jOkob8uCmZg9xzz6Ts8AuuWx9IWejIpdcy16GuCO30em7i16Sy5yc11Wy1gc+yFrujNarpYbiGy41uGtyCe3k/9eB9OC1Mf4JP2gw35N6npBjKfWQm9F33jpGx8a9nAOH4msq9+ozXwAn0Q3y5OfAsfgLeR39AniM3mCW/A7ZUty22E3X68HPGa7XAvvJV6aoo1/iNPm6PUrXd5mtkg/uOfm9ux3vh/ImeW+rbd8Pf+V52dB7+Kvvf62PaH1H7VNaX9P6oNY3tT6r9WWtj2t9X9snaPsKdR+i7Vumfd0v9FdjPP/LItpYaQAAAABJRU5ErkJggg==",
|
||||||
|
"eyesPinchMiddle": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABhklEQVR42u3WMU7DMBQG4D+K1LBAD4BQrsDGwFCOwi1gszkBR+AKjEw0nICVAYlIrBU1C63U1MbPfs9kwCBQRUE0S1+/+NmvaZJnuHePBhtft3fwRxG/txQPo4cYo+DgMeQaPIhTAUVuY1ySmxjX5DymSNNTrve5T6ORoYyQpStyE2Ibzuo6zFaSt2FFp6mgcNJ1BXkTK2z9UMu/AuQ6jHHGp3VldK3IYzwnr/gqkHMu2XzIV2PknXMteR1j492K+08jXnvnOSnPjHitD7zjtdyZX09xDcNvONfgbrxzaDf+fx1ID14r8U947SYYFGOu5xj721LnBFs9P+o5eo6V+4t/Kyn2S2D3M58BlfgVcCi+AAZyne+Ag+QaO+L3wIn4ssGe+BMwFbetn5N9huIh/Y+nmIkvmmqc/Lqcii9NrZLfugtxq7q3++G5U+l+OH9c+/3wN56XFb2Hv/r+z/WRXN/J9qlcX8v1wVzfzPXZXF/O9fFc38/tE3L7iuw+JLdv2ezrfqG/Ar/2+7GXAFZTAAAAAElFTkSuQmCC",
|
||||||
|
"eyesPinchRight": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABf0lEQVR42u3WO07EMBAG4D8K0nbkAAjlGHTkKnsDtqNAIuYEXIWSAkE4ARdAEImCDlKxQRtsZuyxQ8GAQCsWxKbZyRdPPJvXGO7drcHaV+0DaMvCfstxEdzHqLxDxrAbyCBJBWp2G+KcvQtxyS5jsnR6ziXvKY1H+jJ8lpmwdz62/qgp/dly9tbP6AwX5A+6IWNvQoUtDbXyL8Bu/BjXUdqQBzc1e4h79olcBXbJZesLuRoVueRa9jLEHbmNTr9d9JJczsl5XSVzfeCDzOWOaL5aaii+4VKDuySX0K79/zqQXrw2xj/h/B42mG5JPU/IcTO6GX3jrWN0LN3dFNgRX1DF55/5DNiPbpAnPwH2xF/ID6OfAo/RG0ySXyO7FbctNtP9ekB+Ee/XDNvJ56aoo5/hIPmi3U339zabJx/cc/J7dzU+D8d3yXtbrfp5+BPvy5K+w1/9/mt9ROs7ap/S+prWB7W+qfVZrS9rfVzr+9o6QVtXqOsQbd2yXtf9Qn8F3gkAZhfTUgYAAAAASUVORK5CYII=",
|
||||||
|
"eyesSleeping": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAA/UlEQVR42u2WPQ7DIAyFHWXImCNwlBwt9GbpTTgCYwYE9bOdTnWl/ijtAEtePmxwkMIztYdjo85/zQvxGPQ9Qc/KRdMinCwGPJIFWSrRCl5Vj+BZdQC3mOG+PHKZ75yGSClDsuIEnkVXmY1BVhvBk+zYIgqSyVYG8E0rTBxa7SsIPEpMy5xWRuVxBVe9g092CuCWC7bPdhoLc8ut4EF1Zl4Pzs988MDc1kReXmyvJ7zYXu3C+61Ww/wGtxralbnJ2nnnZ3Oi+w+fDn0G7+ff+Sf8S/fwq/e/5yOe77g+5fma54Oeb3o+6/my5+Oe73t9gtdXuH2I17f0vu4P+Q3tn6K1k3T0owAAAABJRU5ErkJggg==",
|
||||||
|
"eyesTear": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABsElEQVR42u3WzUrDQBAH8H9YML1oHsCPHHwBbx4qxJuvIfgQesuK4PN49Nb01FfwIBjxKhoQ2kDjrjv7kXjIFALFemgunf6yk51s2sxC9x4Ftr5pb2COyH0vKU6c2xiZdfgx5BJ+kE8FcnLlYkFeuTgl92Oi9vKUa7w2aTTSlmGzZExe2VjZszK1VxPkpZ1RSyrIntRNRF64CkszVPm7ALm0Y3Rl0hrhXObkLq7JY78K5D6XrE78amTGfa4iT11cGVfBzWcVPDXur0l5VebnWuGNn0vfmvlyX0NiHGgXrgzxX7i5lxsstJ4a13pZxBOtnKsS4+ALRC/BvwscBv8APoMvZbQX/Bmj69Yh7oM/4eA0+AJilnt/xNE4+BzxXfAHnO13viM6P2n9HaNo4v0S57ud45dj4z43/5aufvj6N/jc7eHqcb/tQb5Yk38x/rYmf2V8OtALxiXneb8j63WFtNcbxmuR9DtmvV7hgvErxo8ZF2vxGvEgb5AMWjfTOVY+R+49PNCHvv+5PsL1HbZPcX2N64Nc3+T6LNeXuT7O9X1un8DtK9h9CLdv2e7r/qH/AE7U1rlxChGvAAAAAElFTkSuQmCC",
|
||||||
|
"eyesTiredLeft": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABiElEQVR42u3WP07DMBQG8M8yUjZyAAQ5Bltzld6AbiBVKOZGjAxITSfGXgCp3tggEw1qZONnPzsMGCQGClK69PUX/3nN4M+wn35aTH5oH+A+IvzWVJfBfY3aO3gMuQIP4qlAQ25CLcm7UFfkPEak5Wmu895No5G+DT9LFeSdr41/qiq/miTXfkerqCH/0A6CvA0dajfU8L8AufJjbOemDTK4ashD3ZMX/BbIeS5ZX/LbqJ3zXENehbpzbqK77y565ZzXpHldzXt94QPvZW/cfg33UP7AuQe7ds6lmXzyyf+HA+mg1LH+Da/muDrnfvaQ61Xsc475BxfRz1qoE/ZXHGE7OkbHwd31j7F/iNU3Xi6ASz5XlwpyFc/bW+CC/dp5E/0OeIneomA3xSPEls/5mcZxOv+f/Zrh/F/gNLrcqbKJOXKPJeeIlXs9S7mzFTvOHSsG+5Zy6sluYk4pWz2kXOtNHXNNNboec1CkHMzlZi5nc7mcy/Fc7ufuCbl7RfYekru3TPe6P+jvVlI6sc0WsN0AAAAASUVORK5CYII=",
|
||||||
|
"eyesTiredMiddle": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABjElEQVR42u3WMU7DMBQG4D+K1LBADlChXIGNgaEchVvAZnMCjsAVGJloOEFXBiQisSIalrZSU5v37PdSBgwSAwUpWfryxc9xM/g3/KdXjcF37R3oyuJ9w3UZPdSYBIeMYbeQQdIKGHYX65y9jXXFLmOyfnruJV9RG48MywhdtmBvQ+3CU1uF2XL2JrzRW15QeOi7jL2OK2xoqJN/AXYbxviW2ro8ujXssV6xF/IV2KWXbVXK15iQS69jr2Ldkjt1+m3VK3KZk/vaibzrC+/kXf6S3mdkDeUPXNbg78mldIMPPvj/cKDfKButf8Mrv8AoN7KeGxyNdZ0v2Mum4mc43d86Pjh27gtKne36Mf7Ol0ChfgucRC/9GhjpvvoAHOt+u7Y4UH8EztU3NQ7VX4F5cNrnXUNzyj6/RPbU7/8XWKqv62Iane7v8rnmyKatTMgRHjfz15o7znQxdzin3jrT59TVs+SUjVmkucaZFHItlYOp3EzlbCqXUzmeyv3UOSF1rkieQ1LnluFc9wf9HedzO02a43rNAAAAAElFTkSuQmCC",
|
||||||
|
"eyesTiredRight": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABjUlEQVR42u3WsU7DMBAG4N8KUjfyAAjlMdjIq/QN6AYSiJg3YmSoSjp15AUQ9cYGmWhQg43vfE4YcAcGClK69PrFl1wz+Dfct58ao+/bO/iPCr8N1XlwrlGyQ9aQa8giaQUqchvqjLwJdUEua1R/e+r13vo2WsljcJeekDdcW76qC75bRm74iU7TQHzRdYq8DhMav9TKvwC55jWu8W1dFlxX5KFuySfyFsill6zN5W2U3qXXkhehbrzb6P67iV54l3tSX1PKs3Z4J89yN/55lcyQ/8BlBrf0LqUdffTR/4cD/UZpYv0bTvNMcXEi82yRLReDT7+4im5r6CPxNxzgaXAMjr27nx/D/FCLXU776gw4l311q5HNo98CZ+If3q+i3wGv0WtMyHmff4Rayz5vDQ77/f8F2X3c/2c4Zqcc2ej8OubIHJeUI5w7W3Pa585abSh3OKc6997n1LN74JziXCtWfa61tuRc4xw05ZCDKuRgKjdTOZvK5VSOp3I/dU5InSuS55DUuWU81/1B/wSObTqxus+kUQAAAABJRU5ErkJggg==",
|
||||||
|
"eyesToxic": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACF0lEQVR42u3WPW4cIRQA4EeINEUikS6dSYrIpXMASxzBR8g1No0HKwfYI+xRvJYLl77CRKnSIaUZSxjyHjx+ipCVo0ixFW+z7LcwPBjgAfGXnz08+792D/gR+fdCZZU9lcEkB65DboErcVOAmTzksiR3uazJuY6oj6e26Cs2o5opjNTKTuQulUP61+r0NEm+pB6jpYDSn9EL8n2OcMGqgUcB5DbViQ6beZndzuS5vJJPPAvk3JZsVTwbBp3bBnKdyw49FMdvV1yj8zOpnTPc12/cc1/xAvubOQb1B84xxCt0Lob/1NO7Yad3U9zRumG3svle7IoHeN3cwllxDy/mzk+ai+YAb4uvAJfFQ9oV2R3tiuavOv90qL5Puyj7QrvokKvpovrmtLneXlX3553fNg+HPejtdY3Tn7dxqeMv1T+fduP92MYlrG7ztpnqvEncz/W9bHR9L3Jf38tl8/vedwuYb9l9xCnR7Fs8KIrPeIwUl/cf4k32VX8/CSZ7AHOMLZI7cWcWxf4Gz6f37NYEUfwIgvjK7ianVI1Tb3+UOMHPu7oOu/Vp2/rc3UIZlw8bqP5SLrLE+U7DEceJQ/S4ULKDWqCMC1dA83mtblPP+fkT9cx+TT1lDxTd3Dx2vha/6dx0/rTPjb90Dj/0/B/lkVHeGeapUV4b5cFR3hzl2VFeHuXxUd4f3RNG94rhPWR0b3m+1z1C/wlHT0r16qhmxgAAAABJRU5ErkJggg==",
|
||||||
|
"eyesUp": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAB5ElEQVR42u3WPU7DMBQH8H8UibBAD1AgV2BjACkchVuUzUFIrByBKzAy0TIxsjIgUYkVQVjaSg15vC+3DA0IFhjipe4vfrYrv+YZtLKN0Plfew1uiX0fS79nrn0U6vAx4iV8kIcCQbyxfipeWT8X9zHJYnqJZZ9xmIzUbWhUmYlX2m/0aZnrbKn4WFekUjakD6lOxEe2wzEPbfxXQLwM1PBUFYfV8snPyyBO9FYHmolnROdPvLw4x97RhRo/a0LNcxbsHHudvlIjntN7lQeq2GXuY0xtjZzmo2zIn+wZNWPsk8RVBU2RPHKc+vsI29FfgFf1ukfzMtkkOuH1Aj1gfUB1zxzpWfR7bO1FnyK9De5X2Nk3z2mC7DTQDTvRJQ761ERfS5e+u/BnrCdD9yMcbiwdnxx/7hPOquX+0f/Op0AW/Qp8Fu5zYI3c74G9hZfYjP4ADKLbeZnrebnb+Zrr+bprPrhrPkSX/HHX/Iku+eau+RZd8tNd8zO65Fp0yefo2tz1z9N5553/zHv+xd6r2upfuNQJbfae1+bv/9We+h68RmnBy63uqFvdMS+sTulaVqes9hVW13ROr2taVoPVQY31Oqg1NHxRN9vqbFtdbqvjbXW/7Z7Qdq9ovYe03Vu6e90/9A9mtWI4APcpSAAAAABJRU5ErkJggg==",
|
||||||
|
"eyesWinking": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABYUlEQVR42u3WMU7EMBAF0G9Z2lBtToByEIpci85eIXENbgKhouQKOQASpiGRCDYee5YCzSAFEFAkzc6+eGyv480YSbwGbP7XviBfpn4fKW6rlxh9cXAbcg9uxKmAI481tuShxh05tzHv3VNu9jmnUcsyjZLlG/JQ4lju+q70ZsnHMmLyNKFyMy2GfKgzHHPTyL8C5L60SSGnLba6d+Q1nskbXgVyziWbW16NPjvnRvKuxiF7PHr+DEfvsnOflBd6HusTX3isdMjjOZ5D+wXnOaTb7BzGzb/l5RkL/rQ40e/TlejX9lH0c0ySxxFnkr8OOJX8xZu96LCXkk+wd07wZzQXsu+s5A84MTeigx18/bZ/nD+sW+ET0DhxPbGT1x/7Nc9Le77aflD3j7bftP2p7eft//5z7+G173+tjmh1R61TWl3T6qBWN7U6q9VlrY5rdV87J2jnCvUcop1btnPdP/Q36EeCwIlHlo8AAAAASUVORK5CYII=",
|
||||||
|
"informationAccept": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACY0lEQVR42r3WTWrbQBTA8adOibsIcZcuGJQjuHTTRYh6lEIvEMgBNLtkEUJP0FxlTIKzKe0VRF3IVm43ExBy51vz3hvTVeuFMT+E9H8zEhbsix8F/8rXB1we8GVT9vO66GNzVfThcl103fVF36mzpuS/1Uld8q0UVyV/hOq+5LcAFyVfAixa7uN7gKO64BcAYsZ9MA733HVnfMV9p4wvW+ZbafyE+xPYD/eNc37+G8sV94V1wdyOC8DnsuMCzJkPH63z9dF2LGiY76TPp74N+dQ3IZ/6t5BP/SbkU1+EfOLjKuQT92Px+3BQIZ+4liGfuBtXcN/GTOKbmEn8a8wkfh0zib+OmdjH05iJ3Y1bcXe7KLg/y5iPfZfysf9I+dgfUj7265SP/XPKx75K+cjduIL78CHlI3e7OOf+POUj/zXlI/855SN/mPKT34VxK+y6DrsosEubdzrlB9fiuxl3Wv3osnk042b53rXonsx3lu9dnoPxPst3roWCT8Hz/xd1BnC533dZvvO1uW3e2Z9TvnN74FtzlSwz+Zv9Ps90bi+48GM1udvwY+9t7oPzIc9M/rLVeaZze4ajts8z/ToYedF02SrH9TFHOp8zrxqV53s3VNUyz/duT3GH8r3blC8of/JjlB/20dgrlO/dLQHK9z4Gnx/wmr4/BG+oSwCcH1wB4HzkFfMOAOcH72l+vM9pfvCB5iNvmI80Pz5fND89dyQ/d1FwRfKjdyQ/ek8yc28Lrklm9IFkRh9JZuaz4nsjyUwu0SpPrnB+5m3ROyi/r/ZV2bU44LOyD/Oyj/X/et/+i/8B3hwL9ySzr9cAAAAASUVORK5CYII=",
|
||||||
|
"informationBackward": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABFElEQVR42u3WPwrCMBQG8Bcj/gGhg4tbBAdn8QB1c/QQHqS5gUfwKNYT9AqCg6tjh9LaphGbl/eGgg5CAl1+lML3pcMHFXlS+LprAIgJrxkU4co+2Jf1++ceDsGDBw8e/P/9CeZokK7fWt+BID2uruR3AO6u529/uF5YFhnt8uR6+XaFclkfxci19YT2Gfa09QXuzQbeYLeBt4wfsdsi9tht4Ax7G1icGFfe/RofxJ7rbg0dN4GHjE/8/8cUMfXdBJ77bgKvGF/7boo4+G4CRz12DjC7qCkiITxlvAlc0S4or4uQlOeM14HHjEeUl524zj7kXH9qQJ6QnjJ+A3qXPgXjkvac8WJMexkxe1j19MtPd7jrL3T4N1ugBC5lAAAAAElFTkSuQmCC",
|
||||||
|
"informationDecline": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAChUlEQVR42rWWQY6bQBBFizgS3oGl7InGByFZ+VigrNjNlYjkgyDnAng1LQUxaXdVdfNraM0sMr3x8PTH/g1d/0Ovu2uk/8v/KnhBflf+B/mk/Dfyayd8QP6rFf6tA140/Lk+tVu+0jPz5Wez5QtdmLv+ecsdncWvCITPdGJ+E4HwiY7iVwTCR/rK/gYRCO/pC/urRSCciDewfhcB85WIN7D8EAHzxfML2xcBc+f5me2LgPnsL09sXwTMJ395ZPsiYD76y+BvUAFz/2vsr1YBc38V/Hn7Igj8YT/48/Zlh4EvgV/YPm8gcBf4me3zBjb6E9snqvD7j2yfqAE/D39D+KMF/w9/deAd7Nf/f7BPhPfH/16wTwXeT6KS7R/w/nvA9kt8Xv4Lkv3N8/Ur2U/nIews2k/n57GS/XTe0sLzGVeB5zmuA57/uEqcl7gqnK+4YL5eE4d5lA1E+5GPaD/lA9qPfEb7kTu0H/mC9iNf0X7KN7SfeA/2Ex/BfuIT2E98BvuJO7Cf+AL2E1/B/iafwf77PPc9ud/N+cztK3cfcvdtyNznev+5yOi+eY48um+fu+v3z8k9c65umXN4zZzbIXPO6/25iPbNHEX7Zu5cvz+n98xc3zI5cM3kxrDh25ypyQy25BjbH22OSXBONvckOGebkxKcjkyuSnAuZHJYgnMlk9ua+2RyXoOzx16IuT9ij8Tcn7B3Yu6nDWx6qtThMD1VqcD0VKMC01OtCkxPdSowPaUC01OFCkxPHVRgeqpUgempSgWmpxoVmJ5qVWB6qlOB6SmdH9NThcaH6amDxo3pqVLjyfRUpXFmekrfG21PaUDantL30v5j77G5997ce/KnvZ//A/4SazfzAqDCAAAAAElFTkSuQmCC",
|
||||||
|
"informationForward": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABLUlEQVR42u3WMWrDMBgF4BcMngLtqE7uEWpygOQoBQ89hj1mKKRjtl7FQ6jHXCE0F4ibxYNRa8mWFEsvQ4YsxRo/hOA9fiHhl64Sd/Ccu1ze5m3Cvdlwryvu5yP344L7V8r9XXDfznPqIqYuX+Il9dcoYd6uZhvmTYE987pAxfwMZMy/gZT5DnhivgYE8y3girjwR8AFdi6fgYh4u+o8IV4CrgjnXQ1wgZ3XHeMt9B/lWegH5WnoO+Ui9LXyeegf2vPAFbsirEvtNrB1VUMX+DNwvd8WYb3Rbouwfuo9u+IL3w+9p76XvQvfi95NYN9j34FREcbl4KYI4+3gJrDv5goYb4xXYz8Zz8Y+xMVDzR2zK+dEwTyrzWzOJ5988skn//eOG1y/hPld/+GX/gcqVSYNBdilYwAAAABJRU5ErkJggg==",
|
||||||
|
"informationLeft": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABAUlEQVR42u3WMWoDMRAF0CEu7NKQJoUxvkOaFLlbBGlcGO+VhHWEXECq3ArSTCFG3saS1/xfbBHS7JQPIX0hhpFUWF4WX/xP/Uo8EP/Gbq/Yyzt2/cWeiIcf7AM+1/bYyyd2zdhTxB4c9kGg2w57+cCuGXvy2IPDfhboYxzZyr26j3Fk0/yruUaRdXvR7slJr5fuQbCfsNsb9nLArh57kkk1vxA/Yrct9iLY1WHPZL0n7mauZ/urzMtfmZN7PQdqnokrcSNemTvikXgmrsSN+DTQQ3+NgVao7yLxTLwQN+KVuSceiWfihbgRr464Jx6JK/FC3Fbz5hSda9flP7D4v/kNWMQjUDSyRnEAAAAASUVORK5CYII=",
|
||||||
|
"informationNoGo": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACvElEQVR42rXWMW7cMBAF0KHXiIoszE2XAAboLrWR1gjducwhfIHcQALcuNsj+CpCYMClryDnBEqnQlhlSM6QHJLukq12H1bSJ0VyBrbmZ4R/75Nq+gLQtXyAG+hrx7+buat9AlCbqh3wo2db+upcrbr02TnYh9In791b6YN3WHrpp8BwY6Sv5FpLX8i7F+kTuXqTPpLDT+kujnYPueyFu7/6qy5s7i6m9qk+mtzdHay/7NMx98VNso919ZL7TO9wgdv73DG+Cc8ZrnMfw+23TcHnwkPeX7DPfYBd8Gc47zOPS+cRPtjkJz8q9znAzggPjz1dgXpKjsMN07XegjoKp9eD8/qafGH/g/N0l3wGWoC/0e9zj/EBviWfUnyA64Yf0L8kH2lYGH8fJk46xr8ME8duOP53OE8+0DRg/D5MqHSMv8FZcp6eZxzfcGYrf8TxDSp3jt9tY+0YX+d+Isf4Bt0kVxzf4unxVLqLL30X4+OcR1/JXXz0Y+kuPvpr6S4++o/kXYyPa6B0H7/hPn7uS3AfH3/cJdcxPl78tXAfv+E+fsN9/NpD/NpD/NpD/NpD/NpDfBp85iG+9C7Gpx/ZvB34SNHCKX7lFF/6LsbH9yid4ldO8XP364rio8v1SfFxz0qn+NIhxqe9GffFSiclitgvCx9Z0vuZ994gfYJYCWy+r0c+msJ10ePJmjtm45OV9ib7BZ+sq/Q9xxfuq5elyqEK72ufQ5nKjszkigviTpzDHH8MeeO5zScoHY3JNU+PFnWB4m/0JbnlYRlRd2J8sKJOxfjQt+oafynrIEhPdZPGXdVZGndVl2ncVR2Pq66o+2DafQLYdl8BfbsPgXbfwq+t7HPS65F9Eeh2HwWm3XdB3+zT+LFlX8erpegD42OLvpFnoegz0+1lX5o1sqKPhVYf+27f+16f/H/687+bJlUxAa4nQgAAAABJRU5ErkJggg==",
|
||||||
|
"informationQuestionMark": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABoUlEQVR42s3WQW7DIBAF0KGuyiZKLhCJZa/QrjhOt80Fam7QI/Qq3mXZKyDlAl6ysNwaiAIzzFSx1EjJIlKeLPLBwwD8sJ8B/t8BLOcjAHDuFjetTwAq6NbjMHZo3S+ux77xODx8mMYjg9lRn5PrI/UpuTpRD8nhQH3M/kLdZ9/3xIfsW0vcZdeGeGboPrHPZ1dH7DG+illPrZv4Jwfs8dE+hnptXKXvF+zLdHUabd/4LqXdsO5g0yP3uRYcPDZu088n1j31IcaP/mA5HxnPsTrWA3UXlyG6WuVT6935NVzr+i8P1EuVrvJRcC/4eVkbd4ID77Pg06V1YA+Cj5CXm/oSv+N8ENwBaM4hVy/1uXQy5DG+ZTxcpou96pTI/WVa2Et87CU+9hIfeRUfeRUfeRUfed3oa6/iU+84H0p85K7Ep27486gcSMR7wYVzTXE+V/GJ65W+43yqpnWl23vym983bu1e8+6UcP957/n3uxfWbcv6cuYY3tWXcL/6XuPLtnsT/FlwfU8+CvtF8iDsl7ByH81SnVft5Ko6kerqJvX8C9BZnroLD70HAAAAAElFTkSuQmCC",
|
||||||
|
"informationRight": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABD0lEQVR42u3WMa4CIRSF4cvDxFdJa2HCFrCzm8INuQPsLHVJJhauY/I28KKNVhrBqwbOKSjshvIrmD8MAyM3OPYy+ODf9ivxf+aRuMd+dsSP2E898UB8FrFPPHbrsJsjdumJB+IaVLoGla5BpWtQ6RpUeSA+jW/fy3P8iAYlX6uPRIOSb3Wei4jZvH0nH6MnHsD8j5GDas9Bteeg2nNQ7TkIeCCegoCnIOApCHgKAi5/xAPxRcS+Is8l81vSMyb9k65tfVZkPQ9t78uS9z7q2vbPnOy3JfENduPB9/LKKc89kd+I3dywW+KOuCfeEcfns+ZUbok74p54JE7uC0N8TNy13Tv0nmq8B4f/hMG/7HcdRCwW5RggbgAAAABJRU5ErkJggg==",
|
||||||
|
"informationStop1": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACf0lEQVR42q3WvWocMRAH8BEqtotSurp9BncJOdDDuMgrXGGsLVIG+5UEgbjLMyy4DxvSbIhYZfT9cRpwQq45+BW7/5FWowE7/Gn4Vwf/k70fwefeTaCn3ncQ7u+59y085673Nfht7zokuel9Uf7vs+o8/n+VrR/Mv4C9zK0brv17fzy1vk+L98tz65sIOc93ra+n4Kfb1vU5uLhpfbkEn2IBySGuD48FRI/xSwHRU/xcQPQUPxcQPcXPBURP8XMB0VP8XED0FD8XED3FzwUEz/FzAcFL/FRA8BI/FRC8xE8FBC/xUwHBS/xUQPASPxUQvMRPBXiv4qcCvNfxYwHe6/ixAO91fCzgY/ZT7afi59rfFH/3t86ML2nvfbIc44oDz/tWu7Bf0GeLbmqf7Qu6tIu0R+3S/kRX/rTWruxvdNwg9MfaBfqvQ2wS5m+VH3xnhpkJXdRu2MYM3/kuYap9B42+MeffGwd8ygpGAq/dEH5411eunAsN993zlQ7+0OWRW/Ym/2zGLmx29djsyxK9XU9u16GD3ZI3+wJyT97sIwiTvNl34MmX9rtihAPl93Hden+I6z901vtb535/W7+49w589+6+k8Y/HC4/Jtpaf49nGP2Y1+45+NHikXG3T+vKu7ry2e0LuHPRurD4fYI7R0t37nBFYTK9gz+hsL7yXFP9geonRP+h+hXV36h+SPVPst9S/Znq51T/p+4L6n6h7iPq/qrvu09qfD8CcZ8y4v7lxH09je/3TYzngXUezw9ajueNRY3nEyDmGUbMP5yYl6bxfBXjX81j6zye37Qcz3uLGs+HQMyTjJg/+fW8Gtbner4N6/PaeZian//bfN75HwXO6JISlEzrAAAAAElFTkSuQmCC",
|
||||||
|
"informationStop2": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACbklEQVR42q3WsW7fIBAH4ENUZankNUNVP0P2KH6YDH2LmCiVulTKKzE1r2EpD1C6ebCgBxwYMCdVbbyQ/yfZ+Z0xcOCHl4F/dYjX0rtLPvd+JHrpfYcpDK+92/Sch9635Le9m5Tkpne9xuHH2jmNP5fWnUjj29z6IdP4+6X1XdH42rqd6L6H1jd6rrtt3eQXedM6xS8FZC/TQAWQ5/ilAPIcvxRAnuOXAshz/FIAeY5fCiAv8XMB5CV+LoC8+rpSAcnP+LmA5Gf8XEDyM34uIPkZPxeQ/IyfC0hexacCklfxqYDkzeKIBUSv41MB0ev4VED0Oj4VEL2Oj3d/LT7X7k5f3stdTHqo3nc4AJQV+Huq3cId+gb4W9a+wWd0A6sBUbuBT+ga8A+oXcMHdNw20B9rt+gfhZ2M3+4rF4dy0skd3dYu3eTkoQ5l/F678ljaMbngXxr3+JTZS+OP2iXjIvpycR0cX+tz93y9JH/q8pipeJN/k2O3UFw/NvOykrfvE3fboXucPvJmXrxR2Zt59FZmb+YdP2nytf2uHOOe82d6b70/0fsfuuv9V/A4v61/D/934Cp6+E4avxMhPyaaWr8HCC62uXsOfrS4ZPAY7FxH1xffwrz4sC5atxCWXlhHa7fu8I36Xfbu084x/+d65/YTZv/h9ituf+P2Q27/ZPdbbn/m9nNu/+fOC+584c4j7vyqC/i2js9HYM5TwZy/kjmv1fh8pzsv/cA2j/sHs4z7Db2O+xNg+hnB9D+S6ZfUuL/K9/X92DaP+zezjPs9vY77Q2D6ScH0n/Lar8ZLXfvbeE1/2w9z/fO79eed/wEyMn3kewrZowAAAABJRU5ErkJggg==",
|
||||||
|
"informationThumbsDown": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAA+UlEQVR42u3W0Q2CMBAG4CM8aEzMjcAoTOE87Ty6REdhhD7yQKhSUCm939AIwRjujS+XtPyUA3JiGdr9h90QlZJrokJyIuIUfyBb6ioP/Hy5sPFOgZ+uN9a9q0k/DVVM+kUf9TPo52/6j5R9dhXmMOrPgWeJTsjVRu6WcIa5LXa/5Ua+cs7yOSlgnplb8znOd66A18CdsP9uzjgdv1/+RBng/cKHaO7ZeJ74OWnjueSrfo+3wJs4f1/tc/Oxszi3R8M4cA3cAK+AW7BunejNEOZcb1+xTb5Tcj7proG/r0Kvkl2JboHX0MtVvVnIW+AO3Bd0o/b/sf/yO03ytM7x6NfjAAAAAElFTkSuQmCC",
|
||||||
|
"informationThumbsUp": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAA9klEQVR42u3WQQqDMBAF0C8uLIWSI3gUT+F5kvO0l8hRPIJLF6I1KnTa/AEDiosasvExxOQTRjHS4XH5Ce4td6T5gIp6v5N3hzs/V6t4o/qY5F5xl3HHPj4gp96jSPIOJslbxRuU1L3iTnFwH8DfOznNp8dnIend5DnxFmFY6R7TTVu8lO7CczN7IR1hi372PHIXuwlz5vUIqz/q2nSLQ/r9+TIN8ake4PXUD6j/2v8NGURqsWv1P+tLtzt5dZKn7pPmYNQ8sZfbkzzxnmieU7dqbuYkLze6cYj7UnAvYpPeihi++gz5YokbtcmdaM1Rn7z+x/7I37QxtM54FQn1AAAAAElFTkSuQmCC",
|
||||||
|
"informationWarning": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACT0lEQVR42rXWPYrjMBTAcSkJ4wkMmJB2wUWKnGErlylzne3sqbbMEXIVHUVH8HYqhLV6evrW8yyBHRcJ/GKD/c8DP2bIQ7Bv8IF23dGu9ht+pn05vuby8JqL3UT6vBtp57Qz9qRdkr4yeSedL6RrvlwpV3t12fAz5UunTxt+pFz26+EVF4NJQTOfrY+Uj+aTcmZ9oHwy4tn6Cn6n3N4q4Rr82rriNsVlw8+tL3aa1YnwLg+aXHZyr98J78WfFDS56OdfawyafB7mHylo4R+Gt85G9jbFoJlP1mPQ6DYn203i0bqxHoNG1+jX2m026zFo7efabU7rMWjhbFLH2mUHHoMm78Fj0OiiNzMz62Gq3E6n9Ri0dl67nU7wELT2+Vk6TCd4CNr4vXSYTnB5K10Fv1TO0UPQ4DCd4CFo48fSbU7nIWjjh9JtTuchaHCbzQh7TyFo47x0mw3dB23cT6h3yOb9QbufUO+Q0/std8WD+wn9h0M2I+HDB239mDtkQ/dBWz/kDjnRfVDvc3IMGnwIbgpnyfmQO5ykenfpM7nLZjSe+ajd/9P35Dp/q96Sq9wvmfPsrZ35kvsp82xDUJnjEym3V6j3zLt0FQZFdzmN/GliUHSX04gPE4N6dwXmNxODojPvUwzqfcQ9wfln9LXw+ZEcAb9cUOe69Ftwn9O7C1p6Curc5wwzeYqOOSVPQZ1jzvCzC4qOixt2xsl1Lrp8BdQQ1DnmjC9cCPels6HYJSHoV+5zpoXhga5Zdfze8NuGd//X19r70KHy8Zv28Nz/AgHnbGIis7BaAAAAAElFTkSuQmCC",
|
||||||
|
"legoColorSensor": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABAElEQVR42u3WQQqDMBAF0BEX2dULtOQaXRQ8V6E06Ql6JY/iEbLMQmKdVCw1f0DRUhADgryFmD/jRGrhqmhVb2hYxSI3w5Ofm3OXY7dkxl6bmBFn8xi7J1LIa6IMedWFjNx2boBzUcrUA7tOvemLOHbPrqa7Y89Tr9mz5V5R7jiI1IsGuw7ILV0MB5f6WfBrrHDq5k9etjTDpRzk3HDOa9VR6pO5/dZQVqO+DaQ86vOW91siP9ywH+/YTwG7nunS+whuu32h7912OZgZ80GaJ5JLc8nFMk53ae6FOJa+Pc7VOMbAvH3fp+7V9s6dlc5lXrq/dt9994/3/0WL/Tf/5y+oIIpSoJDd5QAAAABJRU5ErkJggg==",
|
||||||
|
"legoEv3icon": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABgElEQVR42t3WMXLEIAwFUHlcuPQRfBQfDbqkyCRX8lE4AiUF42SNEEh4f5FuE5fPuzMIjPTp++lz0Kt6oMcz3f24nO7ui7ubF6Z99JN9Gz2zr6Mn9mX0yD6PHtin0Xn5RKP76m7wylKA+Cm+Wc/iq/UkvliP4rP1ID5Zl+UTWffNnfHGtYDqZ/dNe+6+ak/dF+2x+6w9dJ+09+UTaffKnXLFXAD7qX3rntt+xVoAe2r7m2oB7LGdR66/YA/ap+6HdurujbvmZHwXP61v4tn6Kp6sL+LR+iweisdr6bmeQPGj+PWWD1rcF7/eulxPoDgf99vjD3uuJ6D8i577J/CPX/o7cH9zP7gz9Xa3fc+rtvQ/HNWL9gft56s5+j7R94y+f3Rf0P1C9xHdX3TfYX9A/QT1H9SvUH9D/RD1T9RvYX9G/Rz1fzQv0HxB8wjNLzTv4HxE8xTNXzSv0XxHeQDlB5Q3YD5BeQblH5SXUL5CeQzlN5T3YD5EeRLlz7+Snwf/AbPOUrbIcZsfAAAAAElFTkSuQmCC",
|
||||||
|
"legoEv3": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAuUlEQVR42u3UsQ0DIQwFUFsUlIzAKDcajMYojEBJccIxJCflgEhOkVQ+CQk/mm+DDmj7JVBXV1f/hwMAVktUXOLtcXnjAoohyjbyzou8IldO7ifvol8cd47jMBAc8aC88xRmZ+xHJPbozzGUfM9z+Zyfm+qtNbFn20cn92KylTtSxeSmnKOvly/9nhD93mHrrb+QzZyH3+9l5OEkgdZ7/+xxLLkn/M6zmZyeOYvt9dv71/+Durr6L/0BgN5AjLS7ff8AAAAASUVORK5CYII=",
|
||||||
|
"legoGyroSensor": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABVUlEQVR42u3WsU7DMBAG4GyMnZiZ6Uv0ETIQizEPwUqxmBmYPHdgKPWSF0BKBVv27Axk6USJkKBqa3K50IrfjtSWVBUoliJHn36dEvuc1jPOMfYa9bm3Gp1fuVxVvt3YYyO38j9Q/3nt15bz2Nzr6rh9QPNSon+EpR+hP0ZUZ3mPfuyTL0QPvBuV763RU5/mRYB1kiHVmWn09Jz808rrIc0zLcHVGc3vAl3fUJ3XbgwuLshzBc95pXml9QA84NVPQ8zzOieO/ENHmgzyl0X+7c6YSfTT+8KYjC4f8iNjEu3Op4E7r4t3cOWVkAfJb/X8lB3ZeVrPabHFmWP958Unxlp/wfuoQuxP9/7K77zVz3F5Y/WP4vNi9xufL6s/a/u/Oi/oWVUffdIrb07RX7g/n9Cn7Dl6XuM7fH8O4Dt9h/+jN/S7TOOkulpvvfW102Fpwvfz//wLrcogjkDLtBMAAAAASUVORK5CYII=",
|
||||||
|
"legoIrBeacon": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABVElEQVR42u3WsWqDQBzHcY8M6XaP4Ft07ZjZZ+hU0Gdop2YoJY9RHIXgLLS0Q+kQuiS4XBUzx05XEP/1ck53P4tS3bwl4YP5Yu7PxTgEV+KM7Imj13/9Tb9I01/05RezD/Ou/RxrLp0uVIWZLpatM9OLOA3ig+k5HRtOb6g0PW84iGzXHduHdr5UZ2f7SXWubZeqs7a9umw+sLG9Vvvg2k7Kr4DrNZnXnKgC9yMXRKel7WVElD3b/n2o3RzsT+5X/APNaytXr2hee/n0jubl/6w6OmvcqTju1C7uEOEOEe4QTdkRzbuE9feu+Q7tTP29xusIhjuC4U7h4U4WDusIhjplVHjHT/C7sctCdC7kovDQ+ap5FqLzSFR4+FxnIfb5+r+vF8x2wbVXzHa1TKdN67c9/a59zvb1R/U4SoOt6fdnjvemP5w59efOtJ2hc+zyqf6f/wIbEfAds1E0cwAAAABJRU5ErkJggg==",
|
||||||
|
"legoIrSensor": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACSElEQVR42u2WS04bQRCGCzOGkTczSLHEzmOx8yU8PKRsEwnvc4QcAMmDBcK7XAGxQj5Fi+QgQC7g7Lyw3KlXPxBtHkrYuTZuf65+VHXXXwabNAMb/jqHJzZ0/P4p33KcvnRHYrVMIL6EsbWdmdgPa5tC+LxoAC7VvwQ42xZuvuPcI/WvALYb4efoDn1xP6X1v42Jr3Zo3xJ9cY9b8tnvEV/uE8/Rtz0afaXxTkF8UdE4GzRQweCGA9glPjc0bnUhb0H3hCP7SfxBguwQ70z5S5/4nUR/yf4lj0viF8KP2J/3gnyMPBfehyyDTzWPs6GBlUylADpyfDxED7lMpQC6cnw8BPKl8myAEd8oLwwsZEkMANNzoldzhbxRH7qBqfLPBubK+Qb0DHBs4I+7VlpP94IDAw+OV/obB2Pg0fEevgC35p6BO0hYaeBXiufr+UWS1zBN8ez/8TzJMYQUbxGnBF97RI9XOFr71BfLyPvT58yvMIv5lveXma2X9v1Yvi4PH53/dff+3vez7h0+pvhe9M5j60d1EdtBVEc2UsbjqO54v1B3vk75fKFOfV1T/Wahrr0OUL1Pgg543SA5PASvG05nKnqA143XGadLh+T/pQ66pAmakP9tFXRMda/NclsG3VOdFEHPg06qrkoDyIKuqg4P2H8SdFh1O8qS6LbovFOexuu89IU6qJPrC9xHruxvaxeWsuj6CPedQviqF/qOFbVibiH0Kb69ofIm6mvkNFZuoj4oJvx5P93wDX8Lv1ft+Fe++b+a4n8BxZLiTc0fV/wAAAAASUVORK5CYII=",
|
||||||
|
"legoLego": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACCElEQVR42u3WMaobMRAA0JFlLBfmy+UWH8tH2DJFiH6Xa+wRcoCAZFz4EoFcI6VMin+FlAq5gEp9EJqM/BMyWlz+dCuWXfbtIlajmWEB744Ab+gZuqEWX3zxucsLcxHARLD0sl9/ZK6LRDoyYOn83YMGfYCDDLrzIAcQCeQDiDObX3gR6SGIH3Rm3yMbtpEAJuYrAK88+QQwMt/ICJiAbm0amO90Ephle1S2zLcmKSwas8C6Yz6YpLGYtrTO95Y8Wyxka+ajiwaza1N1fnTRYsLnYl46nzBYDCiLiZ0/YXBBkNver+idF1VlGzo/oUcvi8quc3FGQJBFJudnLhBUFpGcrUvIVwegK3OpKgVG35zHYaWqRNTpIHrftHQgd6cq6nbmldZ6rbKyfVnrrMh/4vcqC/OdIS/Niyp77rRd2V7nvqVteUmvnlk+DDbqBDa456zzce4uWPLEfG9Dc28v5Czf9i6Y5u6STXz656PzVBXkOpkYuH8xv1qMdbLRc8eWJlcKqbsyPyJ+xeIo1IinO97S6gydm+aZfNX7LT0pSnXDfGwzk0eK6pq7qwqTwUAVvONuqVaCRi8wDcz3Ot7yCrwLI/Phb8sIrSLZfjUUAJup1THLB4BInQR2I1U+cwnyGzxG+PQIivclET9oSO9lVP5z16/+fCcIjHf62LT088UXf3P/n//nvwGyQo0HQOyH8QAAAABJRU5ErkJggg==",
|
||||||
|
"legoLargeMotor": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACiUlEQVR42u3WT2sTQRQA8EmC5qBtLh4Eafcj5FpBm/0o+QCFFrwUikx6qgchHsQKCv0I4ifIQsE9SY/qKRu2NpfiZik1u8nujjPzJrPzr+hFUOiDkuSX8mZn5r3XIuKMAN36v+oFwk7PUM/pOfnLjlh0HM/Dom07f/zYdv4udazLIrvBC2z5jK1b9QxHjTl7hz3dK9RI2LvR0PBezn0cGo7Bk1j3gfB5qjsSvsx03xReahsI0LbwSrvN2vUNcI8Q8owNKK5tQMmjbUBxuoGCHaCZZ5nDjWLDy5zfGZy3kqfKSdzJ8Kln+0V7/+DwxMhD/bzVf4R+7zLPH7rMc9O6087+wakjz7QaXRNX/kaw7sr/nX1tOfQBQi0jz8obVn4iK97lrGBczm7A5cRT6416BOt6Q7U+qcMrSUK1nmufx6L+z3RfpsI/6F5moo92da/oBljfISN/RWcI71PDM3pCvK8ND0iEVU+FHw2e9BQvY/H9Jj7rcJ+Dh+DF4734iPuSf74agmfT/k7MvYAB54GnY7/7rJ4/9NiFj/zuR8ULDB5jv/utnm/0BfyC+N3Leh7SJgL/RPq7l/X8RGJukc9kL/np8/kvYikcgxNyTH8WuHjO/QXCURPa+FUbbTVxtc39Hbsy8JMF+VJh4vEHOO7Mqg3w908nyTomwxH78KY9K9bA325MftzFJByzD69bwdYdkX9tsNPEBA7a9pQP5AeWw4HYvw8T0V4XRozynC97E3If8wKHzZfgoeieAVv3IT2HFngku43GPXpuC/CZ6E7zPLWQ528Evy+Hy/s14orWw1eHy/oxIl/VmxGyPo2Q9WyErH8zVv1i/W0X/WXGTPSjtQD07+3/af+T/wJN3ebtyLaNLQAAAABJRU5ErkJggg==",
|
||||||
|
"legoMindstorms": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAkklEQVR42u3SsQnDMBAF0O9KpRYI0SIZ7ApDBBnAKwm8iLKB1aUQXCSTmMgRIeD2X/mK/+/goN0JoNPpdPpfno46IALrhwUnmOgCRgwRaD3Yvnuz+fkiotOUUu0wtzmPku4lv3E7f/kCl/RxXf0z5+2aj7mqW3ura9552bP1eldxb3Y5Lw+279Ftzv+k0+n0n/4ExFPChY04blkAAAAASUVORK5CYII=",
|
||||||
|
"legoMediumMotor": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABl0lEQVR42u3WvUoDQRAH8DmOJJVcY2Eh3msEFFP6AAbBxmewtBBzdnY+hG0a32BFMaWvcBAk2F1Ms8Fjx9kE9DbOPxg/SIocZBN+hGM/ZmeGWH0M/chz+niiqptPp+94zw+xTWW0s+5STjQvO5OvL25d0td8aOOh5qMiGmvez8lqPjB0rnpGe5r3xDuKPxHtLMUz2l/IDV2onpNTvYh0t7HuLtFdjn5lvCSSIHQyhi4XIC0bLOEf+uXkcvjhLHAfyZdFS6K3WfVHEU5OfPxvVf1BJuJ2367l51Vwj1zC5dHGAZepqfqdjdl26zdcNPLAi4jH7doh53Hg9zLHYbd+yyYqAjfUGbVrx5xR6Bm1Xm38Igs+DdYl3s+oKR6uV7ZnQLQtG9ec9WeiTc3/5P9gPmj+cL1gf9B+ov1H54XOF8UDih8Ybyg+UTyv2r2blx9QPkH5B+UrlN8WzZNLyueovqB6hOoXqneoPqJ6iuovqteovmv9wLz+wVuqvAd5Ov2sfe3/7mYag6z1w5r7vtqXgGymr/593/4ORMuwad9vYW4AAAAASUVORK5CYII=",
|
||||||
|
"legoSoundSensor": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABSUlEQVR42t2WsUrDUBSGk3bImAcQmqGbu6sZ+gBxCR0dfA0hg4Ojj2DBoXIdfAPplsGXqM0LFFwEq9dgEsTT/3PRoPZAQvj4uJx7DrnnBl7GIuibtzH8Lm8/KuDLIAIeEE/1+utE8ZW7i6WfTy+07861n5fgl+BPwK/APwH/6l/718FY+lMv67C61bzKevYpH8p/V/tF/pGpQxuDualbdw5kmg9U/es/+sX6zrmb/fpl/byOg/ohfw6+zb/z7X4739ZnN/xZ3YKx8DNzRHf+5RZv/OOefcqH8v+qPhX4E/BL8EvwP8+LvPCNb+aLm4WNb+fRMvTvE83Mr49IgXvNt+Zpouf7UHP/DD7w1z1Y5x54BfxR84jyoX0VwFPgyQ/x+I9xynNEPNX8EPgp8E2h+/ikuV/DvfQB+JnmmwjusTHwVPPwl+7bwN8AruTZQAWFR1QAAAAASUVORK5CYII=",
|
||||||
|
"legoTempSensor": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABrElEQVR42s3WsUoDMRwG8JwdDgS5BxDM7u5q8AGEDh446nsIERw6+grtJtci9Q2E23QSVBxb+wIFl4LVmNM77p/4fTioaIZQPn6kyeV/lygH26X67bxune/m9Y8ZyScqJbliucHjzzXKH4vzDPo872FflMQfEn9BfJf4M4v9vsV+ZLDfM8Rr4jPiT4nvEV8SXxI/IP6G+DHxDxb7kcX+1mBfaOzvff0mwA+rwgb+7tjnFozvH+iyeSGk3/VdlL/7K98xj8avvAXzId5h31Vr0PfVKvQHga/bSuyb70A8fus/z8fv00s8/6IohptF1cL15r5tVd0XvvnD2De59HK/pJf7K3xQD9LL+pHjy3oTPqhP4YN6Fj6of+GD90X4qcN+QHxJfEl8j/iM+Ix4Tbwh3mAffj9bP7bYh9/n1g8c9jvEh+dFbl3t09D3kw8fn0eTxL0vaKHReari865tDudJfG5qfL53cO6eiSf56zoZ55rkM5I/4Txl82HrsiQ3JNc/lGf/LGfz3GC5wfk2yY9IvrR4Hxc4d3NyL52S/ATny5TcYzOSG5wnf3TfJvkb6gg75melAHsAAAAASUVORK5CYII=",
|
||||||
|
"legoTouchSensor": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABB0lEQVR42mP4jxUcYKCq+B8GOOCnSLweSDQw2APJforFIYB4cVLNn/8fu/is1djFp4YSrf4BQn0jhjhEfeOoehLVi4bKYxP/p/2rH5v4nxh2d2ziPx4wPMYm/rH5hyE28cdy7yWwiR8rlz1Tjy5ewcDAs4ZtGQMDO4a4yBXWVAzxyv//X65h2/T//3M08dDQyBDW4NDQdDTxVatW/mH/tGrVcgz1wQ0MltjUb2pg0MSqnoFBEpv6zUBxUtTjNB+He3C5H7t/cYQPrvDEFf644gtP/OJKD7jSD670hit94krPo/md9PKWLupx1Re0rNeoVC+DgDwUj4qPio+KI8RBmYUa4rRpnwMAklG4kKcHXLEAAAAASUVORK5CYII=",
|
||||||
|
"legoUsSensor": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAADpUlEQVR42u2WPWgcRxTHR6g4YpwsBFRIGLZKs1ViCCRBubG7FCJyohtJnVGhRlVsCKg4bk841oKRc9WBioAxMpJvJHDSpTB47SU6UH/9bpZrVO0ZDGsx2cn/zdz6JOTDhZpAdMXevN+9fTPzPo/p935Cdsk/zNmZDy95fJZPlJyxcy8QV8yPJ0vD2YRuOpZn+H63fci1mrQ89LEFH3Iy3rR8A8vctbio0Dl84mZpHuXveIArVzddfW+4rV8wv3CI5zzHkV9aHuMyFV0hnuHBeGZ5RJfBEzzRjIdO7hseKAYxIR7hvGpS2YM6uEzmkAkd5G7T3SjMQQs3brBGJffBnXjdexz7DnHFN2IZbCgessJ92Re1dR6Y4/uVtvghLlxwfu+BlL+5XcOLTzwpf/LBFXeEEPNOZK6lvoUwy52Q5Y1rUsqdICGe5Hch/OoG4OprqIgoo4hMZn2sl5xuyDJFKjK1fHCE9X4rCtkgXyX9lZzsdJM2CUEasiQ7kk1P/m4cEUSeZFJGScjSQa/G/xJfKeKtrrilp2rpIGRRIjv6RG7XyUHOptzTx3IlC1k3FQtaibkGcfehmNcD8UUOHnnPtPYOqoZflS+0wmYhCw7Fba3bi1WX3DktfF2I5fwGax1CRR/tcuJ8RuItb1uB3xew0PvRJV6dqoGL7+rgWxInPN63/HoHXB4Qf0gqbxYs/3IBQm8O3Nkik6/3XQRMVa/vQzg6qDPmfmT0hdMCr39m9GtV8BkyedJpGb62B+F4l/i0wPKtsHx1CUJ/8b+of8Xct2Pv5Zn7PgF3Sv8YP5T+YeRP6x/rz6F/yJ8Ike6X/qTN2uTP1i8mXtLyT028nhI/NPFatHGcNvFaRhyDQ/kIIdq1cZ9B8Aq5jbh3I6SMFnM2Tz7Gy/+Izyl/Um8HKXPQoHx2t2RLv5Z/5JSHbYHrLteJO5tiSfcF5WGKlOev5J/DvPWe6iuS8jbJeuLGFEzaPBeCiRrl+SBfo3op64LWHaqLTP2MCqmlpnFEgx6EBaqjXH1DKtGAeJodQ9ijukOdkkpZp3cgfE91irpGiT8r63oW+s+prtEHUIPX3vUB7GX6APrG31KO+sYD2TF9g/qMuDnqM4momD4zti+N62Pj+t64Pjmur47rw+P6tu3z5QwZ9Xk7F04PneapOXJq6JRzhOYOxFRrxICN5o5R9oe8OZpTxrge8vDUXNOMnGk5rPKzc3PIz83TS37JL8Jjm4EX5v/v/7H/Ajl+8VgNmvuRAAAAAElFTkSuQmCC",
|
||||||
|
"objectsBomb": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAB8UlEQVR42uXWPU7DMBQHcFcZOnpkQcpFkHwUrsGAcBADY4/QozSoQ8ceAVcZOpIIhgCJH7bTunn2M6kQEgMdKvWnNHp+/vuDAfkp2Z/6S8IXCecA15Q/gs4TLiivvnMde2O9FZG3AMZl5J3UEprYtTTveQLKpSk27pswzmlHIzj6UkNH+baztcb+os3YCN9r2FP+2sM24QvKP3vc6MGfoe87GfvWeEt4AUGZBy/Ns3vC1QWdH50lcvUmErndVrTvFcvInLeMCTr/bZZYF0XCa0G7nsfusvNA5MR+7WoZ5vNQa+jrhC9AOhfY32xsTaGh76S2i0ixHHl/b+ZR2h/YW24Kcs6RKwH94IyNvVkNrozPx27yBitbj//DsQ+N9xx5BUs3Z+xmTju/R74x2XWeKZwT6+79wy5x9IVdLs7v8siVq4ePPbdboh0v44/j/cF5YT1b4/ndAFhms93YV8b14A32CjrnrMW5qtywrEvab0OvB78SyJthWIxdJtzNwNjLCefI34c2RP7hfR6ur4SzCc/Oc/1Tn/0zP7M/8Nt+bh6mnAdeTngeuJpwEe7/Ey4DbxN+WO8MaJ9F58upnfj+lvLCtw176duDXfk2YK99+dg7Xz527csJ7pmFPxiwq2j/P3VOUvfV4+tD9yfbH9+fz/cvYG0fIZhr9gsAAAAASUVORK5CYII=",
|
||||||
|
"objectsBoom": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAEWUlEQVR42rWWzW7bRhCAV5ECx74wEEKgNwroAin4BDmVUp3AYi6xUfGcQ1+jACXbgZNcrLLorUDspEDdfYnIboMAufQRSjcFcgwNX2h4wenM7orcleRbK9i09Wk0fzs/y2Dla8b+a/7nDfw9PT4v89+dLzX8lB4Hy/w5PdaW+c8pQHV/me8jl1sreARQbi7zrwOA4nKZryPPf1ziVezhux+WeYq+H1w4vKJfvgewO3A5uah47OpBkxLOoQpKl6NJGffSsjN1+Z7iQc6nqBGKmj8P4BIqxkBGjvxsDU55xlg2dPnpDfxjTpmfofnoEq4bng/ZrZhP13kWnMMnh3djnuGPdwmHDb8AFtSnHzS85FMtHjMoI5tnhveiGYV2iFwir+tMsg5YPK6/wO6q+jJ6Gt67a8VV6/mLfFtll2ebi9xEtrWgpyK/MdZPNke5YQelMUsHFi+I9zV/ZXN6fJWC/NKkZ55PsnhvnU93MNOLPGzx3SOXj+nh9+HZaK1yOLkeHrEN0eI2Z8T9EfOTAY8aLlv074bA16/S4WT3pwRfD6cWLxV/SfL7Ns9V2yr5J7YeKp4sVvpfWHZzzX2SjxvOGKMSANT/WryStf8zLc5R/lHysMlDaXgoxEux33Cp1VT3kuR28tTKzzlJMyY2xGP0p+Hq1FkraW0/SuxzrDydnQftD+IQrPPdRbu3kySCd+iPVT907N8IEcEH9GdgcYzxSstvQp42+cRSm5L8mTjWrW3mBgWm5Xf41OqXNn4WihT+FifaO8MnWG7JCGs8GXXjtls/oUAHhOjGzKpDtOUn6ECSBFB4TT1jp4e/YQGEGMCwXXOJ3P8W//oYcMyimnvozwSd2hBvdL40rzrZ0O+ngAXXxxBb9fxEHoqgwALFUh3XejCwyh957/DAIr0EDMdpFYo3eMInGGHa8HPKzw7WDzZM35rPObl+gvLHZgHNOeYnGaH8dmbnh7hQ8r9kscmnVCM2lVp+q55jJY5rKPC8dqmej01Dzti14duM6nnAs/q8cGKgnvH3IfWXnh9zu0U37mTUACMdVsNxnFDDHBF/2/AUR8czihdkWjZ5K6h0ySGG9UMtY/MJhnuHZykEVn5AVcORWk6RlQesuMG2j+7jaljgw/GtF9ysDMNVbq+C8ik4c54KUe0LKmCX0x6Jl/hB0aZViM1VetYegc6YoUV0iY/Zms0Z68z5/YZXX6wxj/Rc4xT5ruGyZ2bZdNiqrHMvUafi2dDDPWz4W8Unes17emEQD1QD53pfBGrbK65Fcr0vInuf0o4o1blKhx/Wu0ef+nx/kZBcwcHsHZZK/eazxdEuW+cL95MrgG7cY62eGTtzfk3GZ9Fp6dwfFN/PWFoM8aomFzl25R19v5tzutG8l5ilB84eVBGc9QOzmeZcr74zHATwh8316P1Ik/Afi+uMQT6Zb0r3XlrsWRvd4hdUIHKFvLeal4G5si3yaPV9+CYu09W8+n/v4Qv8X0gYw3qRwhztAAAAAElFTkSuQmCC",
|
||||||
|
"objectsFire": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAEaElEQVR42q3WPWvkRhgH8Ecr7+miInJhQarTlUZVCAQuYLJb3OFy11jCZfIR3ARSBDRxOjcHAdcWXLF742K/QCBqTMg2qbZXjrhxE53DgXzImjzPvEizaztVFrMSP/9XmpdnRgLx4KeA/9+bx3z0sLfBI+7d8zvpzj2/ld/G285r+W2u2WTGK+WZ8rp3KaAbWq17a7wcdT6SHm16Gdle9E5dbSDY9MJ9xKlL9QMOax4ZZ3SojJedAx3KzgPt7X96Ybzy1px17mpvpMM9r8mb3h3b697B9uohd6g5vWeP+Mi4S83s+gWR5QDdfPXurXvQe9t7YbzpnAW2t+gNOQUsF8bV1LDO8efYZ2p2oAawrxPpjG7d2t5gn2Ekm9T0Xgd1DQP4GqhEq96boKphCHvSWe+tVx5jXrrsiFl3PxXHmH8BuDQa2/9kx+DDZ+S6wcrv4HPYgm09b7236D4Co9EEM1/kL2AHsAty3npv8NoxwMCRzXRE708pv+Xo+SzgVjtQ3ldO61QtdWpcYvIe3aTOjGP+qaN3A3Raun/r/DbWRaSdjq91fhc90+7RTqXzU7fbf2o8bTydn1uOt6qlp+t5vH8pnQPktmeidFXemXqW46p3VX6Q2x5pV/nKtJ9GyZF5RnnmKm+w9pn0dDwce62sUOkODlVj8npT6X2s8lXvgIX6AzwHnvu5V0C07tuQTsOxy4xjeWWAlRlDPPdzF9QmavwZhDI/6BwvguX9Kfh5PI/zAU2u07sHwyRMwilWUFZZ7sCAx5znMGBy00THFlABDlP8UF7Q4u8c85zyvtOoOsE/6Y7Kh44cCeWM/kH5OfhuRT1DL7Wnocy7BfUAvdIeDygf40gor7WHX6VpAgl6Jr0x+V9izHNP773khcxHYZo4yRPjrXb//JTzAX8CP+oHAsPVPcb85DJNh8mW42ovgxJnC+LFO85PsIiMt6MSptj+yYeUWrTjmQfLXQm72N/FDXUZ/M6bCmt/mE5uN/J1BTj2fFHL/G7Q+feAY5PuNzSkMLWd8rNW5ueR8WpP5o9ayjvT3p9RPp4J+hXkI8txaMIjEcp87wH4fM5ngq5/YuU9hsmzfXGG+eE469xlPn97uhCnWEMnrHcohunBaiJWSZq+svLwrc8vlguxnMf81M7j6BxeYl522PYBf3P9WiwvsAdreSc9uvpGrA7v52fvzsVyhnnYyH+IxGp/Mw98dvOrWC7W8nKxH70fiasJTZjlzyGefczE9flGfgfz+JZ0NdrI+yye4WvddUYl2nlDi/EIT/4Sa/kGm+/P8OT3di3f0uKi/G9t+iqxHRfXGzz5o+U/W3lB+UP52Ei/tPK4hDm/kI8B/sUchOVpciAfS8l3U8sLrJ9PZH5+Y+dLCJOX8v3x4Grq9F5h/RzT7T7OrnPLscPpHvn7YC3fYofP1fvgde5a76XMSSd0/Edcjm0vcb7URr7MPctrUP0VYjW2vYX4rfJTFtjvySxM1MnZOLK9iLl6ueVM7av/ArwbP0k4AOccAAAAAElFTkSuQmCC",
|
||||||
|
"objectsFlowers": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAFKUlEQVR42rXWT2hcRRgA8O9l0p3ELjuvtwTXfSuKeChlwUuEZV9KQTyoZ4/NxV5EGjyYwpI3azDpqSsI2pMVEb0KHj1klpQkB+32qCDlrSn0UtwJoXTSvr7xm5n33u4mm4MH9xCyv2Vn5vsz31vQU18C/jc/OMPFGQ7hVE/P8ASCM5xNdXWGS2CH0zwGJsLpjjuf8h0BFdzhlIOA8hRPrdNTroBPdYk+9x88tn56XzHp25Oex/Wimzv3OPiwlnnCJj0OTjoYJ5xZ/0qNe71BgVrfmHCoVzLvTPjy1QUgpxxuLscN62koEpaGue/3hPMkQE8K//1AXHXO4oTlKwEMP+PC7pvQuM1UkMXlJ8EGWE+JbDOZ+4JkmwAmrtSTzSDO1uflZS8FsHkA1QriKMsn5ZAARAKONW+3ApHnmQCsgYf5VJFo1oKeC6ATU4D6dVNHGcS1WtBR1uE6M4tHxolE37AbJ7C0jr5n6iY9xSoBFYHtnloNfdu4gia9FTDObJdQPLy7L3hvaLfF7K2SQPDwC57xFNhGv1UD1z3QArhoz6k52Rk2l0jmJYBrNi6tvMNhG4/snHEY8CwPN4ZrLm/oa8KTy66+Inx6w+UHHSRVYOoiA8z9UU/nrlPnMUSsN2SK5P64DeAZp8NqOxKZk6ePm5lDuvJWCtTGBfTHj2vW8f/q8rYElzcIPnrsHHcp85BTdxuhtXv3vHWTRv4tuLpzeLE55vBldp3w8LPmCrj+TC4Ho4tXNm77B7C+o4s3z8H1myDPIBzNh0UOZtSAOblYApLPgQuYIpNPrNfRE+xXmh3IMw0UmvNHPf1IuC9gANhY62D7QX/xz6pyLq1HQ9s/+txuQ8csSzQ2XGT7h6Xzu0uhDk64itILu9UgHyc43ZxjuN5mmRVeK1y9ulmh+VgyDZS5vFxipPCo8JiUqJeHC7pwjs5z9wpPPUKJiLIhTwtPZmbQ8/seoLs5r2ZKlMSjTPPMZblEPOzP4QmPq+jYiz/lFfO2nS9VzpsJtFVc4TtufVWe7yQRo3nlaZDloTzPU124ZCzLQ3Uew9raep7FpruFd5C3jvKT5t5cRN/bGuTed562F/d0Z2erl/te9vxKXu7r3kF356TrX/F0B10bQOqezM6f6yO52u927WnG/EjLtbf7FVN88/5wzKuL/So1BcC3x2Neqd5bCkyBI7uW8wOtyK0H10PzPhzzR1p79/4S+OmYH1qPP/lzB9tK1NGTyNbL+CAJn3fRP603ck/6GKBgt1vrRPmi/V2kU+sDgokS8Ox9SZM5DlfR7XNcblh/nfEoOdcE3MB5+ppJuI4veTopzbzr48bOfesP32QYbe0HH6Nzz8f9EBuAr2I11NxLospyf4iuiKpGWvmeb35udK3/jZ/H7ME6UOnv3zeBOR+E+L3wm18EUfXv+XLhyZ1Qx9HsH5omt680jPedfxDqXnrxt2iYzr7jG99zXjG5eu9uiC3/ho/x5n7J5HZuN8CqNOo3C09L+K+c22M6foV2qK2TrcvX6L25HZxDHsyQkd9F/9zf2TBeJrbert/Qwb/SwyvU/3nccY+yf3mgzW8EYhsod11trDzRDBp2nmWutGKqsYJ55zZtI8eHw+qKKY5vB/Kx82Mdh2rFuPTtKC0c79fgmvGG6UNcwHk6g6saSD5cm3BzCjtRNmftWHF+aDx1T6qK9cj6kflt6TwJxl157mLh3wmXhesJf+i5ZOSeZr5PpvrT+yO/M+aPsOf1i3EPnQt3ObNZgq9Q/AsPqgGNrWWh1wAAAABJRU5ErkJggg==",
|
||||||
|
"objectsForest": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAEt0lEQVR42r2W0WscRRzHZ1nJRozZQh9MMWYsgtYXvZAHo8ZMpWAf/RcSheZFJDUPTej1ZsshVyH2kDwYoe09+OCrvhez8SQnNOYUXyqU3l63cCKlN0cKmfMmO/5mZncvJHugYLynu8/ezO/7+87v95tFMvPjo/+D7/1L3vqPeHjMfHMAvzqAo2weIZrJBSIDOM7k+76bHdd3srk3iNcG6L80QP8gndl6OMqOGyArk/sIZXKEjgRWXADH2ToP8vYAXjvAR+L96SGe6FfLqrKX5mX3uXiPdNK8Ev0umcmfKTRj7qHk4EfIGPDTh/NqXCNjhTPRS4ZHSV6izi6uy3b0ej9fZTRfrj35pS0fP3ebas6AK0E8t3VpvS6fOvEH1hzkaKNFTizd+FW8kfvpiuZgsxYanXqyUlm7scTvc829mMvWSe/KRrkrGoYrrBNwnhlZnLr5iH5xgEMCYqoYTb74+w7JGe6PIItBAvxyb4XkOjvjy93EN0tWpLzXe2tydmWtMdyQJc2ZCVu7Mz0/Q+vSKks78VMfyGcnw2keSrt8ykn8VL61vmrJ0+yuU6w/X0781IUogsLQJPaK7bXUT3WQb4uPIhdjz+7+pjk3/kT4liumyMRrNr9AEj/VshdKlrgo83VbXMCJnxB49J2SN/Yt3/+8KOpu6qcrh14u+U+v85VrRUgy9dORxQecWBXx9Xo1muz7Zsnrn8jwakM63Spb7HPIePzncFP+sCerFlNcyUfYJ53t7ZovS0vQzUJxgSYgbOQ2H65uz8mteenN6zrhSOh8w+C2mKNiSVq8ozizma6T2uQ+Zxi4LQLFAzcwPrwJqTv7Ujh/nlN+BsQzPrwKVsG6wKkR5UNVGh/EQ1WSqvlLFcUrApnC/VFZRQOEy1uLZ2OfIXD0sSpX2JSIRc2ZyTfKU7AKwzdeOHE28RNZD+YIRBoHabzNUz+R3ayXIdIIROpukcQfyLjzZRV2HFadoDk3HPde8ZHDht1olu4onYHhUn7IQAoiEzE3YbEcWuCWGnXX80QqbqJKaUcSXEX0U0Hlhq9M9rVvpVsUK37n+wWlHYrc19t3V/Oa/0U2FWfIMbxzfRmq16M9oblErq+PvVMPNO/utrrp3ACfm3tWRXHRgoGS1DnwludU1P7R+zPvpnUO+sXlnYbibTx+Lp0bUJ9sYYcqHuKbrXRuQKGw8/epuppCLB/TtJ5tGZTWKKylVcKG8YG6DUvfqbU0JP4Y1nUY+1le1pI/INY95b85XhA6cVdvOVZ4ti77/kOnlsxSPl5T3LNhMfYsuV+Kl+bPAwdl6t8c0W5NbQPPhCok7pq+9ijfg4UO/Coo3tGC1NzjZiD4o0TKA30tu8LMLVopp3NSDZqe0M/Djbrbn5NYzy3dT5WYe7F8tSEsC91vcL9/7fR+C0enSNy/OSMo0E93V6ebpn8tZgL75p55NIt0n0K1msBePM6W5oxvmM1pJV58PTR3tW+IsrxWnlxvTaJ9syRT1pEouWaaWNcnlALlanQkPFTcB4WMwh62SPie4mpP4GCSSK+xjfg9genfHPXfa/4JZ9ncCTJ5BHcByh/lAsE5TNMjPKm6QzyuUpyh5+j7z3HzY3wP/xtx7CF4mzMsBwAAAABJRU5ErkJggg==",
|
||||||
|
"objectsLightOff": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABkUlEQVR42u2Wu0oDQRSGRyQE0qTaevv1IVaxsI2Yg6VPYSnxQhDS5BmCTTgWvoGYgI+QPklptQqBRcKsM7Fw2PMfyIAgQqb8+Jjbf87OmgqOF7PjO/43vGxifm16iFtjUsTLRreJ+DzjG8QnRIeI3zKPe5LbBlE3lXydMXNb8pLcaALOT8x3kr9TRXQk+fzRMo8kn5yviDqST930PJa8fzFzB+gJPnjm4MA/PLly++/mgrc+nc+SJ5f+wGmd2+zN+5InK+wfYH9NM++3Bf/2h1v7WazPik9Rvk1IuR9W7gf7VYuV+yclL8Xvb3xQD4zr4dX7qN4Y19vC+6eSL70/QvXvxrHkhfcflP46Qf3lxj3o0zCuev8OQb8n2N8Elio+4JsAcsCTIK6QDxS/H8RV89H3ygcG/WkQV83vVBH+Iog35Msg3m38jyDekLuCOEO80P3jGL9Q/NIY+I6sFa69R+p7Z0weM49btw391ELf5hXeT179zrqKb9NCWbfIY/zodWPvf76HebEf939i2//wv+sL31hb+2Un18IAAAAASUVORK5CYII=",
|
||||||
|
"objectsLightOn": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABpUlEQVR42u3WMW7DIBQAUKoMHjkCR+FoTidGjpCjNJEHj71CIl8AqwuDxW/AQMB8JKOk7dIoSpQXzP8fyHcIoI8z+UVn//4qZ+Flyp0GH3IXYQKR+xCc5n7zbFjuyvvCc9d9/h48jFOb/O28unvEifXe8ziSPuYVXYAhhMU6og+g6dKB3PoE1x7esfW8AFwxF4/sUzf3jBZWuquMlu7mEIjbWofSZzjGXUz9Zg72y8IHm4wqfbSBdenSFrv0pU+4M5uj4aWPuHOZHtHopuqnijPUl96RLJ3/sH84X8jh4WfC/Irtdr9iSL1tvgq2L40uoXIeKj6uh3e3T+uh3u0zAHr+a65j8H3uSpL73VR+184ZbPqh4d5p0feYu0YUfXL1oeirJ+e3og9Ll6sq+vbqvmUlff7TuW+8yX1hdGvkG7p3+2l1X4B3e7V3kXnv9mSOd47gEH3a9tUp2fbUv9xzv8/JcXjGVdKGU9eEdJgvhFDM3f2o5f8PIbxlnmpcZtDxhgOeD4fXxK2MN0xV4ireMr45buv6X99wVwfcdYe7oX/0//kZ/wYyt6dnLKXVqAAAAABJRU5ErkJggg==",
|
||||||
|
"objectsLightning": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABm0lEQVR42s3WMU7DMBQGYKeuGpAqooqFAckDJ2Ark4/AITp0ZmRybtAj9CqpGDJyhUhcIGOGKCGuO9h5/4/oBJmiT0ml917+V6sRXpX6cx+Uv4zw/uwWeDG95ODzYy+98z+zHbG/SG+9vxHfEa+lNxNnn8QP2LWRXk2eW+wr4OXka0d8xP4A3Jf1TPyJ+F76ELdn7gfpfozZkbjBvrDS/XhXDvuS+A34Pv14N8TvgTdxe2aeE38F7sdbEDfELfDyB3fA/VhG7BlxlNPh4g65ljm9PC9yGraAeicuctoF/8Ke1di1yGMb3GCXuQt+J3LXnP2R+Fb0s1I4d8F3xGW+ShWvmbkfsWuZr7Tcma9FjsIYZY6Cb4ijvPiO7onXyJtonaSeGewLlIvpdumww1xMnbslviGuocfrIfUC+RCvgdTtNd7HayB1+H/dxWsgcU08h97GZf3Cm7jcxC1xBz09kUSeYS/1dZ6UG3tB3EAfkrJid9B7NV7nGfZOE8+xtwVxQ9xib5g77BU5T56IfxCviR+IH4lb4u6fnLe/AYeBsM+fqRuCAAAAAElFTkSuQmCC",
|
||||||
|
"objectsNight": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACL0lEQVR42tWWO27kMAyGqVEQuxistjSwA9hHmDLABus9io+QMlWsImUOkyPoKAb2Ai5dGKNYsqiX6UGmWCBR4bG+oUXqJ0UI4PuOWvrXdjTPcp10HWKhtVp+TnbCZIW813paftY5h6PDXGs9+DUPcBeW0cHt3fKP86r1jFRFfDEfAy+BheWVw2wIvIiWF5fAF7cX68/YzIE7t/eWT4H3q9vWyMLHwN2ukGOczIVTQ8pNmBL5IeiAYf5CtZxuLkw/UGcRqRPnpc65y2O7JmUz+kjNjA8U1/tcEZhd4ZLgPOJlxvG9inic3VPKvWyPgHlO+R/zOEIus7CC/IBc5sJyt5uIcytIr3L5mTVtJXIvf8g/wdsd/nuHw3b9fxIaCIdo8pUj4Snicdq7lFebvFzn8GV47Xio9SrWh73BOa4K5Pwenin7w8HbQ8bhU1z49Td8XuMhOeRntciPNVHnn+F8n2vYOadww/mFDX/Y6Q+V7zPKdDs/yrj/cCrxw7XEY1vOEmDKnxQauKSF5ooWrlC0QGKghch574SoR3JjPNdPrBur8/7tAhX5QXCBFhud1kC5z8M5OFZ2H3LtJk+pY/sVU8uHwYFxbL0sorJLtDPzxPmc3hN83U3pvUJv7F24yF93eDbmF5qPNc1V8b9uTkzexrm6jQuXcCHjNLpMmZOlUjkxI+cuk9+ZNA38Bd//llKbaI5+m4YlJYf2545PFK8UbS9kyk8PXqn3mP8sv+sd+gPiXI5c60PqyAAAAABJRU5ErkJggg==",
|
||||||
|
"objectsPirate": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACYUlEQVR42s3WvW4cIRAA4CErhcYSbYpIvELKdJsoL+QyxSmsdYVfa08u8hp0aSmJhJbAwMHws5VjyZbOp/tuj99hGPDTvx3+v5tl6geAnPkGAGr08Dj5QXUbnY9uoi+j79FhdOQva+/YLVxk7w79l+gdhwOS967R+a33NByme9/Q4VHN/WvvieHz2vqR/UG27rJ/FHP/0LnNvvC5s+fWTXa4nvjLif9oXd/9+4mz1/l+d3j3fgnhs4/OwtZLM7oIL3WMLsNaxHAZ3bCZr94uMex6V95x+vmd+LDvZ76ibxPH+Q6e14f1cSu8/haO2dI797tWbvTFg5G2dwvMMrsYmJyvxdUMR88puhjPOz9qpqT5gcd/65h/hK8JkeYx6WtipXlPhmgQb+LP6HGcrbOSyJo8fEDjC/WcEMN0D1Zd5S/i467zLa9Odd+4PXFD2hHUy3gcRs0TbOh7cVxjf33Sj9GhtGPw4rrezM8VP9xd4834O3m6JYuHjbqZi8KAWbPvKY+8mDqq6tL/+Zvztyy+3xNkePxTcQ32vgbh3VZnPkdm+B1vXKeQioN0pd9wsHLIxgm6Mn6HCSb2HI9Zdc/TxRAtRUF7Ly8YMZP7Xfl5ncBn9QbM6w2cgpi4G+uHLajEJW7rhDDzsNuGFCI5HmRoRB0pQA2vLvbYxI5D1SROHrAJ3Cy1MxI/6UbD0mIDEj+pS12uEuKy3PKt1+qiceHpE7VfVcuLOn6oeY04yUeWeuyO7ELdX1MXXqcm767ILojqzJPycS2uiRvwU7evcHPillS8jvghqmNamtXPy1vV4Y3/A9znMVqMtuZiAAAAAElFTkSuQmCC",
|
||||||
|
"objectsSnow": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAE/0lEQVR42o2WwWskRRTGX1shNYE2FdlLDos94+59I+shsCHNopCjR48mrLi3NSEgOUSn1yzqYWHBs5D9Jzx5sIcFRwi4B6/L2u0Ec5KpYVamYzrVvvequnqSibhDMpn5pbrq1Vffe6+guvKVwmvx9w6v5o9ieh/O8EV1JTf/wc+PJ/Snf5mXxydXz7ND/Pfh2SWuIcD3o2d95uUlPv70+SVejmieVw9uMX/85OK65gO7bhr5B37t0n8PusQnWdzl4P2+Dnj8uKhYlVP8/Zo+lQ95/Pjxc+a61s18YsfPCc9/IJ31A0X8WvuawG8Tfe4WH9/n+MXqrsRvuS66lh8d/Ua8d/M7CjTbzhx3uiU3lyg6vZTUuxi+JB622z2vDb/6fxFfBcgpahB+fE5cJ4ATHxqQ1eE0H3x1HdeNqiSqnIDDtIvcyAC5qjAcVbuH+EAK4mNddmvO82RJSDzT2vM6nh3ksJKqC9zGr2Ap8fyl26+spAIAxceSO9+yPoEKQagWcr11YLlYQT1xDsAfDKDoSMtZf+RpdJHzeWUqzHYVCVKCcjzE8y1EuPIsJEUNPeT9UAByq3TN2T8ZhEsHQPOgOFXX+g0nTCyP8NG0ojfrTwN6HuZBS4MhkOjEXwwrIwrcb1ASzysT1T43MkMOX0pEnrOfEuLou+g0H5i4ySPYBFp2EmV5r7zjuQk0wLasRuqXrWS/43kps8WgEKhH0oH9oOGoWbJI/FGntSea+UvkQlRa/gzLRcPN28TxVIJ+speJqfmRKzwHOElPU+h6HhQqMKICuJGOEoiJd1l1jB/nhW2Ra9Fw3m9bVbJALnXNjWJ9YjxayLPI5vsXyIUmXhLvJJZPbtP0xTzMQSExbTqUIMhPPyTZIIQD2KTExRfzf27xOfM5osBrOIK5iTnryA8IegbijPk5zeP9068gLpj//RFlNfptTH5L90GlPk7yZxKTP5t1eb/ez7S/oOHe/wbjj2qu6nyRsQlIh5+szsrnlyxRN1XGU5zzEfZI58Ryg3Kpp1p/rOhceiOw+lclCqUGusTn6BxH9TnyeFsf6NyL+txHLV9PtOzDau0TDb7+oH86y3vOb5ryquB6xX6rfWgEP0QfsydJJ9m/y9x0QuIrVA/HKt1Ky9s2X0S44etndJ5PXF6UMhRUbzUZpsmjP9blokwrrGOV4zYf31lXSqaHXM8rzkfL32gvLwDmds1Tx5P4YSvGHnPKxm7yPYl7zM/qzIxt3fh2LWjJpif5OlOGIpTi0Pewui7puSCcb9qkr2ODdNTa3uvOcFSmlZqmd9Z10ujloDdpmm0+dOu+gmB+MHt/KO9jUpzMcnMvgbV+04M9f1dD8c0sp8rW9CjXpWe5VWSGuy5N87+lQfsW77o08RspLPt4XJem+O8BzG14brs08c9bAfj9ui6N/M9iFdLjmrsujTzXO63U6+y6NPEc9V+quevSyL/ncyRGJ++6NPnhehBy62TuunTtk6Tmtks3nIIe46/t0sShvYA+rMfbLs3+XH9zQRGfnGGHsF3a+pz8TBeaHHdtu7TNI/Q/n9SPG5Xr0i6PFu3V4U48HT/l1/veydyl3b0uDz9jnl3Qx9YHem2Sniu7fnwRNBnR6G/rA68bTZ+XrSccJ1ZY7tKOn9l7w4j21fjB1ivW/3zaP1Ocnaaa+7C7f4wu+LPhPP7F8Gpezt6rz9y18vXv5//P/wU3cEY41pvMeQAAAABJRU5ErkJggg==",
|
||||||
|
"objectsTarget": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAADzElEQVR42q2Wv0/bQBTHL7WCBwSeKiSq1p3pkC6oQ0XMn8BQR50i5i6wgTAlDqjtv8EcGBgz1JGl/gNZIjEgc90iVGEPEY6a4Pa98/nu7MTAgKfk4+/dO797v8i/uY9P5O/Eeh5Otbk8IkSfxwk8rVk+Rm7Mch95ZZYTsvBebiT4lGh/LUrMIo+I1asdiRMJ7lcm17HnV4rcNbzQCcakwBPyMYi98IhYeT4l9yCPPdfM80hDuRNQPc/pEpOH+1qe+2+YPP5ZyXN3h8mda5LnJJV7odtSeaKl8iCmlsqnOpc7fVPl40Uu94aGyqPXXB6MdJXTemb3QFO5v5PZ9SoqdzO5E7gqbwt5uKlyTcjj05bkiSbkTvphnFcR9mr4JlL4dJnJP7CrNCUfG6i8ZVe5byh8BZWRzm5A4RFTErKHr3WFb7AYIQbupnDaBPk2IRpupCkc3eO+eMtuTeE+/if1OsFjtRU+wUi4eefjNSi8DfJL3R7sLmeO4xy/f7XTOddhI1/yE4zAddtuoF+p5C9w+U2nc4YLaUtwjJ62Dc8mLpQco+e4A88pLNwVPNFheRX1W7DwwhIcr2UB9RfgqL7kcOzbJdR/hjgaSg7XMlpF/bkJfhV8Crl4+BL1n+CC9k3BIWq7a6jv4C/JwV2X66hvQJpdSQ7JNfyC8jM4wR/JwVpkswccN5IcrO2mevjyQ8lfwVcOmP4HXJDgY7DbBzF49BvYNQQHu3RgN+BIx2BXcghYCrvDJ5w4wZ3klhf+HtjNDdv+BbVGcB/8TNY6F+BS0owvKoKDn8m6vVW1G2TH6Uu+EHuZfs+LKnJ/J8j2nwSJtAuJss3PA5ktOXzvaXr+E54YD34v+jlK/fN9xs/cnzN+5v4v+rmb6hdVP5feL9zqMI0H/CU5xlUaPyYvBDzewFoabxhXMg7BWi+NT0j87uPxDH4O0/hv8gLE8wXSIc0XLCtKfkEepfmFdULhkEc+y8es8D2cv21MQ5bvuFDhsLyL9QG7jMJ9WH6F9QS7jFpnWHmD+oP1qf14vaJYCCnUN6xXWqFOYj2cVydjwsqbWifHrNtFrCmNjMfr85RtfMd26yr1nBWg8IA1pb7aFzTRRjy1X7DCx9/Q1hP6kSv7Ua5/+U2xUa7f0Vom7+X6Y7SSya/0p/TfaTWzG5n5/p7Z3bby88AkO2YrPz985RuRp80b1Ejll1pxnuHe1IvzTzqeFOefhFjsKovzUtl8BfNYEN+Hs/MYzG8H9WR2foN5L6nNmffK5sPSebJs/iybV0vn27J5+Nnm7bJ5Psf/A67d6k1IisxZAAAAAElFTkSuQmCC",
|
||||||
|
"progressBar0": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAiUlEQVR42u3UoQ2AQAwF0KKQWNzNwATHSjgkNwFho0tOgGAIEgSaYCBpDhjg11+glU80TftTumF5UldXT9G5CBfyM3cH9LGEvq/lJvhssU8Guht66EHwu+rhnLGBnsfWwD0w29/mYSFQmSeP/NUOek0Fas9EBnlU/4pLd5dyIuVKyqH+bXX15P0BRBCejozMPEMAAAAASUVORK5CYII=",
|
||||||
|
"progressBar1": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAA7klEQVR42u3UMQrCMBTG8YhIwcVJ6JYr6NYtbp0LehC3bk0vYPECLi7iKQJZHDyE0sFV6FKhJCqVqu33dsG88cef9kEfZRaOYs6dO/9BV2Vf34DLuJ8WwFeB9ZFvHn4BboKRfxRdrwLPP/Cul4lO11nXrzbXyM82t1PgutBmCVxOPBOD98qAV5X423s4MTA9xRTyh0roMzbc15N9Pr5ibLyoh3+6YU0vWv7q5wnudxb3kcX9luhDos+InhO9wP33+u/+e/13H1ncb4k+JPqM6DnRC9y31m/61vpPH4D1n9+duhPqrqg7dP9t585/3u93SkjFUCzChQAAAABJRU5ErkJggg==",
|
||||||
|
"progressBar2": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAA5klEQVR42u3UsQqCUBTG8dMQQotT4OYzuDkIt81ZqAdxc0ufQHoGF+kpBAcbeojAoTlaCi7edDA1v7ML3TP++KOX60FScArSrl37Aj32zPKN3DWSJ/D0VVnQH7V1R89p/SqAh7V1secunTo5pXNvnLpkXDnAPbdqQuAUGU0E3ptIU0rxt/twIzCrggrkrcbQd7Q5j6a/fkm0PYymv/6Gpr0YfNzvj7jPFe4DhfuM6X2mT5neZnqB++/xf/pc4T5QuM+Y3mf6lOltphe4H44/7Yfjd74Gx+++O7cn3F5xe6j/29q1L94/+ZwRM/Tj2IYAAAAASUVORK5CYII=",
|
||||||
|
"progressBar3": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAA5klEQVR42u3UsQqCUBTG8RMRQktTcLf7DG1ut61ZqAdpcyunRukZXKKnEO5QQw8ROLQaLgbiSYUW/c4udE/bjz900IPEcFJy7tz5CJ38qf1gjwrglc8KesjqBbwMF+phgJeeuuuh52yjczz0N2cWedb8VsBtYes98Fvu1SH431Opq8r87T08CcwkpRR5o0foa5pf+9M804pouevN9sBc07C/cOeDPmDcJ0K/EfpY6LXQG9y366O+XR/1AeM+EfqN0MdCr4Xe4L5bH/Td+qAPGPfJz2dg/fa9S3ci3ZV0h+677dz56P0LdFTgaNorlhkAAAAASUVORK5CYII=",
|
||||||
|
"progressBar4": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAo0lEQVR42mP4jxUcYBgVHxUfFR984vIH/vAf/okh/k++oYK98TOG+A/5norjEtjE5QwfSzzHYj5I/Iw9dvET8ljFGyf2YxU/jEP8vyEWcaA7/xViiP9gBvrrX4U8pjgwHP78sR+x6eEBAxbAeIDhADZxoGgDVnEHBq5VWMAKBgbRUCwghAG7+lWj6oeaelYc6nGlE1zpClc6HC23R8VHxQe9OACi8J4xH2oOzQAAAABJRU5ErkJggg==",
|
||||||
|
"progressDial0": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACXElEQVR42u3Wv27TQBzA8Z8bKR5Q604whMZPwMBSVULB4jlYIhhgyCXNgpBA2BAhXoNH6IAu3WorA2vHqFeplpiDzBJ5cByc+2Pf/XxIMIAYakWK81H89SVxfAdb6xbDrf9zD+xe+n/X9w4Dm8d7h2DxAir32h7vHFpeAjz2AXzsOXjf7xXgYo9hQ3sT7aXcAY+yAS0gML2Ad4ys2STyTM8cRmj1SDumpx5n9tYxPRpwJnMIdS9hLTos9nUvOpJJ5umeu5Lp0tU9PZLMxo7u8SvVmUe6R4qrE4eaOyvFJA0aL9yaae41nh80nZFb+4+sR5oDOrVn6aBh5nyrfbhumEQL5flDjWkyU17oefbcVV56VD/Aq8fZ1fnaV76Z6Z3XQX3ehd45e1H7M71z+bUef6h3pvU4U53JxXvlsf5xq19AeWIwSZUbwyQ0C5UbzE6Vd83OeSC8dM3O0pe+b3ZG0ou+2dl9odx7Jl9JzwdmZ+JKPzY7c+nZG7PDZtIRE+VrNJ6F8BQxeyJ9hTqJ8AQxTaXjzlD4Ao/nTPgMdy5D7h9w51T4J9w5F/4Rd5bCu7jzMuDutjrC93HnWvgB7oy4l3dw54vPvd0Rji8HNhbewx0q/AjzlfC79k7R7njc2x3hx7gzEf4Id+bCH+AO+0WH/GZnY++M+/ZOffM2OtXNOGl3bqp5DaatzjarGE5Weme7pdXTbrsv/yA3/PqMOtsIhD+V1yGElYMzFAx9mf8MAfcL+f6+nDVS7lVH9UM5+fGO3N25Zd7fjf/Evh6IpnaPE7vn7p+tN27Xe/+R/wQBUibT3x/mZwAAAABJRU5ErkJggg==",
|
||||||
|
"progressDial1": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACw0lEQVR42u3Wv27TQBwH8J9rqR5QcScY2sZPwNClQiqpxXOwVHSAIU7KEkWC4oMIsfAQPAJI6JKhCEcZOtIxyqFgqQNTkLtElnAd7Pvnn9NbGEAMjTr4Ps19ffe7OzuwNH4iuPF/7r7Zc+/v+tqmb/JobRMMnkHh7nWPSodrngMceADeqqfg/rybgbPqEVzRrQ5qygtwKWvSDPy6Z/CSBQvWIW7dE4sFtPiL7brHLmf2wqo7aXIOBhBiz2EhcljkYc9syUHiYk8dyXTiYI93JLO2hT3qqpwBwU4UFzcOkVtzxUHsV545mmnqVp7ernJajvbLZCuoOtjak7hZMbMutB8uKg7IWHm6i5iO+sozHM+OHOW5S3EHV49zHfM3T/lVH+f0fH3fMc758ET7Y5xzfqbHH+KcZ3qcMebgyyvl0eIX/k+kfMQ+4w6x8n4wwzdOQuX0B845Vr7OjnDO0BeeO8FHnDPxpG/QyRx1aEnPGqzVXSko96L8+4in0tMmne6gnI4jfY918AoMpCfPg0FtBfrSi8sRnoDyYpfEeDxj4aUlJ6jDQ+nFpHoN1GEkvMw+dVFOLL24nKGdzg6Fj8tjZeMdIbxfzgWdGHoecn9dtki3tjClvy1bEarcUPgbPgZUuYlwXrNjdMSe+tz5M2Bo1xa49A2+uy2004XzhDagBeae3+ItqHI+edx5DoMDfGJKF6UkVeXawsVpjFDlhIsZHVaVmwq/I4pYVU7kyFM9gQXacKWLnBbsow1X+p5oga5cR7j43hR05QbC74kvgX6GMpwzILaeAM5hERhzghhOTDk0gYYxp6dvXM85BcuYM4NdY04b7s9xznKpXi3bsnLf+f4kqvBk+5HchxAWDpZ60DbkxXvwucthkIbcuzF3Il9xy3cPQvny4zn68zUzv/fPUrNfXJo9Hpk9df7s98bN773/yH8D54AvVfwldP8AAAAASUVORK5CYII=",
|
||||||
|
"progressDial2": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACjUlEQVR42u3WsW7TQBjA8XMs4qGk7lSGqPUTMLB0QcHiOVgqGOiQS2CpkEDYECEWBkZGXgApQ3WpBBKOMnSkY9SLwFLnIHeJPLgO9n3fOXcXd2CgYmhUWddffH9fL64csqx9ReTGr939es+9f+uNLb/Oo8YWqfGMFO6ue1Q6WfOckAceIZ7pKXF/38mIY3pELlm7r/yKA+Iy3mEZ8XXPyGtOF7wfuronFqes+Ilt3WNXMH9l6R52WNmhIxKonpPC2NdiQuSpntll5EsxJ3FVTx3IUzZ1VI93GHZ6lurRoeyMQtVDLjs8ChS35uWpZYfG/sozR5wqLpK6K083RWS7PHadyi+SdskjcWR25UncWXW4dV75/mLVoeFEenqPK53xQHoGgJ0njvTcZUqHudU6m2pn5km/HKidF3513YmA/q6YNnxa+WMIb4jj6Um1/kDAWUtMe16tM4Z8D3bp+xvp0ULt8Ej6mKsdGksfaB2WBNKZ1nkmval3jn3w3NE7Uw+9pXe66Bn8/eynfN9Fx+3vyuugp/Dp0iO8Tt9B34PzZrfg/RF68lLv8AE61TtU+kLvsAl4zI3OQ/S50RmDj5nRidHNzj74BKd/k50huNz+qY2D00D4W5x+0ODVB1P6e5x+bMsB+DuzMwVvmp0DX7iz1gFvmZ0Z+KbZ6QrPN8zOkSd8vQO+a3Z64G2zw8B35LZYODgD35bbYmkdeVvRoUWrG6709Q74ntnpg983OyPwu2aHX9Gh19uJr+jsk/pOROZqZ7nE6RHBf5Bf4v4MbZwekke4MBIUTuQyPjZw8Jn4wrHzqXmICxMe4iNu+eF2gA8/0aleP7L65/5JWu/nF/Uej+s9df7u+8bN973/yP8AnFAiX5+Wj+sAAAAASUVORK5CYII=",
|
||||||
|
"progressDial3": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACtElEQVR42u3Wv2/TQBQH8OdGqgdo3akMUfBfwMDCUgWL/6SiAwxxEpaqEhU2RIiN/gcVf0IG5IQJR5HIBh2rXAQndQY5S+TBdcjdvfM9u14YQAyNqurykf31u58OrGs/Mdz6P3ev3nP37/rWnlfn8dYe1HgGG3dueiwcbngO8NgFcKuegvPrXgZ21WO4jpo98hUb4ESsHWXglT2DV8xfsV7olD2xmB9t/nij7NyRzE6tsodtyf4IAuo5rFQOi13qWQPZTxzqqY0cXdrUeQuZdS3q8bHOGYXUQ82bBwfErZ+afe4Zz+yCo9Qxnu6anI5d+DJp+uaGRuEJbxtm1lXhhyvDfjjVnj4kHE0G2jMaz45s7bkT0Rucos5tygtX+/WA5px4xXOnNGf4rPCnNOdiVtQf0JwXRZ2csv/5tfaYdnfEYu0TVrqBay+V2V0lgXZ69SfW117q7tIfe8pzuzRs0aWLvkNzvrAOenaflnkmB1R6afhb0Rw9pbPb3Wc9G/0RyRkf+CP05CXJSTb/B+g0XiwN7XTYxFhNlXNaphirJ+hmMUffxQqeKJ+Q+I5YwRydlin6eKh8SssUG22onA4/F328CKS/oYtZ9LGv/B0pE0RtY+VvSZmW3NvKyTSeyvPguSedTONQtsfKyTRy2V4oJ5s0llPakZ7fIWXKKf3oSt8hZbZxxwg3y2EB8lzpKjfL4QRUQ3nLlKnOlbnyfVNmIzI5ZFnF23oHCy9yeqBO17lys6xAXdJTfmDKVI8aKX9gysRLKjkJYKOSE+MpXc0J8SVQyelCozZnDHiMVnI47NbmHEGzNmeG3cWc9RpzvgLe+UOuz1C/Q94DbrQ+BBsHC3POdXc/gCcdc87hWBfmyRx8RZ7dDfDlJ3OKz7es/r0/S+v9alnvfFLvqf1nvzduf+/9R/4b6couiYOY0EIAAAAASUVORK5CYII=",
|
||||||
|
"progressDial4": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACWUlEQVR42u3Wv27TQBzA8Z8bKTeg1p1giFo/AQNLFxQs3qSiAwy5JCwICYQNEWLrIyAeoQNysuEoQzfUMepVwlLnIrNYHhwb5/7Ydz8fEgwghp4yJB8pX//iWLqDyrpiuPV/7r7dS+/v+s6+b/N4Zx8sXkDtbtfjrUPHS4BHHoCHPQf3+70CCPYYNtFgon2Ub8CN2DAqwDe9gDeMZmwSuqanDqNR/Up6picuZ/baMT0ccqZzCHQvIRMdFnu6Fz3JNHV1z4nkaE10Tw4ks7Gje/xCdeah7qHi+sKB5s6NYpr4rRek4Sh3W8/32s6INP4jHdD2C73G02TYMnOuGz/OWqbhSnn+QONoOVNe6Hl2QpSXbqR/wW3m7Ot85SnfzPTOS7+57krvnD1t/IneuThv5g/0zvNmzkRn+uWt8lj/ufU/oHxpME2UG2PSKA2UG8ymyvtmZ+ELL4nZWXvSd83OSHpxaHa2N5T7wORL6fnQ7EyI9COzM5eevjI7bCYdMVWeoXlWwhPE7LH0G9RZCl8ijhLpuHMsfIXnORM+w52LgPs73JkK/4A7C+HvcWctvI87z3zupNMRvos7V8L3cGfEvbyDO5897t2OcPw4sLHwAe5Ewg8wXwq/a+8U3Y7LvdsRfoQ7E+EPcWcu/D7usF906G92NvbO+NDeWRB7J6mfS1vnJASnsnTOoV5BqTpVJTtft17veNk3/nyGag85BbGCKQS1gyM7H6U7n8DnLjvKg4R7KLe46lT2q5R3msWvG3T3/XpOx3YeuA7t54TtfbB5Tv7svHF73vuP/Ce0kyhgevPCZQAAAABJRU5ErkJggg==",
|
||||||
|
"progressDots0": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAoElEQVR42mP4jxUcYBgVHxUfFR8VH1DxP+woBFz88Q8UAi4+/789MgEXr2G/j0zAxP99/vkeiYCL/z3/rxyJgIv/3v9/NxIBF/9e//8uEgEX//b//1skAi7+DoQQBFz8LUgLgiAofhvopHoEQZQ4gzSMoEycFHtJ9Reu8MEVnrjCH1d84YpfXOkBV/rBmd5wpU9c6Xk0v4+Kj4qPitNfHACn+Bo8YhiGeAAAAABJRU5ErkJggg==",
|
||||||
|
"progressDots1": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAwElEQVR42u3TQQqCUBDG8S+CXNUFopO06FhtAruJB2mRN2gbRLRoWxYEz8J8kzU2KDSUBLUZF3/hh6DvvRH08ophbm5u/le/BrWIb8+1iEc0qkZ8Emyqebo/tg7kT5cy4nmMMeULX0Y8m2JI2ZxmHPEUGFAa0poj7oAuOaI9RzwBepQUd474Dujcn3Oct74E2uGq+C7OR45+QY98503e23Rd2v5o+6ntv3Ze2vlq86DNjzpv2nxq82z/u7m5+e/9Btpp4iJQL2EkAAAAAElFTkSuQmCC",
|
||||||
|
"progressDots2": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAxElEQVR42u3TQQqCUBAG4D+CXNU6iE7SomO5CewmHaRF3qBtENGibWoQaGG+SRsaFBpKgtqMyPDzweM9542gl08Ic3Nz87/6zWsU8cOlUcQXNK0X8Zm3r5enu/M1IXfqJJzEi7XzqQjhcxLPV7SkfI4JJ/EsoB1lwJiTeEoUUQr0OYnH1RsDA07iUbXkCPQ4vfVteaRgA3SDR/rIMSwdo6+9zb5tv0vrj9ZPrf/afWn3q82DNj/qvGnzqc2z/e/m5ua/9zvyr+IiXMvuKQAAAABJRU5ErkJggg==",
|
||||||
|
"progressDots3": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAvElEQVR42mP4jxUcYBgVHxUfFR8VH1DxP+woBFz88Q8UAi4+/789MgEXr2G/j0zAxP99/vkeSnxgfI8Q/3v+XzmUOMBQjhD/vf//bijRwGCNEP9e//8ulGBgkEWIf/v//y2UYGDgRYi/AyEIwcDAhxB/C9ICJt4wMLARFr8NdFI9mLjBwMBcT5Q4gzSIkKBYnBR7SfUXrvDBFZ64wh9XfOGKX1zpAVf6wZnecKVPXOl5NL+Pio+Kj4rTXxwACwTiIq5kKFcAAAAASUVORK5CYII=",
|
||||||
|
"progressHourglass0": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAB8UlEQVR42t2WsU4CQRRFMRZWZHsavoKab7AgWpgQYkdFaeV08B3GxkAi2FmYDHGifAEfsKGzWgqTDVn2uW8X2WHenQSjRONUm7M3s/fNzrszFYJjWvldHlc242SXR5/8eH9+xA8LoFceXvfwL3zX559mm/fuOkw3+n25b57/yl9a+Th3+eswH3ff1c88+udCP3H5otCfCl7oH1weFvqOy5eFfuHyaJ7rjcvjLstHA5cnDZafBS5Pa6y/r4u+q7L+oil4n/WPSnDD+p7s35D1oeR5AUZyLmBr3+JcwNa+xbmArX07N6qWfZv3Lfs2N5Z9m4eWfZtnBRjE425p3+ZJo7Rv87RW2t/JvWppf4f3S/s73PRwfoY3mC8vPfzWM08HczPBfHCFfQZPsK60fg3XIWlquG6xUnCdI9Lwv4Sk4H80pCPEB6RitE8C0gnYV2mdVAr2YabVBPZtNrcisM8zL5pAX2TeFYE+MqyPJB+wPpZ9GrA+EX3N1hWlIgdYqYlEbvDMikjkDDvRRCKXlpTrRY6FhT4UOVnol4hrwPmDbdouhH1epCN8jqxbmL8NMZ+3FOLpcDhGfJ3lFeKrLN8Q5zxEnPNTA85525Z8xfqx5O95/ktenC/xvued93w87L3lJ+973ntd08ODA973/ti9WvAPeLUJTL11Ut0AAAAASUVORK5CYII=",
|
||||||
|
"progressHourglass1": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAB00lEQVR42u2WsWrDMBCGUzp0Ct6z+Cky+ylEOhRC6ZYpY6Zqi5+jdCkKNB0zFBQQbZ8gD2CydXKGgAmO1XPiIMv6BSkktJRqiMyX39JZuvu5loZj3vpZnrWqcWXz9MAvj+cX5cMS6LmHhx7+jX198euP6v/mOcwr/bHct84/Py1X1T03eZUfq2N5Yk2Gr6zXDE+t7Q3P+G6KmzyPdlPQ5EVofq26C/S2f3irzuNCTA671LnaMsZTt36TTyFk4vJ0wVhfuTwbCDGJXZ53GesFLi86QjyHwDfajN1EgI+FmHHAFWND5D+JEAni9AEK8Wxgwq/zvGvCr/OiY8K3fK9twrf42IRvcTXE/pk8YL668/BHzzq3mKsXzOMRjjN4hd9VhPfwHPJIwnPLOIfnnGoJ7yXRHN6j0jJFPNY8Q3kSaJmDvKIS4gXIQ9JKDfKW1uYa5DnFIjWoC4qda1BHqtSDuotLPajTQBcS1DWFTvXu+gApN1PgG7Tyugd8hiIRAvgS+Rj5g+tjiS7IH1zfW+o1+QPyT0H+4PLSfmoGZHx7Q3oxdf18UeqZy8VuuHwnZ6Mmf9/rZ03+ttdfe/RPf01/5r7llP2et6+LPDw4Y7/3y/pqh38B9mUeE1Ss9HoAAAAASUVORK5CYII=",
|
||||||
|
"progressHourglass2": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABqElEQVR42u2VsU7DMBCG3alb/QQoPAIj6kBeAledQH2IbkgpUxhQty4s7Gml9g1AyjN0b9ZOHTtYDT5foTn/RgKEYEmkRs6nz7XvbJ9VHX1eVctb/tf8XqDsg4tOk/TH/LLJp1/gSmmGVqme4B3mm5CrjAeV/LnepJ53ajsV3CbUOOiQ135gmwLPqbHPgJfU2NXAK2pUyHfUKJHvKYAcuaVPjfyQ8C/k3k0jPOcxgJc8J+AVxwB8xzEDd3+ex7ibjI7xQ0LTl3xGb+1DFvzphQLwKZL+LQXgUyr9JQVQAZ8ZCqBEv6AAuhHfDWB1xF9y9iW/mBnzvp0FfyqoA/PHEz93vsmQK+cXxZE/NDj55iYB7v0Fz3MS+sMucO+vyrg/quL+Mc/gu3UxEX+u7VUR8Qdu3V1awV+kda+Yoz90+8plCfyVW3nqFvojt/JrY65Dn/b5NuLTuRijP6dz1Ed/QOfuDP0FffbQH/pzjf7K1wH0R75urMHnOrMFn+vSOPSXvBn6oX93rHvSb9bJJm/WVSX4qQ5LfqrbKv31e+S7/LN7rb33W/7P/A2qqP8PNx6qQwAAAABJRU5ErkJggg==",
|
||||||
|
"progressTimer0": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACJUlEQVR42rXWO27cMBAA0BFUqJRvwCukdKdruTBA2jmIr6JUuQaNAHbLII0ECBzzI4kzIpmVkM02KzxwqeEsORzA4meE265AFh1gKLkFEEUXtuwD3sdr8ze66NjoYvz4osvr/f0LT/u0Pf/kbiQagBbxO3cXigJwiW64j2JxDL0F7q+99t4sD5J51/lpAJ4eB+q2f3WTuKkengX1RYw+GBeS7alPgw5/LjTYUTdyDAFqWBewukYVxk0gf1B/t3EvuK93Nn5xS/1wU6mejTdPzrzrjs0/fXNRfs5uQMviWaCPHh7Iev1rva/v3/PjV+UdYeCOq488b361Ph7UPG/t5obnzUf3Iben5P3mC89n8pblR2xuG+bD5sh8lFs8qKgr4pI62bHUm+TxcPzdbZvciNu+Lj6l5Jb3yRfiE/WOeDroaE+4od4SH0gpPOGaenPCJfGXf/DnPdFaEB+pK+p/9mneqC/Ex//hezyTOONYmedeLit5qOStlue7/Y8X98/V/Vnb51fPS+3c1c7p1fN+tZ5crVfVuqdSyWZ1ciSe1eEZszoc6vaMWd0OdX7GrM5PybvsHpkxu0fCrwPxeye8LbgW2b02f2b3GqphdZCHfiP6sd/QffTl0J+YNrppD/1PE+PRHXd/73tXPffUJxz6q72vOPjehxzcxL6lPfoU+5zu6Db2RSLr62IfJTOPfVfeB8Y+rdAfKjIN9dgHFvrJ2vPVvp35F2dRFhdh12CjAAAAAElFTkSuQmCC",
|
||||||
|
"progressTimer1": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAACuUlEQVR42rXWPW7bMBQHcAoaNCo34BU6dtMpBLdL3bkn8GCUTHqMDNnpwRcoanXqCbwzKGCvLLxIgCCWH5L4nkjDLtp6CfHDPzRJWXyP6OSnIbedE5Z0QqqUD4TQpNMh7ZX+N35t/kwmXWcyuX79KNP7/fVT3+3tNP6BXTGtCMm1/oLdLIUTYg46w97Q3jApB4L9qZTWs/6BIS8KOw0hm7cV9KF8MpOYqR62FHpPG7sYs6ShhN5W0j1ckukCumKNW6Ak4wZGl5q7XEvYd+ivg/st7M2fV5TvzVZP2VHzEuXVxkx/ysRBFmj+9o1Z5bk7rlSO1tOT0rrYuQHYr/3a8/tjPRC0X213ZfLiQCrs2s1frxt8bna3Zj1iL/G55c6P9Urhc7OrOzEhdi1aZ1s6P9Z1j8/T+SchhJsxnA+d8p8z5NU4vzggb9i4nnrNoXM2rl/s3XB2v2uTXzXQx1+fye/8y4G8th/oQz7FxU7RyF0e+rh5mxfuSLC7/AZ6GfJfgfv/9fltAZyG/LfYXf4DcAXzOfAq5Nexu/weuIT5LHKfh85CfvUYuY2bayiZZ8i3+ZSXFHjj3eY/Ug79Ms//DL2/zPO/NJG75/vcJPPrK/kDzs/reUdj948Iur7MN+rL/3AGnEfn5t9AHp2z9/i5jGXjL7wCnt12BT2/w0MBHN9N9F54L+7wMnhfRO+pDmP0Xocr8IYPefDEfRL5dF+Fs7rhHFRqtrwn/ZWN7skGeHQPd+Cr4L3dzZUD3/PdXDlwXejAkcA60s0jXHcc4brjvs25pFFd684WcL3j1eiELfoN78t+Q5be+0V/onLvKl/0P5lfjyyw27pvnZfYxzo9levQX819xcLnPmThyvct+dJb3+cUSx98X0Sjvs73USxy33fFfaDv0xL9IQfTQPd9YKKfvDb+074d+W/1dr0hXblJ3QAAAABJRU5ErkJggg==",
|
||||||
|
"progressTimer2": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAC3klEQVR42q2WsW7bMBCGKQioRmXPwFfo2E1PIahZ6s59Ag9GKaePkaE7PXjtUMTq1CfwzqCAvbLIIgOCWPIoW3cihTppvUT58Pt8dyLvfmain4b9nddMRDljRYz3jPEo532cF+b/8Ln4iYpyk6ho/mat4vX+/mWu5u35+SflWhjNWGrMF8ptKjVjttEJ5Q3vLGZ5zyi/z5XjSXcjCM8yF4ax5bsC8z6/t0FsqJsVx7zjjUvGptTnmLeFgpfLEpNhrkUDCSo2FDBwZWrQtUz8wPyph7OwtX+eiL6zpR6SvalzotdLG/6QyJ3KSPz2rc3yeNpXOiX5dCx3XG7gAdXrfvZ4ty97Ruo1riqrlztWUG4gfrloaN9ctTYfuVW0bynwfVlp2jeX3UFIuWlJnm0OfF+WHe0n8E9SSog49oef9Z8TwoshvtwR3oghn3JRY16LIX+5hccL91VbfdVgPpw+q9/4y0F46T6Y9+lZLjeaBxz0mA/FO72EllAO+iXm+aj/jrj/rtevMsT5qH8MOeg/IK6xPkW8GPWLkIN+i7jC+iTgXo+5GPXVOuBObsdQVC8IX6VnveKIN547/UdeY/58if+Aefd8if+1CTi834cmql/M6HdUf8nnPQ+503+7xdzMxDcz8Ud9OaM3RC9QvXXQN+jnbR302ffzX96XQOdhHT8/1RXn56XnU+Pzn77+vrT4PmbBPfX3MX/9fUfzRF4zT4a5JGfmlftCRbife71rD5l7MBzbzOnJnARR+wbmZzCHT+Zy3cncPrn4j0kw5+16se1Jg70AeroXYHtAfLpH4L64fDZ078CecvlXigd77XSw14XuNVPbf09HezyZmPgNr5/6DZV7fTfxJzoF/VanE/+TQD4LlVHu9r7N3+59yv2ers7revRXzlf0zldM+MWHTLj2viWd8tb7nGzKe++LeODrvI8SAfe+K/SB3qdF/GGNwmDufWDET849v9S3E/4HKuJgRipvQikAAAAASUVORK5CYII=",
|
||||||
|
"progressTimer3": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAADJUlEQVR42q2WvW7bMBCAKQioRmXPwFfo2KXVUwiqlzhz1y4e3FBJHyNDdnrQmgBBrE59Au8MCtiAJxWGATVQyPJHou4kGW2BckiED18ud/w9oiZHSf7Mc8ImOSHJFJeE0ElO5TRP1P/hp+IHYpKrQEzmr67FdL0/f6i/5nX3/R3ziqmKkFCpr5jrVHJC9EQHmJe00ZjEkmB+EwvDg+aMIR5FJgwhi3cJ5DK+0UF0qLMlhbyhpUlGpyRjyOtE2MUlgYogr1hpExSkLaDlQuXWqwn7BvmztHuh0L+ekd/oUrfBRuUx8quFDr8N+FpEKH79Vme5+7XJqhDl05DYcL6yH6Be8293s00qCapXmaq0z9ckwVzZ+Om8xPNmqtX58ELgeQst36RZhefNZLdlnK9qlGcdW75J0wbPp+WfOOc2Yj8/tPOvAsSTNj5fI16yNp90nkOeszZ/XthPz13V2s9KyNvdp/2VOxyIp2ZALsNO56uKjrj1IW+LNz63U4K59ReQx73/CLj7W+cvI8Bp7z+NufUvAK+gHwKe9P58zK1fAC6gH4y48yFnvZ9dj7jR9TU06TPEl2HnCwp46bjxL2kO+cHHv4W8Ofj4d+WI2/W9LSf9+Ql/jX2fz0eKOW/H/Tnk6pC2A8dXh87H8Y+fOz894SvkMx+f3cF5WPh8xHmO5rnzLyleFx9/sF4+PuJX3I8C8vc+/TSD++QD8AO4D3sf7U/oQ057/yKE56L38XkBPuRx7z9G8JxuvL9E53rvfXzeex+edxn2Pr5Pjt6HXAUvnT64r17NzJhFyBDPpdk65keB7r38i9makfHRPVmaBbt/Y+9PfA9rdab8ce/vba0+mPhPAb7ndcEzOz0hfhf2rY/fhTo6tvHxO9KELy6fFX53pCnY5J+JwXunN+jDVteM3zWVJ5t0ttPbk7BBv7G3/rDfEPHR+s2gP6lCafyiCgf9T6BMPnMRYa7f/b3OX7/7mOt3+nWusu657vsr01dI01cMuO9DBrxyfUs45LXrc6Ihl64voqO+zvVRbMRd3zXuA12fNtEf5iAM5K4PnOgnT33/a9+O+G9KZg5hxzWVdwAAAABJRU5ErkJggg==",
|
||||||
|
"progressTimer4": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAADGUlEQVR42q2WsW7bMBCGSQioRmXPwFfo2KXVUxCKl6pTA3Tt4sENlfQxMnSnB68JUMTq1CfwzqBAAnhSERhQAoXsUbKkO1lGk7QabOHDj8PP0/HumBt9cvZ3njE1yhmLx7hlTIxyYcd57P4P3xefm1HuuBn1707N+Hl//3JP5mX7/pPyQrmCscC5r5SDlYwxSDSnPBcVYBZZRvlZZDzn1YEiPAx9GMamb2LMbXQGQSDUwUxgXoncmwFLNsK8jE39cRl3IeaFymuDhm0PsOXGZbWuZOoH5teWCevcAv6uib5iavORr1wWEX0x5U7fcL00IYlfvg4e5fH9KikC4qdi0YO+vddzeCHnZWIjjycraRk5r2Ox9nq9ZDHlTvr4Ms1p3rjV3o9eGJo3sCPBv0wKmrfwAfRK63lJfJbRBvRqJWVF8xmtQf9Ja10FJD9itdWfcMLBfh1fLwnPlWz8yDTDPDvRjX+9yBTmYF8qB/okx5yDfe0c/Myby9FysL99MLeB19fPvBCY93rMq3Dd6nUZYb7q9FPMo06uvyNeRr1+FiIuev0V4Z1cvke8wPoA8bjXp5i/6/ULxA3Wc8SxHvMvvT45RVy5Vg5tiPBWrgifBa3eCMTzWdDqP4gM87su/jnm1V0X/1tOePd9z/NRfbpHv6T6zs+RoLzVXxxi7vbEd3vibz53+dyjd0Sv0HlxHqZ9Pg8zkuc+n//yvU5QPWD+FtXDE+rnufVZ4PoPXn5fSnwfQ3xP0X2MXn7fbdDrn9JPmn7V1CftV76/pf4jJIRn1peO/1mQvpf5C3kUej3pk7n/YBev6v5J+zBIJ6677n3fBumlj3/FaZ+HA0/q9AR0Lqy3ejoXynCzjU/nSOUbrvczp3PH+gN7/4kZzDso0MsbODOday6LV3JyC+XJ1GDfWNf64b5hYIB5fTXYT4rAev2iCAb7D3feT2pCymHur8E/zH3KYU4/pi5px3W/X/m9wvq9YsC7PWTAi2ZvCYa8bPaccMhtsxeJnb2u2aPUDm/2rt09sNnTRvbDDIXBvNkDR/bJfe/P3dsJ/wMGWMEfZruZ5wAAAABJRU5ErkJggg==",
|
||||||
|
"progressWaterLevel0": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAz0lEQVR42u2NMQ7CMBAE11jCDSJtCgRfoKTLV3gJBCHR8iVX8AzMCzBdJILDLhIdBSXFnbSXu/HExvC1IowbN27cuHHjxo0b/4k/9b2pbZmHhot4p+nMlIbtru0onqXtmX7BlsSDeJLmmK7SxbSKFz9RKzVR1mUHWr0T99T6mlq8ko9pddhGFB+ozri2awpB27KJ6PyOr03CULBshvcWV1VE8nyqnTr+vaqG5Lih9hFw2WcELFrM0QM5JEyhahgeASTARkPEpyZqa2b0Gf6rXmaqYCuI4ImXAAAAAElFTkSuQmCC",
|
||||||
|
"progressWaterLevel1": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAzklEQVR42u2NMQ7CMBRDHSKRBdG1A4IrMLL1KpwEipBYuVImOAbhBIStEiXFRmJjYWP4lvz7/eImGL4qwrhx48aNGzf+A3/qe9PY0g8tF/FO25kuDcdd6SieVdvT/YIjiQfxpJqju0oXs1W8+Im1UhNlXXZgq3finrW+Zi1eycdsddhGFB9YnTG2axaC0rKJ6PyOr03CULBshneKqyoieT7VTh3/XlVDckyofQRc9hkBixZz9EAOCVNIDc0jgATYaIn4aKKxpkefxWQy/YtegRuKvotww9gAAAAASUVORK5CYII=",
|
||||||
|
"progressWaterLevel2": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAAAzUlEQVR42u2NMQ7CMBRDHSKRBdG1A4IrMLL1KpwEipBYuVImOAbhBIStEiXFRmJjhO1b8u/3i5tg+KoI48aNG/87f+p709jSDy0X8U7bmS4Nx13pKJ5V29P9giOJB/GkmqO7ShezVbz4ibVSE2VddmCrd+Ketb5mLV7Jx2x12EYUH1idMbZrFoLSsono/I6vTcJQsGyGd4qrKiJ5PtVOHf9eVUNyTKh9BFz2GQGLFnP0QA4JU0gNzSOABNhoifhoorGmR5/FZDKZTKbf6wUwPOKl8QPByAAAAABJRU5ErkJggg==",
|
||||||
|
"progressWaterLevel3": "iVBORw0KGgoAAAANSUhEUgAAALIAAACAAQAAAACHQw5jAAABP0lEQVR42u2VMWrEMBBFRxGsGrNqXYT4CltuFV9lj5EqVlhImyPkKoKFbJcrrHKCKJ2XKFb+BGxsGBdbBRJ/mGHm+VtjJGNT/srQO6cG8cnFKXvKLVdHRFcjfXD3xDyybY9IFVJgbpgHtilEa5E8XJ1m/gJbVwJFXuwRrqSYa9hSCZt/A1/B1VLjqdMG1mu0bgeD4W5Te2r1A6YVJne0qfNP57fWU9AY5dYKd29tDgodldoTqagjGaoc3VAiiibQmlg1ApeIQIjuufDUq+C0Q1z1xaJFi/60iluZ28aJ3Jy9yPU5iFxlmesZvsry+kUz8/z/5qTuZKyizM2F3IYZfnDyuR7kc61fL+P4qYmq0mCZ+tuhfHZjPvhV8iKnMVejuZNXrjnK3O77aeo03hEzNGqyUcbNnJdfPjqLFv2+vgH+JtBJ4nz/SAAAAABJRU5ErkJggg==",
|
||||||
|
"systemAccept1": "iVBORw0KGgoAAAANSUhEUgAAABgAAAAQAQAAAAAkX4I4AAAAQElEQVR42mM4wMDMsP//X4b6//+AWI6h/p8dQ/2fOob6H/8YKj/+Y6h4/I+hxvkfQx07UJ4fiOf/A6sF6QHqBQDsYh9jNdETHwAAAABJRU5ErkJggg==",
|
||||||
|
"systemAccept2": "iVBORw0KGgoAAAANSUhEUgAAABgAAAAQAQAAAAAkX4I4AAAAQUlEQVR42mM4wMDMsP//X4YEBjYGB4ZHDA6MhxgcmJsYHNiZGNz4mBjcZZgYnPcwMTj+YGJw+ADECUxgtSA9QL0A+IIPxwiZFtwAAAAASUVORK5CYII=",
|
||||||
|
"systemAlert": "iVBORw0KGgoAAAANSUhEUgAAABgAAAAVAQAAAAB0khOLAAAARUlEQVR42iXD0QmAIAAE0IvWCtqmMbLG8MtVhAZwjBvALyE85XzwoE/QO8f5WPsd0K+An6c3Jq8sThIUUVg9qfmDDRn7AD/WMAQEJ+pCAAAAAElFTkSuQmCC",
|
||||||
|
"systemBox": "iVBORw0KGgoAAAANSUhEUgAAABgAAAAQAQAAAAAkX4I4AAAAGklEQVR42mM4wMDMsP//X4b6//9IwiA9QL0AlQYkzY8nCoAAAAAASUVORK5CYII=",
|
||||||
|
"systemBusy0": "iVBORw0KGgoAAAANSUhEUgAAABMAAAAPAQAAAAAuP8mBAAAAMElEQVR42mPY/38Bg/x/BgbnHw4Msd8dGKLeA+k4IL0XSGdB6TioOFAepA6kHqgPAJCPFdTsOCPGAAAAAElFTkSuQmCC",
|
||||||
|
"systemBusy1": "iVBORw0KGgoAAAANSUhEUgAAAA8AAAATAQAAAABnuWoHAAAAOUlEQVR42mNoYGKw/8EAJO9/A6GrYQyv1jF8jWP4tY/hbx3D730M3+4xvH/HcO8bw90yhlvbGGDqAdjhGYsKgwC5AAAAAElFTkSuQmCC",
|
||||||
|
"systemDecline1": "iVBORw0KGgoAAAANSUhEUgAAABgAAAAQAQAAAAAkX4I4AAAANUlEQVR42mM4wMDMsP//X4b6//8Y6urtGOrt6hjq5/xjqD8JxI+hGMQGiQHlwGqAakF6gHoBybUeX0RcSJEAAAAASUVORK5CYII=",
|
||||||
|
"systemDecline2": "iVBORw0KGgoAAAANSUhEUgAAABgAAAAQAQAAAAAkX4I4AAAANUlEQVR42mM4wMDMsP//X4YEBjYGx4ZDDA4HmxgckpkYHMyAWAaKQWyQGFAOpAakFqQHqBcAGz4QyzSHE/gAAAAASUVORK5CYII=",
|
||||||
|
"systemDotEmpty": "iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfAQAAAAA31SuUAAAAYklEQVR42oWOMQ5AQBRE5yZ7FEfSKTmC25CQuILehkahkc12IvgjRqHUTPX/ew+MzsByNlhxGq7ochz90mFL9gmBFjCSGTxZoSUb1OTwTkquP/Md61cU8USWQzZ5VaCWp+oGeLZn9EklJaAAAAAASUVORK5CYII=",
|
||||||
|
"systemDotFull": "iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfAQAAAAA31SuUAAAAX0lEQVR42mP4/0H+H8P//sf/GP7V//nH8PeDfB3D7wPs+xi+MzDeY3jHAER3GBjKGG4wMJgx7GBgsGLYwMAgBSESGBh4CBAIxWC9YFPA5oFNBtsBtg1sL9gFYLeAXAUAPIwyHWLMTS8AAAAASUVORK5CYII=",
|
||||||
|
"systemEv3small": "iVBORw0KGgoAAAANSUhEUgAAACsAAAANAQAAAAAY06pGAAAAPUlEQVR42mNg4LFvYwACFEr+n32fPYiS5/8P5PLLyz8AUXwQqs8eSMn/b7H/D6IO1NmDBA/UgbTzP/gHpwBcwBWO2QYBDwAAAABJRU5ErkJggg==",
|
||||||
|
"systemPlay": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQAAAAA3iMLMAAAAMElEQVR42mP4Y8/wsZ/h+XOG858Z5v5k2POXwcaGoUYOhIAMIBcoCJQCKgAq+2MPAAFKFVmziLfGAAAAAElFTkSuQmCC",
|
||||||
|
"systemSlider0": "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAbAQAAAABg3VNpAAAAEklEQVR42mM4YMBwfwPV0AEDAHmKKNims3dJAAAAAElFTkSuQmCC",
|
||||||
|
"systemSlider1": "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAbAQAAAABg3VNpAAAAGklEQVR42mM4YMBwfwNFCAKuGjCc2sBwwAAAT7olG9TpXdAAAAAASUVORK5CYII=",
|
||||||
|
"systemSlider2": "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAbAQAAAABg3VNpAAAAG0lEQVR42mM4YMBwfwM5CAJObWC4aoAgDxgAACpUJGftPg+WAAAAAElFTkSuQmCC",
|
||||||
|
"systemSlider3": "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAbAQAAAABg3VNpAAAAG0lEQVR42mM4YMBwfwMJCAKuGjCc2oCFPGAAAPSIIz417p4OAAAAAElFTkSuQmCC",
|
||||||
|
"systemSlider4": "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAbAQAAAABg3VNpAAAAG0lEQVR42mM4YMBwfwNhBAGnNjBcNcBHHjAAAMJ6IooqNLP/AAAAAElFTkSuQmCC",
|
||||||
|
"systemSlider5": "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAbAQAAAABg3VNpAAAAG0lEQVR42mM4YMBwfwNOBAFXDRhObSCKPGAAAHfbIWHuFKlNAAAAAElFTkSuQmCC",
|
||||||
|
"systemSlider6": "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAbAQAAAABg3VNpAAAAG0lEQVR42mM4YMBwfwM6goBTGxiuGpBGHjAAADklIK1PooVQAAAAAElFTkSuQmCC",
|
||||||
|
"systemSlider7": "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAbAQAAAABg3VNpAAAAG0lEQVR42mM4YMBwfwMUQcBVA4ZTG8gkDxgAANmVH4SOiUHMAAAAAElFTkSuQmCC",
|
||||||
|
"systemSlider8": "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAbAQAAAABg3VNpAAAAFElEQVR42mM4YMBwagPDVSqRBwwASoohT92wVBIAAAAASUVORK5CYII="
|
||||||
|
}
|
260
libs/core/images.ts
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
namespace images {
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsBigSmile = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsHeartLarge = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsHeartSmall = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsMouth1open = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsMouth1shut = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsMouth2open = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsMouth2shut = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsSad = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsSick = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsSmile = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsSwearing = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsTalking = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsWink = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsZzz = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesAngry = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesAwake = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesBlackEye = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesBottomLeft = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesBottomRight = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesCrazy1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesCrazy2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesDisappointed = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesDizzy = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesDown = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesEvil = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesHurt = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesKnockedOut = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesLove = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesMiddleLeft = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesMiddleRight = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesNeutral = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesNuclear = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesPinchLeft = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesPinchMiddle = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesPinchRight = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesSleeping = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesTear = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesTiredLeft = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesTiredMiddle = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesTiredRight = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesToxic = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesUp = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const eyesWinking = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationAccept = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationBackward = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationDecline = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationForward = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationLeft = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationNoGo = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationQuestionMark = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationRight = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationStop1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationStop2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationThumbsDown = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationThumbsUp = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationWarning = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoColorSensor = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoEv3icon = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoEv3 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoGyroSensor = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoIrBeacon = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoIrSensor = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoLego = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoLargeMotor = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoMindstorms = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoMediumMotor = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoSoundSensor = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoTempSensor = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoTouchSensor = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const legoUsSensor = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsBomb = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsBoom = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsFire = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsFlowers = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsForest = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsLightOff = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsLightOn = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsLightning = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsNight = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsPirate = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsSnow = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const objectsTarget = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressBar0 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressBar1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressBar2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressBar3 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressBar4 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressDial0 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressDial1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressDial2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressDial3 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressDial4 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressDots0 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressDots1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressDots2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressDots3 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressHourglass0 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressHourglass1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressHourglass2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressTimer0 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressTimer1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressTimer2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressTimer3 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressTimer4 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressWaterLevel0 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressWaterLevel1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressWaterLevel2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const progressWaterLevel3 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemAccept1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemAccept2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemAlert = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemBox = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemBusy0 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemBusy1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemDecline1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemDecline2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemDotEmpty = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemDotFull = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemEv3small = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemPlay = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemSlider0 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemSlider1 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemSlider2 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemSlider3 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemSlider4 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemSlider5 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemSlider6 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemSlider7 = screen.unpackPNG(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemSlider8 = screen.unpackPNG(hex``);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace input.internal {
|
namespace sensors.internal {
|
||||||
//% shim=pxt::unsafePollForChanges
|
//% shim=pxt::unsafePollForChanges
|
||||||
export function unsafePollForChanges(
|
export function unsafePollForChanges(
|
||||||
periodMs: number,
|
periodMs: number,
|
||||||
@ -27,30 +27,27 @@ namespace input.internal {
|
|||||||
let analogMM: MMap
|
let analogMM: MMap
|
||||||
let uartMM: MMap
|
let uartMM: MMap
|
||||||
let devcon: Buffer
|
let devcon: Buffer
|
||||||
let sensors: SensorInfo[]
|
let sensorInfos: SensorInfo[]
|
||||||
let autoSensors: Sensor[]
|
|
||||||
|
|
||||||
class SensorInfo {
|
class SensorInfo {
|
||||||
port: number
|
port: number
|
||||||
sensor: Sensor
|
sensor: Sensor
|
||||||
|
sensors: Sensor[]
|
||||||
connType: number
|
connType: number
|
||||||
devType: number
|
devType: number
|
||||||
manual: boolean
|
|
||||||
|
|
||||||
constructor(p: number) {
|
constructor(p: number) {
|
||||||
this.port = p
|
this.port = p
|
||||||
this.connType = DAL.CONN_NONE
|
this.connType = DAL.CONN_NONE
|
||||||
this.devType = DAL.DEVICE_TYPE_NONE
|
this.devType = DAL.DEVICE_TYPE_NONE
|
||||||
this.sensor = null
|
this.sensors = []
|
||||||
this.manual = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
if (sensors) return
|
if (sensorInfos) return
|
||||||
sensors = []
|
sensorInfos = []
|
||||||
for (let i = 0; i < DAL.NUM_INPUTS; ++i) sensors.push(new SensorInfo(i))
|
for (let i = 0; i < DAL.NUM_INPUTS; ++i) sensorInfos.push(new SensorInfo(i))
|
||||||
autoSensors = []
|
|
||||||
devcon = output.createBuffer(DevConOff.Size)
|
devcon = output.createBuffer(DevConOff.Size)
|
||||||
|
|
||||||
analogMM = control.mmap("/dev/lms_analog", AnalogOff.Size, 0)
|
analogMM = control.mmap("/dev/lms_analog", AnalogOff.Size, 0)
|
||||||
@ -64,7 +61,7 @@ namespace input.internal {
|
|||||||
loops.pause(500)
|
loops.pause(500)
|
||||||
})
|
})
|
||||||
|
|
||||||
for (let info_ of sensors) {
|
for (let info_ of sensorInfos) {
|
||||||
let info = info_
|
let info = info_
|
||||||
unsafePollForChanges(50, () => {
|
unsafePollForChanges(50, () => {
|
||||||
if (info.sensor) return info.sensor._query()
|
if (info.sensor) return info.sensor._query()
|
||||||
@ -90,7 +87,7 @@ namespace input.internal {
|
|||||||
let conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
|
let conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
|
||||||
let numChanged = 0
|
let numChanged = 0
|
||||||
|
|
||||||
for (let info of sensors) {
|
for (let info of sensorInfos) {
|
||||||
let newConn = conns[info.port]
|
let newConn = conns[info.port]
|
||||||
if (newConn == info.connType)
|
if (newConn == info.connType)
|
||||||
continue
|
continue
|
||||||
@ -117,77 +114,44 @@ namespace input.internal {
|
|||||||
if (numChanged == 0)
|
if (numChanged == 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
let autos = sensors.filter(s => !s.manual)
|
for (let si of sensorInfos) {
|
||||||
|
if (si.sensor && si.sensor._deviceType() != si.devType) {
|
||||||
// first free up disconnected sensors
|
si.sensor = null
|
||||||
for (let info of autos) {
|
}
|
||||||
if (info.sensor && info.devType == DAL.DEVICE_TYPE_NONE)
|
if (si.devType != DAL.DEVICE_TYPE_NONE) {
|
||||||
info.sensor._setPort(0)
|
// TODO figure out compiler problem when '|| null' is added here!
|
||||||
}
|
si.sensor = si.sensors.filter(s => s._deviceType() == si.devType)[0]
|
||||||
|
if (si.sensor == null) {
|
||||||
for (let info of autos) {
|
control.dmesg(`sensor not found for type=${si.devType} at ${si.port}`)
|
||||||
if (!info.sensor && info.devType != DAL.DEVICE_TYPE_NONE) {
|
} else {
|
||||||
let found = false
|
control.dmesg(`sensor connected type=${si.devType} at ${si.port}`)
|
||||||
for (let s of autoSensors) {
|
si.sensor._activated()
|
||||||
if (s.getPort() == 0 && s._deviceType() == info.devType) {
|
|
||||||
s._setPort(info.port + 1)
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!found)
|
|
||||||
control.dmesg(`sensor not found for type=${info.devType} at ${info.port}`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Sensor extends control.Component {
|
export class Sensor extends control.Component {
|
||||||
protected port: number
|
protected port: number // this is 0-based
|
||||||
|
|
||||||
constructor() {
|
constructor(port_: number) {
|
||||||
super()
|
super()
|
||||||
|
if (!(1 <= port_ && port_ <= DAL.NUM_INPUTS))
|
||||||
|
control.panic(120)
|
||||||
|
this.port = port_ - 1
|
||||||
init()
|
init()
|
||||||
this.port = -1
|
sensorInfos[this.port].sensors.push(this)
|
||||||
let tp = this._deviceType()
|
|
||||||
if (autoSensors.filter(s => s._deviceType() == tp).length == 0) {
|
|
||||||
autoSensors.push(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0 - disable, 1-4 port number
|
_activated() { }
|
||||||
_setPort(port: number, manual = false) {
|
|
||||||
port = Math.clamp(0, 4, port | 0) - 1;
|
|
||||||
if (port == this.port) return
|
|
||||||
this.port = port
|
|
||||||
control.dmesg(`sensor set port ${port} on devtype=${this._deviceType()}`)
|
|
||||||
for (let i = 0; i < sensors.length; ++i) {
|
|
||||||
if (i != this.port && sensors[i].sensor == this) {
|
|
||||||
sensors[i].sensor = null
|
|
||||||
sensors[i].manual = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.port >= 0) {
|
|
||||||
let prev = sensors[this.port].sensor
|
|
||||||
if (prev && prev != this)
|
|
||||||
prev._setPort(0)
|
|
||||||
sensors[this.port].sensor = this
|
|
||||||
sensors[this.port].manual = manual
|
|
||||||
}
|
|
||||||
this._portUpdated()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _portUpdated() { }
|
|
||||||
|
|
||||||
setPort(port: number) {
|
|
||||||
this._setPort(port, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 1-based
|
||||||
getPort() {
|
getPort() {
|
||||||
return this.port + 1
|
return this.port + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
isManual() {
|
isActive() {
|
||||||
return this.port >= 0 && sensors[this.port].manual
|
return sensorInfos[this.port].sensor == this
|
||||||
}
|
}
|
||||||
|
|
||||||
_query() {
|
_query() {
|
||||||
@ -203,12 +167,12 @@ namespace input.internal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AnalogSensor extends Sensor {
|
export class AnalogSensor extends Sensor {
|
||||||
constructor() {
|
constructor(port: number) {
|
||||||
super()
|
super(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
_readPin6() {
|
_readPin6() {
|
||||||
if (this.port < 0) return 0
|
if (!this.isActive()) return 0
|
||||||
return analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.InPin6 + 2 * this.port)
|
return analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.InPin6 + 2 * this.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,32 +180,26 @@ namespace input.internal {
|
|||||||
|
|
||||||
|
|
||||||
export class UartSensor extends Sensor {
|
export class UartSensor extends Sensor {
|
||||||
protected mode: number
|
protected mode: number // the mode user asked for
|
||||||
protected realmode: number
|
protected realmode: number // the mode the hardware is in
|
||||||
|
|
||||||
constructor() {
|
constructor(port: number) {
|
||||||
super()
|
super(port)
|
||||||
this.mode = 0
|
this.mode = 0
|
||||||
this.realmode = -1
|
this.realmode = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _portUpdated() {
|
_activated() {
|
||||||
this.realmode = -1
|
this.realmode = 0
|
||||||
if (this.port >= 0) {
|
// uartReset(this.port) // TODO is it ever needed?
|
||||||
if (this.isManual()) {
|
this._setMode(this.mode)
|
||||||
uartReset(this.port)
|
|
||||||
} else {
|
|
||||||
this.realmode = 0
|
|
||||||
}
|
|
||||||
this._setMode(this.mode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _setMode(m: number) {
|
protected _setMode(m: number) {
|
||||||
//control.dmesg(`_setMode p=${this.port} m: ${this.realmode} -> ${m}`)
|
//control.dmesg(`_setMode p=${this.port} m: ${this.realmode} -> ${m}`)
|
||||||
let v = m | 0
|
let v = m | 0
|
||||||
this.mode = v
|
this.mode = v
|
||||||
if (this.port < 0) return
|
if (!this.isActive()) return
|
||||||
if (this.realmode != this.mode) {
|
if (this.realmode != this.mode) {
|
||||||
this.realmode = v
|
this.realmode = v
|
||||||
setUartMode(this.port, v)
|
setUartMode(this.port, v)
|
||||||
@ -249,10 +207,12 @@ namespace input.internal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getBytes(): Buffer {
|
getBytes(): Buffer {
|
||||||
return getUartBytes(this.port)
|
return getUartBytes(this.isActive() ? this.port : -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
getNumber(fmt: NumberFormat, off: number) {
|
getNumber(fmt: NumberFormat, off: number) {
|
||||||
|
if (!this.isActive())
|
||||||
|
return 0
|
||||||
return getUartNumber(fmt, off, this.port)
|
return getUartNumber(fmt, off, this.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
266
libs/core/ir.ts
@ -21,7 +21,14 @@ const enum IrRemoteButton {
|
|||||||
BottomRight = 0x10,
|
BottomRight = 0x10,
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace input {
|
const enum InfraredSensorEvent {
|
||||||
|
//% block="object near"
|
||||||
|
ObjectNear = 1,
|
||||||
|
//% block="object detected"
|
||||||
|
ObjectDetected = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sensors {
|
||||||
function mapButton(v: number) {
|
function mapButton(v: number) {
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case 0: return IrRemoteButton.None
|
case 0: return IrRemoteButton.None
|
||||||
@ -40,41 +47,130 @@ namespace input {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IrSensor extends internal.UartSensor {
|
let buttons: RemoteInfraredBeaconButton[]
|
||||||
private channel: IrRemoteChannel
|
|
||||||
private buttons: Button[];
|
|
||||||
|
|
||||||
constructor() {
|
function create(ir: InfraredSensor) {
|
||||||
super()
|
// it's created by referencing it
|
||||||
this.channel = IrRemoteChannel.Ch0
|
}
|
||||||
this.buttons = []
|
|
||||||
// otherwise button events won't work
|
export function irButton(id: IrRemoteButton): RemoteInfraredBeaconButton {
|
||||||
this.mode = IrSensorMode.RemoteControl
|
if (buttons == null) {
|
||||||
|
buttons = []
|
||||||
for (let i = 0; i < 5; ++i) {
|
for (let i = 0; i < 5; ++i) {
|
||||||
this.buttons.push(new Button())
|
buttons.push(new RemoteInfraredBeaconButton(new brick.Button()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure sensors are up
|
||||||
|
create(infraredSensor1)
|
||||||
|
create(infraredSensor2)
|
||||||
|
create(infraredSensor3)
|
||||||
|
create(infraredSensor4)
|
||||||
}
|
}
|
||||||
|
|
||||||
button(id: IrRemoteButton) {
|
let num = -1
|
||||||
let num = -1
|
while (id) {
|
||||||
while (id) {
|
id >>= 1;
|
||||||
id >>= 1;
|
num++;
|
||||||
num++;
|
}
|
||||||
}
|
num = Math.clamp(0, buttons.length - 1, num)
|
||||||
num = Math.clamp(0, this.buttons.length - 1, num)
|
return buttons[num]
|
||||||
return this.buttons[num]
|
}
|
||||||
|
|
||||||
|
//% fixedInstances
|
||||||
|
export class RemoteInfraredBeaconButton extends control.Component {
|
||||||
|
private button: brick.Button;
|
||||||
|
constructor(button: brick.Button) {
|
||||||
|
super();
|
||||||
|
this.button = button;
|
||||||
|
}
|
||||||
|
|
||||||
|
_update(curr: boolean) {
|
||||||
|
this.button._update(curr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a remote button is currently pressed or not.
|
||||||
|
* @param button the remote button to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/remote-infrared-beacon/is-pressed
|
||||||
|
//% block="%button|is pressed"
|
||||||
|
//% blockId=remoteButtonIsPressed
|
||||||
|
//% parts="remote"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=81 blockGap=8
|
||||||
|
//% group="Remote Infrared Beacon"
|
||||||
|
isPressed() {
|
||||||
|
return this.button.isPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See if the remote button was pressed again since the last time you checked.
|
||||||
|
* @param button the remote button to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/remote-infrared-beacon/was-pressed
|
||||||
|
//% block="%button|was pressed"
|
||||||
|
//% blockId=remotebuttonWasPressed
|
||||||
|
//% parts="remote"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=80 blockGap=8
|
||||||
|
//% group="Remote Infrared Beacon"
|
||||||
|
wasPressed() {
|
||||||
|
return this.button.wasPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do something when a button or sensor is clicked, up or down
|
||||||
|
* @param button the button that needs to be clicked or used
|
||||||
|
* @param event the kind of button gesture that needs to be detected
|
||||||
|
* @param body code to run when the event is raised
|
||||||
|
*/
|
||||||
|
//% help=input/remote-infrared-beacon/on-event
|
||||||
|
//% blockId=remotebuttonEvent block="on %button|%event"
|
||||||
|
//% parts="remote"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=99 blockGap=8
|
||||||
|
//% group="Remote Infrared Beacon"
|
||||||
|
onEvent(ev: ButtonEvent, body: () => void) {
|
||||||
|
this.button.onEvent(ev, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//% fixedInstances
|
||||||
|
export class InfraredSensor extends internal.UartSensor {
|
||||||
|
private channel: IrRemoteChannel;
|
||||||
|
private proximityThreshold: number;
|
||||||
|
|
||||||
|
constructor(port: number) {
|
||||||
|
super(port)
|
||||||
|
this.channel = IrRemoteChannel.Ch0
|
||||||
|
this.proximityThreshold = 10;
|
||||||
|
irButton(0) // make sure buttons array is initalized
|
||||||
|
|
||||||
|
// and set the mode, as otherwise button events won't work
|
||||||
|
this.mode = IrSensorMode.RemoteControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
_query() {
|
_query() {
|
||||||
if (this.mode == IrSensorMode.RemoteControl)
|
if (this.mode == IrSensorMode.RemoteControl)
|
||||||
return mapButton(this.getNumber(NumberFormat.UInt8LE, this.channel))
|
return mapButton(this.getNumber(NumberFormat.UInt8LE, this.channel));
|
||||||
|
else if (this.mode == IrSensorMode.Proximity) {
|
||||||
|
const d = this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff;
|
||||||
|
return d < this.proximityThreshold ? UltrasonicSensorEvent.ObjectNear
|
||||||
|
: d > this.proximityThreshold + 5 ? UltrasonicSensorEvent.ObjectDetected
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
_update(prev: number, curr: number) {
|
_update(prev: number, curr: number) {
|
||||||
for (let i = 0; i < this.buttons.length; ++i) {
|
if (this.mode == IrSensorMode.RemoteControl) {
|
||||||
let v = !!(curr & (1 << i))
|
for (let i = 0; i < buttons.length; ++i) {
|
||||||
this.buttons[i].update(v)
|
let v = !!(curr & (1 << i))
|
||||||
|
buttons[i]._update(v)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (curr)
|
||||||
|
control.raiseEvent(this._id, curr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,59 +181,123 @@ namespace input {
|
|||||||
setRemoteChannel(c: IrRemoteChannel) {
|
setRemoteChannel(c: IrRemoteChannel) {
|
||||||
c = Math.clamp(0, 3, c | 0)
|
c = Math.clamp(0, 3, c | 0)
|
||||||
this.channel = c
|
this.channel = c
|
||||||
this.setMode(IrSensorMode.RemoteControl)
|
this._setMode(IrSensorMode.RemoteControl)
|
||||||
}
|
}
|
||||||
|
|
||||||
setMode(m: IrSensorMode) {
|
setMode(m: IrSensorMode) {
|
||||||
this._setMode(m)
|
this._setMode(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
getDistance() {
|
/**
|
||||||
this.setMode(IrSensorMode.Proximity)
|
* Registers code to run when an object is getting near.
|
||||||
|
* @param handler the code to run when detected
|
||||||
|
*/
|
||||||
|
//% help=input/infrared/on
|
||||||
|
//% block="on %sensor|%event"
|
||||||
|
//% blockId=infraredOn
|
||||||
|
//% parts="infraredsensor"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=100 blockGap=8
|
||||||
|
//% group="Infrared Sensor"
|
||||||
|
on(event: InfraredSensorEvent, handler: () => void) {
|
||||||
|
control.onEvent(this._id, InfraredSensorEvent.ObjectNear, handler);
|
||||||
|
if ( this.proximity() == InfraredSensorEvent.ObjectNear)
|
||||||
|
control.runInBackground(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for the event to occur
|
||||||
|
*/
|
||||||
|
//% help=input/ultrasonic/wait
|
||||||
|
//% block="wait %sensor|for %event"
|
||||||
|
//% blockId=infraredwait
|
||||||
|
//% parts="infraredsensor"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=99 blockGap=8
|
||||||
|
//% group="Ultrasonic Sensor"
|
||||||
|
wait(event: InfraredSensorEvent) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the promixity measured by the infrared sensor, from ``0`` (close) to ``100`` (far)
|
||||||
|
* @param ir the infrared sensor
|
||||||
|
*/
|
||||||
|
//% help=input/infrared/proximity
|
||||||
|
//% block="%infrared|proximity"
|
||||||
|
//% blockId=infraredGetProximity
|
||||||
|
//% parts="infrared"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=65 blockGap=8
|
||||||
|
//% group="Infrared Sensor"
|
||||||
|
proximity() {
|
||||||
|
this._setMode(IrSensorMode.Proximity)
|
||||||
return this.getNumber(NumberFormat.UInt8LE, 0)
|
return this.getNumber(NumberFormat.UInt8LE, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
getRemoteCommand() {
|
/**
|
||||||
this.setMode(IrSensorMode.RemoteControl)
|
* Get the remote commandreceived the infrared sensor.
|
||||||
|
* @param ir the infrared sensor
|
||||||
|
*/
|
||||||
|
//% help=input/infrared/remote-command
|
||||||
|
//% block="%infrared|remote command"
|
||||||
|
//% blockId=infraredGetRemoteCommand
|
||||||
|
//% parts="infrared"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=65 blockGap=8
|
||||||
|
//% group="Infrared Sensor"
|
||||||
|
remoteCommand() {
|
||||||
|
this._setMode(IrSensorMode.RemoteControl)
|
||||||
return this.getNumber(NumberFormat.UInt8LE, this.channel)
|
return this.getNumber(NumberFormat.UInt8LE, this.channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
getDirectionAndDistance() {
|
getDirectionAndDistance() {
|
||||||
this.setMode(IrSensorMode.Seek)
|
this._setMode(IrSensorMode.Seek)
|
||||||
return this.getNumber(NumberFormat.UInt16LE, this.channel * 2)
|
return this.getNumber(NumberFormat.UInt16LE, this.channel * 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//% whenUsed
|
//% fixedInstance whenUsed block="infrared sensor 1"
|
||||||
export const ir: IrSensor = new IrSensor()
|
export const infraredSensor1: InfraredSensor = new InfraredSensor(1)
|
||||||
|
|
||||||
/**
|
//% fixedInstance whenUsed block="infrared sensor 2"
|
||||||
* Remote top-left button.
|
export const infraredSensor2: InfraredSensor = new InfraredSensor(2)
|
||||||
*/
|
|
||||||
//% whenUsed block="remote top-left" weight=95 fixedInstance
|
|
||||||
export const remoteTopLeft = ir.button(IrRemoteButton.TopLeft)
|
|
||||||
|
|
||||||
/**
|
//% fixedInstance whenUsed block="infrared sensor 3"
|
||||||
* Remote top-right button.
|
export const infraredSensor3: InfraredSensor = new InfraredSensor(3)
|
||||||
*/
|
|
||||||
//% whenUsed block="remote top-right" weight=95 fixedInstance
|
|
||||||
export const remoteTopRight = ir.button(IrRemoteButton.TopRight)
|
|
||||||
|
|
||||||
/**
|
//% fixedInstance whenUsed block="infrared sensor 4"
|
||||||
* Remote bottom-left button.
|
export const infraredSensor4: InfraredSensor = new InfraredSensor(4)
|
||||||
*/
|
|
||||||
//% whenUsed block="remote bottom-left" weight=95 fixedInstance
|
|
||||||
export const remoteBottomLeft = ir.button(IrRemoteButton.BottomLeft)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote bottom-right button.
|
|
||||||
*/
|
|
||||||
//% whenUsed block="remote bottom-right" weight=95 fixedInstance
|
|
||||||
export const remoteBottomRight = ir.button(IrRemoteButton.BottomRight)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote beacon (center) button.
|
* Remote beacon (center) button.
|
||||||
*/
|
*/
|
||||||
//% whenUsed block="remote center" weight=95 fixedInstance
|
//% whenUsed block="remote button center" weight=95 fixedInstance
|
||||||
export const remoteCenter = ir.button(IrRemoteButton.CenterBeacon)
|
export const remoteButtonCenter = irButton(IrRemoteButton.CenterBeacon)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote top-left button.
|
||||||
|
*/
|
||||||
|
//% whenUsed block="remote button top-left" weight=95 fixedInstance
|
||||||
|
export const remoteButtonTopLeft = irButton(IrRemoteButton.TopLeft)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote top-right button.
|
||||||
|
*/
|
||||||
|
//% whenUsed block="remote button top-right" weight=95 fixedInstance
|
||||||
|
export const remoteButtonTopRight = irButton(IrRemoteButton.TopRight)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote bottom-left button.
|
||||||
|
*/
|
||||||
|
//% whenUsed block="remote button bottom-left" weight=95 fixedInstance
|
||||||
|
export const remoteButtonBottomLeft = irButton(IrRemoteButton.BottomLeft)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote bottom-right button.
|
||||||
|
*/
|
||||||
|
//% whenUsed block="remote button bottom-right" weight=95 fixedInstance
|
||||||
|
export const remoteButtonBottomRight = irButton(IrRemoteButton.BottomRight)
|
||||||
}
|
}
|
||||||
|
@ -269,11 +269,13 @@ void waitForEvent(int source, int value) {
|
|||||||
pthread_mutex_lock(&eventMutex);
|
pthread_mutex_lock(&eventMutex);
|
||||||
t->waitSource = source;
|
t->waitSource = source;
|
||||||
t->waitValue = value;
|
t->waitValue = value;
|
||||||
|
stopUser();
|
||||||
// spourious wake ups may occur they say
|
// spourious wake ups may occur they say
|
||||||
while (t->waitSource) {
|
while (t->waitSource) {
|
||||||
pthread_cond_wait(&t->waitCond, &eventMutex);
|
pthread_cond_wait(&t->waitCond, &eventMutex);
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&eventMutex);
|
pthread_mutex_unlock(&eventMutex);
|
||||||
|
startUser();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,7 +352,8 @@ void raiseEvent(int id, int event) {
|
|||||||
pthread_mutex_unlock(&eventMutex);
|
pthread_mutex_unlock(&eventMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerWithDal(int id, int event, Action a) {
|
void registerWithDal(int id, int event, Action a, int flags) {
|
||||||
|
// TODO support flags
|
||||||
setBinding(id, event, a);
|
setBinding(id, event, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
|
//% weight=100
|
||||||
|
namespace brick {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//% color="#B4009E" weight=98 icon="\uf192"
|
//% color="#B4009E" weight=98 icon="\uf192"
|
||||||
namespace input {
|
//% groups='["Ultrasonic Sensor", "Touch Sensor", "Color Sensor", "Infrared Sensor", "Remote Infrared Beacon", "Gyro Sensor"]'
|
||||||
|
namespace sensors {
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
enum Output {
|
enum Output {
|
||||||
|
//% block="A"
|
||||||
A = 0x01,
|
A = 0x01,
|
||||||
|
//% block="B"
|
||||||
B = 0x02,
|
B = 0x02,
|
||||||
|
//% block="C"
|
||||||
C = 0x04,
|
C = 0x04,
|
||||||
|
//% block="D"
|
||||||
D = 0x08,
|
D = 0x08,
|
||||||
|
//% block="All"
|
||||||
ALL = 0x0f
|
ALL = 0x0f
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12,10 +17,9 @@ enum OutputType {
|
|||||||
MiniTacho = 8,
|
MiniTacho = 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace output {
|
namespace motors {
|
||||||
let pwmMM: MMap
|
let pwmMM: MMap
|
||||||
let motorMM: MMap
|
let motorMM: MMap
|
||||||
let currentSpeed: number[] = []
|
|
||||||
|
|
||||||
const enum MotorDataOff {
|
const enum MotorDataOff {
|
||||||
TachoCounts = 0, // int32
|
TachoCounts = 0, // int32
|
||||||
@ -31,13 +35,7 @@ namespace output {
|
|||||||
if (!pwmMM) control.fail("no PWM file")
|
if (!pwmMM) control.fail("no PWM file")
|
||||||
motorMM = control.mmap("/dev/lms_motor", MotorDataOff.Size * DAL.NUM_OUTPUTS, 0)
|
motorMM = control.mmap("/dev/lms_motor", MotorDataOff.Size * DAL.NUM_OUTPUTS, 0)
|
||||||
|
|
||||||
stop(Output.ALL)
|
resetMotors()
|
||||||
|
|
||||||
currentSpeed[Output.A] = -1;
|
|
||||||
currentSpeed[Output.B] = -1;
|
|
||||||
currentSpeed[Output.C] = -1;
|
|
||||||
currentSpeed[Output.D] = -1;
|
|
||||||
currentSpeed[Output.ALL] = -1;
|
|
||||||
|
|
||||||
let buf = output.createBuffer(1)
|
let buf = output.createBuffer(1)
|
||||||
buf[0] = DAL.opProgramStart
|
buf[0] = DAL.opProgramStart
|
||||||
@ -55,69 +53,196 @@ namespace output {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mkCmd(out: Output, cmd: number, addSize: number) {
|
function mkCmd(out: Output, cmd: number, addSize: number) {
|
||||||
let b = createBuffer(2 + addSize)
|
const b = output.createBuffer(2 + addSize)
|
||||||
b.setNumber(NumberFormat.UInt8LE, 0, cmd)
|
b.setNumber(NumberFormat.UInt8LE, 0, cmd)
|
||||||
b.setNumber(NumberFormat.UInt8LE, 1, out)
|
b.setNumber(NumberFormat.UInt8LE, 1, out)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function resetMotors() {
|
||||||
* Turn a motor on for a specified number of milliseconds.
|
reset(Output.ALL)
|
||||||
* @param out the output connection that the motor is connected to
|
|
||||||
* @param ms the number of milliseconds to turn the motor on, eg: 500
|
|
||||||
* @param useBrake whether or not to use the brake, defaults to false
|
|
||||||
*/
|
|
||||||
//% blockId=output_turn block="turn motor %out| on for %ms|milliseconds"
|
|
||||||
//% weight=100 group="Motors"
|
|
||||||
export function turn(out: Output, ms: number, useBrake = false) {
|
|
||||||
// TODO: use current power / speed configuration
|
|
||||||
output.step(out, {
|
|
||||||
speed: 100,
|
|
||||||
step1: 0,
|
|
||||||
step2: ms,
|
|
||||||
step3: 0,
|
|
||||||
useSteps: false,
|
|
||||||
useBrake: useBrake
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turn motor off.
|
* Stops all motors
|
||||||
* @param out the output connection that the motor is connected to
|
|
||||||
*/
|
*/
|
||||||
//% blockId=output_stop block="turn motor %out|off"
|
//% blockId=motorStopAll block="stop all motors"
|
||||||
//% weight=90 group="Motors"
|
//% weight=10 group="Motors" blockGap=8
|
||||||
export function stop(out: Output, useBrake = false) {
|
export function stopAllMotors() {
|
||||||
let b = mkCmd(out, DAL.opOutputStop, 1)
|
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
|
||||||
b.setNumber(NumberFormat.UInt8LE, 2, useBrake ? 1 : 0)
|
|
||||||
writePWM(b)
|
writePWM(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
//% fixedInstances
|
||||||
* Turn motor on.
|
export class Motor extends control.Component {
|
||||||
* @param out the output connection that the motor is connected to
|
private port: Output;
|
||||||
*/
|
private large: boolean;
|
||||||
//% blockId=output_start block="turn motor %out|on"
|
private brake: boolean;
|
||||||
//% weight=95 group="Motors"
|
|
||||||
export function start(out: Output) {
|
|
||||||
if (currentSpeed[out] == -1) setSpeed(out, 50)
|
|
||||||
let b = mkCmd(out, DAL.opOutputStart, 0)
|
|
||||||
writePWM(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function reset(out: Output) {
|
constructor(port: Output, large: boolean) {
|
||||||
let b = mkCmd(out, DAL.opOutputReset, 0)
|
super();
|
||||||
writePWM(b)
|
this.port = port;
|
||||||
}
|
this.large = large;
|
||||||
|
this.brake = false;
|
||||||
|
}
|
||||||
|
|
||||||
export function clearCount(out: Output) {
|
/**
|
||||||
let b = mkCmd(out, DAL.opOutputClearCount, 0)
|
* Sets the motor power level from ``-100`` to ``100``.
|
||||||
writePWM(b)
|
* @param motor the output connection that the motor is connected to
|
||||||
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
|
* @param power the power from ``100`` full forward to ``-100`` full backward, eg: 50
|
||||||
if (out & (1 << i)) {
|
*/
|
||||||
motorMM.setNumber(NumberFormat.Int32LE, i * MotorDataOff.Size + MotorDataOff.TachoSensor, 0)
|
//% blockId=motorSetPower block="power %motor|to %power|%"
|
||||||
|
//% weight=99 group="Motors" blockGap=8
|
||||||
|
//% power.min=-100 power.max=100
|
||||||
|
power(power: number) {
|
||||||
|
power = Math.clamp(-100, 100, power >> 0);
|
||||||
|
|
||||||
|
const b = mkCmd(this.port, DAL.opOutputPower, 1)
|
||||||
|
b.setNumber(NumberFormat.Int8LE, 2, power)
|
||||||
|
writePWM(b)
|
||||||
|
if (power) {
|
||||||
|
const b = mkCmd(this.port, DAL.opOutputStart, 0)
|
||||||
|
writePWM(b);
|
||||||
|
} else {
|
||||||
|
this.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the motor by a number of degrees
|
||||||
|
* @param degrees the angle to turn the motor
|
||||||
|
* @param angle the degrees to rotate, eg: 360
|
||||||
|
* @param power the power from ``100`` full forward to ``-100`` full backward, eg: 50
|
||||||
|
*/
|
||||||
|
//% blockId=motorMove block="move %motor|by %angle|degrees at %power|%"
|
||||||
|
//% weight=98 group="Motors" blockGap=8
|
||||||
|
//% power.min=-100 power.max=100
|
||||||
|
move(angle: number, power: number) {
|
||||||
|
angle = angle >> 0;
|
||||||
|
power = Math.clamp(-100, 100, power >> 0);
|
||||||
|
|
||||||
|
step(this.port, {
|
||||||
|
speed: power,
|
||||||
|
step1: 0,
|
||||||
|
step2: angle,
|
||||||
|
step3: 0,
|
||||||
|
useSteps: true,
|
||||||
|
useBrake: this.brake
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the motor
|
||||||
|
*/
|
||||||
|
//% blockId=motorStop block="stop %motor"
|
||||||
|
//% weight=97 group="Motors"
|
||||||
|
stop() {
|
||||||
|
const b = mkCmd(this.port, DAL.opOutputStop, 1)
|
||||||
|
b.setNumber(NumberFormat.UInt8LE, 2, this.brake ? 1 : 0)
|
||||||
|
writePWM(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the automatic brake on or off when the motor is off
|
||||||
|
* @param brake a value indicating if the motor should break when off
|
||||||
|
*/
|
||||||
|
//% blockId=outputMotorSetBrakeMode block="set %motor|brake %brake"
|
||||||
|
//% brake.fieldEditor=toggleonoff
|
||||||
|
//% weight=60 group="Motors" blockGap=8
|
||||||
|
setBrake(brake: boolean) {
|
||||||
|
this.brake = brake;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverses the motor polarity
|
||||||
|
*/
|
||||||
|
//% blockId=motorSetReversed block="set %motor|reversed %reversed"
|
||||||
|
//% reversed.fieldEditor=toggleonoff
|
||||||
|
//% weight=59 group="Motors"
|
||||||
|
setReversed(reversed: boolean) {
|
||||||
|
const b = mkCmd(this.port, DAL.opOutputPolarity, 1)
|
||||||
|
b.setNumber(NumberFormat.Int8LE, 2, reversed ? -1 : 1);
|
||||||
|
writePWM(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets motor actual speed.
|
||||||
|
* @param motor the port which connects to the motor
|
||||||
|
*/
|
||||||
|
//% blockId=motorSpeed block="%motor|speed"
|
||||||
|
//% weight=72 group="Motors" blockGap=8
|
||||||
|
speed(): number {
|
||||||
|
return getMotorData(this.port).actualSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets motor step count.
|
||||||
|
* @param motor the port which connects to the motor
|
||||||
|
*/
|
||||||
|
//% blockId=motorCount block="%motor|count"
|
||||||
|
//% weight=71 group="Motors" blockGap=8
|
||||||
|
count(): number {
|
||||||
|
return getMotorData(this.port).count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets motor tacho count.
|
||||||
|
* @param motor the port which connects to the motor
|
||||||
|
*/
|
||||||
|
//% blockId=motorTachoCount block="%motor|tacho count"
|
||||||
|
//% weight=70 group="Motors"
|
||||||
|
tachoCount(): number {
|
||||||
|
return getMotorData(this.port).tachoCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the motor count
|
||||||
|
*/
|
||||||
|
clearCount() {
|
||||||
|
const b = mkCmd(this.port, DAL.opOutputClearCount, 0)
|
||||||
|
writePWM(b)
|
||||||
|
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
|
||||||
|
if (this.port & (1 << i)) {
|
||||||
|
motorMM.setNumber(NumberFormat.Int32LE, i * MotorDataOff.Size + MotorDataOff.TachoSensor, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the motor.
|
||||||
|
*/
|
||||||
|
reset() {
|
||||||
|
reset(this.port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//% whenUsed fixedInstance block="large motor A"
|
||||||
|
export const largeMotorA = new Motor(Output.A, true);
|
||||||
|
|
||||||
|
//% whenUsed fixedInstance block="large motor B"
|
||||||
|
export const largeMotorB = new Motor(Output.B, true);
|
||||||
|
|
||||||
|
//% whenUsed fixedInstance block="large motor C"
|
||||||
|
export const largeMotorC = new Motor(Output.C, true);
|
||||||
|
|
||||||
|
//% whenUsed fixedInstance block="large motor D"
|
||||||
|
export const largeMotorD = new Motor(Output.D, true);
|
||||||
|
|
||||||
|
//% whenUsed fixedInstance block="medium motor A"
|
||||||
|
export const mediumMotorA = new Motor(Output.A, false);
|
||||||
|
|
||||||
|
//% whenUsed fixedInstance block="medium motor B"
|
||||||
|
export const mediumMotorB = new Motor(Output.B, false);
|
||||||
|
|
||||||
|
//% whenUsed fixedInstance block="medium motor C"
|
||||||
|
export const mediumMotorC = new Motor(Output.C, false);
|
||||||
|
|
||||||
|
//% whenUsed fixedInstance block="medium motor D"
|
||||||
|
export const mediumMotorD = new Motor(Output.D, false);
|
||||||
|
|
||||||
|
function reset(out: Output) {
|
||||||
|
let b = mkCmd(out, DAL.opOutputReset, 0)
|
||||||
|
writePWM(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
function outOffset(out: Output) {
|
function outOffset(out: Output) {
|
||||||
@ -128,14 +253,14 @@ namespace output {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MotorData {
|
interface MotorData {
|
||||||
actualSpeed: number; // -100..+100
|
actualSpeed: number; // -100..+100
|
||||||
tachoCount: number;
|
tachoCount: number;
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only a single output at a time
|
// only a single output at a time
|
||||||
export function getMotorData(out: Output): MotorData {
|
function getMotorData(out: Output): MotorData {
|
||||||
let buf = motorMM.slice(outOffset(out), MotorDataOff.Size)
|
let buf = motorMM.slice(outOffset(out), MotorDataOff.Size)
|
||||||
return {
|
return {
|
||||||
actualSpeed: buf.getNumber(NumberFormat.Int8LE, MotorDataOff.Speed),
|
actualSpeed: buf.getNumber(NumberFormat.Int8LE, MotorDataOff.Speed),
|
||||||
@ -144,52 +269,7 @@ namespace output {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
interface StepOptions {
|
||||||
* Get motor speed.
|
|
||||||
* @param out the output connection that the motor is connected to
|
|
||||||
*/
|
|
||||||
//% blockId=output_getCurrentSpeed block="motor %out|speed"
|
|
||||||
//% weight=70 group="Motors"
|
|
||||||
export function getCurrentSpeed(out: Output) {
|
|
||||||
return getMotorData(out).actualSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set motor speed.
|
|
||||||
* @param out the output connection that the motor is connected to
|
|
||||||
* @param speed the desired speed to use. eg: 100
|
|
||||||
*/
|
|
||||||
//% blockId=output_setSpeed block="set motor %out| speed to %speed"
|
|
||||||
//% weight=81 group="Motors"
|
|
||||||
//% speed.min=-100 speed.max=100
|
|
||||||
export function setSpeed(out: Output, speed: number) {
|
|
||||||
currentSpeed[out] = speed;
|
|
||||||
let b = mkCmd(out, DAL.opOutputSpeed, 1)
|
|
||||||
b.setNumber(NumberFormat.Int8LE, 2, Math.clamp(-100, 100, speed))
|
|
||||||
writePWM(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set motor power.
|
|
||||||
* @param out the output connection that the motor is connected to
|
|
||||||
* @param power the desired power to use. eg: 100
|
|
||||||
*/
|
|
||||||
//% blockId=output_setPower block="set motor %out| power to %power"
|
|
||||||
//% weight=80 group="Motors"
|
|
||||||
//% power.min=-100 power.max=100
|
|
||||||
export function setPower(out: Output, power: number) {
|
|
||||||
let b = mkCmd(out, DAL.opOutputPower, 1)
|
|
||||||
b.setNumber(NumberFormat.Int8LE, 2, Math.clamp(-100, 100, power))
|
|
||||||
writePWM(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setPolarity(out: Output, polarity: number) {
|
|
||||||
let b = mkCmd(out, DAL.opOutputPolarity, 1)
|
|
||||||
b.setNumber(NumberFormat.Int8LE, 2, Math.clamp(-1, 1, polarity))
|
|
||||||
writePWM(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StepOptions {
|
|
||||||
power?: number;
|
power?: number;
|
||||||
speed?: number; // either speed or power has to be present
|
speed?: number; // either speed or power has to be present
|
||||||
step1: number;
|
step1: number;
|
||||||
@ -199,7 +279,7 @@ namespace output {
|
|||||||
useBrake?: boolean;
|
useBrake?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function step(out: Output, opts: StepOptions) {
|
function step(out: Output, opts: StepOptions) {
|
||||||
let op = opts.useSteps ? DAL.opOutputStepSpeed : DAL.opOutputTimeSpeed
|
let op = opts.useSteps ? DAL.opOutputStepSpeed : DAL.opOutputTimeSpeed
|
||||||
let speed = opts.speed
|
let speed = opts.speed
|
||||||
if (speed == null) {
|
if (speed == null) {
|
||||||
|
130
libs/core/png.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "pxt.h"
|
||||||
|
#include "ev3const.h"
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct PNGHeader {
|
||||||
|
uint8_t pngHeader[8];
|
||||||
|
uint32_t lenIHDR;
|
||||||
|
uint8_t IHDR[4];
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint8_t bitDepth;
|
||||||
|
uint8_t colorType;
|
||||||
|
uint8_t compressionMethod;
|
||||||
|
uint8_t filterMethod;
|
||||||
|
uint8_t interlaceMethod;
|
||||||
|
uint32_t hdCRC;
|
||||||
|
uint32_t lenIDAT;
|
||||||
|
uint8_t IDAT[4];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
namespace screen {
|
||||||
|
|
||||||
|
static uint32_t swap(uint32_t num) {
|
||||||
|
return ((num >> 24) & 0xff) | ((num << 8) & 0xff0000) | ((num >> 8) & 0xff00) |
|
||||||
|
((num << 24) & 0xff000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t revbits(uint8_t v) {
|
||||||
|
v = (v & 0xf0) >> 4 | (v & 0x0f) << 4;
|
||||||
|
v = (v & 0xcc) >> 2 | (v & 0x33) << 2;
|
||||||
|
v = (v & 0xaa) >> 1 | (v & 0x55) << 1;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decompresses a 1-bit gray scale PNG image to image format. */
|
||||||
|
//%
|
||||||
|
Image unpackPNG(Buffer png) {
|
||||||
|
if (!png) {
|
||||||
|
DMESG("PNG: Missing image");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (png->length < sizeof(PNGHeader) + 4) {
|
||||||
|
DMESG("PNG: File too small");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(png->data, "\x89PNG\r\n\x1A\n", 8) != 0) {
|
||||||
|
DMESG("PNG: Invalid header");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PNGHeader hd;
|
||||||
|
memcpy(&hd, png->data, sizeof(hd));
|
||||||
|
|
||||||
|
if (memcmp(hd.IHDR, "IHDR", 4) != 0) {
|
||||||
|
DMESG("PNG: missing IHDR");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hd.lenIHDR = swap(hd.lenIHDR);
|
||||||
|
hd.width = swap(hd.width);
|
||||||
|
hd.height = swap(hd.height);
|
||||||
|
hd.lenIDAT = swap(hd.lenIDAT);
|
||||||
|
|
||||||
|
if (hd.lenIHDR != 13) {
|
||||||
|
DMESG("PNG: bad IHDR len");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (hd.bitDepth != 1 || hd.colorType != 0 || hd.compressionMethod != 0 ||
|
||||||
|
hd.filterMethod != 0 || hd.interlaceMethod != 0) {
|
||||||
|
DMESG("PNG: not 1-bit grayscale");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (memcmp(hd.IDAT, "IDAT", 4) != 0) {
|
||||||
|
DMESG("PNG: missing IDAT");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (hd.lenIDAT + sizeof(hd) >= png->length) {
|
||||||
|
DMESG("PNG: buffer too short");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (hd.width > 300 || hd.height > 300) {
|
||||||
|
DMESG("PNG: too big");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t byteW = (hd.width + 7) >> 3;
|
||||||
|
uint32_t expSize = (byteW + 1) * hd.height;
|
||||||
|
unsigned long sz = expSize;
|
||||||
|
uint8_t *tmp = (uint8_t *)malloc(sz);
|
||||||
|
int code = uncompress(tmp, &sz, png->data + sizeof(hd), hd.lenIDAT);
|
||||||
|
if (code != 0) {
|
||||||
|
DMESG("PNG: zlib failed: %d", code);
|
||||||
|
free(tmp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (sz != expSize) {
|
||||||
|
DMESG("PNG: invalid compressed size");
|
||||||
|
free(tmp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer res = mkBuffer(NULL, 2 + byteW * hd.height);
|
||||||
|
res->data[0] = 0xf0;
|
||||||
|
res->data[1] = hd.width;
|
||||||
|
uint8_t *dst = res->data + 2;
|
||||||
|
uint8_t *src = tmp;
|
||||||
|
uint8_t lastMask = (1 << (hd.width & 7)) - 1;
|
||||||
|
if (lastMask == 0)
|
||||||
|
lastMask = 0xff;
|
||||||
|
for (uint32_t i = 0; i < hd.height; ++i) {
|
||||||
|
if (*src++ != 0) {
|
||||||
|
DMESG("PNG: unsupported filter");
|
||||||
|
free(tmp);
|
||||||
|
decrRC(res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (uint32_t j = 0; j < byteW; ++j) {
|
||||||
|
*dst = ~revbits(*src++);
|
||||||
|
if (j == byteW - 1) {
|
||||||
|
*dst &= lastMask;
|
||||||
|
}
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(tmp);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,8 @@ namespace pxt {
|
|||||||
void raiseEvent(int id, int event);
|
void raiseEvent(int id, int event);
|
||||||
int allocateNotifyEvent();
|
int allocateNotifyEvent();
|
||||||
void sleep_core_us(uint64_t us);
|
void sleep_core_us(uint64_t us);
|
||||||
|
void startUser();
|
||||||
|
void stopUser();
|
||||||
|
|
||||||
class Button;
|
class Button;
|
||||||
typedef Button *Button_;
|
typedef Button *Button_;
|
||||||
@ -27,6 +29,10 @@ class MMap : public RefObject {
|
|||||||
|
|
||||||
extern volatile bool paniced;
|
extern volatile bool paniced;
|
||||||
|
|
||||||
|
// Buffer, Sound, and Image share representation.
|
||||||
|
typedef Buffer Image;
|
||||||
|
typedef Buffer Sound;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEVICE_EVT_ANY 0
|
#define DEVICE_EVT_ANY 0
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"mmap.cpp",
|
"mmap.cpp",
|
||||||
"control.cpp",
|
"control.cpp",
|
||||||
"buttons.ts",
|
"buttons.ts",
|
||||||
|
"png.cpp",
|
||||||
"screen.cpp",
|
"screen.cpp",
|
||||||
"screen.ts",
|
"screen.ts",
|
||||||
"output.cpp",
|
"output.cpp",
|
||||||
@ -25,6 +26,8 @@
|
|||||||
"shims.d.ts",
|
"shims.d.ts",
|
||||||
"enums.d.ts",
|
"enums.d.ts",
|
||||||
"dal.d.ts",
|
"dal.d.ts",
|
||||||
|
"images.ts",
|
||||||
|
"images.jres",
|
||||||
"ns.ts"
|
"ns.ts"
|
||||||
],
|
],
|
||||||
"testFiles": [
|
"testFiles": [
|
||||||
|
@ -130,82 +130,6 @@ void _blitLine(int xw, int y, Buffer buf, Draw mode) {
|
|||||||
blitLineCore(XX(xw), y, YY(xw), buf->data, mode);
|
blitLineCore(XX(xw), y, YY(xw), buf->data, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t ones[] = {
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool isValidIcon(Buffer buf) {
|
|
||||||
return buf->length >= 3 && buf->data[0] == 0xf0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const uint8_t bitdouble[] = {
|
|
||||||
0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Double size of an icon. */
|
|
||||||
//%
|
|
||||||
Buffer doubleIcon(Buffer buf) {
|
|
||||||
if (!isValidIcon(buf))
|
|
||||||
return NULL;
|
|
||||||
int w = buf->data[1];
|
|
||||||
if (w > 126)
|
|
||||||
return NULL;
|
|
||||||
int bw = PIX2BYTES(w);
|
|
||||||
int h = (buf->length - 2) / bw;
|
|
||||||
int bw2 = PIX2BYTES(w * 2);
|
|
||||||
Buffer out = mkBuffer(NULL, 2 + bw2 * h * 2);
|
|
||||||
out->data[0] = 0xf0;
|
|
||||||
out->data[1] = w * 2;
|
|
||||||
uint8_t *src = buf->data + 2;
|
|
||||||
uint8_t *dst = out->data + 2;
|
|
||||||
for (int i = 0; i < h; ++i) {
|
|
||||||
for (int jj = 0; jj < 2; ++jj) {
|
|
||||||
auto p = src;
|
|
||||||
for (int j = 0; j < bw; ++j) {
|
|
||||||
*dst++ = bitdouble[*p & 0xf];
|
|
||||||
*dst++ = bitdouble[*p >> 4];
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
src += bw;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Draw an icon on the screen. */
|
|
||||||
//%
|
|
||||||
void drawIcon(int x, int y, Buffer buf, Draw mode) {
|
|
||||||
if (!isValidIcon(buf))
|
|
||||||
return;
|
|
||||||
if (mode & (Draw::Double | Draw::Quad)) {
|
|
||||||
buf = doubleIcon(buf);
|
|
||||||
if (mode & Draw::Quad) {
|
|
||||||
auto pbuf = buf;
|
|
||||||
buf = doubleIcon(buf);
|
|
||||||
decrRC(pbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int pixwidth = buf->data[1];
|
|
||||||
int ptr = 2;
|
|
||||||
int bytewidth = PIX2BYTES(pixwidth);
|
|
||||||
pixwidth = min(pixwidth, LCD_WIDTH);
|
|
||||||
while (ptr + bytewidth <= buf->length) {
|
|
||||||
if (mode & (Draw::Clear | Draw::Xor | Draw::Transparent)) {
|
|
||||||
// no erase of background
|
|
||||||
} else {
|
|
||||||
blitLineCore(x, y, pixwidth, ones, Draw::Clear);
|
|
||||||
}
|
|
||||||
blitLineCore(x, y, pixwidth, &buf->data[ptr], mode);
|
|
||||||
y++;
|
|
||||||
ptr += bytewidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode & Draw::Double)
|
|
||||||
decrRC(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Clear screen and reset font to normal. */
|
/** Clear screen and reset font to normal. */
|
||||||
//%
|
//%
|
||||||
void clear() {
|
void clear() {
|
||||||
@ -309,11 +233,23 @@ extern "C" void drawPanic(int code) {
|
|||||||
updateLCD();
|
updateLCD();
|
||||||
|
|
||||||
int fd = open("/dev/lms_ui", O_RDWR);
|
int fd = open("/dev/lms_ui", O_RDWR);
|
||||||
uint8_t cmd[] = { 48 + 5, 0 };
|
uint8_t cmd[] = {48 + 5, 0};
|
||||||
write(fd, cmd, 2);
|
write(fd, cmd, 2);
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isValidImage(Buffer buf) {
|
||||||
|
return buf != NULL && buf->length >= 3 && buf->data[0] == 0xf0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Makes an image bound to a buffer. */
|
||||||
|
//%
|
||||||
|
Image imageOf(Buffer buf) {
|
||||||
|
if (!isValidImage(buf))
|
||||||
|
return NULL;
|
||||||
|
incrRC(buf);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace pxt {
|
namespace pxt {
|
||||||
@ -321,3 +257,105 @@ void screen_init() {
|
|||||||
screen::init();
|
screen::init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//% fixedInstances
|
||||||
|
namespace ImageMethods {
|
||||||
|
|
||||||
|
using namespace screen;
|
||||||
|
|
||||||
|
static const uint8_t bitdouble[] = {
|
||||||
|
0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t ones[] = {
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Returns the underlaying Buffer object. */
|
||||||
|
//% property
|
||||||
|
Buffer buffer(Image ic) {
|
||||||
|
incrRC(ic);
|
||||||
|
return ic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the width of an image. */
|
||||||
|
//% property
|
||||||
|
int width(Image ic) {
|
||||||
|
if (!isValidImage(ic))
|
||||||
|
return 0;
|
||||||
|
return ic->data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the height of an image. */
|
||||||
|
//% property
|
||||||
|
int height(Image ic) {
|
||||||
|
if (!isValidImage(ic))
|
||||||
|
return 0;
|
||||||
|
int bw = PIX2BYTES(ic->data[1]);
|
||||||
|
return (ic->length - 2) / bw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Double size of an image. */
|
||||||
|
//%
|
||||||
|
Image doubled(Image buf) {
|
||||||
|
if (!isValidImage(buf))
|
||||||
|
return NULL;
|
||||||
|
int w = buf->data[1];
|
||||||
|
if (w > 126)
|
||||||
|
return NULL;
|
||||||
|
int bw = PIX2BYTES(w);
|
||||||
|
int h = (buf->length - 2) / bw;
|
||||||
|
int bw2 = PIX2BYTES(w * 2);
|
||||||
|
Buffer out = mkBuffer(NULL, 2 + bw2 * h * 2);
|
||||||
|
out->data[0] = 0xf0;
|
||||||
|
out->data[1] = w * 2;
|
||||||
|
uint8_t *src = buf->data + 2;
|
||||||
|
uint8_t *dst = out->data + 2;
|
||||||
|
for (int i = 0; i < h; ++i) {
|
||||||
|
for (int jj = 0; jj < 2; ++jj) {
|
||||||
|
auto p = src;
|
||||||
|
for (int j = 0; j < bw; ++j) {
|
||||||
|
*dst++ = bitdouble[*p & 0xf];
|
||||||
|
*dst++ = bitdouble[*p >> 4];
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
src += bw;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Draw an image on the screen. */
|
||||||
|
//%
|
||||||
|
void draw(Image buf, int x, int y, Draw mode) {
|
||||||
|
if (!isValidImage(buf))
|
||||||
|
return;
|
||||||
|
if (mode & (Draw::Double | Draw::Quad)) {
|
||||||
|
buf = doubled(buf);
|
||||||
|
if (mode & Draw::Quad) {
|
||||||
|
auto pbuf = buf;
|
||||||
|
buf = doubled(buf);
|
||||||
|
decrRC(pbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int pixwidth = buf->data[1];
|
||||||
|
int ptr = 2;
|
||||||
|
int bytewidth = PIX2BYTES(pixwidth);
|
||||||
|
pixwidth = min(pixwidth, LCD_WIDTH);
|
||||||
|
while (ptr + bytewidth <= buf->length) {
|
||||||
|
if (mode & (Draw::Clear | Draw::Xor | Draw::Transparent)) {
|
||||||
|
// no erase of background
|
||||||
|
} else {
|
||||||
|
blitLineCore(x, y, pixwidth, ones, Draw::Clear);
|
||||||
|
}
|
||||||
|
blitLineCore(x, y, pixwidth, &buf->data[ptr], mode);
|
||||||
|
y++;
|
||||||
|
ptr += bytewidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode & (Draw::Double | Draw::Quad))
|
||||||
|
decrRC(buf);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace screen {
|
namespace brick {
|
||||||
//% shim=screen::_setPixel
|
//% shim=screen::_setPixel
|
||||||
function _setPixel(p0: uint32, p1: uint32, mode: Draw): void { }
|
function _setPixel(p0: uint32, p1: uint32, mode: Draw): void { }
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ namespace screen {
|
|||||||
currFont = f
|
currFont = f
|
||||||
}
|
}
|
||||||
|
|
||||||
export const heart = hex`f007 367f7f3e1c08`
|
export const heart = screen.imageOf(hex`f007 367f7f3e1c08`)
|
||||||
|
|
||||||
export function defaultFont(): Font {
|
export function defaultFont(): Font {
|
||||||
return {
|
return {
|
||||||
@ -78,13 +78,21 @@ namespace screen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setPixel(x: number, y: number, mode = Draw.Normal) {
|
/**
|
||||||
|
* Sets a pixel on or off
|
||||||
|
* @param on a value indicating if the pixel should be on or off
|
||||||
|
* @param x the starting position's x coordinate, eg: 0
|
||||||
|
* @param y the starting position's x coordinate, eg: 0
|
||||||
|
*/
|
||||||
|
//% blockId=screen_setpixel block="set pixel %on| at x: %x| y: %y"
|
||||||
|
//% weight=98 group="Screen"
|
||||||
|
//% x.min=0 x.max=178 y.min=0 y.max=128 on.fieldEditor=toggleonoff
|
||||||
|
export function setPixel(on: boolean, x: number, y: number) {
|
||||||
x |= 0
|
x |= 0
|
||||||
y |= 0
|
y |= 0
|
||||||
if (0 <= x && x < DAL.LCD_WIDTH && 0 <= y && y < DAL.LCD_HEIGHT)
|
if (0 <= x && x < DAL.LCD_WIDTH && 0 <= y && y < DAL.LCD_HEIGHT)
|
||||||
_setPixel(x, y, mode)
|
_setPixel(x, y, on ? Draw.Normal : Draw.Clear)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show text on the screen.
|
* Show text on the screen.
|
||||||
@ -92,10 +100,10 @@ namespace screen {
|
|||||||
* @param x the starting position's x coordinate, eg: 0
|
* @param x the starting position's x coordinate, eg: 0
|
||||||
* @param y the starting position's x coordinate, eg: 0
|
* @param y the starting position's x coordinate, eg: 0
|
||||||
*/
|
*/
|
||||||
//% blockId=screen_drawText block="print %text| at x: %x| y: %y"
|
//% blockId=screen_print block="print %text| at x: %x| y: %y"
|
||||||
//% weight=99 group="Screen" blockNamespace=output inlineInputMode="inline"
|
//% weight=99 group="Screen" inlineInputMode="inline" blockGap=8
|
||||||
//% x.min=0 x.max=178 y.min=0 y.max=128
|
//% x.min=0 x.max=178 y.min=0 y.max=128
|
||||||
export function drawText(text: string, x: number, y: number, mode = Draw.Normal) {
|
export function print(text: string, x: number, y: number, mode = Draw.Normal) {
|
||||||
x |= 0
|
x |= 0
|
||||||
y |= 0
|
y |= 0
|
||||||
if (!currFont) currFont = defaultFont()
|
if (!currFont) currFont = defaultFont()
|
||||||
@ -103,10 +111,11 @@ namespace screen {
|
|||||||
let cp = 0
|
let cp = 0
|
||||||
let byteWidth = (currFont.charWidth + 7) >> 3
|
let byteWidth = (currFont.charWidth + 7) >> 3
|
||||||
let charSize = byteWidth * currFont.charHeight
|
let charSize = byteWidth * currFont.charHeight
|
||||||
let iconBuf = output.createBuffer(2 + charSize)
|
let imgBuf = output.createBuffer(2 + charSize)
|
||||||
let double = (mode & Draw.Quad) ? 4 : (mode & Draw.Double) ? 2 : 1
|
let double = (mode & Draw.Quad) ? 4 : (mode & Draw.Double) ? 2 : 1
|
||||||
iconBuf[0] = 0xf0
|
imgBuf[0] = 0xf0
|
||||||
iconBuf[1] = currFont.charWidth
|
imgBuf[1] = currFont.charWidth
|
||||||
|
let img = screen.imageOf(imgBuf)
|
||||||
while (cp < text.length) {
|
while (cp < text.length) {
|
||||||
let ch = text.charCodeAt(cp++)
|
let ch = text.charCodeAt(cp++)
|
||||||
if (ch == 10) {
|
if (ch == 10) {
|
||||||
@ -115,11 +124,11 @@ namespace screen {
|
|||||||
}
|
}
|
||||||
if (ch < 32) continue
|
if (ch < 32) continue
|
||||||
let idx = (ch - currFont.firstChar) * charSize
|
let idx = (ch - currFont.firstChar) * charSize
|
||||||
if (idx < 0 || idx + iconBuf.length - 1 > currFont.data.length)
|
if (idx < 0 || idx + imgBuf.length - 1 > currFont.data.length)
|
||||||
iconBuf.fill(0, 2)
|
imgBuf.fill(0, 2)
|
||||||
else
|
else
|
||||||
iconBuf.write(2, currFont.data.slice(idx, charSize))
|
imgBuf.write(2, currFont.data.slice(idx, charSize))
|
||||||
drawIcon(x, y, iconBuf, mode)
|
img.draw(x, y, mode)
|
||||||
x += double * currFont.charWidth
|
x += double * currFont.charWidth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
libs/core/shims.d.ts
vendored
@ -72,17 +72,44 @@ declare namespace serial {
|
|||||||
}
|
}
|
||||||
declare namespace screen {
|
declare namespace screen {
|
||||||
|
|
||||||
/** Double size of an icon. */
|
/** Decompresses a 1-bit gray scale PNG image to image format. */
|
||||||
//% shim=screen::doubleIcon
|
//% shim=screen::unpackPNG
|
||||||
function doubleIcon(buf: Buffer): Buffer;
|
function unpackPNG(png: Buffer): Image;
|
||||||
|
}
|
||||||
/** Draw an icon on the screen. */
|
declare namespace screen {
|
||||||
//% shim=screen::drawIcon
|
|
||||||
function drawIcon(x: int32, y: int32, buf: Buffer, mode: Draw): void;
|
|
||||||
|
|
||||||
/** Clear screen and reset font to normal. */
|
/** Clear screen and reset font to normal. */
|
||||||
//% shim=screen::clear
|
//% shim=screen::clear
|
||||||
function clear(): void;
|
function clear(): void;
|
||||||
|
|
||||||
|
/** Makes an image bound to a buffer. */
|
||||||
|
//% shim=screen::imageOf
|
||||||
|
function imageOf(buf: Buffer): Image;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//% fixedInstances
|
||||||
|
declare interface Image {
|
||||||
|
/** Returns the underlaying Buffer object. */
|
||||||
|
//% property shim=ImageMethods::buffer
|
||||||
|
buffer: Buffer;
|
||||||
|
|
||||||
|
/** Returns the width of an image. */
|
||||||
|
//% property shim=ImageMethods::width
|
||||||
|
width: int32;
|
||||||
|
|
||||||
|
/** Returns the height of an image. */
|
||||||
|
//% property shim=ImageMethods::height
|
||||||
|
height: int32;
|
||||||
|
|
||||||
|
/** Double size of an image. */
|
||||||
|
//% shim=ImageMethods::doubled
|
||||||
|
doubled(): Image;
|
||||||
|
|
||||||
|
/** Draw an image on the screen. */
|
||||||
|
//% shim=ImageMethods::draw
|
||||||
|
draw(x: int32, y: int32, mode: Draw): void;
|
||||||
}
|
}
|
||||||
declare namespace output {
|
declare namespace output {
|
||||||
|
|
||||||
|
@ -1,48 +1,48 @@
|
|||||||
screen.clear()
|
screen.clear()
|
||||||
screen.drawText("PXT!", 10, 30, Draw.Quad)
|
screen.print("PXT!", 10, 30, Draw.Quad)
|
||||||
|
|
||||||
screen.drawRect(40, 40, 20, 10, Draw.Fill)
|
screen.drawRect(40, 40, 20, 10, Draw.Fill)
|
||||||
output.setLights(LightsPattern.Orange)
|
motors.setStatusLight(LightsPattern.Orange)
|
||||||
|
|
||||||
screen.drawIcon(100, 50, screen.doubleIcon(screen.heart), Draw.Double | Draw.Transparent)
|
screen.heart.doubled().draw(100, 50, Draw.Double | Draw.Transparent)
|
||||||
|
|
||||||
input.buttonEnter.onEvent(ButtonEvent.Click, () => {
|
sensors.buttonEnter.onEvent(ButtonEvent.Click, () => {
|
||||||
screen.clear()
|
screen.clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
input.buttonLeft.onEvent(ButtonEvent.Click, () => {
|
sensors.buttonLeft.onEvent(ButtonEvent.Click, () => {
|
||||||
screen.drawRect(10, 70, 20, 10, Draw.Fill)
|
screen.drawRect(10, 70, 20, 10, Draw.Fill)
|
||||||
output.setLights(LightsPattern.Red)
|
motors.setStatusLight(LightsPattern.Red)
|
||||||
screen.setFont(screen.microbitFont())
|
screen.setFont(screen.microbitFont())
|
||||||
})
|
})
|
||||||
|
|
||||||
input.buttonRight.onEvent(ButtonEvent.Click, () => {
|
sensors.buttonRight.onEvent(ButtonEvent.Click, () => {
|
||||||
screen.drawText("Right!", 10, 60)
|
screen.print("Right!", 10, 60)
|
||||||
})
|
})
|
||||||
|
|
||||||
input.buttonDown.onEvent(ButtonEvent.Click, () => {
|
sensors.buttonDown.onEvent(ButtonEvent.Click, () => {
|
||||||
screen.drawText("Down! ", 10, 60)
|
screen.print("Down! ", 10, 60)
|
||||||
})
|
})
|
||||||
|
|
||||||
input.buttonUp.onEvent(ButtonEvent.Click, () => {
|
sensors.buttonUp.onEvent(ButtonEvent.Click, () => {
|
||||||
screen.drawText("Up! ", 10, 60)
|
screen.print("Up! ", 10, 60)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
let num = 0
|
let num = 0
|
||||||
|
|
||||||
input.touchSensor.onEvent(ButtonEvent.Click, () => {
|
sensors.touchSensor1.onEvent(TouchSensorEvent.Bumped, () => {
|
||||||
screen.drawText("Click! " + num, 10, 60)
|
screen.print("Click! " + num, 10, 60)
|
||||||
num++
|
num++
|
||||||
})
|
})
|
||||||
|
|
||||||
input.remoteTopLeft.onEvent(ButtonEvent.Click, () => {
|
sensors.remoteButtonTopLeft.onEvent(ButtonEvent.Click, () => {
|
||||||
screen.drawText("TOPLEFT " + num, 10, 60)
|
screen.print("TOPLEFT " + num, 10, 60)
|
||||||
num++
|
num++
|
||||||
})
|
})
|
||||||
|
|
||||||
input.remoteTopRight.onEvent(ButtonEvent.Down, () => {
|
sensors.remoteButtonTopRight.onEvent(ButtonEvent.Down, () => {
|
||||||
screen.drawText("TOPRIGH " + num, 10, 60)
|
screen.print("TOPRIGH " + num, 10, 60)
|
||||||
num++
|
num++
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ loops.forever(() => {
|
|||||||
/*
|
/*
|
||||||
loops.forever(() => {
|
loops.forever(() => {
|
||||||
let v = input.color.getColor()
|
let v = input.color.getColor()
|
||||||
screen.drawText(10, 60, v + " ")
|
screen.print(10, 60, v + " ")
|
||||||
loops.pause(200)
|
loops.pause(200)
|
||||||
})
|
})
|
||||||
*/
|
*/
|
||||||
|
@ -1,10 +1,26 @@
|
|||||||
namespace input {
|
// keep TouchSensorEvent in sync with ButtonEvent
|
||||||
export class TouchSensor extends internal.AnalogSensor {
|
|
||||||
button: Button;
|
|
||||||
|
|
||||||
constructor() {
|
/**
|
||||||
super()
|
* Touch sensor interactions
|
||||||
this.button = new Button()
|
*/
|
||||||
|
const enum TouchSensorEvent {
|
||||||
|
//% block="pressed"
|
||||||
|
Pressed = 4,
|
||||||
|
//% block="bumped"
|
||||||
|
Bumped = 1,
|
||||||
|
//% block="released"
|
||||||
|
Released = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sensors {
|
||||||
|
|
||||||
|
//% fixedInstances
|
||||||
|
export class TouchSensor extends internal.AnalogSensor {
|
||||||
|
private button: brick.Button;
|
||||||
|
|
||||||
|
constructor(port: number) {
|
||||||
|
super(port)
|
||||||
|
this.button = new brick.Button();
|
||||||
}
|
}
|
||||||
|
|
||||||
_query() {
|
_query() {
|
||||||
@ -12,17 +28,51 @@ namespace input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_update(prev: number, curr: number) {
|
_update(prev: number, curr: number) {
|
||||||
this.button.update(curr > 0)
|
this.button._update(curr > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
_deviceType() {
|
_deviceType() {
|
||||||
return DAL.DEVICE_TYPE_TOUCH
|
return DAL.DEVICE_TYPE_TOUCH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if touch sensor is touched.
|
||||||
|
* @param sensor the port to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/touch/is-touched
|
||||||
|
//% block="%sensor|is touched"
|
||||||
|
//% blockId=touchIsTouched
|
||||||
|
//% parts="touch"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=81 blockGap=8
|
||||||
|
//% group="Touch Sensor"
|
||||||
|
isTouched() {
|
||||||
|
return this.button.isPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do something when a touch sensor is touched...
|
||||||
|
* @param sensor the touch sensor that needs to be clicked or used
|
||||||
|
* @param event the kind of button gesture that needs to be detected
|
||||||
|
* @param body code to run when the event is raised
|
||||||
|
*/
|
||||||
|
//% help=input/touch/on-event
|
||||||
|
//% blockId=touchEvent block="on %sensor|%event"
|
||||||
|
//% parts="touch"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=99 blockGap=8
|
||||||
|
//% group="Touch Sensor"
|
||||||
|
onEvent(ev: TouchSensorEvent, body: () => void) {
|
||||||
|
this.button.onEvent(<ButtonEvent><number>ev, body)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//% whenUsed
|
//% whenUsed block="touch sensor 1" weight=95 fixedInstance
|
||||||
export const touchSensorImpl: TouchSensor = new TouchSensor()
|
export const touchSensor1: TouchSensor = new TouchSensor(1)
|
||||||
|
//% whenUsed block="touch sensor 2" weight=95 fixedInstance
|
||||||
//% whenUsed
|
export const touchSensor2: TouchSensor = new TouchSensor(2)
|
||||||
export const touchSensor: Button = touchSensorImpl.button
|
//% whenUsed block="touch sensor 3" weight=95 fixedInstance
|
||||||
|
export const touchSensor3: TouchSensor = new TouchSensor(3)
|
||||||
|
//% whenUsed block="touch sensor 4" weight=95 fixedInstance
|
||||||
|
export const touchSensor4: TouchSensor = new TouchSensor(4)
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,100 @@
|
|||||||
namespace input {
|
const enum UltrasonicSensorEvent {
|
||||||
|
//% block="object near"
|
||||||
|
ObjectNear = 1,
|
||||||
|
//% block="object detected"
|
||||||
|
ObjectDetected = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sensors {
|
||||||
|
|
||||||
|
//% fixedInstances
|
||||||
export class UltraSonicSensor extends internal.UartSensor {
|
export class UltraSonicSensor extends internal.UartSensor {
|
||||||
constructor() {
|
private promixityThreshold: number;
|
||||||
super()
|
private movementThreshold: number;
|
||||||
|
|
||||||
|
constructor(port: number) {
|
||||||
|
super(port)
|
||||||
|
this.promixityThreshold = 10;
|
||||||
|
this.movementThreshold = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_deviceType() {
|
_deviceType() {
|
||||||
return DAL.DEVICE_TYPE_ULTRASONIC
|
return DAL.DEVICE_TYPE_ULTRASONIC
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get distance in mm */
|
_query(): number {
|
||||||
getDistance() {
|
return this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff;
|
||||||
// it supposedly also has an inch mode, but we stick to mm
|
}
|
||||||
|
|
||||||
|
_update(prev: number, curr: number) {
|
||||||
|
// is there an object near?
|
||||||
|
if (prev >= this.promixityThreshold && curr < this.promixityThreshold)
|
||||||
|
control.raiseEvent(this._id, UltrasonicSensorEvent.ObjectNear); // TODO proper HI-LO sensor
|
||||||
|
|
||||||
|
// did something change?
|
||||||
|
if (Math.abs(prev - curr) > this.movementThreshold)
|
||||||
|
control.raiseEvent(this._id, UltrasonicSensorEvent.ObjectDetected); // TODO debouncing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers code to run when the given color is close
|
||||||
|
* @param handler the code to run when detected
|
||||||
|
*/
|
||||||
|
//% help=input/ultrasonic/on
|
||||||
|
//% block="on %sensor|%event"
|
||||||
|
//% blockId=ultrasonicOn
|
||||||
|
//% parts="infraredsensor"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=100 blockGap=8
|
||||||
|
//% group="Ultrasonic Sensor"
|
||||||
|
on(event: UltrasonicSensorEvent, handler: () => void) {
|
||||||
|
control.onEvent(this._id, event, handler);
|
||||||
|
if (event == UltrasonicSensorEvent.ObjectNear
|
||||||
|
&& this.distance() < this.promixityThreshold)
|
||||||
|
control.runInBackground(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for the event to occur
|
||||||
|
*/
|
||||||
|
//% help=input/ultrasonic/wait
|
||||||
|
//% block="wait %sensor|for %event"
|
||||||
|
//% blockId=ultrasonicWait
|
||||||
|
//% parts="infraredsensor"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=99 blockGap=8
|
||||||
|
//% group="Ultrasonic Sensor"
|
||||||
|
wait(event: UltrasonicSensorEvent) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the distance from the sonar in millimeters
|
||||||
|
* @param sensor the ultrasonic sensor port
|
||||||
|
*/
|
||||||
|
//% help=input/ultrasonic/distance
|
||||||
|
//% block="%sensor|distance"
|
||||||
|
//% blockId=sonarGetDistance
|
||||||
|
//% parts="ultrasonic"
|
||||||
|
//% blockNamespace=sensors
|
||||||
|
//% weight=65 blockGap=8
|
||||||
|
//% group="Ultrasonic Sensor"
|
||||||
|
distance() {
|
||||||
|
// it supposedly also has an inch mode, but we stick to cm
|
||||||
this._setMode(0)
|
this._setMode(0)
|
||||||
return this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff
|
return this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//% whenUsed
|
//% fixedInstance whenUsed block="ultrasonic sensor 4"
|
||||||
export const ultrasonic: UltraSonicSensor = new UltraSonicSensor()
|
export const ultrasonic4: UltraSonicSensor = new UltraSonicSensor(4)
|
||||||
|
|
||||||
|
//% fixedInstance whenUsed block="ultrasonic sensor 1"
|
||||||
|
export const ultrasonic1: UltraSonicSensor = new UltraSonicSensor(1)
|
||||||
|
|
||||||
|
//% fixedInstance whenUsed block="ultrasonic sensor 2"
|
||||||
|
export const ultrasonic2: UltraSonicSensor = new UltraSonicSensor(2)
|
||||||
|
|
||||||
|
//% fixedInstance whenUsed block="ultrasonic sensor 3"
|
||||||
|
export const ultrasonic3: UltraSonicSensor = new UltraSonicSensor(3)
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
|
|
||||||
//% color="#D42878"
|
//% color="#00A5C8" weight=100
|
||||||
//% groups='["Brick buttons"]'
|
//% groups='["Light", "Buttons", "Screen"]'
|
||||||
namespace input {
|
namespace brick {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//% color="#D42878" weight=95
|
||||||
|
namespace sensors {
|
||||||
}
|
}
|
||||||
|
|
||||||
//% color="#8AC044" weight=90 icon="\uf185"
|
//% color="#8AC044" weight=90 icon="\uf185"
|
||||||
//% groups='["Lights", "Screen", "Motors"]'
|
namespace motors {
|
||||||
namespace output {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//% color="#DF5014" weight=80
|
//% color="#DF5014" weight=80
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
{
|
{
|
||||||
|
"Sound.buffer": "Returns the underlaying Buffer object.",
|
||||||
|
"Sound.play": "Play sound.",
|
||||||
"music": "Generation of music tones.",
|
"music": "Generation of music tones.",
|
||||||
"music.beat": "Return the duration of a beat in milliseconds (the beat fraction).",
|
"music.beat": "Return the duration of a beat in milliseconds (the beat fraction).",
|
||||||
"music.beat|param|fraction": "the fraction of the current whole note, eg: BeatFraction.Half",
|
"music.beat|param|fraction": "the fraction of the current whole note, eg: BeatFraction.Half",
|
||||||
"music.changeTempoBy": "Change the tempo up or down by some amount of beats per minute (bpm).",
|
"music.changeTempoBy": "Change the tempo up or down by some amount of beats per minute (bpm).",
|
||||||
"music.changeTempoBy|param|bpm": "The change in beats per minute to the tempo, eg: 20",
|
"music.changeTempoBy|param|bpm": "The change in beats per minute to the tempo, eg: 20",
|
||||||
|
"music.fromWAV": "Makes a sound bound to a buffer in WAV format.",
|
||||||
"music.noteFrequency": "Get the frequency of a note.",
|
"music.noteFrequency": "Get the frequency of a note.",
|
||||||
"music.noteFrequency|param|name": "the note name, eg: Note.C",
|
"music.noteFrequency|param|name": "the note name, eg: Note.C",
|
||||||
"music.playSound": "Start playing a sound and don't wait for it to finish.\nNotes are expressed as a string of characters with this format: NOTE[octave][:duration]",
|
"music.playSound": "Start playing a sound and don't wait for it to finish.\nNotes are expressed as a string of characters with this format: NOTE[octave][:duration]",
|
||||||
"music.playSoundUntilDone": "Play a sound and wait until the sound is done.\nNotes are expressed as a string of characters with this format: NOTE[octave][:duration]",
|
"music.playSoundUntilDone": "Play a sound and wait until the sound is done.\nNotes are expressed as a string of characters with this format: NOTE[octave][:duration]",
|
||||||
"music.playSoundUntilDone|param|sound": "the melody to play, eg: 'g5:1'",
|
"music.playSoundUntilDone|param|sound": "the melody to play, eg: music.sounds(Sounds.PowerUp)",
|
||||||
"music.playSound|param|sound": "the melody to play, eg: 'g5:1'",
|
"music.playSound|param|sound": "the melody to play, eg: music.sounds(Sounds.PowerUp)",
|
||||||
"music.playTone": "Play a tone through the speaker for some amount of time.",
|
"music.playTone": "Play a tone through the speaker for some amount of time.",
|
||||||
"music.playTone|param|frequency": "pitch of the tone to play in Hertz (Hz)",
|
"music.playTone|param|frequency": "pitch of the tone to play in Hertz (Hz)",
|
||||||
"music.playTone|param|ms": "tone duration in milliseconds (ms)",
|
"music.playTone|param|ms": "tone duration in milliseconds (ms)",
|
||||||
"music.rest": "Rest, or play silence, for some time (in milleseconds).",
|
"music.rest": "Rest, or play silence, for some time (in milleseconds).",
|
||||||
"music.rest|param|ms": "rest duration in milliseconds (ms)",
|
"music.rest|param|ms": "rest duration in milliseconds (ms), eg: 100",
|
||||||
"music.ringTone": "Play a tone.",
|
"music.ringTone": "Play a tone.",
|
||||||
"music.ringTone|param|frequency": "pitch of the tone to play in Hertz (Hz)",
|
"music.ringTone|param|frequency": "pitch of the tone to play in Hertz (Hz), eg: Note.C",
|
||||||
"music.setTempo": "Set the tempo a number of beats per minute (bpm).",
|
"music.setTempo": "Set the tempo a number of beats per minute (bpm).",
|
||||||
"music.setTempo|param|bpm": "The new tempo in beats per minute, eg: 120",
|
"music.setTempo|param|bpm": "The new tempo in beats per minute, eg: 120",
|
||||||
"music.setVolume": "Set the output volume of the sound synthesizer.",
|
"music.setVolume": "Set the output volume of the sound synthesizer.",
|
||||||
|
@ -29,18 +29,20 @@
|
|||||||
"Sounds.Siren|block": "siren",
|
"Sounds.Siren|block": "siren",
|
||||||
"Sounds.Wawawawaa|block": "wawawawaa",
|
"Sounds.Wawawawaa|block": "wawawawaa",
|
||||||
"music.beat|block": "%fraction|beat",
|
"music.beat|block": "%fraction|beat",
|
||||||
"music.changeTempoBy|block": "change tempo by (bpm)|%value",
|
"music.changeTempoBy|block": "change tempo by %value|(bpm)",
|
||||||
"music.noteFrequency|block": "%note",
|
"music.noteFrequency|block": "%note",
|
||||||
"music.playSoundUntilDone|block": "play sound %sound=music_sounds|until done",
|
"music.playSoundUntilDone|block": "play sound %sound=music_sounds|until done",
|
||||||
"music.playSound|block": "play sound %sound=music_sounds",
|
"music.playSound|block": "play sound %sound=music_sounds",
|
||||||
"music.playTone|block": "play tone|at %note=device_note|for %duration=device_beat",
|
"music.playTone|block": "play tone|at %note=device_note|for %duration=device_beat",
|
||||||
"music.rest|block": "rest|for %duration=device_beat",
|
"music.rest|block": "rest|for %duration=device_beat",
|
||||||
"music.ringTone|block": "ring tone|at %note=device_note",
|
"music.ringTone|block": "ring tone|at %note=device_note",
|
||||||
"music.setTempo|block": "set tempo to (bpm)|%value",
|
"music.setTempo|block": "set tempo to %value|(bpm)",
|
||||||
"music.setVolume|block": "set volume %volume",
|
"music.setVolume|block": "set volume %volume",
|
||||||
"music.sounds|block": "%name",
|
"music.sounds|block": "%name",
|
||||||
"music.stopAllSounds|block": "stop all sounds",
|
"music.stopAllSounds|block": "stop all sounds",
|
||||||
"music.tempo|block": "tempo (bpm)",
|
"music.tempo|block": "tempo (bpm)",
|
||||||
"music|block": "music",
|
"music|block": "music",
|
||||||
"{id:category}Music": "Music"
|
"{id:category}Music": "Music",
|
||||||
|
"{id:category}Sound": "Sound",
|
||||||
|
"{id:category}Sounds": "Sounds"
|
||||||
}
|
}
|
@ -5,18 +5,22 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#define NOTE_PAUSE 20
|
#define NOTE_PAUSE 20
|
||||||
|
|
||||||
namespace music {
|
namespace music {
|
||||||
|
|
||||||
uint8_t currVolume = 2;
|
uint8_t currVolume = 2;
|
||||||
|
uint8_t *lmsSoundMMap;
|
||||||
|
|
||||||
void writeDev(void *data, int size) {
|
int writeDev(void *data, int size) {
|
||||||
int fd = open("/dev/lms_sound", O_WRONLY);
|
int fd = open("/dev/lms_sound", O_WRONLY);
|
||||||
write(fd, data, size);
|
int res = write(fd, data, size);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,8 +55,7 @@ static void _stopSound() {
|
|||||||
writeDev(&cmd, sizeof(cmd));
|
writeDev(&cmd, sizeof(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _playTone(uint16_t frequency, uint16_t duration, uint8_t volume)
|
static void _playTone(uint16_t frequency, uint16_t duration, uint8_t volume) {
|
||||||
{
|
|
||||||
ToneCmd cmd;
|
ToneCmd cmd;
|
||||||
cmd.cmd = SOUND_CMD_TONE;
|
cmd.cmd = SOUND_CMD_TONE;
|
||||||
cmd.vol = volume;
|
cmd.vol = volume;
|
||||||
@ -62,15 +65,85 @@ static void _playTone(uint16_t frequency, uint16_t duration, uint8_t volume)
|
|||||||
writeDev(&cmd, sizeof(cmd));
|
writeDev(&cmd, sizeof(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool pumpMusicThreadRunning;
|
||||||
|
static pthread_mutex_t pumpMutex;
|
||||||
|
static Buffer currentSample;
|
||||||
|
static int samplePtr;
|
||||||
|
static pthread_cond_t sampleDone;
|
||||||
|
|
||||||
|
static void pumpMusic() {
|
||||||
|
if (currentSample == NULL) {
|
||||||
|
if (samplePtr > 0 && *lmsSoundMMap == 0) {
|
||||||
|
samplePtr = 0;
|
||||||
|
pthread_cond_broadcast(&sampleDone);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t buf[250]; // max size supported by hardware
|
||||||
|
buf[0] = SOUND_CMD_SERVICE;
|
||||||
|
int len = min((int)sizeof(buf) - 1, currentSample->length - samplePtr);
|
||||||
|
if (len == 0) {
|
||||||
|
decrRC(currentSample);
|
||||||
|
currentSample = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buf + 1, currentSample->data + samplePtr, len);
|
||||||
|
int rc = writeDev(buf, len + 1);
|
||||||
|
if (rc > 0) {
|
||||||
|
samplePtr += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *pumpMusicThread(void *dummy) {
|
||||||
|
while (true) {
|
||||||
|
sleep_core_us(10000);
|
||||||
|
pthread_mutex_lock(&pumpMutex);
|
||||||
|
pumpMusic();
|
||||||
|
pthread_mutex_unlock(&pumpMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void playSample(Buffer buf) {
|
||||||
|
if (lmsSoundMMap == NULL) {
|
||||||
|
int fd = open("/dev/lms_sound", O_RDWR);
|
||||||
|
lmsSoundMMap = (uint8_t *)mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pumpMusicThreadRunning) {
|
||||||
|
pumpMusicThreadRunning = true;
|
||||||
|
pthread_t pid;
|
||||||
|
pthread_create(&pid, NULL, pumpMusicThread, NULL);
|
||||||
|
pthread_detach(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopUser();
|
||||||
|
pthread_mutex_lock(&pumpMutex);
|
||||||
|
*lmsSoundMMap = 1; // BUSY
|
||||||
|
uint8_t cmd[] = {SOUND_CMD_PLAY, (uint8_t)((currVolume / 33) + 1)};
|
||||||
|
writeDev(cmd, 2);
|
||||||
|
decrRC(currentSample);
|
||||||
|
currentSample = buf;
|
||||||
|
incrRC(buf);
|
||||||
|
samplePtr = 44; // WAV header is 44 bytes
|
||||||
|
pumpMusic();
|
||||||
|
pthread_cond_wait(&sampleDone, &pumpMutex);
|
||||||
|
pthread_mutex_unlock(&pumpMutex);
|
||||||
|
startUser();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Play a tone through the speaker for some amount of time.
|
* Play a tone through the speaker for some amount of time.
|
||||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||||
* @param ms tone duration in milliseconds (ms)
|
* @param ms tone duration in milliseconds (ms)
|
||||||
*/
|
*/
|
||||||
//% help=music/play-tone weight=90
|
//% help=music/play-tone
|
||||||
//% blockId=music_play_note block="play tone|at %note=device_note|for %duration=device_beat"
|
//% blockId=music_play_note block="play tone|at %note=device_note|for %duration=device_beat"
|
||||||
//% parts="headphone" async blockGap=8
|
//% parts="headphone" async
|
||||||
//% blockNamespace=music
|
//% blockNamespace=music
|
||||||
|
//% weight=76 blockGap=8
|
||||||
void playTone(int frequency, int ms) {
|
void playTone(int frequency, int ms) {
|
||||||
if (frequency <= 0) {
|
if (frequency <= 0) {
|
||||||
_stopSound();
|
_stopSound();
|
||||||
@ -89,4 +162,28 @@ void playTone(int frequency, int ms) {
|
|||||||
}
|
}
|
||||||
sleep_ms(1);
|
sleep_ms(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Makes a sound bound to a buffer in WAV format. */
|
||||||
|
//%
|
||||||
|
Sound fromWAV(Buffer buf) {
|
||||||
|
incrRC(buf);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//% fixedInstances
|
||||||
|
namespace SoundMethods {
|
||||||
|
|
||||||
|
/** Returns the underlaying Buffer object. */
|
||||||
|
//% property
|
||||||
|
Buffer buffer(Sound snd) {
|
||||||
|
incrRC(snd);
|
||||||
|
return snd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Play sound. */
|
||||||
|
//% promise
|
||||||
|
void play(Sound snd) {
|
||||||
|
music::playSample(snd);
|
||||||
|
}
|
||||||
}
|
}
|
@ -8,6 +8,8 @@
|
|||||||
"shims.d.ts",
|
"shims.d.ts",
|
||||||
"melodies.ts",
|
"melodies.ts",
|
||||||
"music.ts",
|
"music.ts",
|
||||||
|
"sounds.jres",
|
||||||
|
"sounds.ts",
|
||||||
"ns.ts"
|
"ns.ts"
|
||||||
],
|
],
|
||||||
"testFiles": [
|
"testFiles": [
|
||||||
|
24
libs/music/shims.d.ts
vendored
@ -18,11 +18,29 @@ declare namespace music {
|
|||||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||||
* @param ms tone duration in milliseconds (ms)
|
* @param ms tone duration in milliseconds (ms)
|
||||||
*/
|
*/
|
||||||
//% help=music/play-tone weight=90
|
//% help=music/play-tone
|
||||||
//% blockId=music_play_note block="play tone|at %note=device_note|for %duration=device_beat"
|
//% blockId=music_play_note block="play tone|at %note=device_note|for %duration=device_beat"
|
||||||
//% parts="headphone" async blockGap=8
|
//% parts="headphone" async
|
||||||
//% blockNamespace=music shim=music::playTone
|
//% blockNamespace=music
|
||||||
|
//% weight=76 blockGap=8 shim=music::playTone
|
||||||
function playTone(frequency: int32, ms: int32): void;
|
function playTone(frequency: int32, ms: int32): void;
|
||||||
|
|
||||||
|
/** Makes a sound bound to a buffer in WAV format. */
|
||||||
|
//% shim=music::fromWAV
|
||||||
|
function fromWAV(buf: Buffer): Sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//% fixedInstances
|
||||||
|
declare interface Sound {
|
||||||
|
/** Returns the underlaying Buffer object. */
|
||||||
|
//% property shim=SoundMethods::buffer
|
||||||
|
buffer: Buffer;
|
||||||
|
|
||||||
|
/** Play sound. */
|
||||||
|
//% promise shim=SoundMethods::play
|
||||||
|
play(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-generated. Do not edit. Really.
|
// Auto-generated. Do not edit. Really.
|
||||||
|
134
libs/music/sounds.jres
Normal file
256
libs/music/sounds.ts
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
namespace sounds {
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsCatPurr = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsDogBark1 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsDogBark2 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsDogGrowl = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsDogSniff = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsDogWhine = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsElephantCall = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsInsectBuzz1 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsInsectBuzz2 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsInsectChirp = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsSnakeHiss = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsSnakeRattle = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const animalsTRexRoar = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const colorsBlack = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const colorsBlue = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const colorsBrown = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const colorsGreen = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const colorsRed = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const colorsWhite = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const colorsYellow = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationBravo = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationEv3 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationFantastic = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationGameOver = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationGo = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationGoodJob = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationGood = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationGoodbye = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationHello = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationHi = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationLego = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationMindstorms = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationMorning = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationNo = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationOkay = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationOkeyDokey = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationSorry = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationThankYou = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const communicationYes = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsBoing = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsBoo = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsCheering = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsCrunching = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsCrying = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsFanfare = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsKungFu = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsLaughing1 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsLaughing2 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsMagicWand = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsOuch = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsShouting = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsSmack = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsSneezing = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsSnoring = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const expressionsUhOh = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationActivate = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationAnalyze = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationBackwards = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationColor = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationDetected = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationDown = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationErrorAlarm = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationError = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationFlashing = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationForward = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationLeft = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationObject = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationRight = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationSearching = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationStart = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationStop = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationTouch = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationTurn = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const informationUp = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalAirRelease = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalAirbrake = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalBackingAlert = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalBlip1 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalBlip2 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalBlip3 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalBlip4 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalHorn1 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalHorn2 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalLaser = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalMotorIdle = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalMotorStart = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalMotorStop = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalRatchet = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalSonar = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalTickTack = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const mechanicalWalk = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsArm1 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsArm2 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsArm3 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsArm4 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsDropLoad = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsLiftLoad = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsServo1 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsServo2 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsServo3 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsServo4 = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsSlideLoad = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsSnap = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsSpeedDown = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsSpeedIdle = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsSpeedUp = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const movementsSpeeding = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersEight = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersFive = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersFour = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersNine = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersOne = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersSeven = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersSix = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersTen = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersThree = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersTwo = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const numbersZero = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemClick = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemConfirm = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemConnect = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemDownload = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemGeneralAlert = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemOverpower = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemPowerDown = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemReady = music.fromWAV(hex``);
|
||||||
|
//% fixedInstance jres
|
||||||
|
export const systemStartUp = music.fromWAV(hex``);
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pxt-ev3",
|
"name": "pxt-ev3",
|
||||||
"version": "0.0.19",
|
"version": "0.0.29",
|
||||||
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
|
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
|
||||||
"private": true,
|
"private": true,
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@ -39,8 +39,8 @@
|
|||||||
"semantic-ui-less": "^2.2.4"
|
"semantic-ui-less": "^2.2.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pxt-common-packages": "0.9.6",
|
"pxt-common-packages": "0.12.17",
|
||||||
"pxt-core": "2.0.49"
|
"pxt-core": "2.3.19"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node node_modules/pxt-core/built/pxt.js travis"
|
"test": "node node_modules/pxt-core/built/pxt.js travis"
|
||||||
|
@ -62,16 +62,16 @@
|
|||||||
},
|
},
|
||||||
"compileService": {
|
"compileService": {
|
||||||
"buildEngine": "dockermake",
|
"buildEngine": "dockermake",
|
||||||
"dockerImage": "pext/ev3",
|
"dockerImage": "pext/ev3:zlib",
|
||||||
"serviceId": "ev3"
|
"serviceId": "ev3"
|
||||||
},
|
},
|
||||||
"appTheme": {
|
"appTheme": {
|
||||||
"accentColor": "#0089BF",
|
"accentColor": "#0089BF",
|
||||||
"logoUrl": "https://lego.makecode.com",
|
"logoUrl": "https://lego.makecode.com",
|
||||||
"logo": "./static/logo.svg",
|
"logo": "./static/lego_education_logo.png",
|
||||||
"docsLogo": "./static/logo.square.svg",
|
"docsLogo": "./static/lego-logo.svg",
|
||||||
"portraitLogo": "./static/logo.square.svg",
|
"portraitLogo": "./static/lego-logo.svg",
|
||||||
"footerLogo": "./static/logo.square.svg",
|
"footerLogo": "./static/lego-logo.svg",
|
||||||
"cardLogo": "./static/icons/android-chrome-192x192.png",
|
"cardLogo": "./static/icons/android-chrome-192x192.png",
|
||||||
"appLogo": "./static/icons/android-chrome-192x192.png",
|
"appLogo": "./static/icons/android-chrome-192x192.png",
|
||||||
"organization": "Microsoft MakeCode",
|
"organization": "Microsoft MakeCode",
|
||||||
@ -89,10 +89,6 @@
|
|||||||
"name": "About",
|
"name": "About",
|
||||||
"path": "/about"
|
"path": "/about"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Examples",
|
|
||||||
"path": "#projects:Examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Blocks",
|
"name": "Blocks",
|
||||||
"path": "/blocks"
|
"path": "/blocks"
|
||||||
@ -104,22 +100,14 @@
|
|||||||
{
|
{
|
||||||
"name": "Reference",
|
"name": "Reference",
|
||||||
"path": "/reference"
|
"path": "/reference"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Buy",
|
|
||||||
"path": "https://www.lego.com/en-us/mindstorms/products/mindstorms-ev3-31313"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"galleries": {
|
"showHomeScreen": true,
|
||||||
"Make": "projects",
|
"homeScreenHero": "./static/hero.png",
|
||||||
"Examples": "examples"
|
|
||||||
},
|
|
||||||
"sideDoc": "tutorials/getting-started",
|
|
||||||
"invertedMenu": false,
|
"invertedMenu": false,
|
||||||
"invertedMonaco": false,
|
"invertedMonaco": false,
|
||||||
"monacoToolbox": true,
|
"monacoToolbox": true,
|
||||||
"invertedToolbox": true,
|
"invertedToolbox": true,
|
||||||
"exampleGallery": "examples",
|
|
||||||
"hasAudio": true,
|
"hasAudio": true,
|
||||||
"usbHelp": [],
|
"usbHelp": [],
|
||||||
"extendEditor": true,
|
"extendEditor": true,
|
||||||
|
173
scripts/rgf2png.js
Executable file
@ -0,0 +1,173 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require("fs")
|
||||||
|
const zlib = require("zlib")
|
||||||
|
|
||||||
|
function compressImg(fn) {
|
||||||
|
const rgf = fs.readFileSync(fn)
|
||||||
|
|
||||||
|
const width = rgf[0]
|
||||||
|
const height = rgf[1]
|
||||||
|
|
||||||
|
const expSz = ((width + 7) >> 3) * height + 2
|
||||||
|
|
||||||
|
console.log(`w=${width} h=${height} sz=${rgf.length} exp=${expSz}`)
|
||||||
|
|
||||||
|
let crcTable
|
||||||
|
|
||||||
|
function crc32(buf) {
|
||||||
|
if (!crcTable) {
|
||||||
|
crcTable = []
|
||||||
|
for (let i = 0; i < 256; i++) {
|
||||||
|
let curr = i;
|
||||||
|
for (let j = 0; j < 8; j++) {
|
||||||
|
if (curr & 1) {
|
||||||
|
curr = 0xedb88320 ^ (curr >>> 1);
|
||||||
|
} else {
|
||||||
|
curr = curr >>> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crcTable[i] = curr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let crc = -1;
|
||||||
|
for (var i = 0; i < buf.length; i++) {
|
||||||
|
crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
|
||||||
|
}
|
||||||
|
return (crc ^ -1) >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Width 4 bytes
|
||||||
|
Height 4 bytes
|
||||||
|
Bit depth 1 byte
|
||||||
|
Colour type 1 byte
|
||||||
|
Compression method 1 byte
|
||||||
|
Filter method 1 byte
|
||||||
|
Interlace method 1 byte
|
||||||
|
*/
|
||||||
|
|
||||||
|
const chunks = []
|
||||||
|
|
||||||
|
function addChunk(mark, addsize) {
|
||||||
|
const buf = Buffer.alloc(mark.length + addsize)
|
||||||
|
for (let i = 0; i < mark.length; ++i) {
|
||||||
|
buf[i] = mark.charCodeAt(i)
|
||||||
|
}
|
||||||
|
chunks.push(buf)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
const hd = addChunk("IHDR", 4 + 4 + 5)
|
||||||
|
hd.writeInt32BE(width, 4)
|
||||||
|
hd.writeInt32BE(height, 8)
|
||||||
|
hd[12] = 1 // bit depth
|
||||||
|
hd[13] = 0 // color type - grayscale
|
||||||
|
hd[14] = 0 // compression - deflate
|
||||||
|
hd[15] = 0 // filter method
|
||||||
|
hd[16] = 0 // interlace method - no interlace
|
||||||
|
|
||||||
|
const scanlines = []
|
||||||
|
const scanlen = (width + 7) >> 3
|
||||||
|
|
||||||
|
let srcptr = 2
|
||||||
|
let srcmask = 0x01
|
||||||
|
for (let y = 0; y < height; ++y) {
|
||||||
|
const scan = Buffer.alloc(1 + scanlen)
|
||||||
|
scanlines.push(scan)
|
||||||
|
let dstptr = 1
|
||||||
|
let dstmask = 0x80
|
||||||
|
for (let x = 0; x < width; ++x) {
|
||||||
|
if (!(rgf[srcptr] & srcmask)) {
|
||||||
|
scan[dstptr] |= dstmask
|
||||||
|
}
|
||||||
|
dstmask >>= 1;
|
||||||
|
if (dstmask == 0) {
|
||||||
|
dstmask = 0x80
|
||||||
|
dstptr++
|
||||||
|
}
|
||||||
|
srcmask <<= 1;
|
||||||
|
if (srcmask > 0x80) {
|
||||||
|
srcmask = 0x01
|
||||||
|
srcptr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (srcmask != 0x01) {
|
||||||
|
srcmask = 0x01
|
||||||
|
srcptr++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
// seems to increase file size
|
||||||
|
scan[0] = 1 // sub
|
||||||
|
let prev = 0
|
||||||
|
for (let i = 1; i < scan.length; ++i) {
|
||||||
|
let p = scan[i]
|
||||||
|
scan[i] = (p - prev) & 0xff
|
||||||
|
prev = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dat = zlib.deflateSync(Buffer.concat(scanlines), {
|
||||||
|
level: 9
|
||||||
|
})
|
||||||
|
const idat = addChunk("IDAT", dat.length)
|
||||||
|
dat.copy(idat, 4)
|
||||||
|
addChunk("IEND", 0)
|
||||||
|
|
||||||
|
const output = [new Buffer([137, 80, 78, 71, 13, 10, 26, 10])]
|
||||||
|
|
||||||
|
function intBuf(v) {
|
||||||
|
let b = new Buffer(4)
|
||||||
|
b.writeUInt32BE(v, 0)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
for (let ch of chunks) {
|
||||||
|
output.push(intBuf(ch.length - 4))
|
||||||
|
output.push(ch)
|
||||||
|
output.push(intBuf(crc32(ch)))
|
||||||
|
}
|
||||||
|
|
||||||
|
let outp = Buffer.concat(output)
|
||||||
|
return outp
|
||||||
|
}
|
||||||
|
|
||||||
|
let sz = 0
|
||||||
|
let bf
|
||||||
|
let out = {
|
||||||
|
"*": {
|
||||||
|
namespace: "images",
|
||||||
|
dataEncoding: "base64",
|
||||||
|
mimeType: "image/png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ts = "namespace images {\n"
|
||||||
|
|
||||||
|
for (let i = 2; i < process.argv.length; ++i) {
|
||||||
|
let fn = process.argv[i]
|
||||||
|
let m = /([^\/]+\/[^/]+)\.rgf$/.exec(fn)
|
||||||
|
let bn = m[1]
|
||||||
|
bf = compressImg(fn)
|
||||||
|
bn = bn.replace(/\//g, " ")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/(\d)\s+/g, (f, n) => n)
|
||||||
|
.replace(/\s+(.)/g, (f, l) => l.toUpperCase())
|
||||||
|
out[bn] = bf.toString("base64")
|
||||||
|
sz += bf.length
|
||||||
|
ts += ` //% fixedInstance jres\n`
|
||||||
|
ts += ` export const ${bn} = screen.unpackPNG(hex\`\`);\n`
|
||||||
|
}
|
||||||
|
ts += `}\n`
|
||||||
|
|
||||||
|
console.log("total " + sz)
|
||||||
|
fs.writeFileSync("out.json", JSON.stringify(out, null, 4))
|
||||||
|
fs.writeFileSync("out.ts", ts)
|
||||||
|
fs.writeFileSync("out.png", bf)
|
||||||
|
|
||||||
|
//if (require("os").platform() == "darwin")
|
||||||
|
// require('child_process').execSync("afplay out.wav")
|
||||||
|
|
||||||
|
// TODO also play on Windows
|
75
scripts/rsf2wav.js
Executable file
@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require("fs")
|
||||||
|
|
||||||
|
function convertFile(fn) {
|
||||||
|
const rsf = fs.readFileSync(fn)
|
||||||
|
|
||||||
|
const fmt = rsf.readInt16BE(0) // 0x0100
|
||||||
|
if (fmt != 0x0100) {
|
||||||
|
throw new Error("Invalid input format: " + fmt)
|
||||||
|
}
|
||||||
|
const size = rsf.readInt16BE(2)
|
||||||
|
const samplerate = rsf.readInt16BE(4)
|
||||||
|
const repeat = rsf.readInt16BE(6)
|
||||||
|
|
||||||
|
const datasize = rsf.length - 8
|
||||||
|
|
||||||
|
const wavHd = new Buffer(44)
|
||||||
|
|
||||||
|
function writeMark(off, mark) {
|
||||||
|
for (let i = 0; i < mark.length; ++i) {
|
||||||
|
wavHd[off + i] = mark.charCodeAt(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeMark(0, 'RIFF')
|
||||||
|
wavHd.writeInt32LE(datasize + 36, 4)
|
||||||
|
writeMark(8, 'WAVE')
|
||||||
|
writeMark(12, 'fmt ')
|
||||||
|
wavHd.writeInt32LE(16, 16) // fmt size
|
||||||
|
wavHd.writeInt16LE(1, 20) // PCM
|
||||||
|
wavHd.writeInt16LE(1, 22) // mono, 1ch
|
||||||
|
wavHd.writeInt32LE(samplerate, 24)
|
||||||
|
wavHd.writeInt32LE(samplerate, 28) // byterate
|
||||||
|
wavHd.writeInt16LE(1, 32) // block align
|
||||||
|
wavHd.writeInt16LE(8, 34) // bits per sample
|
||||||
|
writeMark(36, 'data')
|
||||||
|
wavHd.writeInt32LE(datasize, 40)
|
||||||
|
|
||||||
|
let wav = Buffer.concat([wavHd, rsf.slice(8)])
|
||||||
|
return wav
|
||||||
|
}
|
||||||
|
|
||||||
|
let out = {
|
||||||
|
"*": {
|
||||||
|
namespace: "sounds",
|
||||||
|
dataEncoding: "base64",
|
||||||
|
mimeType: "audio/wav"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ts = "namespace sounds {\n"
|
||||||
|
let bf
|
||||||
|
for (let i = 2; i < process.argv.length; ++i) {
|
||||||
|
let fn = process.argv[i]
|
||||||
|
let m = /([^\/]+\/[^/]+)\.rsf$/.exec(fn)
|
||||||
|
let bn = m[1]
|
||||||
|
bf = convertFile(fn)
|
||||||
|
bn = bn.replace(/[\/\-]/g, " ")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/(\d)\s+/g, (f, n) => n)
|
||||||
|
.replace(/\s+(.)/g, (f, l) => l.toUpperCase())
|
||||||
|
out[bn] = bf.toString("base64")
|
||||||
|
ts += ` //% fixedInstance jres\n`
|
||||||
|
ts += ` export const ${bn} = music.fromWAV(hex\`\`);\n`
|
||||||
|
}
|
||||||
|
ts += `}\n`
|
||||||
|
|
||||||
|
fs.writeFileSync("out.json", JSON.stringify(out, null, 4))
|
||||||
|
fs.writeFileSync("out.ts", ts)
|
||||||
|
fs.writeFileSync("out.wav", bf)
|
||||||
|
|
||||||
|
//if (require("os").platform() == "darwin")
|
||||||
|
// require('child_process').execSync("afplay out.wav")
|
||||||
|
|
||||||
|
// TODO also play on Windows
|
@ -25,7 +25,7 @@ namespace pxsim {
|
|||||||
export class DalBoard extends CoreBoard implements
|
export class DalBoard extends CoreBoard implements
|
||||||
AccelerometerBoard,
|
AccelerometerBoard,
|
||||||
CommonBoard,
|
CommonBoard,
|
||||||
LightBoard,
|
// LightBoard,
|
||||||
LightSensorBoard,
|
LightSensorBoard,
|
||||||
MicrophoneBoard,
|
MicrophoneBoard,
|
||||||
MusicBoard,
|
MusicBoard,
|
||||||
@ -34,7 +34,7 @@ namespace pxsim {
|
|||||||
InfraredBoard,
|
InfraredBoard,
|
||||||
CapTouchBoard {
|
CapTouchBoard {
|
||||||
// state & update logic for component services
|
// state & update logic for component services
|
||||||
neopixelState: CommonNeoPixelState;
|
// neopixelState: CommonNeoPixelState;
|
||||||
buttonState: EV3ButtonState;
|
buttonState: EV3ButtonState;
|
||||||
slideSwitchState: SlideSwitchState;
|
slideSwitchState: SlideSwitchState;
|
||||||
lightSensorState: AnalogSensorState;
|
lightSensorState: AnalogSensorState;
|
||||||
@ -62,11 +62,12 @@ namespace pxsim {
|
|||||||
this.builtinParts["buttons"] = this.buttonState = new EV3ButtonState();
|
this.builtinParts["buttons"] = this.buttonState = new EV3ButtonState();
|
||||||
this.builtinParts["light"] = this.lightState = new EV3LightState();
|
this.builtinParts["light"] = this.lightState = new EV3LightState();
|
||||||
this.builtinParts["screen"] = this.screenState = new EV3ScreenState();
|
this.builtinParts["screen"] = this.screenState = new EV3ScreenState();
|
||||||
|
this.builtinParts["audio"] = this.audioState = new AudioState();
|
||||||
|
|
||||||
/*this.builtinParts["neopixel"] = this.neopixelState = new CommonNeoPixelState();
|
/*this.builtinParts["neopixel"] = this.neopixelState = new CommonNeoPixelState();
|
||||||
this.builtinParts["buttonpair"] = this.buttonState = new CommonButtonState();
|
this.builtinParts["buttonpair"] = this.buttonState = new CommonButtonState();
|
||||||
|
|
||||||
this.builtinParts["switch"] = this.slideSwitchState = new SlideSwitchState();
|
this.builtinParts["switch"] = this.slideSwitchState = new SlideSwitchState();
|
||||||
this.builtinParts["audio"] = this.audioState = new AudioState();
|
|
||||||
this.builtinParts["lightsensor"] = this.lightSensorState = new AnalogSensorState(DAL.DEVICE_ID_LIGHT_SENSOR, 0, 255);
|
this.builtinParts["lightsensor"] = this.lightSensorState = new AnalogSensorState(DAL.DEVICE_ID_LIGHT_SENSOR, 0, 255);
|
||||||
this.builtinParts["thermometer"] = this.thermometerState = new AnalogSensorState(DAL.DEVICE_ID_THERMOMETER, -5, 50);
|
this.builtinParts["thermometer"] = this.thermometerState = new AnalogSensorState(DAL.DEVICE_ID_THERMOMETER, -5, 50);
|
||||||
this.builtinParts["soundsensor"] = this.microphoneState = new AnalogSensorState(DAL.DEVICE_ID_TOUCH_SENSOR + 1, 0, 255);
|
this.builtinParts["soundsensor"] = this.microphoneState = new AnalogSensorState(DAL.DEVICE_ID_TOUCH_SENSOR + 1, 0, 255);
|
||||||
@ -174,9 +175,9 @@ namespace pxsim {
|
|||||||
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
|
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultNeopixelPin() {
|
//defaultNeopixelPin() {
|
||||||
return this.edgeConnectorState.getPin(CPlayPinName.D8);
|
// return this.edgeConnectorState.getPin(CPlayPinName.D8);
|
||||||
}
|
//}
|
||||||
|
|
||||||
getDefaultPitchPin() {
|
getDefaultPitchPin() {
|
||||||
return this.edgeConnectorState.getPin(CPlayPinName.D6);
|
return this.edgeConnectorState.getPin(CPlayPinName.D6);
|
||||||
|
375
sim/inflate.ts
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
// adapted from https://github.com/devongovett/tiny-inflate
|
||||||
|
// License: MIT
|
||||||
|
|
||||||
|
namespace tinf {
|
||||||
|
|
||||||
|
var TINF_OK = 0;
|
||||||
|
var TINF_DATA_ERROR = -3;
|
||||||
|
|
||||||
|
class Tree {
|
||||||
|
table = new Uint16Array(16); /* table of code length counts */
|
||||||
|
trans = new Uint16Array(288); /* code -> symbol translation table */
|
||||||
|
}
|
||||||
|
|
||||||
|
class Data {
|
||||||
|
sourceIndex = 0;
|
||||||
|
tag = 0;
|
||||||
|
bitcount = 0;
|
||||||
|
|
||||||
|
destLen = 0;
|
||||||
|
|
||||||
|
ltree = new Tree(); /* dynamic length/symbol tree */
|
||||||
|
dtree = new Tree(); /* dynamic distance tree */
|
||||||
|
|
||||||
|
constructor(public source: Uint8Array, public dest: Uint8Array) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------- *
|
||||||
|
* -- uninitialized global data (static structures) -- *
|
||||||
|
* --------------------------------------------------- */
|
||||||
|
|
||||||
|
var sltree = new Tree();
|
||||||
|
var sdtree = new Tree();
|
||||||
|
|
||||||
|
/* extra bits and base tables for length codes */
|
||||||
|
var length_bits = new Uint8Array(30);
|
||||||
|
var length_base = new Uint16Array(30);
|
||||||
|
|
||||||
|
/* extra bits and base tables for distance codes */
|
||||||
|
var dist_bits = new Uint8Array(30);
|
||||||
|
var dist_base = new Uint16Array(30);
|
||||||
|
|
||||||
|
/* special ordering of code length codes */
|
||||||
|
var clcidx = new Uint8Array([
|
||||||
|
16, 17, 18, 0, 8, 7, 9, 6,
|
||||||
|
10, 5, 11, 4, 12, 3, 13, 2,
|
||||||
|
14, 1, 15
|
||||||
|
]);
|
||||||
|
|
||||||
|
/* used by tinf_decode_trees, avoids allocations every call */
|
||||||
|
var code_tree = new Tree();
|
||||||
|
var lengths = new Uint8Array(288 + 32);
|
||||||
|
|
||||||
|
/* ----------------------- *
|
||||||
|
* -- utility functions -- *
|
||||||
|
* ----------------------- */
|
||||||
|
|
||||||
|
/* build extra bits and base tables */
|
||||||
|
function tinf_build_bits_base(bits: Uint8Array, base: Uint16Array, delta: number, first: number) {
|
||||||
|
/* build bits table */
|
||||||
|
for (let i = 0; i < delta; ++i) bits[i] = 0;
|
||||||
|
for (let i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta | 0;
|
||||||
|
|
||||||
|
/* build base table */
|
||||||
|
for (let sum = first, i = 0; i < 30; ++i) {
|
||||||
|
base[i] = sum;
|
||||||
|
sum += 1 << bits[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build the fixed huffman trees */
|
||||||
|
function tinf_build_fixed_trees(lt: Tree, dt: Tree) {
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
/* build fixed length tree */
|
||||||
|
for (i = 0; i < 7; ++i) lt.table[i] = 0;
|
||||||
|
|
||||||
|
lt.table[7] = 24;
|
||||||
|
lt.table[8] = 152;
|
||||||
|
lt.table[9] = 112;
|
||||||
|
|
||||||
|
for (i = 0; i < 24; ++i) lt.trans[i] = 256 + i;
|
||||||
|
for (i = 0; i < 144; ++i) lt.trans[24 + i] = i;
|
||||||
|
for (i = 0; i < 8; ++i) lt.trans[24 + 144 + i] = 280 + i;
|
||||||
|
for (i = 0; i < 112; ++i) lt.trans[24 + 144 + 8 + i] = 144 + i;
|
||||||
|
|
||||||
|
/* build fixed distance tree */
|
||||||
|
for (i = 0; i < 5; ++i) dt.table[i] = 0;
|
||||||
|
|
||||||
|
dt.table[5] = 32;
|
||||||
|
|
||||||
|
for (i = 0; i < 32; ++i) dt.trans[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* given an array of code lengths, build a tree */
|
||||||
|
var offs = new Uint16Array(16);
|
||||||
|
|
||||||
|
function tinf_build_tree(t: Tree, lengths: Uint8Array, off: number, num: number) {
|
||||||
|
var i = 0, sum = 0;
|
||||||
|
|
||||||
|
/* clear code length count table */
|
||||||
|
for (i = 0; i < 16; ++i) t.table[i] = 0;
|
||||||
|
|
||||||
|
/* scan symbol lengths, and sum code length counts */
|
||||||
|
for (i = 0; i < num; ++i) t.table[lengths[off + i]]++;
|
||||||
|
|
||||||
|
t.table[0] = 0;
|
||||||
|
|
||||||
|
/* compute offset table for distribution sort */
|
||||||
|
for (sum = 0, i = 0; i < 16; ++i) {
|
||||||
|
offs[i] = sum;
|
||||||
|
sum += t.table[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create code->symbol translation table (symbols sorted by code) */
|
||||||
|
for (i = 0; i < num; ++i) {
|
||||||
|
if (lengths[off + i]) t.trans[offs[lengths[off + i]]++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------- *
|
||||||
|
* -- decode functions -- *
|
||||||
|
* ---------------------- */
|
||||||
|
|
||||||
|
/* get one bit from source stream */
|
||||||
|
function tinf_getbit(d: Data) {
|
||||||
|
/* check if tag is empty */
|
||||||
|
if (!d.bitcount--) {
|
||||||
|
/* load next tag */
|
||||||
|
d.tag = d.source[d.sourceIndex++];
|
||||||
|
d.bitcount = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shift bit out of tag */
|
||||||
|
var bit = d.tag & 1;
|
||||||
|
d.tag >>>= 1;
|
||||||
|
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read a num bit value from a stream and add base */
|
||||||
|
function tinf_read_bits(d: Data, num: number, base: number) {
|
||||||
|
if (!num)
|
||||||
|
return base;
|
||||||
|
|
||||||
|
while (d.bitcount < 24) {
|
||||||
|
d.tag |= d.source[d.sourceIndex++] << d.bitcount;
|
||||||
|
d.bitcount += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
var val = d.tag & (0xffff >>> (16 - num));
|
||||||
|
d.tag >>>= num;
|
||||||
|
d.bitcount -= num;
|
||||||
|
return val + base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* given a data stream and a tree, decode a symbol */
|
||||||
|
function tinf_decode_symbol(d: Data, t: Tree) {
|
||||||
|
while (d.bitcount < 24) {
|
||||||
|
d.tag |= d.source[d.sourceIndex++] << d.bitcount;
|
||||||
|
d.bitcount += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum = 0, cur = 0, len = 0;
|
||||||
|
var tag = d.tag;
|
||||||
|
|
||||||
|
/* get more bits while code value is above sum */
|
||||||
|
do {
|
||||||
|
cur = 2 * cur + (tag & 1);
|
||||||
|
tag >>>= 1;
|
||||||
|
++len;
|
||||||
|
|
||||||
|
sum += t.table[len];
|
||||||
|
cur -= t.table[len];
|
||||||
|
} while (cur >= 0);
|
||||||
|
|
||||||
|
d.tag = tag;
|
||||||
|
d.bitcount -= len;
|
||||||
|
|
||||||
|
return t.trans[sum + cur];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* given a data stream, decode dynamic trees from it */
|
||||||
|
function tinf_decode_trees(d: Data, lt: Tree, dt: Tree) {
|
||||||
|
var i = 0, num = 0, length = 0;
|
||||||
|
|
||||||
|
/* get 5 bits HLIT (257-286) */
|
||||||
|
let hlit = tinf_read_bits(d, 5, 257);
|
||||||
|
|
||||||
|
/* get 5 bits HDIST (1-32) */
|
||||||
|
let hdist = tinf_read_bits(d, 5, 1);
|
||||||
|
|
||||||
|
/* get 4 bits HCLEN (4-19) */
|
||||||
|
let hclen = tinf_read_bits(d, 4, 4);
|
||||||
|
|
||||||
|
for (i = 0; i < 19; ++i) lengths[i] = 0;
|
||||||
|
|
||||||
|
/* read code lengths for code length alphabet */
|
||||||
|
for (i = 0; i < hclen; ++i) {
|
||||||
|
/* get 3 bits code length (0-7) */
|
||||||
|
var clen = tinf_read_bits(d, 3, 0);
|
||||||
|
lengths[clcidx[i]] = clen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build code length tree */
|
||||||
|
tinf_build_tree(code_tree, lengths, 0, 19);
|
||||||
|
|
||||||
|
/* decode code lengths for the dynamic trees */
|
||||||
|
for (num = 0; num < hlit + hdist;) {
|
||||||
|
var sym = tinf_decode_symbol(d, code_tree);
|
||||||
|
|
||||||
|
switch (sym) {
|
||||||
|
case 16:
|
||||||
|
/* copy previous code length 3-6 times (read 2 bits) */
|
||||||
|
var prev = lengths[num - 1];
|
||||||
|
for (length = tinf_read_bits(d, 2, 3); length; --length) {
|
||||||
|
lengths[num++] = prev;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
/* repeat code length 0 for 3-10 times (read 3 bits) */
|
||||||
|
for (length = tinf_read_bits(d, 3, 3); length; --length) {
|
||||||
|
lengths[num++] = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
/* repeat code length 0 for 11-138 times (read 7 bits) */
|
||||||
|
for (length = tinf_read_bits(d, 7, 11); length; --length) {
|
||||||
|
lengths[num++] = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* values 0-15 represent the actual code lengths */
|
||||||
|
lengths[num++] = sym;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build dynamic trees */
|
||||||
|
tinf_build_tree(lt, lengths, 0, hlit);
|
||||||
|
tinf_build_tree(dt, lengths, hlit, hdist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- *
|
||||||
|
* -- block inflate functions -- *
|
||||||
|
* ----------------------------- */
|
||||||
|
|
||||||
|
/* given a stream and two trees, inflate a block of data */
|
||||||
|
function tinf_inflate_block_data(d: Data, lt: Tree, dt: Tree) {
|
||||||
|
while (true) {
|
||||||
|
var sym = tinf_decode_symbol(d, lt);
|
||||||
|
|
||||||
|
/* check for end of block */
|
||||||
|
if (sym === 256) {
|
||||||
|
return TINF_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sym < 256) {
|
||||||
|
d.dest[d.destLen++] = sym;
|
||||||
|
} else {
|
||||||
|
sym -= 257;
|
||||||
|
|
||||||
|
/* possibly get more bits from length code */
|
||||||
|
let length = tinf_read_bits(d, length_bits[sym], length_base[sym]);
|
||||||
|
|
||||||
|
let dist = tinf_decode_symbol(d, dt);
|
||||||
|
|
||||||
|
/* possibly get more bits from distance code */
|
||||||
|
let offs = d.destLen - tinf_read_bits(d, dist_bits[dist], dist_base[dist]);
|
||||||
|
|
||||||
|
/* copy match */
|
||||||
|
for (let i = offs; i < offs + length; ++i) {
|
||||||
|
d.dest[d.destLen++] = d.dest[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* inflate an uncompressed block of data */
|
||||||
|
function tinf_inflate_uncompressed_block(d: Data) {
|
||||||
|
/* unread from bitbuffer */
|
||||||
|
while (d.bitcount > 8) {
|
||||||
|
d.sourceIndex--;
|
||||||
|
d.bitcount -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get length */
|
||||||
|
let length = d.source[d.sourceIndex + 1];
|
||||||
|
length = 256 * length + d.source[d.sourceIndex];
|
||||||
|
|
||||||
|
/* get one's complement of length */
|
||||||
|
let invlength = d.source[d.sourceIndex + 3];
|
||||||
|
invlength = 256 * invlength + d.source[d.sourceIndex + 2];
|
||||||
|
|
||||||
|
/* check length */
|
||||||
|
if (length !== (~invlength & 0x0000ffff))
|
||||||
|
return TINF_DATA_ERROR;
|
||||||
|
|
||||||
|
d.sourceIndex += 4;
|
||||||
|
|
||||||
|
/* copy block */
|
||||||
|
for (let i = length; i; --i)
|
||||||
|
d.dest[d.destLen++] = d.source[d.sourceIndex++];
|
||||||
|
|
||||||
|
/* make sure we start next block on a byte boundary */
|
||||||
|
d.bitcount = 0;
|
||||||
|
|
||||||
|
return TINF_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* inflate stream from source to dest */
|
||||||
|
export function uncompress(source: Uint8Array, dest: Uint8Array) {
|
||||||
|
var d = new Data(source, dest);
|
||||||
|
var bfinal = 0, res = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* read final block flag */
|
||||||
|
bfinal = tinf_getbit(d);
|
||||||
|
|
||||||
|
/* read block type (2 bits) */
|
||||||
|
let btype = tinf_read_bits(d, 2, 0);
|
||||||
|
|
||||||
|
/* decompress block */
|
||||||
|
switch (btype) {
|
||||||
|
case 0:
|
||||||
|
/* decompress uncompressed block */
|
||||||
|
res = tinf_inflate_uncompressed_block(d);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* decompress block with fixed huffman trees */
|
||||||
|
res = tinf_inflate_block_data(d, sltree, sdtree);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* decompress block with dynamic huffman trees */
|
||||||
|
tinf_decode_trees(d, d.ltree, d.dtree);
|
||||||
|
res = tinf_inflate_block_data(d, d.ltree, d.dtree);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
res = TINF_DATA_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res !== TINF_OK)
|
||||||
|
throw new Error('Data error');
|
||||||
|
|
||||||
|
} while (!bfinal);
|
||||||
|
|
||||||
|
if (d.destLen >= d.dest.length)
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (d.destLen < d.dest.length) {
|
||||||
|
if (typeof d.dest.slice === 'function')
|
||||||
|
return d.dest.slice(0, d.destLen);
|
||||||
|
else
|
||||||
|
return d.dest.subarray(0, d.destLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------- *
|
||||||
|
* -- initialization -- *
|
||||||
|
* -------------------- */
|
||||||
|
|
||||||
|
/* build fixed huffman trees */
|
||||||
|
tinf_build_fixed_trees(sltree, sdtree);
|
||||||
|
|
||||||
|
/* build extra bits and base tables */
|
||||||
|
tinf_build_bits_base(length_bits, length_base, 4, 3);
|
||||||
|
tinf_build_bits_base(dist_bits, dist_base, 2, 1);
|
||||||
|
|
||||||
|
/* fix a special case */
|
||||||
|
length_bits[28] = 0;
|
||||||
|
length_base[28] = 258;
|
||||||
|
|
||||||
|
}
|
@ -90,36 +90,172 @@ namespace pxsim.screen {
|
|||||||
screenState.blitLineCore(XX(xw), y, YY(xw), buf, mode)
|
screenState.blitLineCore(XX(xw), y, YY(xw), buf, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidImage(buf: RefBuffer) {
|
||||||
|
return buf.data.length >= 3 && buf.data[0] == 0xf0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PIX2BYTES(x: number) {
|
||||||
|
return ((x + 7) >> 3)
|
||||||
|
}
|
||||||
|
export function clear(): void {
|
||||||
|
const screenState = (board() as DalBoard).screenState;
|
||||||
|
screenState.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dump() {
|
||||||
|
// No need for this one.
|
||||||
|
}
|
||||||
|
|
||||||
|
export function imageOf(buf: RefBuffer) {
|
||||||
|
return incr(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace pxsim.screen {
|
||||||
|
function DMESG(msg: string) {
|
||||||
|
control.dmesg(msg)
|
||||||
|
}
|
||||||
|
const NULL: RefBuffer = null;
|
||||||
|
function revbits(v: number) {
|
||||||
|
v = (v & 0xf0) >> 4 | (v & 0x0f) << 4;
|
||||||
|
v = (v & 0xcc) >> 2 | (v & 0x33) << 2;
|
||||||
|
v = (v & 0xaa) >> 1 | (v & 0x55) << 1;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unpackPNG(png: RefBuffer) {
|
||||||
|
function memcmp(off: number, mark: string) {
|
||||||
|
for (let i = 0; i < mark.length; ++i) {
|
||||||
|
if (mark.charCodeAt(i) != png.data[off + i])
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
function readInt(off: number) {
|
||||||
|
return ((png.data[off] << 24) | (png.data[off + 1] << 16) |
|
||||||
|
(png.data[off + 2] << 8) | (png.data[off + 3])) >>> 0
|
||||||
|
}
|
||||||
|
if (!png) {
|
||||||
|
DMESG("PNG: Missing image");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (png.data.length < 45) {
|
||||||
|
DMESG("PNG: File too small");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(0, "\x89PNG\r\n\x1A\n") != 0) {
|
||||||
|
DMESG("PNG: Invalid header");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(12, "IHDR") != 0) {
|
||||||
|
DMESG("PNG: missing IHDR");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lenIHDR = readInt(8);
|
||||||
|
const width = readInt(16);
|
||||||
|
const height = readInt(20);
|
||||||
|
const lenIDAT = readInt(33);
|
||||||
|
const sizeOfHD = 41;
|
||||||
|
|
||||||
|
if (lenIHDR != 13) {
|
||||||
|
DMESG("PNG: bad IHDR len");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (memcmp(24, "\x01\x00\x00\x00\x00") != 0) {
|
||||||
|
DMESG("PNG: not 1-bit grayscale");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (memcmp(37, "IDAT") != 0) {
|
||||||
|
DMESG("PNG: missing IDAT");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (lenIDAT + sizeOfHD >= png.data.length) {
|
||||||
|
DMESG("PNG: buffer too short");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (width > 300 || height > 300) {
|
||||||
|
DMESG("PNG: too big");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byteW = (width + 7) >> 3;
|
||||||
|
const sz = (byteW + 1) * height;
|
||||||
|
const tmp = new Uint8Array(sz + 1);
|
||||||
|
// uncompress doesn't take the zlib header, hence + 2
|
||||||
|
const two = tinf.uncompress(png.data.slice(sizeOfHD + 2, sizeOfHD + lenIDAT), tmp);
|
||||||
|
if (two.length != sz) {
|
||||||
|
DMESG("PNG: invalid compressed size");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = output.createBuffer(2 + byteW * height);
|
||||||
|
res.data[0] = 0xf0;
|
||||||
|
res.data[1] = width;
|
||||||
|
let dst = 2
|
||||||
|
let src = 0
|
||||||
|
let lastMask = (1 << (width & 7)) - 1;
|
||||||
|
if (lastMask == 0)
|
||||||
|
lastMask = 0xff;
|
||||||
|
for (let i = 0; i < height; ++i) {
|
||||||
|
if (two[src++] != 0) {
|
||||||
|
DMESG("PNG: unsupported filter");
|
||||||
|
decr(res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (let j = 0; j < byteW; ++j) {
|
||||||
|
res.data[dst] = ~revbits(two[src++]);
|
||||||
|
if (j == byteW - 1) {
|
||||||
|
res.data[dst] &= lastMask;
|
||||||
|
}
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace pxsim.ImageMethods {
|
||||||
const bitdouble = [
|
const bitdouble = [
|
||||||
0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff,
|
0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff,
|
||||||
]
|
]
|
||||||
|
|
||||||
export function isValidIcon(buf: RefBuffer) {
|
export function buffer(buf: RefBuffer) {
|
||||||
return buf.data.length >= 3 && buf.data[0] == 0xf0;
|
return incr(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
function PIX2BYTES(x: number) {
|
export function width(buf: RefBuffer) {
|
||||||
return ((x + 7) >> 3)
|
if (!screen.isValidImage(buf)) return 0
|
||||||
|
return buf.data[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function drawIcon(x: number, y: number, buf: RefBuffer, mode: Draw): void {
|
export function height(buf: RefBuffer) {
|
||||||
|
if (!screen.isValidImage(buf)) return 0
|
||||||
|
const bw = screen.PIX2BYTES(buf.data[1]);
|
||||||
|
const h = ((buf.data.length - 2) / bw) | 0;
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
export function draw(buf: RefBuffer, x: number, y: number, mode: Draw): void {
|
||||||
const screenState = (board() as DalBoard).screenState;
|
const screenState = (board() as DalBoard).screenState;
|
||||||
|
|
||||||
if (!isValidIcon(buf))
|
if (!screen.isValidImage(buf))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mode & (Draw.Double | Draw.Quad)) {
|
if (mode & (Draw.Double | Draw.Quad)) {
|
||||||
buf = doubleIcon(buf);
|
buf = doubled(buf);
|
||||||
if (mode & Draw.Quad) {
|
if (mode & Draw.Quad) {
|
||||||
let pbuf = buf;
|
let pbuf = buf;
|
||||||
buf = doubleIcon(buf);
|
buf = doubled(buf);
|
||||||
decr(pbuf);
|
decr(pbuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pixwidth = buf.data[1];
|
let pixwidth = buf.data[1];
|
||||||
let ptr = 2;
|
let ptr = 2;
|
||||||
const bytewidth = PIX2BYTES(pixwidth);
|
const bytewidth = screen.PIX2BYTES(pixwidth);
|
||||||
pixwidth = Math.min(pixwidth, visuals.SCREEN_WIDTH);
|
pixwidth = Math.min(pixwidth, visuals.SCREEN_WIDTH);
|
||||||
while (ptr + bytewidth <= buf.data.length) {
|
while (ptr + bytewidth <= buf.data.length) {
|
||||||
if (mode & (Draw.Clear | Draw.Xor | Draw.Transparent)) {
|
if (mode & (Draw.Clear | Draw.Xor | Draw.Transparent)) {
|
||||||
@ -132,24 +268,19 @@ namespace pxsim.screen {
|
|||||||
ptr += bytewidth;
|
ptr += bytewidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode & Draw.Double)
|
if (mode & (Draw.Double | Draw.Quad))
|
||||||
decr(buf);
|
decr(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clear(): void {
|
export function doubled(buf: RefBuffer): RefBuffer {
|
||||||
const screenState = (board() as DalBoard).screenState;
|
if (!screen.isValidImage(buf))
|
||||||
screenState.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function doubleIcon(buf: RefBuffer): RefBuffer {
|
|
||||||
if (!isValidIcon(buf))
|
|
||||||
return null;
|
return null;
|
||||||
const w = buf.data[1];
|
const w = buf.data[1];
|
||||||
if (w > 126)
|
if (w > 126)
|
||||||
return null;
|
return null;
|
||||||
const bw = PIX2BYTES(w);
|
const bw = screen.PIX2BYTES(w);
|
||||||
const h = ((buf.data.length - 2) / bw) | 0;
|
const h = ((buf.data.length - 2) / bw) | 0;
|
||||||
const bw2 = PIX2BYTES(w * 2);
|
const bw2 = screen.PIX2BYTES(w * 2);
|
||||||
const out = pins.createBuffer(2 + bw2 * h * 2)
|
const out = pins.createBuffer(2 + bw2 * h * 2)
|
||||||
out.data[0] = 0xf0;
|
out.data[0] = 0xf0;
|
||||||
out.data[1] = w * 2;
|
out.data[1] = w * 2;
|
||||||
@ -169,7 +300,5 @@ namespace pxsim.screen {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dump() {
|
|
||||||
// do we need it?
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
33
sim/state/sounds.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
namespace pxsim.music {
|
||||||
|
export function fromWAV(buf: RefBuffer) {
|
||||||
|
return incr(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace pxsim.SoundMethods {
|
||||||
|
|
||||||
|
export function buffer(buf: RefBuffer) {
|
||||||
|
return incr(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function uint8ArrayToString(input: Uint8Array) {
|
||||||
|
let len = input.length;
|
||||||
|
let res = ""
|
||||||
|
for (let i = 0; i < len; ++i)
|
||||||
|
res += String.fromCharCode(input[i]);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function play(buf: RefBuffer, volume: number) {
|
||||||
|
return new Promise<void>(resolve => {
|
||||||
|
let url = "data:audio/wav;base64," + btoa(uint8ArrayToString(buf.data))
|
||||||
|
let audio = new Audio(url)
|
||||||
|
audio.onended = () => {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
audio.play()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -480,6 +480,7 @@ namespace pxsim.visuals {
|
|||||||
this.screenCanvasCtx.putImageData(this.screenCanvasData, 0, 0);
|
this.screenCanvasCtx.putImageData(this.screenCanvasData, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
private updateNeoPixels() {
|
private updateNeoPixels() {
|
||||||
let state = this.board;
|
let state = this.board;
|
||||||
if (!state || !state.neopixelState) return;
|
if (!state || !state.neopixelState) return;
|
||||||
@ -509,6 +510,7 @@ namespace pxsim.visuals {
|
|||||||
if (p_inner) svg.filter(p_inner, `url(#neopixelglow)`);
|
if (p_inner) svg.filter(p_inner, `url(#neopixelglow)`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
private updateSound() {
|
private updateSound() {
|
||||||
let state = this.board;
|
let state = this.board;
|
||||||
|
@ -6,5 +6,8 @@
|
|||||||
],
|
],
|
||||||
"approvedRepos": [
|
"approvedRepos": [
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"galleries": {
|
||||||
|
"Maker Activities": "maker"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,13 +34,6 @@ span.blocklyTreeLabel {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blocklyFlyoutBackground {
|
|
||||||
fill: #282828 !important;
|
|
||||||
}
|
|
||||||
.monacoFlyout {
|
|
||||||
background: rgba(40, 40, 40, 0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search box */
|
/* Search box */
|
||||||
#blocklySearchArea {
|
#blocklySearchArea {
|
||||||
margin: 0.5rem;
|
margin: 0.5rem;
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
/* Lego colors */
|
/* Lego colors */
|
||||||
@red: @brightRed;
|
@red: @brightRed;
|
||||||
@blue: #008ad2;
|
@blue: #00a5c8; /*#008ad2;*/
|
||||||
@yellow: #fccd00;
|
@yellow: #fccd00;
|
||||||
@orange: #fa7f2a;
|
@orange: #fa7f2a;
|
||||||
@green: @brightYellowishGreen;
|
@green: @brightYellowishGreen;
|
||||||
@ -96,9 +96,11 @@
|
|||||||
Menu
|
Menu
|
||||||
--------------------*/
|
--------------------*/
|
||||||
|
|
||||||
|
@mainMenuBackground: #f4f7f9;
|
||||||
@mainMenuInvertedBackground: @yellow;
|
@mainMenuInvertedBackground: @yellow;
|
||||||
@mainMenuTutorialBackground: @orange;
|
@mainMenuTutorialBackground: @orange;
|
||||||
|
|
||||||
|
|
||||||
@tutorialSelectedMenuColor: @blue;
|
@tutorialSelectedMenuColor: @blue;
|
||||||
|
|
||||||
/*-------------------
|
/*-------------------
|
||||||
@ -117,6 +119,8 @@
|
|||||||
@editorToolsBackground: #fcfbfa;
|
@editorToolsBackground: #fcfbfa;
|
||||||
@blocklySvgColor: #ecf6ff;
|
@blocklySvgColor: #ecf6ff;
|
||||||
|
|
||||||
|
@homeScreenBackground: #EAEEEF;
|
||||||
|
|
||||||
/*-------------------
|
/*-------------------
|
||||||
Full screen
|
Full screen
|
||||||
--------------------*/
|
--------------------*/
|
||||||
@ -152,3 +156,13 @@
|
|||||||
@blocklyRowHeightComputer: 50px;
|
@blocklyRowHeightComputer: 50px;
|
||||||
@blocklyRowHeightTablet: 50px;
|
@blocklyRowHeightTablet: 50px;
|
||||||
@blocklyRowHeightMobile: 50px;
|
@blocklyRowHeightMobile: 50px;
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------
|
||||||
|
Flyout
|
||||||
|
--------------------*/
|
||||||
|
|
||||||
|
@flyoutLabelColor: white;
|
||||||
|
@blocklyFlyoutColor: #282828;
|
||||||
|
@blocklyFlyoutColorOpacity: 0.9;
|
||||||
|
@monacoFlyoutColor: rgba(40, 40, 40,0.9);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
/*******************************
|
/*******************************
|
||||||
User Variable Overrides
|
User Variable Overrides
|
||||||
*******************************/
|
*******************************/
|
||||||
|
|
||||||
|
@exampleCardFullHeight: true;
|
@ -43,10 +43,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Menu bar colors */
|
/* Menu bar colors */
|
||||||
#menubar .ui.item {
|
.menubar .ui.item {
|
||||||
color: @red;
|
color: #373737;
|
||||||
}
|
}
|
||||||
.fullscreensim #menubar .ui.menu {
|
.fullscreensim .menubar .ui.menu {
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
border-bottom: 0 !important;
|
border-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
@ -57,13 +57,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Editor menu toggle */
|
/* Editor menu toggle */
|
||||||
#menubar .ui.menu.fixed .item.editor-menuitem .ui.grid {
|
.menubar .ui.menu.fixed .item.editor-menuitem .ui.grid {
|
||||||
background: @blue !important;
|
background: @blue !important;
|
||||||
|
border-radius: 0px !important;
|
||||||
}
|
}
|
||||||
|
.menubar .ui.menu.fixed .ui.item.editor-menuitem .item:not(.active) {
|
||||||
#menubar .ui.menu.fixed .ui.item.editor-menuitem .item:not(.active) {
|
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
.menubar .ui.menu.fixed .ui.item.editor-menuitem .item {
|
||||||
|
border-radius: 0px !important;
|
||||||
|
}
|
||||||
|
.menubar .ui.menu.fixed .ui.item.editor-menuitem .item.active {
|
||||||
|
color: @blue;
|
||||||
|
}
|
||||||
|
|
||||||
.sandboxfooter a:not(.thin) {
|
.sandboxfooter a:not(.thin) {
|
||||||
color:white !important;
|
color:white !important;
|
||||||
|
@ -39,38 +39,38 @@
|
|||||||
@step : 'pxt';
|
@step : 'pxt';
|
||||||
|
|
||||||
/* Collections */
|
/* Collections */
|
||||||
@breadcrumb : 'default';
|
@breadcrumb : 'pxt';
|
||||||
@form : 'default';
|
@form : 'pxt';
|
||||||
@grid : 'pxt';
|
@grid : 'pxt';
|
||||||
@menu : 'pxt';
|
@menu : 'pxt';
|
||||||
@message : 'default';
|
@message : 'pxt';
|
||||||
@table : 'default';
|
@table : 'pxt';
|
||||||
|
|
||||||
/* Modules */
|
/* Modules */
|
||||||
@accordion : 'default';
|
@accordion : 'pxt';
|
||||||
@checkbox : 'default';
|
@checkbox : 'pxt';
|
||||||
@dimmer : 'pxt';
|
@dimmer : 'pxt';
|
||||||
@dropdown : 'default';
|
@dropdown : 'pxt';
|
||||||
@embed : 'default';
|
@embed : 'pxt';
|
||||||
@modal : 'pxt';
|
@modal : 'pxt';
|
||||||
@nag : 'default';
|
@nag : 'pxt';
|
||||||
@popup : 'default';
|
@popup : 'pxt';
|
||||||
@progress : 'default';
|
@progress : 'pxt';
|
||||||
@rating : 'default';
|
@rating : 'pxt';
|
||||||
@search : 'default';
|
@search : 'pxt';
|
||||||
@shape : 'default';
|
@shape : 'pxt';
|
||||||
@sidebar : 'default';
|
@sidebar : 'pxt';
|
||||||
@sticky : 'default';
|
@sticky : 'pxt';
|
||||||
@tab : 'default';
|
@tab : 'pxt';
|
||||||
@transition : 'default';
|
@transition : 'default';
|
||||||
|
|
||||||
/* Views */
|
/* Views */
|
||||||
@ad : 'default';
|
@ad : 'pxt';
|
||||||
@card : 'pxt';
|
@card : 'pxt';
|
||||||
@comment : 'default';
|
@comment : 'pxt';
|
||||||
@feed : 'default';
|
@feed : 'pxt';
|
||||||
@item : 'default';
|
@item : 'pxt';
|
||||||
@statistic : 'default';
|
@statistic : 'pxt';
|
||||||
|
|
||||||
/*******************************
|
/*******************************
|
||||||
Folders
|
Folders
|
||||||
|