Compare commits

..

97 Commits

Author SHA1 Message Date
3d29c5e323 0.0.35 2017-11-30 10:16:04 -08:00
15d59269d4 bump pxt-core to 2.3.31, 2017-11-30 10:15:54 -08:00
73b5e7dc3f Merge pull request #65 from Microsoft/ultrasonicsensor
Ultrasonic/IR sensor refactoring work
2017-11-30 10:14:30 -08:00
a92edcffee adding waitUntil to buttons 2017-11-30 10:05:00 -08:00
305a650125 using threshold detector 2017-11-30 09:59:28 -08:00
34a5aeb7d2 moving IR as well 2017-11-30 09:53:43 -08:00
32f524ddd8 fixing build 2017-11-30 09:48:43 -08:00
b690c1634b updated distance detection 2017-11-30 09:41:34 -08:00
8089841892 wait until 2017-11-30 09:39:24 -08:00
3d8c697586 moving into separate project 2017-11-30 09:38:04 -08:00
e365e3d1be Merge pull request #64 from Microsoft/color-sensor
Color sensor refactoring
2017-11-30 08:55:50 -08:00
4ed41adb6a avoid conflicts of event values 2017-11-30 08:31:31 -08:00
39b7f99741 update mode selection 2017-11-30 08:20:00 -08:00
ad17191ae2 collapse reflected/ambient blocks 2017-11-29 22:47:17 -08:00
4a8633f506 updated descriptions 2017-11-29 22:11:59 -08:00
3690f409b0 docs skeleton 2017-11-29 22:09:12 -08:00
97663d2b83 updated naming 2017-11-29 21:57:05 -08:00
4d2b7ced71 moving color sensor to separate project 2017-11-29 21:41:00 -08:00
5676103052 Merge pull request #63 from Microsoft/speedispower
use speed when setting power
2017-11-29 21:22:32 -08:00
cb8c14fbb1 Allow waitForEvent on main thread; fixes #60
also use target_panic() instead of assert
2017-11-29 19:54:17 -03:00
815d438d86 use speed when setting power 2017-11-29 00:04:54 -08:00
da8de1e31e Merge pull request #56 from Microsoft/note 2017-11-28 17:00:57 -08:00
b028916025 Increment pxt-core and pxt-common-packages dependencies 2017-11-28 16:48:16 -08:00
b9be74bad8 Merge pull request #59 from Microsoft/touch
adding touch button examples
2017-11-28 16:35:48 -08:00
7e502b1749 adding bare docs 2017-11-28 16:24:22 -08:00
f22edac84d moving touch stuff into separate projects 2017-11-28 16:11:15 -08:00
ef2807a84e Fill in real min and max 2017-11-28 16:04:25 -08:00
580b40876c fixing bumped 2017-11-28 16:02:04 -08:00
b57ae5d588 implement wasPressed 2017-11-28 15:33:43 -08:00
71479d0caa adding touch button examples 2017-11-28 15:23:54 -08:00
7e39cdde9d Add a todo 2017-11-27 17:21:32 -08:00
85ca6b3698 Support for new data structure 2017-11-27 14:12:04 -08:00
2c89848fda More 2017-11-21 17:11:11 -08:00
ed1c187514 Initial work 2017-11-21 11:35:53 -08:00
52816e6de7 Merge pull request #51 from Microsoft/debugging
Add debug function
2017-11-20 10:54:44 -08:00
60ac3a9d00 0.0.34 2017-11-18 07:37:59 -08:00
d2c7a5ace0 support for wait/when 2017-11-18 07:37:46 -08:00
79fcd1c01e 0.0.33 2017-11-18 07:34:50 -08:00
2d48725c63 bump pxt-core to 2.3.28, bump pxt-common-packages to 0.14.3, 2017-11-18 07:34:40 -08:00
802c3db0ba 0.0.32 2017-11-17 14:12:32 -08:00
4a7e8c5fa8 adding block for sounds 2017-11-16 23:28:44 -08:00
0ebffd8549 0.0.31 2017-11-16 22:55:51 -08:00
988a2b638b add icon 2017-11-16 22:52:29 -08:00
fb743dea74 Merge pull request #50 from Microsoft/icons
Icons in blocks
2017-11-16 22:48:35 -08:00
e400637ba1 Merge branch 'master' into icons 2017-11-16 22:47:40 -08:00
b2768b1099 added showimage 2017-11-16 22:46:51 -08:00
3a5885b28e 0.0.30 2017-11-16 22:25:36 -08:00
b602b52f7d bump pxt-core to 2.3.27, 2017-11-16 22:25:26 -08:00
90afb60cc4 bump pxt-core to 2.3.26, bump pxt-common-packages to 0.14.2, 2017-11-16 22:04:29 -08:00
c936c16c33 more icons 2017-11-16 22:03:43 -08:00
ead69e3c6e Add debug function 2017-11-16 16:28:10 -08:00
d100026d5c more icons 2017-11-16 13:05:50 -08:00
41c2899feb more icons 2017-11-16 13:03:15 -08:00
9437873427 more images 2017-11-16 12:58:37 -08:00
dab281a9cb including a few icons in block names 2017-11-16 12:41:47 -08:00
f27fb4d93c 0.0.29 2017-11-09 10:29:24 -08:00
11fb82eba9 Merge pull request #49 from Microsoft/updatepxt
Update pxt
2017-11-09 19:28:55 +01:00
170e6b700b updating to latest pxt 2017-11-09 10:24:12 -08:00
387ad763f8 bump pxt-core to 2.3.19, bump pxt-common-packages to 0.12.17, 2017-11-09 10:14:47 -08:00
0e61f162bb Fix style to match latest changes in master. 2017-10-31 11:27:18 -07:00
400f01a6be fixing duplicate block id 2017-10-31 09:42:53 -07:00
afcbe69c44 0.0.28 2017-10-31 09:04:27 -07:00
9165fe09fc bump pxt-core to 2.3.6, 2017-10-31 09:04:20 -07:00
08f36fbb94 Merge pull request #30 from Microsoft/jres
Import images and sounds from ev3 EDU
2017-10-31 16:48:24 +01:00
afca2aa4d1 Merge pull request #45 from Microsoft/motorsrename
API tinkering
2017-10-31 16:43:27 +01:00
3c6c766412 removing lego resources 2017-10-30 21:54:23 -07:00
329a1e15ea simplified IR API 2017-10-30 21:39:50 -07:00
16a025f3a0 simplified motor API 2017-10-30 21:29:18 -07:00
26b975b5f2 images 2017-10-30 14:51:14 -07:00
79b9bce904 Merge branch 'master' into jres 2017-10-30 13:29:45 -07:00
d3d9fa6ca0 Merge branch 'master' into jres 2017-10-30 13:29:16 -07:00
8f5c930f76 Update UI to latest. 2017-10-30 13:21:03 -07:00
a082807455 Fix locking in waitForEvent() and sample playing 2017-10-30 19:18:01 +00:00
3469f51b7b Don't need to use freshcoat branch of pxt (#31) 2017-10-30 19:06:49 +00:00
e8e31e6aa7 Fix brick.print() 2017-10-30 19:03:45 +00:00
2df90152fe Remove debug output 2017-10-30 18:56:03 +00:00
e25590539e Add sample-playing code 2017-10-30 18:55:23 +00:00
a7795302fc Make it compile with master 2017-10-30 18:54:53 +00:00
480d89ba8e Added swift stuff 2017-10-30 11:46:06 -07:00
9487d324fd added link to gitlab 2017-10-30 11:45:02 -07:00
bfc95e075e Merge branch 'master' into jres 2017-10-30 18:48:18 +01:00
7d4ba9b2b2 0.0.27 2017-10-30 10:47:03 -07:00
71b83040e6 bump pxt-core to 2.3.5, bump pxt-common-packages to 0.12.4, 2017-10-30 10:46:56 -07:00
1976534da0 Merge pull request #28 from Microsoft/sensorscat
Aligning categories with LabView/RobotC
2017-10-30 18:46:27 +01:00
ebbbe6e86c Add sounds (only simulator for now) 2017-10-30 17:25:58 +00:00
c4a9b4a381 updated brick color 2017-10-30 09:58:00 -07:00
c7d36a5e82 moving motors up 2017-10-30 09:55:36 -07:00
c8c45be057 fixing brick groups 2017-10-30 09:51:18 -07:00
a8a7267851 Add LEGO EDU images 2017-10-30 15:34:47 +00:00
9cdb4081fd Add PNG decompressor in SIM 2017-10-30 14:42:08 +00:00
dcb398d3d5 Rename Icon to Image 2017-10-30 13:28:01 +00:00
f6e350cf9f Add separate type for Icons (same repr as buffer) 2017-10-30 13:04:12 +00:00
c085094394 Add screen::unpackPNG() 2017-10-30 12:45:37 +00:00
7c2ea7c406 fixing more groups 2017-10-28 09:20:34 -07:00
84b98a2788 brick namespace 2017-10-28 09:13:02 -07:00
dfe2fe3cff brick refactoring 2017-10-27 11:05:04 -07:00
18fefa2a44 output -> motors 2017-10-27 11:01:11 -07:00
97 changed files with 4771 additions and 837 deletions

View File

@ -4,6 +4,8 @@
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
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,
* 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-ev3 to ``pxt-ev3`` folder
* go to ``pxt`` and run

View File

@ -4,5 +4,12 @@ Here are some fun programs for your @boardname@!
## Fun stuff
Coming soon.
```codecard
[
{
"name": "Happy unhappy",
"description": "Keep your brick entertained and happy",
"url":"/examples/happy-unhappy",
"cardType": "example"
}]
```

View File

@ -0,0 +1,12 @@
# Happy unhappy
Use a touch sensor to make the brick happy.
```blocks
sensors.touchSensor1.onEvent(TouchSensorEvent.Pressed, function () {
brick.showImage(images.expressionsBigSmile)
})
sensors.touchSensor1.onEvent(TouchSensorEvent.Released, function () {
brick.showImage(images.expressionsSick)
})
```

View File

@ -1,3 +1,8 @@
# Reference
TODO
## See Also
[touch sensor](/reference/sensors/touch-sensor),
[color sensor](/reference/sensors/color-sensor)

View File

@ -9,6 +9,13 @@ eval("if (typeof process === 'object' && process + '' === '[object process]') px
namespace pxt.editor {
import UF2 = pxtc.UF2;
export let ev3: Ev3Wrapper
export function debug() {
return initAsync()
.then(w => w.downloadFileAsync("/tmp/dmesg.txt", v => console.log(pxt.Util.uint8ArrayToString(v))))
}
// this comes from aux/pxt.lms
const rbfTemplate = `
4c45474f580000006d000100000000001c000000000000000e000000821b038405018130813e8053
@ -19,6 +26,7 @@ namespace pxt.editor {
return pxt.HF2.mkPacketIOAsync()
.then(h => {
let w = new Ev3Wrapper(h)
ev3 = w
return w.reconnectAsync(true)
.then(() => w)
})

View File

@ -252,6 +252,13 @@ namespace pxt.editor {
return loop()
}
downloadFileAsync(path: string, cb: (d: Uint8Array) => void) {
return this.lock.enqueue("file", () =>
this.streamFileOnceAsync(path, cb))
}
private initAsync() {
return Promise.resolve()
}

View 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

File diff suppressed because it is too large Load Diff

View File

@ -144,6 +144,12 @@
"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",
"loops.waitUntil": "Busy wait for a condition to be true",
"loops.waitUntil|param|condition": "condition to test for",
"loops.waitUntil|param|timeOut": "if positive, maximum duration to wait for in milliseconds",
"loops.when": "Runs code when the condition becomes true",
"loops.when|param|condition": "condition to test",
"loops.when|param|handler": "code to run",
"parseInt": "Convert a string to an integer.",
"serial": "Reading and writing data over a serial connection.",
"serial.writeBuffer": "Send a buffer across the serial connection.",
@ -151,6 +157,6 @@
"serial.writeNumber": "Write a number 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|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"
}

View File

@ -30,7 +30,7 @@
"control.waitMicros|block": "wait (µs)%micros",
"control|block": "control",
"loops.forever|block": "forever",
"loops.pause|block": "pause %ms=timePicker|ms",
"loops.pause|block": "pause %pause=timePicker|ms",
"loops.timePicker|block": "%ms",
"loops|block": "loops",
"parseInt|block": "parse to integer %text",

View File

@ -17,7 +17,8 @@
"control.cpp",
"control.ts",
"serial.cpp",
"serial.ts"
"serial.ts",
"loops.ts"
],
"testFiles": [
"test.ts"

10
libs/base/shims.d.ts vendored
View File

@ -79,7 +79,7 @@ declare namespace loops {
* @param ms how long to pause for, eg: 100, 200, 500, 1000, 2000
*/
//% help=loops/pause weight=99
//% async block="pause %ms=timePicker|ms"
//% async block="pause %pause=timePicker|ms"
//% blockId=device_pause shim=loops::pause
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"
//% blockExternalInputs=1
//% help="control/on-event" shim=control::onEvent
function onEvent(src: int32, value: int32, handler: () => void): void;
//% help="control/on-event" flags.defl=16 shim=control::onEvent
function onEvent(src: int32, value: int32, handler: () => void, flags?: int32): void;
/**
* Reset the device.
@ -144,14 +144,14 @@ declare namespace serial {
* Write some text to the serial port.
*/
//% help=serial/write-string
//% weight=87
//% weight=87 blockHidden=true
//% blockId=serial_writestring block="serial|write string %text" shim=serial::writeString
function writeString(text: string): void;
/**
* Send a buffer across the serial connection.
*/
//% help=serial/write-buffer weight=6
//% help=serial/write-buffer weight=6 blockHidden=true
//% blockId=serial_writebuffer block="serial|write buffer %buffer" shim=serial::writeBuffer
function writeBuffer(buffer: Buffer): void;
}

View File

@ -0,0 +1,3 @@
# Color sensor
The library to interact with the Touch Sensor.

View File

@ -0,0 +1,12 @@
{
"sensors.ColorSensor": "The color sensor is a digital sensor that can detect the color or intensity\nof light that enters the small window on the face of the sensor.",
"sensors.ColorSensor.color": "Get the current color from the color sensor.",
"sensors.ColorSensor.colorMode": "Gets the current color mode",
"sensors.ColorSensor.light": "Measures the ambient or reflected light value from 0 (darkest) to 100 (brightest).",
"sensors.ColorSensor.onColorDetected": "Registers code to run when the given color is detected.",
"sensors.ColorSensor.onColorDetected|param|color": "the color to detect, eg: ColorSensorColor.Blue",
"sensors.ColorSensor.onColorDetected|param|handler": "the code to run when detected",
"sensors.ColorSensor.onLightChanged": "Registers code to run when the ambient light changes.",
"sensors.ColorSensor.onLightChanged|param|condition": "the light condition",
"sensors.ColorSensor.onLightChanged|param|handler": "the code to run when detected"
}

View File

@ -0,0 +1,27 @@
{
"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",
"ColorSensorMode.AmbientLightIntensity|block": "ambient light intensity",
"ColorSensorMode.Color|block": "color",
"ColorSensorMode.ReflectedLightIntensity|block": "reflected light intensity",
"LightCondition.Dark|block": "dark",
"LightIntensityMode.Ambient|block": "ambient light",
"LightIntensityMode.Reflected|block": "reflected light",
"sensors.ColorSensor.color|block": "`icons.colorSensor` %color| color",
"sensors.ColorSensor.light|block": "`icons.colorSensor` %color|%mode",
"sensors.ColorSensor.onColorDetected|block": "on `icons.colorSensor` %sensor|detected color %color",
"sensors.ColorSensor.onLightChanged|block": "on `icons.colorSensor` %sensor|%mode|%condition",
"sensors.color1|block": "1",
"sensors.color2|block": "2",
"sensors.color3|block": "3",
"sensors.color4|block": "4",
"sensors|block": "sensors",
"{id:category}Sensors": "Sensors",
"{id:group}Color Sensor": "Color Sensor"
}

185
libs/color-sensor/color.ts Normal file
View File

@ -0,0 +1,185 @@
const enum ColorSensorMode {
None = -1,
//% block="reflected light intensity"
ReflectedLightIntensity = 0,
//% block="ambient light intensity"
AmbientLightIntensity = 1,
//% block="color"
Color = 2,
RefRaw = 3,
RgbRaw = 4,
ColorCal = 5,
}
enum LightIntensityMode {
//% block="reflected light"
Reflected = ColorSensorMode.ReflectedLightIntensity,
//% block="ambient light"
Ambient = ColorSensorMode.AmbientLightIntensity
}
const enum ColorSensorColor {
//% block="none"
None,
//% block="black"
Black,
//% block="blue"
Blue,
//% block="green"
Green,
//% block="yellow"
Yellow,
//% block="red"
Red,
//% block="white"
White,
//% block="brown"
Brown,
}
enum LightCondition {
//% block="dark"
Dark = sensors.internal.ThresholdState.Low,
//$ block="bright"
Bright = sensors.internal.ThresholdState.High
}
namespace sensors {
/**
* The color sensor is a digital sensor that can detect the color or intensity
* of light that enters the small window on the face of the sensor.
*/
//% fixedInstances
export class ColorSensor extends internal.UartSensor {
thresholdDetector: sensors.internal.ThresholdDetector;
constructor(port: number) {
super(port)
this.thresholdDetector = new sensors.internal.ThresholdDetector(this.id());
}
_colorEventValue(value: number) {
return 0xff00 | value;
}
_deviceType() {
return DAL.DEVICE_TYPE_COLOR
}
setMode(m: ColorSensorMode) {
this._setMode(m)
}
/**
* Gets the current color mode
*/
colorMode() {
return <ColorSensorMode>this.mode;
}
_query() {
if (this.mode == ColorSensorMode.Color)
return this.getNumber(NumberFormat.UInt8LE, 0)
return 0
}
_update(prev: number, curr: number) {
if (this.mode == ColorSensorMode.Color)
control.raiseEvent(this._id, this._colorEventValue(curr));
else
this.thresholdDetector.setLevel(curr);
}
/**
* Registers code to run when the given color is detected.
* @param color the color to detect, eg: ColorSensorColor.Blue
* @param handler the code to run when detected
*/
//% help=sensors/color-sensor/on-color-detected
//% block="on `icons.colorSensor` %sensor|detected color %color"
//% blockId=colorOnColorDetected
//% parts="colorsensor"
//% blockNamespace=sensors
//% weight=100 blockGap=8
//% group="Color Sensor"
onColorDetected(color: ColorSensorColor, handler: () => void) {
const v = this._colorEventValue(<number>color);
control.onEvent(this._id, v, handler);
this.setMode(ColorSensorMode.Color)
if (this.color() == color)
control.raiseEvent(this._id, v);
}
/**
* Get the current color from the color sensor.
* @param color the color sensor to query the request
*/
//% help=sensors/color-sensor/color
//% block="`icons.colorSensor` %color| color"
//% blockId=colorGetColor
//% parts="colorsensor"
//% blockNamespace=sensors
//% weight=99
//% group="Color Sensor"
color(): ColorSensorColor {
this.setMode(ColorSensorMode.Color)
return this.getNumber(NumberFormat.UInt8LE, 0)
}
/**
* Registers code to run when the ambient light changes.
* @param condition the light condition
* @param handler the code to run when detected
*/
//% help=sensors/color-sensor/on-light-changed
//% block="on `icons.colorSensor` %sensor|%mode|%condition"
//% blockId=colorOnLightChanged
//% parts="colorsensor"
//% blockNamespace=sensors
//% weight=89 blockGap=8
//% group="Color Sensor"
onLightChanged(mode: LightIntensityMode, condition: LightCondition, handler: () => void) {
control.onEvent(this._id, <number>condition, handler);
this.setMode(<ColorSensorMode><number>mode)
}
/**
* Measures the ambient or reflected light value from 0 (darkest) to 100 (brightest).
* @param color the color sensor port
*/
//% help=sensors/color-sensor/light
//% block="`icons.colorSensor` %color|%mode"
//% blockId=colorLight
//% parts="colorsensor"
//% blockNamespace=sensors
//% weight=88
//% group="Color Sensor"
light(mode: LightIntensityMode) {
this.setMode(<ColorSensorMode><number>mode)
return this.getNumber(NumberFormat.UInt8LE, 0)
}
//%
ambientLight() {
return this.light(LightIntensityMode.Ambient);
}
//%
reflectedLight() {
return this.light(LightIntensityMode.Reflected);
}
}
//% whenUsed block="1" weight=95 fixedInstance
export const color1: ColorSensor = new ColorSensor(1)
//% whenUsed block="3" weight=90 fixedInstance
export const color3: ColorSensor = new ColorSensor(3)
//% whenUsed block="2" weight=90 fixedInstance
export const color2: ColorSensor = new ColorSensor(2)
//% whenUsed block="4" weight=90 fixedInstance
export const color4: ColorSensor = new ColorSensor(4)
}

View File

@ -0,0 +1,16 @@
# Color Sensor
```cards
sensors.color1.onColorDetected(ColorSensorColor.Blue, function () {
})
sensors.color1.color();
sensors.color1.ambientLight();
sensors.color1.reflectedLight();
```
## See Also
[on color detected](/reference/sensors/color-sensor/on-color-detected),
[color](/reference/sensors/color-sensor/color),
[ambient light](/reference/sensors/color-sensor/ambient-light),
[reflected light](/reference/sensors/color-sensor/reflected-light),

View File

@ -0,0 +1,11 @@
# Ambient Light
```blocks
loops.forever(function () {
if (sensors.color1.ambientLight() > 20) {
brick.setStatusLight(LightsPattern.Green)
} else {
brick.setStatusLight(LightsPattern.Orange)
}
})
```

View File

@ -0,0 +1,11 @@
# color
```blocks
loops.forever(function () {
if (sensors.color1.color() == ColorSensorColor.Green) {
brick.setStatusLight(LightsPattern.Green)
} else {
brick.setStatusLight(LightsPattern.Orange)
}
})
```

View File

@ -0,0 +1,16 @@
# On Color Detected
```sig
sensors.color1.onColorDetected(ColorSensorColor.Blue, function () { })
```
# Parameters
## Examples
```blocks
sensors.color1.onColorDetected(ColorSensorColor.Blue, function () {
brick.showImage(images.expressionsSick)
})
```

View File

@ -0,0 +1,11 @@
# Reflected Light
```blocks
loops.forever(function () {
if (sensors.color1.reflectedLight() > 20) {
brick.setStatusLight(LightsPattern.Green)
} else {
brick.setStatusLight(LightsPattern.Orange)
}
})
```

View File

@ -0,0 +1,15 @@
{
"name": "color-sensor",
"description": "Color Sensor support",
"files": [
"README.md",
"color.ts"
],
"testFiles": [
"test.ts"
],
"public": true,
"dependencies": {
"core": "file:../core"
}
}

View File

View File

@ -20,7 +20,7 @@ DEPS = $(PXT_HEADERS) package.json Makefile Makefile.inc
all: $(EXE)
$(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)
$(PREF)strip $(EXE)
node -p 'require("fs").readFileSync("$(EXE)").toString("hex")' > $(HEX)

View File

@ -1,6 +1,11 @@
{
"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",
@ -9,7 +14,34 @@
"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.waitUntil": "Waits until the event is raised",
"brick.Button.waitUntil|param|ev": "the event to wait for",
"brick.Button.wasPressed": "See if the button was pressed again since the last time you checked.",
"brick._imagePicker": "An image",
"brick._imagePicker|param|image": "the image",
"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.lightPattern": "Pattern block.",
"brick.lightPattern|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.",
"brick.showImage": "Shows an image on screen",
"brick.showImage|param|image": "image to draw",
"control": "Program controls and events.",
"control.allocateNotifyEvent": "Allocates the next user notification event",
"control.deviceFirmwareVersion": "Determine the version of system software currently running.",
@ -18,73 +50,28 @@
"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|value": "Component specific code indicating the cause of the event.",
"input": "Respond to and read data from buttons and sensors.",
"input.Button": "Generic button class, for device buttons and sensors.",
"input.Button.isPressed": "Check if button is currently pressed or not.",
"input.Button.onEvent": "Do something when a button or sensor is clicked, up or down.",
"input.Button.onEvent|param|body": "code to run when the event is raised",
"input.Button.wasPressed": "See if the button was pressed again since the last time you checked.",
"input.ColorSensor.ambientLight": "Get current ambient light value from the color sensor.",
"input.ColorSensor.color": "Get the current color from the color sensor.",
"input.ColorSensor.onColorDetected": "Registers code to run when the given color is detected",
"input.ColorSensor.onColorDetected|param|color": "the color to dtect",
"input.ColorSensor.onColorDetected|param|handler": "the code to run when detected",
"input.ColorSensor.reflectedLight": "Get current reflected light value from the color sensor.",
"input.GyroSensor.angle": "Get the current angle from the gyroscope.",
"input.GyroSensor.rate": "Get the current rotation rate from the gyroscope.",
"input.InfraredSensor.onObjectNear": "Registers code to run when an object is getting near.",
"input.InfraredSensor.onObjectNear|param|handler": "the code to run when detected",
"input.InfraredSensor.proximity": "Get the promixity measured by the infrared sensor, from ``0`` (close) to ``100`` (far)",
"input.InfraredSensor.remoteCommand": "Get the remote commandreceived the infrared sensor.",
"input.RemoteInfraredBeaconButton.isPressed": "Check if a remote button is currently pressed or not.",
"input.RemoteInfraredBeaconButton.onEvent": "Do something when a button or sensor is clicked, up or down",
"input.RemoteInfraredBeaconButton.onEvent|param|body": "code to run when the event is raised",
"input.RemoteInfraredBeaconButton.wasPressed": "See if the remote button was pressed again since the last time you checked.",
"input.TouchSensor.isTouched": "Check if touch sensor is touched.",
"input.TouchSensor.onEvent": "Do something when a touch sensor is touched...",
"input.TouchSensor.onEvent|param|body": "code to run when the event is raised",
"input.UltraSonicSensor.distance": "Gets the distance from the sonar in millimeters",
"input.UltraSonicSensor.onObjectNear": "Registers code to run when the given color is close",
"input.UltraSonicSensor.onObjectNear|param|handler": "the code to run when detected",
"input.buttonDown": "Down button on the EV3 Brick.",
"input.buttonEnter": "Enter button on the EV3 Brick.",
"input.buttonLeft": "Left button on the EV3 Brick.",
"input.buttonRight": "Right button on the EV3 Brick.",
"input.buttonUp": "Up button on the EV3 Brick.",
"input.remoteButtonBottomLeft": "Remote bottom-left button.",
"input.remoteButtonBottomRight": "Remote bottom-right button.",
"input.remoteButtonCenter": "Remote beacon (center) button.",
"input.remoteButtonTopLeft": "Remote top-left button.",
"input.remoteButtonTopRight": "Remote top-right button.",
"output.Motor.clearCount": "Clears the motor count",
"output.Motor.count": "Gets motor step count.",
"output.Motor.on": "Power on or off the motor.",
"output.Motor.reset": "Resets the motor.",
"output.Motor.setBrake": "Sets the automatic brake on or off when the motor is off",
"output.Motor.setBrake|param|brake": "a value indicating if the motor should break when off",
"output.Motor.setPower": "Sets the motor power level from ``-100`` to ``100``.",
"output.Motor.setPower|param|power": "the desired speed to use. eg: 50",
"output.Motor.setReversed": "Reverses the motor polarity",
"output.Motor.speed": "Gets motor actual speed.",
"output.Motor.tachoCount": "Gets motor tacho count.",
"motors.Motor.clearCount": "Clears the motor count",
"motors.Motor.count": "Gets motor step count.",
"motors.Motor.move": "Moves the motor by a number of degrees",
"motors.Motor.move|param|angle": "the degrees to rotate, eg: 360",
"motors.Motor.move|param|power": "the power from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.Motor.power": "Sets the motor power level from ``-100`` to ``100``.",
"motors.Motor.power|param|power": "the power from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.Motor.reset": "Resets the motor.",
"motors.Motor.setBrake": "Sets the automatic brake on or off when the motor is off",
"motors.Motor.setBrake|param|brake": "a value indicating if the motor should break when off",
"motors.Motor.setReversed": "Reverses the motor polarity",
"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|param|size": "number of bytes in the buffer",
"output.pattern": "Pattern block.",
"output.pattern|param|pattern": "the lights pattern to use. eg: LightsPattern.Green",
"output.setStatusLight": "Set lights.",
"output.setStatusLight|param|pattern": "the lights pattern to use.",
"output.stopAllMotors": "Stops all motors",
"screen.clear": "Clear screen and reset font to normal.",
"screen.doubleIcon": "Double size of an icon.",
"screen.drawIcon": "Draw an icon on the screen.",
"screen.print": "Show text on the screen.",
"screen.print|param|text": "the text to print on the screen, eg: \"Hello world\"",
"screen.print|param|x": "the starting position's x coordinate, eg: 0",
"screen.print|param|y": "the starting position's x coordinate, eg: 0",
"screen.setPixel": "Sets a pixel on or off",
"screen.setPixel|param|on": "a value indicating if the pixel should be on or off",
"screen.setPixel|param|x": "the starting position's x coordinate, eg: 0",
"screen.setPixel|param|y": "the starting position's x coordinate, eg: 0",
"screen.imageOf": "Makes an image bound to a buffer.",
"screen.unpackPNG": "Decompresses a 1-bit gray scale PNG image to image format.",
"sensors.GyroSensor.angle": "Get the current angle from the gyroscope.",
"sensors.GyroSensor.rate": "Get the current rotation rate from the gyroscope.",
"serial": "Reading and writing data over a serial connection.",
"serial.writeDmesg": "Send DMESG debug buffer over serial."
}

View File

@ -2,14 +2,6 @@
"ButtonEvent.Click|block": "click",
"ButtonEvent.Down|block": "down",
"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",
"LightsPattern.GreenFlash|block": "Flashing Green",
"LightsPattern.GreenPulse|block": "Pulsing Green",
"LightsPattern.Green|block": "Green",
@ -25,98 +17,64 @@
"Output.B|block": "B",
"Output.C|block": "C",
"Output.D|block": "D",
"PromixityEvent.ObjectDetected|block": "object detected",
"PromixityEvent.ObjectNear|block": "object near",
"TouchSensorEvent.Bumped|block": "bumped",
"TouchSensorEvent.Pressed|block": "pressed",
"TouchSensorEvent.Released|block": "released",
"brick.Button.isPressed|block": "`icons.brickButtons` %button|is pressed",
"brick.Button.onEvent|block": "on `icons.brickButtons` %button|%event",
"brick.Button.waitUntil|block": "wait until `icons.brickButtons` %button|%event",
"brick.Button.wasPressed|block": "`icons.brickButtons` %button|was pressed",
"brick._imagePicker|block": "%image",
"brick.buttonDown|block": "down",
"brick.buttonEnter|block": "enter",
"brick.buttonLeft|block": "left",
"brick.buttonRight|block": "right",
"brick.buttonUp|block": "up",
"brick.lightPattern|block": "%pattern",
"brick.print|block": "`icons.brickDisplay` print %text| at x: %x| y: %y",
"brick.setPixel|block": "`icons.brickDisplay` set pixel %on| at x: %x| y: %y",
"brick.setStatusLight|block": "set `icons.brickButtons` to %pattern=led_pattern",
"brick.showImage|block": "`icons.brickDisplay` show image %image=scren_image_picker",
"brick|block": "brick",
"control.raiseEvent|block": "raise event|from %src|with value %value",
"control|block": "control",
"input.Button.isPressed|block": "%button|is pressed",
"input.Button.onEvent|block": "on %button|%event",
"input.Button.wasPressed|block": "%button|was pressed",
"input.ColorSensor.ambientLight|block": "%color| ambient light",
"input.ColorSensor.color|block": "%color| color",
"input.ColorSensor.onColorDetected|block": "on %sensor|detected %color",
"input.ColorSensor.reflectedLight|block": "%color| reflected light",
"input.GyroSensor.angle|block": "%sensor|angle",
"input.GyroSensor.rate|block": "%sensor|rotation rate",
"input.InfraredSensor.onObjectNear|block": "on %sensor|object near",
"input.InfraredSensor.proximity|block": "%infrared|proximity",
"input.InfraredSensor.remoteCommand|block": "%infrared|remote command",
"input.RemoteInfraredBeaconButton.isPressed|block": "%button|is pressed",
"input.RemoteInfraredBeaconButton.onEvent|block": "on %button|%event",
"input.RemoteInfraredBeaconButton.wasPressed|block": "%button|was pressed",
"input.TouchSensor.isTouched|block": "%sensor|is touched",
"input.TouchSensor.onEvent|block": "on %sensor|%event",
"input.UltraSonicSensor.distance|block": "%sensor|distance",
"input.UltraSonicSensor.onObjectNear|block": "on %sensor|object near",
"input.buttonDown|block": "brick button down",
"input.buttonEnter|block": "brick button enter",
"input.buttonLeft|block": "brick button left",
"input.buttonRight|block": "brick button right",
"input.buttonUp|block": "brick button up",
"input.color1|block": "color sensor 1",
"input.color2|block": "color sensor 2",
"input.color3|block": "color sensor 3",
"input.color4|block": "color sensor 4",
"input.gyro1|block": "gyro sensor 1",
"input.gyro2|block": "gyro sensor 2",
"input.gyro3|block": "gyro sensor 3",
"input.gyro4|block": "gyro sensor 4",
"input.infraredSensor1|block": "infrared sensor 1",
"input.infraredSensor2|block": "infrared sensor 2",
"input.infraredSensor3|block": "infrared sensor 3",
"input.infraredSensor4|block": "infrared sensor 4",
"input.remoteButtonBottomLeft|block": "remote button bottom-left",
"input.remoteButtonBottomRight|block": "remote button bottom-right",
"input.remoteButtonCenter|block": "remote button center",
"input.remoteButtonTopLeft|block": "remote button top-left",
"input.remoteButtonTopRight|block": "remote button top-right",
"input.touchSensor1|block": "touch sensor 1",
"input.touchSensor2|block": "touch sensor 2",
"input.touchSensor3|block": "touch sensor 3",
"input.touchSensor4|block": "touch sensor 4",
"input.ultrasonic1|block": "ultrasonic sensor 1",
"input.ultrasonic2|block": "ultrasonic sensor 2",
"input.ultrasonic3|block": "ultrasonic sensor 3",
"input.ultrasonic4|block": "ultrasonic sensor 4",
"input|block": "input",
"output.Motor.count|block": "%motor|count",
"output.Motor.on|block": "%motor|%onOrOff",
"output.Motor.setBrake|block": "%motor|set brake %brake",
"output.Motor.setPower|block": "%motor|set power to %speed",
"output.Motor.setReversed|block": "%motor|set reversed %reversed",
"output.Motor.speed|block": "%motor|speed",
"output.Motor.tachoCount|block": "%motor|tacho count",
"output.largeMotorA|block": "large motor A",
"output.largeMotorB|block": "large motor B",
"output.largeMotorC|block": "large motor C",
"output.largeMotorD|block": "large motor D",
"output.mediumMotorA|block": "medium motor A",
"output.mediumMotorB|block": "medium motor B",
"output.mediumMotorC|block": "medium motor C",
"output.mediumMotorD|block": "medium motor D",
"output.pattern|block": "%pattern",
"output.setStatusLight|block": "set status light %pattern=led_pattern",
"output.stopAllMotors|block": "stop all motors",
"motors.Motor.count|block": "`icons.motorLarge` %motor|count",
"motors.Motor.move|block": "move `icons.motorLarge` %motor|by %angle|degrees at %power|%",
"motors.Motor.power|block": "power `icons.motorLarge` %motor|to %power|%",
"motors.Motor.setBrake|block": "set `icons.motorLarge` %motor|brake %brake",
"motors.Motor.setReversed|block": "set `icons.motorLarge` %motor|reversed %reversed",
"motors.Motor.speed|block": "`icons.motorLarge` %motor|speed",
"motors.Motor.stop|block": "stop `icons.motorLarge` %motor",
"motors.Motor.tachoCount|block": "`icons.motorLarge` %motor|tacho count",
"motors.largeMotorA|block": "large A",
"motors.largeMotorB|block": "large B",
"motors.largeMotorC|block": "large C",
"motors.largeMotorD|block": "large D",
"motors.mediumMotorA|block": "medium A",
"motors.mediumMotorB|block": "medium B",
"motors.mediumMotorC|block": "medium C",
"motors.mediumMotorD|block": "medium D",
"motors.stopAllMotors|block": "stop all `icons.motorLarge`",
"motors|block": "motors",
"output|block": "output",
"screen.print|block": "print %text| at x: %x| y: %y",
"screen.setPixel|block": "set pixel %on| at x: %x| y: %y",
"screen|block": "screen",
"sensors.GyroSensor.angle|block": "`icons.gyroSensor` %sensor|angle",
"sensors.GyroSensor.rate|block": "`icons.gyroSensor` %sensor|rotation rate",
"sensors.gyro1|block": "1",
"sensors.gyro2|block": "2",
"sensors.gyro3|block": "3",
"sensors.gyro4|block": "4",
"serial|block": "serial",
"{id:category}Brick": "Brick",
"{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}Screen": "Screen",
"{id:category}Sensors": "Sensors",
"{id:category}Serial": "Serial",
"{id:group}Brick": "Brick",
"{id:group}Color Sensor": "Color Sensor",
"{id:group}Buttons": "Buttons",
"{id:group}Gyro Sensor": "Gyro Sensor",
"{id:group}Infrared Sensor": "Infrared Sensor",
"{id:group}Light": "Light",
"{id:group}Motors": "Motors",
"{id:group}Remote Infrared Beacon": "Remote Infrared Beacon",
"{id:group}Touch Sensor": "Touch Sensor",
"{id:group}Ultrasonic Sensor": "Ultrasonic Sensor"
"{id:group}Screen": "Screen"
}

View File

@ -4,34 +4,34 @@
*/
const enum LightsPattern {
//% block=Off enumval=0
//% blockIdentity=output.pattern
//% blockIdentity=brick.lightPattern
Off = 0,
//% block=Green enumval=1
//% blockIdentity=output.pattern
//% blockIdentity=brick.lightPattern
Green = 1,
//% block=Red enumval=2
//% blockIdentity=output.pattern
//% blockIdentity=brick.lightPattern
Red = 2,
//% block=Orange enumval=3
//% blockIdentity=output.pattern
//% blockIdentity=brick.lightPattern
Orange = 3,
//% block="Flashing Green" enumval=4
//% blockIdentity=output.pattern
//% blockIdentity=brick.lightPattern
GreenFlash = 4,
//% block="Flashing Red" enumval=5
//% blockIdentity=output.pattern
//% blockIdentity=brick.lightPattern
RedFlash = 5,
//% block="Flashing Orange" enumval=6
//% blockIdentity=output.pattern
//% blockIdentity=brick.lightPattern
OrangeFlash = 6,
//% block="Pulsing Green" enumval=7
//% blockIdentity=output.pattern
//% blockIdentity=brick.lightPattern
GreenPulse = 7,
//% block="Pulsing Red" enumval=8
//% blockIdentity=output.pattern
//% blockIdentity=brick.lightPattern
RedPulse = 8,
//% block="Pulsing Orange" enumval=9
//% blockIdentity=output.pattern
//% blockIdentity=brick.lightPattern
OrangePulse = 9,
}
@ -47,7 +47,7 @@ const enum ButtonEvent {
Down = 4,
}
namespace input {
namespace brick {
/**
* Generic button class, for device buttons and sensors.
*/
@ -69,13 +69,14 @@ namespace input {
if (this._isPressed == curr) return
this._isPressed = curr
if (curr) {
this._wasPressed = true;
this.downTime = control.millis()
control.raiseEvent(this._id, ButtonEvent.Down)
} else {
control.raiseEvent(this._id, ButtonEvent.Up)
let delta = control.millis() - this.downTime
control.raiseEvent(this._id, ButtonEvent.Click)
//control.raiseEvent(this._id, delta > 500 ? ButtonEvent.LongClick : ButtonEvent.Click)
const delta = control.millis() - this.downTime;
if (delta < 500)
control.raiseEvent(this._id, ButtonEvent.Click)
}
}
@ -84,12 +85,12 @@ namespace input {
* @param button the button to query the request
*/
//% help=input/button/is-pressed
//% block="%button|is pressed"
//% block="`icons.brickButtons` %button|is pressed"
//% blockId=buttonIsPressed
//% parts="brick"
//% blockNamespace=input
//% blockNamespace=brick
//% weight=81 blockGap=8
//% group="Brick"
//% group="Buttons"
isPressed() {
return this._isPressed
}
@ -99,12 +100,12 @@ namespace input {
* @param button the button to query the request
*/
//% help=input/button/was-pressed
//% block="%button|was pressed"
//% block="`icons.brickButtons` %button|was pressed"
//% blockId=buttonWasPressed
//% parts="brick"
//% blockNamespace=input
//% blockNamespace=brick
//% weight=80 blockGap=8
//% group="Brick"
//% group="Buttons"
wasPressed() {
const r = this._wasPressed
this._wasPressed = false
@ -118,18 +119,32 @@ namespace input {
* @param body code to run when the event is raised
*/
//% help=input/button/on-event
//% blockId=buttonEvent block="on %button|%event"
//% blockId=buttonEvent block="on `icons.brickButtons` %button|%event"
//% parts="brick"
//% blockNamespace=input
//% blockNamespace=brick
//% weight=99 blockGap=8
//% group="Brick"
//% group="Buttons"
onEvent(ev: ButtonEvent, body: () => void) {
control.onEvent(this._id, ev, body)
}
/**
* Waits until the event is raised
* @param ev the event to wait for
*/
//% help=input/button/wait-until
//% blockId=buttonWaitUntil block="wait until `icons.brickButtons` %button|%event"
//% parts="brick"
//% blockNamespace=brick
//% weight=98 blockGap=8
//% group="Buttons"
waitUntil(ev: ButtonEvent) {
control.waitForEvent(this._id, ev);
}
}
}
namespace input {
namespace brick {
let btnsMM: MMap
let buttons: DevButton[]
@ -155,7 +170,7 @@ namespace input {
btnsMM = control.mmap("/dev/lms_ui", DAL.NUM_BUTTONS, 0)
if (!btnsMM) control.fail("no buttons?")
buttons = []
input.internal.unsafePollForChanges(50, readButtons, (prev, curr) => {
sensors.internal.unsafePollForChanges(50, readButtons, (prev, curr) => {
if (curr & DAL.BUTTON_ID_ESCAPE)
control.reset()
for (let b of buttons)
@ -180,31 +195,31 @@ namespace input {
/**
* Enter button on the EV3 Brick.
*/
//% whenUsed block="brick button enter" weight=95 fixedInstance
//% whenUsed block="enter" weight=95 fixedInstance
export const buttonEnter: Button = new DevButton(DAL.BUTTON_ID_ENTER)
/**
* Left button on the EV3 Brick.
*/
//% whenUsed block="brick button left" weight=95 fixedInstance
//% whenUsed block="left" weight=95 fixedInstance
export const buttonLeft: Button = new DevButton(DAL.BUTTON_ID_LEFT)
/**
* Right button on the EV3 Brick.
*/
//% whenUsed block="brick button right" weight=94 fixedInstance
//% whenUsed block="right" weight=94 fixedInstance
export const buttonRight: Button = new DevButton(DAL.BUTTON_ID_RIGHT)
/**
* Up button on the EV3 Brick.
*/
//% whenUsed block="brick button up" weight=95 fixedInstance
//% whenUsed block="up" weight=95 fixedInstance
export const buttonUp: Button = new DevButton(DAL.BUTTON_ID_UP)
/**
* Down button on the EV3 Brick.
*/
//% whenUsed block="brick button down" weight=95 fixedInstance
//% whenUsed block="down" weight=95 fixedInstance
export const buttonDown: Button = new DevButton(DAL.BUTTON_ID_DOWN)
}
@ -215,7 +230,7 @@ namespace control {
*/
export function deviceFirmwareVersion(): string {
let buf = output.createBuffer(6)
input.internal.getBtnsMM().read(buf)
brick.internal.getBtnsMM().read(buf)
let r = ""
for (let i = 0; i < buf.length; ++i) {
let c = buf[i]
@ -226,22 +241,22 @@ namespace control {
}
}
namespace output {
namespace brick {
let currPattern: LightsPattern
/**
* Set lights.
* @param pattern the lights pattern to use.
*/
//% blockId=setLights block="set status light %pattern=led_pattern"
//% weight=100 group="Brick"
//% blockId=setLights block="set `icons.brickButtons` to %pattern=led_pattern"
//% weight=100 group="Light"
export function setStatusLight(pattern: number): void {
if (currPattern === pattern)
return
currPattern = pattern
let cmd = output.createBuffer(2)
cmd[0] = pattern + 48
input.internal.getBtnsMM().write(cmd)
brick.internal.getBtnsMM().write(cmd)
}
@ -250,9 +265,9 @@ namespace output {
* @param pattern the lights pattern to use. eg: LightsPattern.Green
*/
//% 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
export function pattern(pattern: LightsPattern): number {
export function lightPattern(pattern: LightsPattern): number {
return pattern;
}
}

View File

@ -1,135 +0,0 @@
const enum ColorSensorMode {
None = -1,
Reflect = 0,
Ambient = 1,
Color = 2,
RefRaw = 3,
RgbRaw = 4,
ColorCal = 5,
}
const enum ColorSensorColor {
//% block="none"
None,
//% block="black"
Black,
//% block="blue"
Blue,
//% block="green"
Green,
//% block="yellow"
Yellow,
//% block="red"
Red,
//% block="white"
White,
//% block="brown"
Brown,
}
namespace input {
//% fixedInstances
export class ColorSensor extends internal.UartSensor {
constructor(port: number) {
super(port)
}
_deviceType() {
return DAL.DEVICE_TYPE_COLOR
}
setMode(m: ColorSensorMode) {
this._setMode(m)
}
_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=input
//% 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=input
//% weight=65 blockGap=8
//% group="Color Sensor"
ambientLight() {
this.setMode(ColorSensorMode.Ambient)
return this.getNumber(NumberFormat.UInt8LE, 0)
}
/**
* 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=input
//% weight=64 blockGap=8
//% group="Color Sensor"
reflectedLight(): number {
this.setMode(ColorSensorMode.Reflect)
return this.getNumber(NumberFormat.UInt8LE, 0)
}
/**
* 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=input
//% weight=66 blockGap=8
//% group="Color Sensor"
color(): ColorSensorColor {
this.setMode(ColorSensorMode.Color)
return this.getNumber(NumberFormat.UInt8LE, 0)
}
}
//% whenUsed block="color sensor 3" weight=95 fixedInstance
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)
}

View File

@ -10,7 +10,7 @@ namespace control {
this._id = id
}
getId() {
id() {
return this._id;
}
}

View File

@ -4,7 +4,7 @@ const enum GyroSensorMode {
Rate = 1,
}
namespace input {
namespace sensors {
//% fixedInstances
export class GyroSensor extends internal.UartSensor {
constructor(port: number) {
@ -24,10 +24,10 @@ namespace input {
* @param sensor the gyroscope to query the request
*/
//% help=input/gyro/angle
//% block="%sensor|angle"
//% block="`icons.gyroSensor` %sensor|angle"
//% blockId=gyroGetAngle
//% parts="gyroscope"
//% blockNamespace=input
//% blockNamespace=sensors
//% weight=65 blockGap=8
//% group="Gyro Sensor"
angle() {
@ -40,10 +40,10 @@ namespace input {
* @param sensor the gyroscope to query the request
*/
//% help=input/gyro/rate
//% block="%sensor|rotation rate"
//% block="`icons.gyroSensor` %sensor|rotation rate"
//% blockId=gyroGetRate
//% parts="gyroscope"
//% blockNamespace=input
//% blockNamespace=sensors
//% weight=65 blockGap=8
//% group="Gyro Sensor"
rate() {
@ -51,16 +51,16 @@ namespace input {
return this.getNumber(NumberFormat.Int16LE, 0)
}
}
//% fixedInstance whenUsed block="gyro sensor 2"
export const gyro2: GyroSensor = new GyroSensor(2)
//% fixedInstance whenUsed block="gyro sensor 1"
//% fixedInstance whenUsed block="1"
export const gyro1: GyroSensor = new GyroSensor(1)
//% fixedInstance whenUsed block="gyro sensor 3"
//% fixedInstance whenUsed block="2" weight=95
export const gyro2: GyroSensor = new GyroSensor(2)
//% fixedInstance whenUsed block="3"
export const gyro3: GyroSensor = new GyroSensor(3)
//% fixedInstance whenUsed block="gyro sensor 4"
//% fixedInstance whenUsed block="4"
export const gyro4: GyroSensor = new GyroSensor(4)
}

33
libs/core/icons.jres Normal file

File diff suppressed because one or more lines are too long

136
libs/core/images.jres Normal file
View 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
View 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``);
}

View File

@ -1,4 +1,4 @@
namespace input.internal {
namespace sensors.internal {
//% shim=pxt::unsafePollForChanges
export function unsafePollForChanges(
periodMs: number,
@ -132,26 +132,26 @@ namespace input.internal {
}
export class Sensor extends control.Component {
protected port: number // this is 0-based
protected _port: number // this is 0-based
constructor(port_: number) {
super()
if (!(1 <= port_ && port_ <= DAL.NUM_INPUTS))
control.panic(120)
this.port = port_ - 1
this._port = port_ - 1
init()
sensorInfos[this.port].sensors.push(this)
sensorInfos[this._port].sensors.push(this)
}
_activated() { }
// 1-based
getPort() {
return this.port + 1
port() {
return this._port + 1
}
isActive() {
return sensorInfos[this.port].sensor == this
return sensorInfos[this._port].sensor == this
}
_query() {
@ -173,11 +173,85 @@ namespace input.internal {
_readPin6() {
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)
}
}
export enum ThresholdState {
Normal = 1,
High = 2,
Low = 3,
}
export class ThresholdDetector {
public id: number;
private min: number;
private max: number;
private lowThreshold: number;
private highThreshold: number;
private level: number;
private state: ThresholdState;
constructor(id: number, min = 0, max = 100, lowThreshold = 20, highThreshold = 80) {
this.id = id;
this.min = min;
this.max = max;
this.lowThreshold = lowThreshold;
this.highThreshold = highThreshold;
this.level = Math.ceil((max - min) / 2);
this.state = ThresholdState.Normal;
}
public setLevel(level: number) {
this.level = this.clampValue(level);
if (this.level >= this.highThreshold) {
this.setState(ThresholdState.High);
}
else if (this.level <= this.lowThreshold) {
this.setState(ThresholdState.Low);
}
else {
this.setState(ThresholdState.Normal);
}
}
public setLowThreshold(value: number) {
this.lowThreshold = this.clampValue(value);
this.highThreshold = Math.max(this.lowThreshold + 1, this.highThreshold);
}
public setHighThreshold(value: number) {
this.highThreshold = this.clampValue(value);
this.lowThreshold = Math.min(this.highThreshold - 1, this.lowThreshold);
}
private clampValue(value: number) {
if (value < this.min) {
return this.min;
}
else if (value > this.max) {
return this.max;
}
return value;
}
private setState(state: ThresholdState) {
if (this.state == state) return;
this.state = state;
switch (state) {
case ThresholdState.High:
control.raiseEvent(this.id, ThresholdState.High);
break;
case ThresholdState.Low:
control.raiseEvent(this.id, ThresholdState.Low);
break;
case ThresholdState.Normal:
break;
}
}
}
export class UartSensor extends Sensor {
protected mode: number // the mode user asked for
@ -202,18 +276,18 @@ namespace input.internal {
if (!this.isActive()) return
if (this.realmode != this.mode) {
this.realmode = v
setUartMode(this.port, v)
setUartMode(this._port, v)
}
}
getBytes(): Buffer {
return getUartBytes(this.isActive() ? this.port : -1)
return getUartBytes(this.isActive() ? this._port : -1)
}
getNumber(fmt: NumberFormat, off: number) {
if (!this.isActive())
return 0
return getUartNumber(fmt, off, this.port)
return getUartNumber(fmt, off, this._port)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -6,7 +6,6 @@
#include <time.h>
#include <cstdarg>
#include <pthread.h>
#include <assert.h>
#include <unistd.h>
#include <dirent.h>
#include <signal.h>
@ -15,6 +14,8 @@
#include <errno.h>
#include <fcntl.h>
#define THREAD_DBG(...)
void *operator new(size_t size) {
return malloc(size);
}
@ -229,6 +230,8 @@ static void runAct(Thread *thr) {
disposeThread(thr);
}
static void mainThread(Thread *) {}
void setupThread(Action a, TValue arg = 0, void (*runner)(Thread *) = NULL, TValue d0 = 0,
TValue d1 = 0) {
if (runner == NULL)
@ -242,8 +245,13 @@ void setupThread(Action a, TValue arg = 0, void (*runner)(Thread *) = NULL, TVal
thr->data0 = incr(d0);
thr->data1 = incr(d1);
pthread_cond_init(&thr->waitCond, NULL);
pthread_create(&thr->pid, NULL, (void *(*)(void *))runner, thr);
pthread_detach(thr->pid);
if (runner == mainThread) {
thr->pid = pthread_self();
} else {
pthread_create(&thr->pid, NULL, (void *(*)(void *))runner, thr);
THREAD_DBG("setup thread: %p (pid %p)", thr, thr->pid);
pthread_detach(thr->pid);
}
}
void runInBackground(Action a) {
@ -263,21 +271,26 @@ void runForever(Action a) {
}
void waitForEvent(int source, int value) {
THREAD_DBG("waitForEv: %d %d", source, value);
auto self = pthread_self();
for (auto t = allThreads; t; t = t->next) {
THREAD_DBG("t: %p", t);
if (t->pid == self) {
pthread_mutex_lock(&eventMutex);
t->waitSource = source;
t->waitValue = value;
stopUser();
// spourious wake ups may occur they say
while (t->waitSource) {
pthread_cond_wait(&t->waitCond, &eventMutex);
}
pthread_mutex_unlock(&eventMutex);
startUser();
return;
}
}
assert(0);
DMESG("current thread not registered!");
target_panic(901);
}
static void dispatchEvent(Event &e) {
@ -340,7 +353,8 @@ void raiseEvent(int id, int event) {
auto e = mkEvent(id, event);
pthread_mutex_lock(&eventMutex);
if (eventTail == NULL) {
assert(eventHead == NULL);
if (eventHead != NULL)
target_panic(902);
eventHead = eventTail = e;
} else {
eventTail->next = e;
@ -350,7 +364,8 @@ void raiseEvent(int id, int event) {
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);
}
@ -464,6 +479,7 @@ void initRuntime() {
pthread_t disp;
pthread_create(&disp, NULL, evtDispatcher, NULL);
pthread_detach(disp);
setupThread(0, 0, mainThread);
target_init();
screen_init();
startUser();
@ -505,4 +521,4 @@ void dmesg(const char *format, ...) {
fflush(dmesgFile);
fdatasync(fileno(dmesgFile));
}
}
} // namespace pxt

View File

@ -1,5 +1,9 @@
//% weight=100
namespace brick {
}
//% color="#B4009E" weight=98 icon="\uf192"
//% groups='["Brick", "Touch Sensor", "Color Sensor", "Ultrasonic Sensor", "Infrared Sensor", "Remote Infrared Beacon", "Gyro Sensor"]'
namespace input {
//% groups='["Ultrasonic Sensor", "Touch Sensor", "Color Sensor", "Infrared Sensor", "Remote Infrared Beacon", "Gyro Sensor"]'
namespace sensors {
}

View File

@ -17,7 +17,7 @@ enum OutputType {
MiniTacho = 8,
}
namespace output {
namespace motors {
let pwmMM: MMap
let motorMM: MMap
@ -53,7 +53,7 @@ namespace output {
}
function mkCmd(out: Output, cmd: number, addSize: number) {
const b = createBuffer(2 + addSize)
const b = output.createBuffer(2 + addSize)
b.setNumber(NumberFormat.UInt8LE, 0, cmd)
b.setNumber(NumberFormat.UInt8LE, 1, out)
return b
@ -66,7 +66,7 @@ namespace output {
/**
* Stops all motors
*/
//% blockId=motorStopAll block="stop all motors"
//% blockId=motorStopAll block="stop all `icons.motorLarge`"
//% weight=10 group="Motors" blockGap=8
export function stopAllMotors() {
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
@ -87,43 +87,67 @@ namespace output {
}
/**
* Power on or off the motor.
* @param motor the motor to turn on
* @param power the motor power level from ``-100`` to ``100``, eg: 50
* Sets the motor power level from ``-100`` to ``100``.
* @param motor the output connection that the motor is connected to
* @param power the power from ``100`` full forward to ``-100`` full backward, eg: 50
*/
//% blockId=outputMotorOn block="%motor|%onOrOff"
//% onOrOff.fieldEditor=toggleonoff
//% blockId=motorSetPower block="power `icons.motorLarge` %motor|to %power|%"
//% weight=99 group="Motors" blockGap=8
on(onOrOff: boolean = true) {
if (onOrOff) {
//% power.min=-100 power.max=100
power(power: number) {
power = Math.clamp(-100, 100, power >> 0);
// per LEGO: call it power, use speed
const b = mkCmd(this.port, DAL.opOutputSpeed, 1)
b.setNumber(NumberFormat.Int8LE, 2, power)
writePWM(b)
if (power) {
const b = mkCmd(this.port, DAL.opOutputStart, 0)
writePWM(b);
} else {
const b = mkCmd(this.port, DAL.opOutputStop, 1)
b.setNumber(NumberFormat.UInt8LE, 2, this.brake ? 1 : 0)
writePWM(b)
this.stop();
}
}
/**
* Sets the motor power level from ``-100`` to ``100``.
* @param motor the output connection that the motor is connected to
* @param power the desired speed to use. eg: 50
* 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=motorSetPower block="%motor|set power to %speed"
//% weight=62 group="Motors" blockGap=8
//% speed.min=-100 speed.max=100
setPower(power: number) {
const b = mkCmd(this.port, DAL.opOutputPower, 1)
b.setNumber(NumberFormat.Int8LE, 2, Math.clamp(-100, 100, power))
writePWM(b)
//% blockId=motorMove block="move `icons.motorLarge` %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 `icons.motorLarge` %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="%motor|set brake %brake"
//% blockId=outputMotorSetBrakeMode block="set `icons.motorLarge` %motor|brake %brake"
//% brake.fieldEditor=toggleonoff
//% weight=60 group="Motors" blockGap=8
setBrake(brake: boolean) {
@ -133,7 +157,7 @@ namespace output {
/**
* Reverses the motor polarity
*/
//% blockId=motorSetReversed block="%motor|set reversed %reversed"
//% blockId=motorSetReversed block="set `icons.motorLarge` %motor|reversed %reversed"
//% reversed.fieldEditor=toggleonoff
//% weight=59 group="Motors"
setReversed(reversed: boolean) {
@ -146,8 +170,8 @@ namespace output {
* Gets motor actual speed.
* @param motor the port which connects to the motor
*/
//% blockId=motorSpeed block="%motor|speed"
//% weight=50 group="Motors" blockGap=8
//% blockId=motorSpeed block="`icons.motorLarge` %motor|speed"
//% weight=72 group="Motors" blockGap=8
speed(): number {
return getMotorData(this.port).actualSpeed;
}
@ -156,8 +180,8 @@ namespace output {
* Gets motor step count.
* @param motor the port which connects to the motor
*/
//% blockId=motorCount block="%motor|count"
//% weight=49 group="Motors" blockGap=8
//% blockId=motorCount block="`icons.motorLarge` %motor|count"
//% weight=71 group="Motors" blockGap=8
count(): number {
return getMotorData(this.port).count;
}
@ -166,8 +190,8 @@ namespace output {
* Gets motor tacho count.
* @param motor the port which connects to the motor
*/
//% blockId=motorTachoCount block="%motor|tacho count"
//% weight=48 group="Motors"
//% blockId=motorTachoCount block="`icons.motorLarge` %motor|tacho count"
//% weight=70 group="Motors"
tachoCount(): number {
return getMotorData(this.port).tachoCount;
}
@ -193,28 +217,28 @@ namespace output {
}
}
//% whenUsed fixedInstance block="large motor A"
//% whenUsed fixedInstance block="large A"
export const largeMotorA = new Motor(Output.A, true);
//% whenUsed fixedInstance block="large motor B"
//% whenUsed fixedInstance block="large B"
export const largeMotorB = new Motor(Output.B, true);
//% whenUsed fixedInstance block="large motor C"
//% whenUsed fixedInstance block="large C"
export const largeMotorC = new Motor(Output.C, true);
//% whenUsed fixedInstance block="large motor D"
//% whenUsed fixedInstance block="large D"
export const largeMotorD = new Motor(Output.D, true);
//% whenUsed fixedInstance block="medium motor A"
//% whenUsed fixedInstance block="medium A"
export const mediumMotorA = new Motor(Output.A, false);
//% whenUsed fixedInstance block="medium motor B"
//% whenUsed fixedInstance block="medium B"
export const mediumMotorB = new Motor(Output.B, false);
//% whenUsed fixedInstance block="medium motor C"
//% whenUsed fixedInstance block="medium C"
export const mediumMotorC = new Motor(Output.C, false);
//% whenUsed fixedInstance block="medium motor D"
//% whenUsed fixedInstance block="medium D"
export const mediumMotorD = new Motor(Output.D, false);
function reset(out: Output) {

130
libs/core/png.cpp Normal file
View 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;
}
}

View File

@ -7,6 +7,8 @@ namespace pxt {
void raiseEvent(int id, int event);
int allocateNotifyEvent();
void sleep_core_us(uint64_t us);
void startUser();
void stopUser();
class Button;
typedef Button *Button_;
@ -27,6 +29,10 @@ class MMap : public RefObject {
extern volatile bool paniced;
// Buffer, Sound, and Image share representation.
typedef Buffer Image;
typedef Buffer Sound;
}
#define DEVICE_EVT_ANY 0

View File

@ -11,20 +11,20 @@
"mmap.cpp",
"control.cpp",
"buttons.ts",
"png.cpp",
"screen.cpp",
"screen.ts",
"output.cpp",
"output.ts",
"core.ts",
"input.ts",
"ir.ts",
"color.ts",
"gyro.ts",
"ultrasonic.ts",
"touch.ts",
"shims.d.ts",
"enums.d.ts",
"dal.d.ts",
"images.ts",
"images.jres",
"icons.jres",
"ns.ts"
],
"testFiles": [

View File

@ -130,82 +130,6 @@ void _blitLine(int xw, int y, Buffer buf, Draw 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. */
//%
void clear() {
@ -256,7 +180,7 @@ void init() {
mappedFrameBuffer = (uint8_t *)mmap(NULL, FB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
DMESG("map %p", mappedFrameBuffer);
if (mappedFrameBuffer == MAP_FAILED) {
target_panic(111);
target_panic(903);
}
clear();
@ -309,11 +233,23 @@ extern "C" void drawPanic(int code) {
updateLCD();
int fd = open("/dev/lms_ui", O_RDWR);
uint8_t cmd[] = { 48 + 5, 0 };
uint8_t cmd[] = {48 + 5, 0};
write(fd, cmd, 2);
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 {
@ -321,3 +257,105 @@ void 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);
}
}

View File

@ -1,4 +1,4 @@
namespace screen {
namespace brick {
//% shim=screen::_setPixel
function _setPixel(p0: uint32, p1: uint32, mode: Draw): void { }
@ -27,7 +27,7 @@ namespace screen {
currFont = f
}
export const heart = hex`f007 367f7f3e1c08`
export const heart = screen.imageOf(hex`f007 367f7f3e1c08`)
export function defaultFont(): Font {
return {
@ -84,8 +84,8 @@ namespace screen {
* @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="Brick" blockNamespace=output
//% blockId=screen_setpixel block="`icons.brickDisplay` 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
@ -100,8 +100,8 @@ namespace screen {
* @param x the starting position's x coordinate, eg: 0
* @param y the starting position's x coordinate, eg: 0
*/
//% blockId=screen_print block="print %text| at x: %x| y: %y"
//% weight=99 group="Brick" blockNamespace=output inlineInputMode="inline"
//% blockId=screen_print block="`icons.brickDisplay` print %text| at x: %x| y: %y"
//% weight=99 group="Screen" inlineInputMode="inline" blockGap=8
//% x.min=0 x.max=178 y.min=0 y.max=128
export function print(text: string, x: number, y: number, mode = Draw.Normal) {
x |= 0
@ -111,10 +111,11 @@ namespace screen {
let cp = 0
let byteWidth = (currFont.charWidth + 7) >> 3
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
iconBuf[0] = 0xf0
iconBuf[1] = currFont.charWidth
imgBuf[0] = 0xf0
imgBuf[1] = currFont.charWidth
let img = screen.imageOf(imgBuf)
while (cp < text.length) {
let ch = text.charCodeAt(cp++)
if (ch == 10) {
@ -123,15 +124,42 @@ namespace screen {
}
if (ch < 32) continue
let idx = (ch - currFont.firstChar) * charSize
if (idx < 0 || idx + iconBuf.length - 1 > currFont.data.length)
iconBuf.fill(0, 2)
if (idx < 0 || idx + imgBuf.length - 1 > currFont.data.length)
imgBuf.fill(0, 2)
else
iconBuf.write(2, currFont.data.slice(idx, charSize))
drawIcon(x, y, iconBuf, mode)
imgBuf.write(2, currFont.data.slice(idx, charSize))
img.draw(x, y, mode)
x += double * currFont.charWidth
}
}
/**
* Shows an image on screen
* @param image image to draw
*/
//% blockId=screen_show_image block="`icons.brickDisplay` show image %image=scren_image_picker"
//% weight=95 group="Screen" blockGap=8
export function showImage(image: Image, delay: number = 400) {
if (!image) return;
image.draw(0, 0, Draw.Normal);
delay = Math.max(0, delay);
if (delay > 0)
loops.pause(delay);
}
/**
* An image
* @param image the image
*/
//% blockId=scren_image_picker block="%image" shim=TD_ID
//% image.fieldEditor="imagedropdown"
//% image.fieldOptions.columns=6
//% block.fieldOptions.hasSearchBar=true
//% group="Screen" weight=0 blockHidden=1
export function _imagePicker(image: Image): Image {
return image;
}
export function drawRect(x: number, y: number, w: number, h: number, mode = Draw.Normal) {
x |= 0;
y |= 0;

41
libs/core/shims.d.ts vendored
View File

@ -72,17 +72,44 @@ declare namespace serial {
}
declare namespace screen {
/** Double size of an icon. */
//% shim=screen::doubleIcon
function doubleIcon(buf: Buffer): Buffer;
/** Draw an icon on the screen. */
//% shim=screen::drawIcon
function drawIcon(x: int32, y: int32, buf: Buffer, mode: Draw): void;
/** Decompresses a 1-bit gray scale PNG image to image format. */
//% shim=screen::unpackPNG
function unpackPNG(png: Buffer): Image;
}
declare namespace screen {
/** Clear screen and reset font to normal. */
//% shim=screen::clear
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 {

View File

@ -2,46 +2,46 @@ screen.clear()
screen.print("PXT!", 10, 30, Draw.Quad)
screen.drawRect(40, 40, 20, 10, Draw.Fill)
output.setStatusLight(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()
})
input.buttonLeft.onEvent(ButtonEvent.Click, () => {
sensors.buttonLeft.onEvent(ButtonEvent.Click, () => {
screen.drawRect(10, 70, 20, 10, Draw.Fill)
output.setStatusLight(LightsPattern.Red)
motors.setStatusLight(LightsPattern.Red)
screen.setFont(screen.microbitFont())
})
input.buttonRight.onEvent(ButtonEvent.Click, () => {
sensors.buttonRight.onEvent(ButtonEvent.Click, () => {
screen.print("Right!", 10, 60)
})
input.buttonDown.onEvent(ButtonEvent.Click, () => {
sensors.buttonDown.onEvent(ButtonEvent.Click, () => {
screen.print("Down! ", 10, 60)
})
input.buttonUp.onEvent(ButtonEvent.Click, () => {
sensors.buttonUp.onEvent(ButtonEvent.Click, () => {
screen.print("Up! ", 10, 60)
})
let num = 0
input.touchSensor1.onEvent(TouchSensorEvent.Bumped, () => {
sensors.touchSensor1.onEvent(TouchSensorEvent.Bumped, () => {
screen.print("Click! " + num, 10, 60)
num++
})
input.remoteButtonTopLeft.onEvent(ButtonEvent.Click, () => {
sensors.remoteButtonTopLeft.onEvent(ButtonEvent.Click, () => {
screen.print("TOPLEFT " + num, 10, 60)
num++
})
input.remoteButtonTopRight.onEvent(ButtonEvent.Down, () => {
sensors.remoteButtonTopRight.onEvent(ButtonEvent.Down, () => {
screen.print("TOPRIGH " + num, 10, 60)
num++
})

View File

@ -1,78 +0,0 @@
// keep TouchSensorEvent in sync with ButtonEvent
/**
* Touch sensor interactions
*/
const enum TouchSensorEvent {
//% block="pressed"
Pressed = 4,
//% block="bumped"
Bumped = 1,
//% block="released"
Released = 3,
}
namespace input {
//% fixedInstances
export class TouchSensor extends internal.AnalogSensor {
private button: Button;
constructor(port: number) {
super(port)
this.button = new Button();
}
_query() {
return this._readPin6() > 2500 ? 1 : 0
}
_update(prev: number, curr: number) {
this.button._update(curr > 0)
}
_deviceType() {
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=input
//% 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=input
//% weight=99 blockGap=8
//% group="Touch Sensor"
onEvent(ev: TouchSensorEvent, body: () => void) {
this.button.onEvent(<ButtonEvent><number>ev, body)
}
}
//% whenUsed block="touch sensor 1" weight=95 fixedInstance
export const touchSensor1: TouchSensor = new TouchSensor(1)
//% whenUsed block="touch sensor 2" weight=95 fixedInstance
export const touchSensor2: TouchSensor = new TouchSensor(2)
//% 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)
}

View File

@ -1,85 +0,0 @@
const enum PromixityEvent {
//% block="object near"
ObjectNear = 1,
//% block="object detected"
ObjectDetected = 2
}
namespace input {
//% fixedInstances
export class UltraSonicSensor extends internal.UartSensor {
private promixityThreshold: number;
constructor(port: number) {
super(port)
this.promixityThreshold = 10;
}
_deviceType() {
return DAL.DEVICE_TYPE_ULTRASONIC
}
_query(): number {
const d = this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff;
return d < this.promixityThreshold ? PromixityEvent.ObjectNear
: d > this.promixityThreshold + 5 ? PromixityEvent.ObjectDetected
: 0;
}
_update(prev: number, curr: number) {
if (curr)
control.raiseEvent(this._id, curr);
}
/**
* Registers code to run when the given color is close
* @param handler the code to run when detected
*/
//% help=input/ultrasonic/on-object-near
//% block="on %sensor|object near"
//% blockId=ultrasonicOnObjectClose
//% parts="infraredsensor"
//% blockNamespace=input
//% weight=100 blockGap=8
//% group="Ultrasonic Sensor"
onObjectNear(handler: () => void) {
control.onEvent(this._id, PromixityEvent.ObjectNear, handler);
if (this.distance() == PromixityEvent.ObjectNear)
control.runInBackground(handler);
}
setObjectNearThreshold(distance: number) {
this.promixityThreshold = Math.max(1, Math.min(250, distance));
}
/**
* 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=input
//% weight=65 blockGap=8
//% group="Ultrasonic Sensor"
distance() {
// it supposedly also has an inch mode, but we stick to mm
this._setMode(0)
return this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff;
}
}
//% fixedInstance whenUsed block="ultrasonic sensor 4"
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)
}

View File

@ -1,11 +1,16 @@
//% color="#D42878"
namespace input {
//% color="#00A5C8" weight=100
//% groups='["Light", "Buttons", "Screen"]'
namespace brick {
}
//% color="#D42878" weight=95
namespace sensors {
}
//% color="#8AC044" weight=90 icon="\uf185"
//% groups='["Motors", "Brick"]'
namespace output {
namespace motors {
}
//% color="#DF5014" weight=80

View File

@ -8,7 +8,11 @@
"dependencies": {
"base": "file:../base",
"core": "file:../core",
"music": "file:../music"
"music": "file:../music",
"color-sensor": "file:../color-sensor",
"touch-sensor": "file:../touch-sensor",
"ultrasonic-sensor": "file:../ultrasonic-sensor",
"infrared-sensor": "file:../infrared-sensor"
},
"public": true
}

View File

@ -0,0 +1,3 @@
# Infrared sensor
The library to interact with the Infrared Sensor.

View File

@ -0,0 +1,16 @@
{
"sensors.InfraredSensor.onEvent": "Registers code to run when an object is getting near.",
"sensors.InfraredSensor.onEvent|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.waitUntil": "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.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."
}

View File

@ -0,0 +1,24 @@
{
"InfraredSensorEvent.ObjectDetected|block": "object detected",
"InfraredSensorEvent.ObjectNear|block": "object near",
"sensors.InfraredSensor.onEvent|block": "on `icons.infraredSensor` %sensor|%event",
"sensors.InfraredSensor.proximity|block": "`icons.infraredSensor` %infrared|proximity",
"sensors.InfraredSensor.remoteCommand|block": "`icons.infraredSensor` %infrared|remote command",
"sensors.InfraredSensor.waitUntil|block": "wait until `icons.infraredSensor` %sensor| %event",
"sensors.RemoteInfraredBeaconButton.isPressed|block": "`icons.infraredSensor` %button|is pressed",
"sensors.RemoteInfraredBeaconButton.onEvent|block": "on `icons.infraredSensor` %button|%event",
"sensors.RemoteInfraredBeaconButton.wasPressed|block": "`icons.infraredSensor` %button|was pressed",
"sensors.infraredSensor1|block": "1",
"sensors.infraredSensor2|block": "2",
"sensors.infraredSensor3|block": "3",
"sensors.infraredSensor4|block": "4",
"sensors.remoteButtonBottomLeft|block": "bottom-left",
"sensors.remoteButtonBottomRight|block": "bottom-right",
"sensors.remoteButtonCenter|block": "center",
"sensors.remoteButtonTopLeft|block": "top-left",
"sensors.remoteButtonTopRight|block": "top-right",
"sensors|block": "sensors",
"{id:category}Sensors": "Sensors",
"{id:group}Infrared Sensor": "Infrared Sensor",
"{id:group}Remote Infrared Beacon": "Remote Infrared Beacon"
}

View File

@ -21,7 +21,14 @@ const enum IrRemoteButton {
BottomRight = 0x10,
}
namespace input {
const enum InfraredSensorEvent {
//% block="object near"
ObjectNear = 1,
//% block="object detected"
ObjectDetected = 2
}
namespace sensors {
function mapButton(v: number) {
switch (v) {
case 0: return IrRemoteButton.None
@ -50,7 +57,7 @@ namespace input {
if (buttons == null) {
buttons = []
for (let i = 0; i < 5; ++i) {
buttons.push(new RemoteInfraredBeaconButton(new Button()))
buttons.push(new RemoteInfraredBeaconButton(new brick.Button()))
}
// make sure sensors are up
@ -71,8 +78,8 @@ namespace input {
//% fixedInstances
export class RemoteInfraredBeaconButton extends control.Component {
private button: Button;
constructor(button: Button) {
private button: brick.Button;
constructor(button: brick.Button) {
super();
this.button = button;
}
@ -86,10 +93,10 @@ namespace input {
* @param button the remote button to query the request
*/
//% help=input/remote-infrared-beacon/is-pressed
//% block="%button|is pressed"
//% block="`icons.infraredSensor` %button|is pressed"
//% blockId=remoteButtonIsPressed
//% parts="remote"
//% blockNamespace=input
//% blockNamespace=sensors
//% weight=81 blockGap=8
//% group="Remote Infrared Beacon"
isPressed() {
@ -101,10 +108,10 @@ namespace input {
* @param button the remote button to query the request
*/
//% help=input/remote-infrared-beacon/was-pressed
//% block="%button|was pressed"
//% block="`icons.infraredSensor` %button|was pressed"
//% blockId=remotebuttonWasPressed
//% parts="remote"
//% blockNamespace=input
//% blockNamespace=sensors
//% weight=80 blockGap=8
//% group="Remote Infrared Beacon"
wasPressed() {
@ -118,25 +125,25 @@ namespace input {
* @param body code to run when the event is raised
*/
//% help=input/remote-infrared-beacon/on-event
//% blockId=remotebuttonEvent block="on %button|%event"
//% blockId=remotebuttonEvent block="on `icons.infraredSensor` %button|%event"
//% parts="remote"
//% blockNamespace=input
//% 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;
private proximityThreshold: sensors.internal.ThresholdDetector;
constructor(port: number) {
super(port)
this.channel = IrRemoteChannel.Ch0
this.proximityThreshold = 10;
this.proximityThreshold = new sensors.internal.ThresholdDetector(this._id, 0, 100, 10, 90);
irButton(0) // make sure buttons array is initalized
// and set the mode, as otherwise button events won't work
@ -147,10 +154,7 @@ namespace input {
if (this.mode == IrSensorMode.RemoteControl)
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 ? PromixityEvent.ObjectNear
: d > this.proximityThreshold + 5 ? PromixityEvent.ObjectDetected
: 0;
return this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff;
}
return 0
}
@ -161,9 +165,8 @@ namespace input {
let v = !!(curr & (1 << i))
buttons[i]._update(v)
}
} else {
if (curr)
control.raiseEvent(this._id, curr);
} else if (this.mode == IrSensorMode.Proximity) {
this.proximityThreshold.setLevel(curr);
}
}
@ -174,7 +177,7 @@ namespace input {
setRemoteChannel(c: IrRemoteChannel) {
c = Math.clamp(0, 3, c | 0)
this.channel = c
this._setMode(IrSensorMode.RemoteControl)
this.setMode(IrSensorMode.RemoteControl)
}
setMode(m: IrSensorMode) {
@ -185,35 +188,43 @@ namespace input {
* Registers code to run when an object is getting near.
* @param handler the code to run when detected
*/
//% help=input/infrared/on-object-near
//% block="on %sensor|object near"
//% blockId=infraredOnObjectNear
//% help=input/infrared/on
//% block="on `icons.infraredSensor` %sensor|%event"
//% blockId=infraredOn
//% parts="infraredsensor"
//% blockNamespace=input
//% blockNamespace=sensors
//% weight=100 blockGap=8
//% group="Infrared Sensor"
onObjectNear(handler: () => void) {
control.onEvent(this._id, PromixityEvent.ObjectNear, handler);
if (this.proximity() == PromixityEvent.ObjectNear)
control.runInBackground(handler);
onEvent(event: InfraredSensorEvent, handler: () => void) {
control.onEvent(this._id, event, handler);
}
setObjectNearThreshold(distance: number) {
this.proximityThreshold = Math.max(1, Math.min(95, distance));
/**
* Waits for the event to occur
*/
//% help=input/ultrasonic/wait
//% block="wait until `icons.infraredSensor` %sensor| %event"
//% blockId=infraredwait
//% parts="infraredsensor"
//% blockNamespace=sensors
//% weight=99 blockGap=8
//% group="Infrared Sensor"
waitUntil(event: InfraredSensorEvent) {
control.waitForEvent(this._id, event);
}
/**
* 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"
//% block="`icons.infraredSensor` %infrared|proximity"
//% blockId=infraredGetProximity
//% parts="infrared"
//% blockNamespace=input
//% blockNamespace=sensors
//% weight=65 blockGap=8
//% group="Infrared Sensor"
proximity() {
proximity(): number {
this._setMode(IrSensorMode.Proximity)
return this.getNumber(NumberFormat.UInt8LE, 0)
}
@ -223,13 +234,13 @@ namespace input {
* @param ir the infrared sensor
*/
//% help=input/infrared/remote-command
//% block="%infrared|remote command"
//% block="`icons.infraredSensor` %infrared|remote command"
//% blockId=infraredGetRemoteCommand
//% parts="infrared"
//% blockNamespace=input
//% blockNamespace=sensors
//% weight=65 blockGap=8
//% group="Infrared Sensor"
remoteCommand() {
remoteCommand(): number {
this._setMode(IrSensorMode.RemoteControl)
return this.getNumber(NumberFormat.UInt8LE, this.channel)
}
@ -241,46 +252,46 @@ namespace input {
}
}
//% fixedInstance whenUsed block="infrared sensor 1"
//% fixedInstance whenUsed block="1"
export const infraredSensor1: InfraredSensor = new InfraredSensor(1)
//% fixedInstance whenUsed block="infrared sensor 2"
//% fixedInstance whenUsed block="2"
export const infraredSensor2: InfraredSensor = new InfraredSensor(2)
//% fixedInstance whenUsed block="infrared sensor 3"
//% fixedInstance whenUsed block="3"
export const infraredSensor3: InfraredSensor = new InfraredSensor(3)
//% fixedInstance whenUsed block="infrared sensor 4"
//% fixedInstance whenUsed block="4"
export const infraredSensor4: InfraredSensor = new InfraredSensor(4)
/**
* Remote beacon (center) button.
*/
//% whenUsed block="remote button center" weight=95 fixedInstance
//% whenUsed block="center" weight=95 fixedInstance
export const remoteButtonCenter = irButton(IrRemoteButton.CenterBeacon)
/**
* Remote top-left button.
*/
//% whenUsed block="remote button top-left" weight=95 fixedInstance
//% whenUsed block="top-left" weight=95 fixedInstance
export const remoteButtonTopLeft = irButton(IrRemoteButton.TopLeft)
/**
* Remote top-right button.
*/
//% whenUsed block="remote button top-right" weight=95 fixedInstance
//% whenUsed block="top-right" weight=95 fixedInstance
export const remoteButtonTopRight = irButton(IrRemoteButton.TopRight)
/**
* Remote bottom-left button.
*/
//% whenUsed block="remote button bottom-left" weight=95 fixedInstance
//% whenUsed block="bottom-left" weight=95 fixedInstance
export const remoteButtonBottomLeft = irButton(IrRemoteButton.BottomLeft)
/**
* Remote bottom-right button.
*/
//% whenUsed block="remote button bottom-right" weight=95 fixedInstance
//% whenUsed block="bottom-right" weight=95 fixedInstance
export const remoteButtonBottomRight = irButton(IrRemoteButton.BottomRight)
}

View File

@ -0,0 +1,15 @@
{
"name": "infrared-sensor",
"description": "Infrared Sensor support",
"files": [
"README.md",
"ir.ts"
],
"testFiles": [
"test.ts"
],
"public": true,
"dependencies": {
"core": "file:../core"
}
}

View File

View File

@ -1,12 +1,19 @@
{
"Sound.buffer": "Returns the underlaying Buffer object.",
"Sound.play": "Play sound.",
"music": "Generation of music tones.",
"music._soundPicker": "A sound",
"music._soundPicker|param|sound": "the sound",
"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.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.fromWAV": "Makes a sound bound to a buffer in WAV format.",
"music.noteFrequency": "Get the frequency of a note.",
"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.playSoundEffect": "Plays a sound",
"music.playSoundEffect|param|sound": "the sound to play",
"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: music.sounds(Sounds.PowerUp)",
"music.playSound|param|sound": "the melody to play, eg: music.sounds(Sounds.PowerUp)",

View File

@ -28,9 +28,11 @@
"Sounds.PowerUp|block": "power up",
"Sounds.Siren|block": "siren",
"Sounds.Wawawawaa|block": "wawawawaa",
"music._soundPicker|block": "%sound",
"music.beat|block": "%fraction|beat",
"music.changeTempoBy|block": "change tempo by %value|(bpm)",
"music.noteFrequency|block": "%note",
"music.playSoundEffect|block": "play %sound",
"music.playSoundUntilDone|block": "play sound %sound=music_sounds|until done",
"music.playSound|block": "play sound %sound=music_sounds",
"music.playTone|block": "play tone|at %note=device_note|for %duration=device_beat",
@ -42,5 +44,134 @@
"music.stopAllSounds|block": "stop all sounds",
"music.tempo|block": "tempo (bpm)",
"music|block": "music",
"{id:category}Music": "Music"
"sounds.animalsCatPurr|block": "Animals cat purr",
"sounds.animalsDogBark1|block": "Animals dog bark 1",
"sounds.animalsDogBark2|block": "Animals dog bark 2",
"sounds.animalsDogGrowl|block": "Animals dog growl",
"sounds.animalsDogSniff|block": "Animals dog sniff",
"sounds.animalsDogWhine|block": "Animals dog whine",
"sounds.animalsElephantCall|block": "Animals elephant call",
"sounds.animalsInsectBuzz1|block": "Animals insect buzz1",
"sounds.animalsInsectBuzz2|block": "Animals insect buzz2",
"sounds.animalsInsectChirp|block": "Animals insect chirp",
"sounds.animalsSnakeHiss|block": "Animals snake hiss",
"sounds.animalsSnakeRattle|block": "Animals snake rattle",
"sounds.animalsTRexRoar|block": "Animals trex roar",
"sounds.colorsBlack|block": "Colors black",
"sounds.colorsBlue|block": "Colors blue",
"sounds.colorsBrown|block": "Colors brown",
"sounds.colorsGreen|block": "Colors green",
"sounds.colorsRed|block": "Colors red",
"sounds.colorsWhite|block": "Colors white",
"sounds.colorsYellow|block": "Colors yellow",
"sounds.communicationBravo|block": "Communication bravo",
"sounds.communicationEv3|block": "Communication ev3",
"sounds.communicationFantastic|block": "Communication fantastic",
"sounds.communicationGameOver|block": "Communication game over",
"sounds.communicationGoodJob|block": "communicationGoodJob",
"sounds.communicationGoodbye|block": "communication goodbye",
"sounds.communicationGood|block": "communication good",
"sounds.communicationGo|block": "Communication go",
"sounds.communicationHello|block": "communication hello",
"sounds.communicationHi|block": "communication hi",
"sounds.communicationLego|block": "communication lego",
"sounds.communicationMindstorms|block": "communication mindstorms",
"sounds.communicationMorning|block": "communication morning",
"sounds.communicationNo|block": "communication no",
"sounds.communicationOkay|block": "communication okay",
"sounds.communicationOkeyDokey|block": "communication okey dokey",
"sounds.communicationSorry|block": "communication sorry",
"sounds.communicationThankYou|block": "communication thank you",
"sounds.communicationYes|block": "communication yes",
"sounds.expressionsBoing|block": "expressions boing",
"sounds.expressionsBoo|block": "expressions boo",
"sounds.expressionsCheering|block": "expressions cheering",
"sounds.expressionsCrunching|block": "expressions crunching",
"sounds.expressionsCrying|block": "expressions crying",
"sounds.expressionsFanfare|block": "expressions fanfare",
"sounds.expressionsKungFu|block": "expressions kung fu",
"sounds.expressionsLaughing1|block": "expressions laughing1",
"sounds.expressionsLaughing2|block": "expressions laughing2",
"sounds.expressionsMagicWand|block": "expressions magic wand",
"sounds.expressionsOuch|block": "expressions ouch",
"sounds.expressionsShouting|block": "expressions shouting",
"sounds.expressionsSmack|block": "expressions smack",
"sounds.expressionsSneezing|block": "expressions sneezing",
"sounds.expressionsSnoring|block": "expressions snoring",
"sounds.expressionsUhOh|block": "expressions uh oh",
"sounds.informationActivate|block": "information activate",
"sounds.informationAnalyze|block": "information analyze",
"sounds.informationBackwards|block": "information backwards",
"sounds.informationColor|block": "information color",
"sounds.informationDetected|block": "information detected",
"sounds.informationDown|block": "information down",
"sounds.informationErrorAlarm|block": "information error alarm",
"sounds.informationError|block": "information error",
"sounds.informationFlashing|block": "information flashing",
"sounds.informationForward|block": "information forward",
"sounds.informationLeft|block": "information left",
"sounds.informationObject|block": "information object",
"sounds.informationRight|block": "information right",
"sounds.informationSearching|block": "information searching",
"sounds.informationStart|block": "information start",
"sounds.informationStop|block": "information stop",
"sounds.informationTouch|block": "information touch",
"sounds.informationTurn|block": "information turn",
"sounds.informationUp|block": "information up",
"sounds.mechanicalAirRelease|block": "mechanical air release",
"sounds.mechanicalAirbrake|block": "mechanical airbrake",
"sounds.mechanicalBackingAlert|block": "mechanical backing alert",
"sounds.mechanicalBlip1|block": "mechanical blip1",
"sounds.mechanicalBlip2|block": "mechanical blip2",
"sounds.mechanicalBlip3|block": "mechanical blip3",
"sounds.mechanicalBlip4|block": "mechanical blip4",
"sounds.mechanicalHorn1|block": "mechanical horn1",
"sounds.mechanicalHorn2|block": "mechanical horn2",
"sounds.mechanicalLaser|block": "mechanical laser",
"sounds.mechanicalMotorIdle|block": "mechanical motor idle",
"sounds.mechanicalMotorStart|block": "mechanical motor start",
"sounds.mechanicalMotorStop|block": "mechanical motor stop",
"sounds.mechanicalRatchet|block": "mechanical ratchet",
"sounds.mechanicalSonar|block": "\"mechanical sonar\"",
"sounds.mechanicalTickTack|block": "mechanical tick tack",
"sounds.mechanicalWalk|block": "mechanical walk",
"sounds.movementsArm1|block": "movements arm1",
"sounds.movementsArm2|block": "movements arm2",
"sounds.movementsArm3|block": "movements arm3",
"sounds.movementsArm4|block": "movements arm4",
"sounds.movementsDropLoad|block": "movements drop load",
"sounds.movementsLiftLoad|block": "movements lift load",
"sounds.movementsServo1|block": "movements servo1",
"sounds.movementsServo2|block": "movements servo2",
"sounds.movementsServo3|block": "movements servo3",
"sounds.movementsServo4|block": "movements servo4",
"sounds.movementsSlideLoad|block": "movements slide load",
"sounds.movementsSnap|block": "movements snap",
"sounds.movementsSpeedDown|block": "movements speed down",
"sounds.movementsSpeedIdle|block": "movements speed idle",
"sounds.movementsSpeedUp|block": "movements speed up",
"sounds.movementsSpeeding|block": "movements speeding",
"sounds.numbersEight|block": "numbers eight",
"sounds.numbersFive|block": "numbers five",
"sounds.numbersFour|block": "numbers four",
"sounds.numbersNine|block": "numbers nine",
"sounds.numbersOne|block": "numbers one",
"sounds.numbersSeven|block": "numbers seven",
"sounds.numbersSix|block": "numbers six",
"sounds.numbersTen|block": "numbers ten",
"sounds.numbersThree|block": "numbers three",
"sounds.numbersTwo|block": "numbers two",
"sounds.numbersZero|block": "numbers zero",
"sounds.systemClick|block": "system click",
"sounds.systemConfirm|block": "system confirm",
"sounds.systemConnect|block": "system connect",
"sounds.systemDownload|block": "system download",
"sounds.systemGeneralAlert|block": "system general alert",
"sounds.systemOverpower|block": "system overpower",
"sounds.systemPowerDown|block": "system power down",
"sounds.systemReady|block": "system ready",
"sounds.systemStartUp|block": "S",
"{id:category}Music": "Music",
"{id:category}Sound": "Sound",
"{id:category}Sounds": "Sounds"
}

View File

@ -5,18 +5,22 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#define NOTE_PAUSE 20
namespace music {
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);
write(fd, data, size);
int res = write(fd, data, size);
close(fd);
return res;
}
/**
@ -51,8 +55,7 @@ static void _stopSound() {
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;
cmd.cmd = SOUND_CMD_TONE;
cmd.vol = volume;
@ -62,6 +65,75 @@ static void _playTone(uint16_t frequency, uint16_t duration, uint8_t volume)
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.
* @param frequency pitch of the tone to play in Hertz (Hz)
@ -90,4 +162,28 @@ void playTone(int frequency, int ms) {
}
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);
}
}

18
libs/music/piano.ts Normal file
View File

@ -0,0 +1,18 @@
namespace music {
/**
* Get the frequency of a note.
* @param name the note name, eg: Note.C
*/
//% weight=1 help=music/note-frequency
//% blockId=device_note block="%note"
//% shim=TD_ID color="#FFFFFF" colorSecondary="#FFFFFF"
//% note.fieldEditor="note" note.defl="262"
//% note.fieldOptions.editorColour="#FF1493" note.fieldOptions.decompileLiterals=true
//% note.fieldOptions.minNote=52 note.fieldOptions.maxNote=75
//% useEnumVal=1
//% weight=10 blockGap=8
export function noteFrequency(name: Note): number {
//TODO fill in actual min/max note values
return name;
}
}

View File

@ -8,6 +8,9 @@
"shims.d.ts",
"melodies.ts",
"music.ts",
"piano.ts",
"sounds.jres",
"sounds.ts",
"ns.ts"
],
"testFiles": [

17
libs/music/shims.d.ts vendored
View File

@ -24,6 +24,23 @@ declare namespace music {
//% blockNamespace=music
//% weight=76 blockGap=8 shim=music::playTone
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.

134
libs/music/sounds.jres Normal file

File diff suppressed because one or more lines are too long

280
libs/music/sounds.ts Normal file
View File

@ -0,0 +1,280 @@
namespace sounds {
//% fixedInstance jres block="Animals cat purr"
export const animalsCatPurr = music.fromWAV(hex``);
//% fixedInstance jres block="Animals dog bark 1"
export const animalsDogBark1 = music.fromWAV(hex``);
//% fixedInstance jres block="Animals dog bark 2"
export const animalsDogBark2 = music.fromWAV(hex``);
//% fixedInstance jres block="Animals dog growl"
export const animalsDogGrowl = music.fromWAV(hex``);
//% fixedInstance jres block="Animals dog sniff"
export const animalsDogSniff = music.fromWAV(hex``);
//% fixedInstance jres block="Animals dog whine"
export const animalsDogWhine = music.fromWAV(hex``);
//% fixedInstance jres block="Animals elephant call"
export const animalsElephantCall = music.fromWAV(hex``);
//% fixedInstance jres block="Animals insect buzz1"
export const animalsInsectBuzz1 = music.fromWAV(hex``);
//% fixedInstance jres block="Animals insect buzz2"
export const animalsInsectBuzz2 = music.fromWAV(hex``);
//% fixedInstance jres block="Animals insect chirp"
export const animalsInsectChirp = music.fromWAV(hex``);
//% fixedInstance jres block="Animals snake hiss"
export const animalsSnakeHiss = music.fromWAV(hex``);
//% fixedInstance jres block="Animals snake rattle"
export const animalsSnakeRattle = music.fromWAV(hex``);
//% fixedInstance jres block="Animals trex roar"
export const animalsTRexRoar = music.fromWAV(hex``);
//% fixedInstance jres block="Colors black"
export const colorsBlack = music.fromWAV(hex``);
//% fixedInstance jres block="Colors blue"
export const colorsBlue = music.fromWAV(hex``);
//% fixedInstance jres block="Colors brown"
export const colorsBrown = music.fromWAV(hex``);
//% fixedInstance jres block="Colors green"
export const colorsGreen = music.fromWAV(hex``);
//% fixedInstance jres block="Colors red"
export const colorsRed = music.fromWAV(hex``);
//% fixedInstance jres block="Colors white"
export const colorsWhite = music.fromWAV(hex``);
//% fixedInstance jres block="Colors yellow"
export const colorsYellow = music.fromWAV(hex``);
//% fixedInstance jres block="Communication bravo"
export const communicationBravo = music.fromWAV(hex``);
//% fixedInstance jres block="Communication ev3"
export const communicationEv3 = music.fromWAV(hex``);
//% fixedInstance jres block="Communication fantastic"
export const communicationFantastic = music.fromWAV(hex``);
//% fixedInstance jres block="Communication game over"
export const communicationGameOver = music.fromWAV(hex``);
//% fixedInstance jres block="Communication go"
export const communicationGo = music.fromWAV(hex``);
//% fixedInstance jres block="communicationGoodJob"
export const communicationGoodJob = music.fromWAV(hex``);
//% fixedInstance jres block="communication good"
export const communicationGood = music.fromWAV(hex``);
//% fixedInstance jres block="communication goodbye"
export const communicationGoodbye = music.fromWAV(hex``);
//% fixedInstance jres block="communication hello"
export const communicationHello = music.fromWAV(hex``);
//% fixedInstance jres block="communication hi"
export const communicationHi = music.fromWAV(hex``);
//% fixedInstance jres block="communication lego"
export const communicationLego = music.fromWAV(hex``);
//% fixedInstance jres block="communication mindstorms"
export const communicationMindstorms = music.fromWAV(hex``);
//% fixedInstance jres block="communication morning"
export const communicationMorning = music.fromWAV(hex``);
//% fixedInstance jres block="communication no"
export const communicationNo = music.fromWAV(hex``);
//% fixedInstance jres block="communication okay"
export const communicationOkay = music.fromWAV(hex``);
//% fixedInstance jres block="communication okey dokey"
export const communicationOkeyDokey = music.fromWAV(hex``);
//% fixedInstance jres block="communication sorry"
export const communicationSorry = music.fromWAV(hex``);
//% fixedInstance jres block="communication thank you"
export const communicationThankYou = music.fromWAV(hex``);
//% fixedInstance jres block="communication yes"
export const communicationYes = music.fromWAV(hex``);
//% fixedInstance jres block="expressions boing"
export const expressionsBoing = music.fromWAV(hex``);
//% fixedInstance jres block="expressions boo"
export const expressionsBoo = music.fromWAV(hex``);
//% fixedInstance jres block="expressions cheering"
export const expressionsCheering = music.fromWAV(hex``);
//% fixedInstance jres block="expressions crunching"
export const expressionsCrunching = music.fromWAV(hex``);
//% fixedInstance jres block="expressions crying"
export const expressionsCrying = music.fromWAV(hex``);
//% fixedInstance jres block="expressions fanfare"
export const expressionsFanfare = music.fromWAV(hex``);
//% fixedInstance jres block="expressions kung fu"
export const expressionsKungFu = music.fromWAV(hex``);
//% fixedInstance jres block="expressions laughing1"
export const expressionsLaughing1 = music.fromWAV(hex``);
//% fixedInstance jres block="expressions laughing2"
export const expressionsLaughing2 = music.fromWAV(hex``);
//% fixedInstance jres block="expressions magic wand"
export const expressionsMagicWand = music.fromWAV(hex``);
//% fixedInstance jres block="expressions ouch"
export const expressionsOuch = music.fromWAV(hex``);
//% fixedInstance jres block="expressions shouting"
export const expressionsShouting = music.fromWAV(hex``);
//% fixedInstance jres block="expressions smack"
export const expressionsSmack = music.fromWAV(hex``);
//% fixedInstance jres block="expressions sneezing"
export const expressionsSneezing = music.fromWAV(hex``);
//% fixedInstance jres block="expressions snoring"
export const expressionsSnoring = music.fromWAV(hex``);
//% fixedInstance jres block="expressions uh oh"
export const expressionsUhOh = music.fromWAV(hex``);
//% fixedInstance jres block="information activate"
export const informationActivate = music.fromWAV(hex``);
//% fixedInstance jres block="information analyze"
export const informationAnalyze = music.fromWAV(hex``);
//% fixedInstance jres block="information backwards"
export const informationBackwards = music.fromWAV(hex``);
//% fixedInstance jres block="information color"
export const informationColor = music.fromWAV(hex``);
//% fixedInstance jres block="information detected"
export const informationDetected = music.fromWAV(hex``);
//% fixedInstance jres block="information down"
export const informationDown = music.fromWAV(hex``);
//% fixedInstance jres block="information error alarm"
export const informationErrorAlarm = music.fromWAV(hex``);
//% fixedInstance jres block="information error"
export const informationError = music.fromWAV(hex``);
//% fixedInstance jres block="information flashing"
export const informationFlashing = music.fromWAV(hex``);
//% fixedInstance jres block="information forward"
export const informationForward = music.fromWAV(hex``);
//% fixedInstance jres block="information left"
export const informationLeft = music.fromWAV(hex``);
//% fixedInstance jres block="information object"
export const informationObject = music.fromWAV(hex``);
//% fixedInstance jres block="information right"
export const informationRight = music.fromWAV(hex``);
//% fixedInstance jres block="information searching"
export const informationSearching = music.fromWAV(hex``);
//% fixedInstance jres block="information start"
export const informationStart = music.fromWAV(hex``);
//% fixedInstance jres block="information stop"
export const informationStop = music.fromWAV(hex``);
//% fixedInstance jres block="information touch"
export const informationTouch = music.fromWAV(hex``);
//% fixedInstance jres block="information turn"
export const informationTurn = music.fromWAV(hex``);
//% fixedInstance jres block="information up"
export const informationUp = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical air release"
export const mechanicalAirRelease = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical airbrake"
export const mechanicalAirbrake = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical backing alert"
export const mechanicalBackingAlert = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical blip1"
export const mechanicalBlip1 = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical blip2"
export const mechanicalBlip2 = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical blip3"
export const mechanicalBlip3 = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical blip4"
export const mechanicalBlip4 = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical horn1"
export const mechanicalHorn1 = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical horn2"
export const mechanicalHorn2 = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical laser"
export const mechanicalLaser = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical motor idle"
export const mechanicalMotorIdle = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical motor start"
export const mechanicalMotorStart = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical motor stop"
export const mechanicalMotorStop = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical ratchet"
export const mechanicalRatchet = music.fromWAV(hex``);
//% fixedInstance jres block='"mechanical sonar"'
export const mechanicalSonar = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical tick tack"
export const mechanicalTickTack = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical walk"
export const mechanicalWalk = music.fromWAV(hex``);
//% fixedInstance jres block="movements arm1"
export const movementsArm1 = music.fromWAV(hex``);
//% fixedInstance jres block="movements arm2"
export const movementsArm2 = music.fromWAV(hex``);
//% fixedInstance jres block="movements arm3"
export const movementsArm3 = music.fromWAV(hex``);
//% fixedInstance jres block="movements arm4"
export const movementsArm4 = music.fromWAV(hex``);
//% fixedInstance jres block="movements drop load"
export const movementsDropLoad = music.fromWAV(hex``);
//% fixedInstance jres block="movements lift load"
export const movementsLiftLoad = music.fromWAV(hex``);
//% fixedInstance jres block="movements servo1"
export const movementsServo1 = music.fromWAV(hex``);
//% fixedInstance jres block="movements servo2"
export const movementsServo2 = music.fromWAV(hex``);
//% fixedInstance jres block="movements servo3"
export const movementsServo3 = music.fromWAV(hex``);
//% fixedInstance jres block="movements servo4"
export const movementsServo4 = music.fromWAV(hex``);
//% fixedInstance jres block="movements slide load"
export const movementsSlideLoad = music.fromWAV(hex``);
//% fixedInstance jres block="movements snap"
export const movementsSnap = music.fromWAV(hex``);
//% fixedInstance jres block="movements speed down"
export const movementsSpeedDown = music.fromWAV(hex``);
//% fixedInstance jres block="movements speed idle"
export const movementsSpeedIdle = music.fromWAV(hex``);
//% fixedInstance jres block="movements speed up"
export const movementsSpeedUp = music.fromWAV(hex``);
//% fixedInstance jres block="movements speeding"
export const movementsSpeeding = music.fromWAV(hex``);
//% fixedInstance jres block="numbers eight"
export const numbersEight = music.fromWAV(hex``);
//% fixedInstance jres block="numbers five"
export const numbersFive = music.fromWAV(hex``);
//% fixedInstance jres block="numbers four"
export const numbersFour = music.fromWAV(hex``);
//% fixedInstance jres block="numbers nine"
export const numbersNine = music.fromWAV(hex``);
//% fixedInstance jres block="numbers one"
export const numbersOne = music.fromWAV(hex``);
//% fixedInstance jres block="numbers seven"
export const numbersSeven = music.fromWAV(hex``);
//% fixedInstance jres block="numbers six"
export const numbersSix = music.fromWAV(hex``);
//% fixedInstance jres block="numbers ten"
export const numbersTen = music.fromWAV(hex``);
//% fixedInstance jres block="numbers three"
export const numbersThree = music.fromWAV(hex``);
//% fixedInstance jres block="numbers two"
export const numbersTwo = music.fromWAV(hex``);
//% fixedInstance jres block="numbers zero"
export const numbersZero = music.fromWAV(hex``);
//% fixedInstance jres block="system click"
export const systemClick = music.fromWAV(hex``);
//% fixedInstance jres block="system confirm"
export const systemConfirm = music.fromWAV(hex``);
//% fixedInstance jres block="system connect"
export const systemConnect = music.fromWAV(hex``);
//% fixedInstance jres block="system download"
export const systemDownload = music.fromWAV(hex``);
//% fixedInstance jres block="system general alert"
export const systemGeneralAlert = music.fromWAV(hex``);
//% fixedInstance jres block="system overpower"
export const systemOverpower = music.fromWAV(hex``);
//% fixedInstance jres block="system power down"
export const systemPowerDown = music.fromWAV(hex``);
//% fixedInstance jres block="system ready"
export const systemReady = music.fromWAV(hex``);
//% fixedInstance jres block=S
export const systemStartUp = music.fromWAV(hex``);
}
namespace music {
/**
* Plays a sound
* @param sound the sound to play
*/
//% blockId=music_play_sound_effect block="play %sound"
//% weight=98
export function playSoundEffect(sound: Sound) {
if (!sound) return;
sound.play();
}
/**
* A sound
* @param sound the sound
*/
//% blockId=music_sound_picker block="%sound" shim=TD_ID
//% weight=0 blockHidden=1
export function _soundPicker(sound: Sound): Sound {
return sound;
}
}

View File

@ -0,0 +1,3 @@
# Touch sensor
The library to interact with the Touch Sensor.

View File

@ -0,0 +1,8 @@
{
"TouchSensorEvent": "Touch sensor interactions",
"sensors.TouchSensor.isPressed": "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.TouchSensor.waitUntil": "Wait until the touch sensor is touched",
"sensors.TouchSensor.wasPressed": "Check if touch sensor is touched since it was last checked."
}

View File

@ -0,0 +1,15 @@
{
"TouchSensorEvent.Bumped|block": "bumped",
"TouchSensorEvent.Pressed|block": "pressed",
"TouchSensorEvent.Released|block": "released",
"sensors.TouchSensor.isPressed|block": "`icons.touchSensor` %sensor|is pressed",
"sensors.TouchSensor.onEvent|block": "on `icons.touchSensor` %sensor|%event",
"sensors.TouchSensor.waitUntil|block": "wait until `icons.touchSensor` %sensor|%event",
"sensors.TouchSensor.wasPressed|block": "`icons.touchSensor` %sensor|was pressed",
"sensors.touchSensor1|block": "1",
"sensors.touchSensor2|block": "2",
"sensors.touchSensor3|block": "3",
"sensors.touchSensor4|block": "4",
"{id:category}Sensors": "Sensors",
"{id:group}Touch Sensor": "Touch Sensor"
}

View File

@ -0,0 +1,15 @@
# Touch Sensor
```cards
sensors.touchSensor1.onEvent(TouchSensorEvent.Pressed, function () {
brick.showImage(images.expressionsBigSmile)
})
sensors.touchSensor1.isPressed();
sensors.touchSensor1.wasPressed();
```
## See Also
[on Event](/reference/sensors/touch-sensor/on-event),
[is pressed](reference/sensors/touch-sensor/is-pressed),
[was pressed](reference/sensors/touch-sensor/was-pressed)

View File

@ -0,0 +1,11 @@
# is Pressed
```blocks
loops.forever(function () {
if (sensors.touchSensor1.isPressed()) {
brick.setStatusLight(LightsPattern.Green)
} else {
brick.setStatusLight(LightsPattern.Orange)
}
})
```

View File

@ -0,0 +1,16 @@
# On Event
```sig
sensors.touchSensor1.onEvent(TouchSensorEvent.Released, function () { })
```
# Parameters
## Examples
```blocks
sensors.touchSensor1.onEvent(TouchSensorEvent.Released, function () {
brick.showImage(images.expressionsSick)
})
```

View File

@ -0,0 +1,11 @@
# was Pressed
```blocks
loops.forever(function () {
if (sensors.touchSensor1.wasPressed()) {
brick.setStatusLight(LightsPattern.Green)
} else {
brick.setStatusLight(LightsPattern.Orange)
}
})
```

View File

@ -0,0 +1,15 @@
{
"name": "touch-sensor",
"description": "Touch Sensor support",
"files": [
"README.md",
"touch.ts"
],
"testFiles": [
"test.ts"
],
"public": true,
"dependencies": {
"core": "file:../core"
}
}

View File

@ -0,0 +1,8 @@
sensors.touchSensor1.onEvent(TouchSensorEvent.Pressed, function () {
})
sensors.touchSensor2.onEvent(TouchSensorEvent.Bumped, function () {
})
sensors.touchSensor3.onEvent(TouchSensorEvent.Released, function () {
})
sensors.touchSensor4.isPressed();
sensors.touchSensor4.wasPressed();

108
libs/touch-sensor/touch.ts Normal file
View File

@ -0,0 +1,108 @@
// keep TouchSensorEvent in sync with ButtonEvent
/**
* Touch sensor interactions
*/
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() {
return this._readPin6() > 2500 ? 1 : 0
}
_update(prev: number, curr: number) {
this.button._update(curr > 0)
}
_deviceType() {
return DAL.DEVICE_TYPE_TOUCH
}
/**
* 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-sensor/on-event
//% blockId=touchEvent block="on `icons.touchSensor` %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)
}
/**
* Wait until the 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
*/
//% help=input/touch-sensor/wait-until
//% blockId=touchWaitUntil block="wait until `icons.touchSensor` %sensor|%event"
//% parts="touch"
//% blockNamespace=sensors
//% weight=98 blockGap=8
//% group="Touch Sensor"
waitUntil(ev: TouchSensorEvent) {
this.button.waitUntil(<ButtonEvent><number>ev);
}
/**
* Check if touch sensor is touched.
* @param sensor the port to query the request
*/
//% help=input/touch-sensor/is-pressed
//% block="`icons.touchSensor` %sensor|is pressed"
//% blockId=touchIsPressed
//% parts="touch"
//% blockNamespace=sensors
//% weight=81 blockGap=8
//% group="Touch Sensor"
isPressed() {
return this.button.isPressed();
}
/**
* Check if touch sensor is touched since it was last checked.
* @param sensor the port to query the request
*/
//% help=input/touch-sensor/was-pressed
//% block="`icons.touchSensor` %sensor|was pressed"
//% blockId=touchWasPressed
//% parts="touch"
//% blockNamespace=sensors
//% weight=81 blockGap=8
//% group="Touch Sensor"
wasPressed() {
return this.button.wasPressed();
}
}
//% whenUsed block="1" weight=95 fixedInstance
export const touchSensor1: TouchSensor = new TouchSensor(1)
//% whenUsed block="2" weight=95 fixedInstance
export const touchSensor2: TouchSensor = new TouchSensor(2)
//% whenUsed block="3" weight=95 fixedInstance
export const touchSensor3: TouchSensor = new TouchSensor(3)
//% whenUsed block="4" weight=95 fixedInstance
export const touchSensor4: TouchSensor = new TouchSensor(4)
}

View File

@ -0,0 +1,3 @@
# Ultrasonic sensor
The library to interact with the Ultrasonic Sensor.

View File

@ -0,0 +1,6 @@
{
"sensors.UltraSonicSensor.distance": "Gets the distance from the sonar in centimeters",
"sensors.UltraSonicSensor.onEvent": "Registers code to run when the given color is close",
"sensors.UltraSonicSensor.onEvent|param|handler": "the code to run when detected",
"sensors.UltraSonicSensor.waitUntil": "Waits for the event to occur"
}

View File

@ -0,0 +1,14 @@
{
"UltrasonicSensorEvent.ObjectDetected|block": "object detected",
"UltrasonicSensorEvent.ObjectFar|block": "object far",
"UltrasonicSensorEvent.ObjectNear|block": "object near",
"sensors.UltraSonicSensor.distance|block": "`icons.ultrasonicSensor` %sensor|distance",
"sensors.UltraSonicSensor.onEvent|block": "on `icons.ultrasonicSensor` %sensor|%event",
"sensors.UltraSonicSensor.waitUntil|block": "wait until `icons.ultrasonicSensor` %sensor| %event",
"sensors.ultrasonic1|block": "1",
"sensors.ultrasonic2|block": "2",
"sensors.ultrasonic3|block": "3",
"sensors.ultrasonic4|block": "4",
"{id:category}Sensors": "Sensors",
"{id:group}Ultrasonic Sensor": "Ultrasonic Sensor"
}

View File

@ -0,0 +1,15 @@
{
"name": "ultrasonic-sensor",
"description": "Ultrasonic Sensor support",
"files": [
"README.md",
"ultrasonic.ts"
],
"testFiles": [
"test.ts"
],
"public": true,
"dependencies": {
"core": "file:../core"
}
}

View File

View File

@ -0,0 +1,98 @@
enum UltrasonicSensorEvent {
//% block="object detected"
ObjectDetected = 10,
//% block="object near"
ObjectNear = sensors.internal.ThresholdState.Low,
//% block="object far"
ObjectFar = sensors.internal.ThresholdState.High
}
namespace sensors {
//% fixedInstances
export class UltraSonicSensor extends internal.UartSensor {
private promixityThreshold: sensors.internal.ThresholdDetector;
private movementThreshold: number;
constructor(port: number) {
super(port)
this.promixityThreshold = new sensors.internal.ThresholdDetector(this.id(), 0, 255, 10, 100); // range is 0..255cm
this.movementThreshold = 1;
}
_deviceType() {
return DAL.DEVICE_TYPE_ULTRASONIC
}
_query(): number {
return this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff;
}
_update(prev: number, curr: number) {
// is there an object near?
this.promixityThreshold.setLevel(curr);
// did something change?
if (Math.abs(prev - curr) > this.movementThreshold)
control.raiseEvent(this._id, UltrasonicSensorEvent.ObjectDetected);
}
/**
* Registers code to run when the given color is close
* @param handler the code to run when detected
*/
//% help=input/ultrasonic/on
//% blockId=ultrasonicOn
//% block="on `icons.ultrasonicSensor` %sensor|%event"
//% parts="ultrasonicsensor"
//% blockNamespace=sensors
//% weight=100 blockGap=8
//% group="Ultrasonic Sensor"
onEvent(event: UltrasonicSensorEvent, handler: () => void) {
control.onEvent(this._id, event, handler);
}
/**
* Waits for the event to occur
*/
//% help=input/ultrasonic/wait
//% block="wait until `icons.ultrasonicSensor` %sensor| %event"
//% blockId=ultrasonicWait
//% parts="ultrasonicsensor"
//% blockNamespace=sensors
//% weight=99 blockGap=8
//% group="Ultrasonic Sensor"
waitUntil(event: UltrasonicSensorEvent) {
control.waitForEvent(this._id, event);
}
/**
* Gets the distance from the sonar in centimeters
* @param sensor the ultrasonic sensor port
*/
//% help=input/ultrasonic/distance
//% block="`icons.ultrasonicSensor` %sensor|distance"
//% blockId=sonarGetDistance
//% parts="ultrasonicsensor"
//% blockNamespace=sensors
//% weight=65 blockGap=8
//% group="Ultrasonic Sensor"
distance(): number {
// it supposedly also has an inch mode, but we stick to cm
this._setMode(0)
return this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff; // range is 0..255
}
}
//% fixedInstance whenUsed block="1"
export const ultrasonic1: UltraSonicSensor = new UltraSonicSensor(1)
//% fixedInstance whenUsed block="4"
export const ultrasonic4: UltraSonicSensor = new UltraSonicSensor(4)
//% fixedInstance whenUsed block="2"
export const ultrasonic2: UltraSonicSensor = new UltraSonicSensor(2)
//% fixedInstance whenUsed block="3"
export const ultrasonic3: UltraSonicSensor = new UltraSonicSensor(3)
}

View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.26",
"version": "0.0.35",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true,
"keywords": [
@ -39,8 +39,8 @@
"semantic-ui-less": "^2.2.4"
},
"dependencies": {
"pxt-common-packages": "0.11.7",
"pxt-core": "2.3.4"
"pxt-common-packages": "0.14.5",
"pxt-core": "2.3.31"
},
"scripts": {
"test": "node node_modules/pxt-core/built/pxt.js travis"

View File

@ -10,6 +10,10 @@
"libs/base",
"libs/core",
"libs/music",
"libs/color-sensor",
"libs/touch-sensor",
"libs/ultrasonic-sensor",
"libs/infrared-sensor",
"libs/ev3"
],
"simulator": {
@ -62,7 +66,7 @@
},
"compileService": {
"buildEngine": "dockermake",
"dockerImage": "pext/ev3",
"dockerImage": "pext/ev3:zlib",
"serviceId": "ev3"
},
"appTheme": {

View File

@ -137,18 +137,34 @@ function compressImg(fn) {
let sz = 0
let bf
let out = {}
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)
out[bn] = "data:image/png;base64," + bf.toString("base64")
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")

View File

@ -1,46 +1,75 @@
#!/usr/bin/env node
const fs = require("fs")
const rsf = fs.readFileSync(process.argv[2])
const fmt = rsf.readInt16BE(0) // 0x0100
if (fmt != 0x0100) {
throw new Error("Invalid input format: " + fmt)
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
}
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)
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`
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)
fs.writeFileSync("out.json", JSON.stringify(out, null, 4))
fs.writeFileSync("out.ts", ts)
fs.writeFileSync("out.wav", bf)
const wav = Buffer.concat([wavHd, rsf.slice(8)])
console.log("writing out.wav; " + samplerate + "Hz")
fs.writeFileSync("out.wav", wav)
if (require("os").platform() == "darwin")
require('child_process').execSync("afplay out.wav")
//if (require("os").platform() == "darwin")
// require('child_process').execSync("afplay out.wav")
// TODO also play on Windows

375
sim/inflate.ts Normal file
View 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;
}

View File

@ -90,36 +90,172 @@ namespace pxsim.screen {
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 = [
0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff,
]
export function isValidIcon(buf: RefBuffer) {
return buf.data.length >= 3 && buf.data[0] == 0xf0;
export function buffer(buf: RefBuffer) {
return incr(buf)
}
function PIX2BYTES(x: number) {
return ((x + 7) >> 3)
export function width(buf: RefBuffer) {
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;
if (!isValidIcon(buf))
if (!screen.isValidImage(buf))
return;
if (mode & (Draw.Double | Draw.Quad)) {
buf = doubleIcon(buf);
buf = doubled(buf);
if (mode & Draw.Quad) {
let pbuf = buf;
buf = doubleIcon(buf);
buf = doubled(buf);
decr(pbuf);
}
}
let pixwidth = buf.data[1];
let ptr = 2;
const bytewidth = PIX2BYTES(pixwidth);
const bytewidth = screen.PIX2BYTES(pixwidth);
pixwidth = Math.min(pixwidth, visuals.SCREEN_WIDTH);
while (ptr + bytewidth <= buf.data.length) {
if (mode & (Draw.Clear | Draw.Xor | Draw.Transparent)) {
@ -132,24 +268,19 @@ namespace pxsim.screen {
ptr += bytewidth;
}
if (mode & Draw.Double)
if (mode & (Draw.Double | Draw.Quad))
decr(buf);
}
export function clear(): void {
const screenState = (board() as DalBoard).screenState;
screenState.clear()
}
export function doubleIcon(buf: RefBuffer): RefBuffer {
if (!isValidIcon(buf))
export function doubled(buf: RefBuffer): RefBuffer {
if (!screen.isValidImage(buf))
return null;
const w = buf.data[1];
if (w > 126)
return null;
const bw = PIX2BYTES(w);
const bw = screen.PIX2BYTES(w);
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)
out.data[0] = 0xf0;
out.data[1] = w * 2;
@ -169,7 +300,5 @@ namespace pxsim.screen {
return out;
}
export function dump() {
// do we need it?
}
}
}

33
sim/state/sounds.ts Normal file
View 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()
})
}
}

View File

@ -1,3 +1,5 @@
/*******************************
User Variable Overrides
*******************************/
@exampleCardFullHeight: true;

View File

@ -43,10 +43,10 @@
}
/* Menu bar colors */
#menubar .ui.item {
.menubar .ui.item {
color: #373737;
}
.fullscreensim #menubar .ui.menu {
.fullscreensim .menubar .ui.menu {
box-shadow: none !important;
border-bottom: 0 !important;
}
@ -57,17 +57,17 @@
}
/* Editor menu toggle */
#menubar .ui.menu.fixed .item.editor-menuitem .ui.grid {
.menubar .ui.menu.fixed .item.editor-menuitem .ui.grid {
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;
}
#menubar .ui.menu.fixed .ui.item.editor-menuitem .item {
.menubar .ui.menu.fixed .ui.item.editor-menuitem .item {
border-radius: 0px !important;
}
#menubar .ui.menu.fixed .ui.item.editor-menuitem .item.active {
.menubar .ui.menu.fixed .ui.item.editor-menuitem .item.active {
color: @blue;
}

View File

@ -39,38 +39,38 @@
@step : 'pxt';
/* Collections */
@breadcrumb : 'default';
@form : 'default';
@breadcrumb : 'pxt';
@form : 'pxt';
@grid : 'pxt';
@menu : 'pxt';
@message : 'default';
@table : 'default';
@message : 'pxt';
@table : 'pxt';
/* Modules */
@accordion : 'default';
@checkbox : 'default';
@accordion : 'pxt';
@checkbox : 'pxt';
@dimmer : 'pxt';
@dropdown : 'default';
@embed : 'default';
@dropdown : 'pxt';
@embed : 'pxt';
@modal : 'pxt';
@nag : 'default';
@popup : 'default';
@progress : 'default';
@rating : 'default';
@search : 'default';
@shape : 'default';
@sidebar : 'default';
@sticky : 'default';
@tab : 'default';
@nag : 'pxt';
@popup : 'pxt';
@progress : 'pxt';
@rating : 'pxt';
@search : 'pxt';
@shape : 'pxt';
@sidebar : 'pxt';
@sticky : 'pxt';
@tab : 'pxt';
@transition : 'default';
/* Views */
@ad : 'default';
@ad : 'pxt';
@card : 'pxt';
@comment : 'default';
@feed : 'default';
@item : 'default';
@statistic : 'default';
@comment : 'pxt';
@feed : 'pxt';
@item : 'pxt';
@statistic : 'pxt';
/*******************************
Folders